Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
tempo_delay.hpp
Go to the documentation of this file.
1#pragma once
2
5
8
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12#include <cstdint>
13#include <limits>
14
15namespace pulp::signal {
16
26
29 double samples = 0.0;
30
31 constexpr explicit operator bool() const noexcept {
33 }
34};
35
41inline constexpr double kMaximumTempoDelaySampleRate =
42 static_cast<double>(timebase::kMaximumCompiledSampleRate);
43
51 double beats_per_minute,
52 double sample_rate) noexcept {
53 if (!std::isfinite(beats_per_minute) || beats_per_minute < kMinimumTempoDelayBpm ||
54 beats_per_minute > kMaximumTempoDelayBpm)
56 if (!std::isfinite(sample_rate) || !(sample_rate > 0.0) ||
57 sample_rate > kMaximumTempoDelaySampleRate)
59
60 const auto fraction = timebase::beat_fraction(division);
61 if (!fraction)
63 const auto value = fraction.value();
64 if (value.numerator <= 0 || value.denominator <= 0)
66
67 const long double samples =
68 static_cast<long double>(value.numerator) * 60.0L * static_cast<long double>(sample_rate) /
69 (static_cast<long double>(value.denominator) * static_cast<long double>(beats_per_minute));
70 constexpr long double maximum = static_cast<long double>(std::numeric_limits<int>::max() - 1);
71 if (!std::isfinite(samples) || samples < 1.0L || samples > maximum)
73 return {TempoDelayError::none, static_cast<double>(samples)};
74}
75
94 public:
95 static constexpr std::uint32_t kDefaultTransitionSamples = 64;
96
97 [[nodiscard]] TempoDelayError
99 std::uint32_t transition_samples = kDefaultTransitionSamples) noexcept {
100 if (!std::isfinite(sample_rate) || !(sample_rate > 0.0) ||
103 constexpr double maximum = static_cast<double>(std::numeric_limits<int>::max() - 1);
104 if (!std::isfinite(maximum_delay_samples) || maximum_delay_samples < 1.0)
108
109 sample_rate_ = sample_rate;
110 maximum_delay_samples_ = maximum_delay_samples;
111 transition_samples_ = transition_samples;
112 current_ = 1.0;
113 start_ = 1.0;
114 target_ = 1.0;
115 remaining_ = 0;
116 initialized_ = false;
117 prepared_ = true;
119 }
120
123 [[nodiscard]] TempoDelayError set_delay_samples(double samples) noexcept {
124 if (!prepared_)
126 if (!std::isfinite(samples))
128 if (samples < 1.0 || samples > maximum_delay_samples_)
130 set_valid_target(samples);
132 }
133
137 double beats_per_minute) noexcept {
138 if (!prepared_)
140 const auto result = tempo_delay_samples(division, beats_per_minute, sample_rate_);
141 if (!result)
142 return result.error;
143 return set_delay_samples(result.samples);
144 }
145
146 [[nodiscard]] double next() noexcept {
147 if (!prepared_)
148 return 0.0;
149 if (remaining_ == 0)
150 return current_;
151 const std::uint32_t elapsed = transition_samples_ - remaining_ + 1U;
152 const double amount =
153 static_cast<double>(elapsed) / static_cast<double>(transition_samples_);
154 current_ = start_ + (target_ - start_) * amount;
155 --remaining_;
156 if (remaining_ == 0) {
157 current_ = target_;
158 start_ = target_;
159 }
160 return current_;
161 }
162
163 void render(double* output, std::size_t sample_count) noexcept {
164 if (output == nullptr)
165 return;
166 for (std::size_t i = 0; i < sample_count; ++i)
167 output[i] = next();
168 }
169
170 void reset() noexcept {
171 if (!prepared_ || !initialized_)
172 return;
173 current_ = target_;
174 start_ = target_;
175 remaining_ = 0;
176 }
177
178 [[nodiscard]] bool prepared() const noexcept {
179 return prepared_;
180 }
181 [[nodiscard]] bool initialized() const noexcept {
182 return initialized_;
183 }
184 [[nodiscard]] bool transition_active() const noexcept {
185 return remaining_ != 0;
186 }
187 [[nodiscard]] std::uint32_t remaining_transition_samples() const noexcept {
188 return remaining_;
189 }
190 [[nodiscard]] double sample_rate() const noexcept {
191 return sample_rate_;
192 }
193 [[nodiscard]] double maximum_delay_samples() const noexcept {
194 return maximum_delay_samples_;
195 }
196 [[nodiscard]] double current_delay_samples() const noexcept {
197 return prepared_ ? current_ : 0.0;
198 }
199 [[nodiscard]] double target_delay_samples() const noexcept {
200 return prepared_ ? target_ : 0.0;
201 }
202 [[nodiscard]] double conservative_delay_samples() const noexcept {
203 return prepared_ ? std::max({start_, current_, target_}) : 0.0;
204 }
205 [[nodiscard]] static constexpr int latency_samples() noexcept {
206 return 0;
207 }
208 [[nodiscard]] static constexpr int tail_samples() noexcept {
209 return 0;
210 }
211
212 private:
213 void set_valid_target(double samples) noexcept {
214 if (!initialized_) {
215 initialized_ = true;
216 current_ = samples;
217 start_ = samples;
218 target_ = samples;
219 remaining_ = 0;
220 return;
221 }
222 if (samples == target_)
223 return;
224 if (samples == current_) {
225 start_ = samples;
226 target_ = samples;
227 remaining_ = 0;
228 return;
229 }
230 start_ = current_;
231 target_ = samples;
232 remaining_ = transition_samples_;
233 if (remaining_ == 0) {
234 current_ = target_;
235 start_ = target_;
236 }
237 }
238
239 double sample_rate_ = 0.0;
240 double maximum_delay_samples_ = 0.0;
241 double start_ = 0.0;
242 double current_ = 0.0;
243 double target_ = 0.0;
244 std::uint32_t transition_samples_ = kDefaultTransitionSamples;
245 std::uint32_t remaining_ = 0;
246 bool prepared_ = false;
247 bool initialized_ = false;
248};
249
250} // namespace pulp::signal
Fixed-state controller for deterministic delay read-time modulation.
Definition tempo_delay.hpp:93
TempoDelayError prepare(double sample_rate, double maximum_delay_samples, std::uint32_t transition_samples=kDefaultTransitionSamples) noexcept
Definition tempo_delay.hpp:98
static constexpr int latency_samples() noexcept
Definition tempo_delay.hpp:205
bool initialized() const noexcept
Definition tempo_delay.hpp:181
double current_delay_samples() const noexcept
Definition tempo_delay.hpp:196
void reset() noexcept
Definition tempo_delay.hpp:170
double sample_rate() const noexcept
Definition tempo_delay.hpp:190
bool transition_active() const noexcept
Definition tempo_delay.hpp:184
double conservative_delay_samples() const noexcept
Definition tempo_delay.hpp:202
TempoDelayError set_delay_samples(double samples) noexcept
Adopt a direct fractional-sample target.
Definition tempo_delay.hpp:123
static constexpr std::uint32_t kDefaultTransitionSamples
Definition tempo_delay.hpp:95
double maximum_delay_samples() const noexcept
Definition tempo_delay.hpp:193
std::uint32_t remaining_transition_samples() const noexcept
Definition tempo_delay.hpp:187
static constexpr int tail_samples() noexcept
Definition tempo_delay.hpp:208
TempoDelayError set_tempo(timebase::BeatDivision division, double beats_per_minute) noexcept
Convert and adopt a musical target transactionally.
Definition tempo_delay.hpp:136
double target_delay_samples() const noexcept
Definition tempo_delay.hpp:199
bool prepared() const noexcept
Definition tempo_delay.hpp:178
double next() noexcept
Definition tempo_delay.hpp:146
void render(double *output, std::size_t sample_count) noexcept
Definition tempo_delay.hpp:163
Definition additive_bank.hpp:218
constexpr double kMaximumTempoDelayBpm
Definition tempo_delay.hpp:40
@ invalid_delay
Definition fractional_delay.hpp:38
@ not_prepared
Definition comb_filter.hpp:33
@ invalid_sample_rate
Definition auto_ducked_send.hpp:18
constexpr double kMaximumTempoDelaySampleRate
Definition tempo_delay.hpp:41
TempoDelayError
Definition tempo_delay.hpp:17
@ none
Definition tempo_delay.hpp:18
@ invalid_sample_rate
Definition tempo_delay.hpp:22
@ out_of_range
Definition tempo_delay.hpp:24
@ invalid_tempo
Definition tempo_delay.hpp:21
@ invalid_delay
Definition tempo_delay.hpp:23
@ invalid_division
Definition tempo_delay.hpp:20
@ not_prepared
Definition tempo_delay.hpp:19
@ maximum
Definition spectrum_trace.hpp:34
@ i
Definition formant_filter_bank.hpp:20
constexpr double kMinimumTempoDelayBpm
These bounds intentionally match TempoMap / CompiledTempoMap: a delay synchronized to a transport mus...
Definition tempo_delay.hpp:39
@ none
Definition dither.hpp:17
TempoDelayResult tempo_delay_samples(timebase::BeatDivision division, double beats_per_minute, double sample_rate) noexcept
Convert an exact persisted musical division to a fractional delay length.
Definition tempo_delay.hpp:50
@ out_of_range
Definition formant_filter_bank.hpp:27
constexpr std::uint32_t kMaximumCompiledSampleRate
Definition compiled_tempo_map.hpp:17
BeatDivision
Definition beat_division.hpp:15
runtime::Result< BeatFraction, BeatDivisionError > beat_fraction(BeatDivision division) noexcept
Definition beat_division.hpp:77
constexpr double kMinimumCompiledTempoBpm
Definition compiled_tempo_map.hpp:15
constexpr double kMaximumCompiledTempoBpm
Definition compiled_tempo_map.hpp:16
Definition tempo_delay.hpp:27
TempoDelayError error
Definition tempo_delay.hpp:28
double samples
Definition tempo_delay.hpp:29