40template <
typename SampleType =
float>
47 virtual void loop_process(
const SampleType*
const* in, SampleType*
const* out,
54template <
typename SampleType =
float>
67 void prepare(
double sample_rate,
const Config& config) {
68 assert(sample_rate > 0.0 && config.channels >= 1);
70 sample_rate_ = sample_rate;
72 while (ring_size_ <
static_cast<int>(config.max_delay_seconds * sample_rate) + 8)
74 ring_mask_ = ring_size_ - 1;
75 ring_.assign(
static_cast<size_t>(config.channels) * ring_size_, SampleType{0});
76 chunk_in_.assign(
static_cast<size_t>(config.channels) * kMaxChunk, SampleType{0});
77 chunk_out_.assign(
static_cast<size_t>(config.channels) * kMaxChunk, SampleType{0});
78 in_ptrs_.resize(
static_cast<size_t>(config.channels));
79 out_ptrs_.resize(
static_cast<size_t>(config.channels));
81 gate_coeff_ = std::exp(-1.0 / (0.05 * sample_rate));
82 delay_coeff_ = std::exp(-1.0 / (0.03 * sample_rate));
87 std::fill(ring_.begin(), ring_.end(), SampleType{0});
89 delay_current_ = delay_target_;
94 delay_target_ = std::clamp(
static_cast<double>(ms) * 0.001 * sample_rate_,
96 static_cast<double>(ring_size_ - 4));
98 if (write_pos_ == 0) delay_current_ = delay_target_;
103 assert(tempo_bpm > 0.0 && beats > 0.0);
104 set_delay_ms(
static_cast<float>(beats * 60000.0 / tempo_bpm));
109 feedback_ = std::clamp(
static_cast<SampleType
>(
feedback),
110 SampleType{0},
static_cast<SampleType
>(0.99));
116 delay_target_ = std::max(delay_target_,
static_cast<double>(
min_delay_samples()));
121 return (loop_ !=
nullptr ? loop_->loop_latency_samples() : 0) + 1;
124 void process(
const SampleType*
const* in, SampleType*
const* out,
int num_samples) {
125 assert(num_samples <= config_.max_block);
127 while (done < num_samples) {
130 const int recursion = std::max(
133 std::min({num_samples - done, recursion, kMaxChunk});
134 process_chunk(in, out, done, chunk);
140 void process_chunk(
const SampleType*
const* in, SampleType*
const* out,
int offset,
int n) {
141 const bool frozen = loop_ !=
nullptr && loop_->loop_is_frozen();
142 const double gate_target = frozen ? 0.0 : 1.0;
143 const int read_latency = loop_ !=
nullptr ? loop_->loop_latency_samples() : 0;
146 for (
int ch = 0; ch < config_.
channels; ++ch) {
147 in_ptrs_[
static_cast<size_t>(ch)] =
148 chunk_in_.data() +
static_cast<size_t>(ch) * kMaxChunk;
149 out_ptrs_[
static_cast<size_t>(ch)] =
150 chunk_out_.data() +
static_cast<size_t>(ch) * kMaxChunk;
152 double d = delay_current_;
153 for (
int i = 0;
i < n; ++
i) {
154 d += (1.0 - delay_coeff_) * (delay_target_ - d);
155 const double read_at =
static_cast<double>(write_pos_ +
i)
156 - (d -
static_cast<double>(read_latency));
157 const auto i0 =
static_cast<std::int64_t
>(std::floor(read_at));
158 const SampleType frac =
static_cast<SampleType
>(read_at -
static_cast<double>(i0));
159 for (
int ch = 0; ch < config_.
channels; ++ch) {
160 const SampleType* ring = ring_.data() +
static_cast<size_t>(ch) * ring_size_;
161 const SampleType
a = ring[
static_cast<size_t>(i0 & ring_mask_)];
162 const SampleType b = ring[
static_cast<size_t>((i0 + 1) & ring_mask_)];
163 in_ptrs_[
static_cast<size_t>(ch)][
i] =
a + frac * (b -
a);
169 if (loop_ !=
nullptr) {
171 const_cast<const SampleType* const*
>(in_ptrs_.data()),
172 out_ptrs_.data(), n);
174 for (
int ch = 0; ch < config_.channels; ++ch)
175 std::copy(in_ptrs_[
static_cast<size_t>(ch)],
176 in_ptrs_[
static_cast<size_t>(ch)] + n,
177 out_ptrs_[
static_cast<size_t>(ch)]);
181 for (
int i = 0;
i <
n; ++
i) {
182 gate_ += (1.0 - gate_coeff_) * (gate_target - gate_);
183 const SampleType
g = feedback_ *
static_cast<SampleType
>(
184 std::sin(gate_ * 1.5707963));
185 for (
int ch = 0; ch < config_.channels; ++ch) {
186 const SampleType wet = out_ptrs_[
static_cast<size_t>(ch)][
i];
187 SampleType*
ring = ring_.data() +
static_cast<size_t>(ch) * ring_size_;
188 ring[
static_cast<size_t>((write_pos_ +
i) & ring_mask_)] =
189 in[ch][offset +
i] + g * wet;
190 out[ch][offset +
i] = wet;
196 static constexpr int kMaxChunk = 256;
199 double sample_rate_ = 48000.0;
203 std::vector<SampleType> ring_;
204 std::vector<SampleType> chunk_in_;
205 std::vector<SampleType> chunk_out_;
206 std::vector<SampleType*> in_ptrs_;
207 std::vector<SampleType*> out_ptrs_;
209 FeedbackLoopProcessorT<SampleType>* loop_ =
nullptr;
210 SampleType feedback_ = SampleType{0};
211 double delay_target_ = 24000.0;
212 double delay_current_ = 24000.0;
214 double gate_coeff_ = 0.0;
215 double delay_coeff_ = 0.0;
216 std::int64_t write_pos_ = 0;
Interface for a processor placed inside the feedback loop.
Definition pitched_feedback_delay.hpp:41
virtual int loop_latency_samples() const =0
virtual void loop_process(const SampleType *const *in, SampleType *const *out, int num_samples)=0
Equal-length, RT-safe block processing.
virtual bool loop_is_frozen() const
Definition pitched_feedback_delay.hpp:45
virtual ~FeedbackLoopProcessorT()=default
Definition pitched_feedback_delay.hpp:55
int min_delay_samples() const
Minimum legal delay: the in-loop processor's latency plus one.
Definition pitched_feedback_delay.hpp:120
void process(const SampleType *const *in, SampleType *const *out, int num_samples)
Definition pitched_feedback_delay.hpp:124
void prepare(double sample_rate, const Config &config)
RT contract: prepare() allocates ring/chunk storage and is not audio-thread safe.
Definition pitched_feedback_delay.hpp:67
void set_feedback(float feedback)
Clamped below unity for stability.
Definition pitched_feedback_delay.hpp:108
void set_loop_processor(FeedbackLoopProcessorT< SampleType > *processor)
Definition pitched_feedback_delay.hpp:113
void set_delay_sync(double tempo_bpm, double beats)
Tempo-synced delay: beats at tempo_bpm (e.g. 0.5 = eighth note).
Definition pitched_feedback_delay.hpp:102
void reset()
Definition pitched_feedback_delay.hpp:86
void set_delay_ms(float ms)
Definition pitched_feedback_delay.hpp:93
@ d
Definition pitch.hpp:22
@ g
Definition pitch.hpp:27
@ ring
Definition string.hpp:18
Definition additive_bank.hpp:218
@ feedback
Definition comb_filter.hpp:18
FeedbackLoopProcessorT< float > FeedbackLoopProcessor
Definition pitched_feedback_delay.hpp:51
FeedbackLoopProcessorT< double > FeedbackLoopProcessor64
Definition pitched_feedback_delay.hpp:52
PitchedFeedbackDelayT< float > PitchedFeedbackDelay
Definition pitched_feedback_delay.hpp:219
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
PitchedFeedbackDelayT< double > PitchedFeedbackDelay64
Definition pitched_feedback_delay.hpp:220
@ n
Definition input_events.hpp:40
Definition pitched_feedback_delay.hpp:57
int channels
Definition pitched_feedback_delay.hpp:59
float max_delay_seconds
Definition pitched_feedback_delay.hpp:58
int max_block
Definition pitched_feedback_delay.hpp:60