51 void prepare(std::size_t capacity_samples) {
53 capture_.assign(capacity_, 0.0f);
54 playback_.assign(capacity_, 0.0f);
59 std::fill(capture_.begin(), capture_.end(), 0.0f);
60 std::fill(playback_.begin(), playback_.end(), 0.0f);
70 double process(
double x,
double segment_samples,
double mod_offset_samples)
noexcept {
71 const auto requested =
static_cast<std::size_t
>(
73 static_cast<double>(capacity_)));
75 capture_[position_] =
static_cast<float>(x);
77 const double backward =
78 static_cast<double>(playback_length_ - 1u) -
static_cast<double>(position_);
79 const double read_position = std::clamp(
80 backward + mod_offset_samples, 0.0,
static_cast<double>(playback_length_ - 1u));
81 const double sample = interpolate(playback_, playback_length_, read_position);
82 const double windowed = sample * boundary_gain();
84 if (++position_ >= capture_length_) {
85 capture_.swap(playback_);
86 playback_length_ = capture_length_;
87 std::fill(capture_.begin(), capture_.end(), 0.0f);
88 capture_length_ = requested;
96 double boundary_gain() const noexcept {
98 const std::size_t from_end = capture_length_ - 1u - position_;
99 const std::size_t
nearest = std::min(position_, from_end);
100 if (
nearest >= fade)
return 1.0;
101 const double t =
static_cast<double>(
nearest) /
static_cast<double>(fade);
102 return 0.5 * (1.0 - std::cos(
kPi * t));
108 static double interpolate(
const std::vector<float>&
buffer, std::size_t length,
109 double position)
noexcept {
110 const auto base =
static_cast<long long>(std::floor(position));
111 const double frac = position -
static_cast<double>(base);
112 const auto last =
static_cast<long long>(length) - 1;
114 auto at = [&](
long long index) ->
double {
115 const long long clamped = std::clamp(index, 0LL, last);
116 return static_cast<double>(
buffer[
static_cast<std::size_t
>(clamped)]);
119 const double s0 = at(base - 1);
120 const double s1 = at(base);
121 const double s2 = at(base + 1);
122 const double s3 = at(base + 2);
124 const double d0 = frac + 1.0;
125 const double d1 = frac;
126 const double d2 = frac - 1.0;
127 const double d3 = frac - 2.0;
129 return (-d1 * d2 * d3 / 6.0) * s0 + (d0 * d2 * d3 * 0.5) * s1 +
130 (-d0 * d1 * d3 * 0.5) * s2 + (d0 * d1 * d2 / 6.0) * s3;
133 std::vector<float> capture_;
134 std::vector<float> playback_;
136 std::size_t position_ = 0;
137 std::size_t capture_length_ = 1;
138 std::size_t playback_length_ = 1;
Definition reverse.hpp:33
double process(double x, double segment_samples, double mod_offset_samples) noexcept
One sample of capture-forward / play-backward.
Definition reverse.hpp:70
static constexpr std::size_t minimum_capacity_samples() noexcept
Smallest capacity this segmenter can be prepared with.
Definition reverse.hpp:42
void reset() noexcept
Definition reverse.hpp:58
void prepare(std::size_t capacity_samples)
Allocates both buffers.
Definition reverse.hpp:51
constexpr double kPi
Definition primitives.hpp:38
constexpr int kReverseFadeSamples
Raised-cosine splice fade at each end of a reversed segment, in samples.
Definition tables.hpp:331
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
@ nearest
Snap to the nearest in-scale degree in either direction.
Definition harmony_engine.hpp:175