Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
units.hpp
Go to the documentation of this file.
1#pragma once
2
41
42#include <algorithm>
43#include <cmath>
44#include <cstdint>
46
48
52inline constexpr double kDbFloorAmplitude = 1e-10;
53
55inline constexpr double kMidiA4 = 69.0;
56
58inline constexpr double kA4Hz = 440.0;
59
61inline constexpr double kSemitonesPerOctave = 12.0;
62inline constexpr double kCentsPerSemitone = 100.0;
63
65inline constexpr double kSemitonesPerVolt = kSemitonesPerOctave;
66
67// ── Amplitude ↔ decibels ──────────────────────────────────────────────────
68
70template <typename T>
71inline T db_to_linear(T db) {
72 return std::pow(T{10}, db / T{20});
73}
74
78template <typename T>
79inline T linear_to_db(T linear) {
80 return T{20} * std::log10(std::max(std::abs(linear), static_cast<T>(kDbFloorAmplitude)));
81}
82
83// ── Pitch ─────────────────────────────────────────────────────────────────
84
87template <typename T>
88inline T midi_to_hz(T note) {
89 return static_cast<T>(kA4Hz) *
90 std::pow(T{2}, (note - static_cast<T>(kMidiA4)) / static_cast<T>(kSemitonesPerOctave));
91}
92
96template <typename T>
97inline T hz_to_midi(T hz) {
98 const T safe = std::max(hz, static_cast<T>(kDbFloorAmplitude));
99 return static_cast<T>(kMidiA4) +
100 static_cast<T>(kSemitonesPerOctave) * std::log2(safe / static_cast<T>(kA4Hz));
101}
102
104template <typename T>
105inline T semitones_to_ratio(T semitones) {
106 return std::pow(T{2}, semitones / static_cast<T>(kSemitonesPerOctave));
107}
108
110template <typename T>
112 return static_cast<T>(kSemitonesPerOctave) *
113 std::log2(std::max(ratio, static_cast<T>(kDbFloorAmplitude)));
114}
115
117template <typename T>
118inline T cents_to_ratio(T cents) {
119 return std::pow(T{2}, cents / (static_cast<T>(kSemitonesPerOctave) *
120 static_cast<T>(kCentsPerSemitone)));
121}
122
124template <typename T>
126 return ratio_to_semitones(ratio) * static_cast<T>(kCentsPerSemitone);
127}
128
130template <typename T>
131inline T volts_to_semitones(T volts) {
132 return volts * static_cast<T>(kSemitonesPerVolt);
133}
134
136template <typename T>
137inline T semitones_to_volts(T semitones) {
138 return semitones / static_cast<T>(kSemitonesPerVolt);
139}
140
141// ── Time ↔ samples ────────────────────────────────────────────────────────
142
146template <typename T>
147inline T ms_to_samples(T ms, T sample_rate) {
148 return ms * sample_rate / T{1000};
149}
150
152template <typename T>
153inline T samples_to_ms(T samples, T sample_rate) {
154 return sample_rate > T{0} ? samples * T{1000} / sample_rate : T{0};
155}
156
158template <typename T>
159inline T hz_to_period_samples(T hz, T sample_rate) {
160 return hz > T{0} ? sample_rate / hz : T{0};
161}
162
164template <typename T>
165inline T period_samples_to_hz(T samples, T sample_rate) {
166 return samples > T{0} ? sample_rate / samples : T{0};
167}
168
169// ── Frequency → internal filter/oscillator quantities ─────────────────────
170
173template <typename T>
174inline T hz_to_cycles_per_sample(T hz, T sample_rate) {
175 return sample_rate > T{0} ? hz / sample_rate : T{0};
176}
177
180template <typename T>
181inline T hz_to_radians_per_sample(T hz, T sample_rate) {
182 constexpr T two_pi = static_cast<T>(6.283185307179586476925286766559L);
183 return sample_rate > T{0} ? two_pi * hz / sample_rate : T{0};
184}
185
186// ── One-pole coefficients ─────────────────────────────────────────────────
187
201template <typename T>
202inline T ms_to_onepole_coef(T ms, T sample_rate) {
203 if (!(ms > T{0}) || !(sample_rate > T{0})) return T{1};
204 const T samples = ms * sample_rate / T{1000};
205 return T{1} - std::exp(T{-1} / samples);
206}
207
210template <typename T>
211inline T ms_to_onepole_pole(T ms, T sample_rate) {
212 return T{1} - ms_to_onepole_coef(ms, sample_rate);
213}
214
217template <typename T>
218inline T t60_ms_to_tau_ms(T t60_ms) {
219 constexpr T ln1000 = static_cast<T>(6.907755278982137L);
220 return t60_ms / ln1000;
221}
222
223// ── Parameter tapers ──────────────────────────────────────────────────────
224
232template <typename T>
233inline T taper_log(T u, T lo, T hi) {
234 if (!(lo > T{0}) || !(hi > T{0})) return lo;
235 const T clamped = std::clamp(u, T{0}, T{1});
236 return lo * std::pow(hi / lo, clamped);
237}
238
240template <typename T>
241inline T untaper_log(T value, T lo, T hi) {
242 if (!(lo > T{0}) || !(hi > T{0}) || hi == lo) return T{0};
243 const T clamped = std::clamp(value, std::min(lo, hi), std::max(lo, hi));
244 return std::log(clamped / lo) / std::log(hi / lo);
245}
246
249template <typename T>
250inline T taper_linear(T u, T lo, T hi) {
251 return lo + std::clamp(u, T{0}, T{1}) * (hi - lo);
252}
253
255template <typename T>
256inline T untaper_linear(T value, T lo, T hi) {
257 if (hi == lo) return T{0};
258 return std::clamp((value - lo) / (hi - lo), T{0}, T{1});
259}
260
262template <typename T>
263inline T taper_log_inverse(T value, T lo, T hi) {
264 return untaper_log(value, lo, hi);
265}
266
268template <typename T>
269inline T t60_to_per_sample_gain(T t60_seconds, T sample_rate) {
270 const T samples = std::max(T{1}, t60_seconds * sample_rate);
271 return std::pow(T{10}, T{-3} / samples);
272}
273
274template <typename T>
275inline T beats_to_seconds(T beats, T bpm) {
276 return beats * T{60} / std::max(T{1e-3}, bpm);
277}
278
279template <typename T>
280inline T seconds_to_beats(T seconds, T bpm) {
281 return seconds * std::max(T{1e-3}, bpm) / T{60};
282}
283
310
311inline constexpr int kDivisionCount = static_cast<int>(Division::count);
312
316 return static_cast<timebase::BeatDivision>(static_cast<std::uint8_t>(division));
317}
318
319static_assert(static_cast<std::uint8_t>(Division::count) ==
320 static_cast<std::uint8_t>(timebase::BeatDivision::Count));
321
322constexpr float division_to_beats(Division division) {
323 const int index = static_cast<int>(division);
324 if (index < 0 || index >= kDivisionCount) return 1.0f;
325 const auto fraction = timebase::beat_fraction_or(to_beat_division(division), {1, 1});
326 return static_cast<float>(fraction.numerator) /
327 static_cast<float>(fraction.denominator);
328}
329
330constexpr float division_to_beats(int index) {
331 return division_to_beats(static_cast<Division>(index));
332}
333
334inline float division_to_seconds(Division division, float bpm) {
335 return beats_to_seconds(division_to_beats(division), bpm);
336}
337
338inline double division_to_samples(Division division, float bpm, double sample_rate) {
339 return static_cast<double>(division_to_seconds(division, bpm)) * sample_rate;
340}
341
342inline float division_to_hz(Division division, float bpm) {
343 return 1.0f / std::max(1.0e-6f, division_to_seconds(division, bpm));
344}
345
346} // namespace pulp::signal::units
Definition units.hpp:47
T hz_to_radians_per_sample(T hz, T sample_rate)
Hz → radians per sample (2π·f / fs) — the unit a sin/cos oscillator and most filter design formulas w...
Definition units.hpp:181
T hz_to_cycles_per_sample(T hz, T sample_rate)
Hz → normalised frequency in cycles per sample (f / fs) — the unit a phase accumulator increments by.
Definition units.hpp:174
T taper_log(T u, T lo, T hi)
Geometric ("log") taper: maps a normalised knob position u ∈ [0, 1] onto [lo, hi] so equal knob trave...
Definition units.hpp:233
T period_samples_to_hz(T samples, T sample_rate)
A period in samples → its frequency in Hz.
Definition units.hpp:165
T semitones_to_ratio(T semitones)
Semitones → frequency ratio. +12 → 2, −12 → 0.5.
Definition units.hpp:105
constexpr double kCentsPerSemitone
Definition units.hpp:62
T untaper_log(T value, T lo, T hi)
Inverse of taper_log: a value in [lo, hi] → its knob position in [0, 1].
Definition units.hpp:241
T untaper_linear(T value, T lo, T hi)
Inverse of taper_linear.
Definition units.hpp:256
constexpr double kDbFloorAmplitude
Amplitude floor used when converting to dB.
Definition units.hpp:52
T linear_to_db(T linear)
Linear gain → amplitude dB, floored at kDbFloorAmplitude so silence maps to a finite −200 dB rather t...
Definition units.hpp:79
constexpr float division_to_beats(Division division)
Definition units.hpp:322
double division_to_samples(Division division, float bpm, double sample_rate)
Definition units.hpp:338
T t60_to_per_sample_gain(T t60_seconds, T sample_rate)
Per-sample gain for a feedback path that decays by 60 dB in t60_seconds.
Definition units.hpp:269
T ratio_to_semitones(T ratio)
Frequency ratio → semitones. Inverse of semitones_to_ratio.
Definition units.hpp:111
constexpr double kSemitonesPerOctave
Semitones per octave, and cents per semitone. Fixed by 12-TET notation.
Definition units.hpp:61
T semitones_to_volts(T semitones)
Semitones → pitch CV volts, on the 1 V/octave standard.
Definition units.hpp:137
T samples_to_ms(T samples, T sample_rate)
Samples → milliseconds at sample_rate Hz.
Definition units.hpp:153
T taper_log_inverse(T value, T lo, T hi)
Compatibility spelling used by the shared modulation toolkit.
Definition units.hpp:263
T hz_to_period_samples(T hz, T sample_rate)
Frequency → its period in samples. A 100 Hz tone at 48 kHz is 480 samples.
Definition units.hpp:159
T taper_linear(T u, T lo, T hi)
Linear taper — the arithmetic counterpart of taper_log, present so a parameter table can name its law...
Definition units.hpp:250
float division_to_seconds(Division division, float bpm)
Definition units.hpp:334
T ms_to_onepole_coef(T ms, T sample_rate)
A time constant in ms → the a in the one-pole smoother y += a·(x − y), i.e.
Definition units.hpp:202
T beats_to_seconds(T beats, T bpm)
Definition units.hpp:275
T midi_to_hz(T note)
MIDI note number → Hz (note 69 = 440 Hz).
Definition units.hpp:88
T seconds_to_beats(T seconds, T bpm)
Definition units.hpp:280
T cents_to_ratio(T cents)
Cents → frequency ratio. +100 → 2^(1/12), +1200 → 2.
Definition units.hpp:118
T ratio_to_cents(T ratio)
Frequency ratio → cents. Inverse of cents_to_ratio.
Definition units.hpp:125
constexpr double kSemitonesPerVolt
Semitones per volt on the 1 V/octave pitch-CV standard.
Definition units.hpp:65
T ms_to_onepole_pole(T ms, T sample_rate)
The complementary pole of ms_to_onepole_coef: p = 1 − a = exp(−1/(τ·fs)), the p in y = p·y + (1 − p)·...
Definition units.hpp:211
T t60_ms_to_tau_ms(T t60_ms)
A 60 dB decay time in ms → the one-pole time constant τ in ms that produces it.
Definition units.hpp:218
T ms_to_samples(T ms, T sample_rate)
Milliseconds → samples at sample_rate Hz.
Definition units.hpp:147
float division_to_hz(Division division, float bpm)
Definition units.hpp:342
Division
Shared serialized musical-division vocabulary.
Definition units.hpp:286
@ eighth_dotted
Definition units.hpp:297
@ whole_dotted
Definition units.hpp:288
@ whole
Definition units.hpp:287
@ thirty_second
Definition units.hpp:302
@ half_triplet
Definition units.hpp:292
@ whole_triplet
Definition units.hpp:289
@ thirty_second_dotted
Definition units.hpp:303
@ sixty_fourth_dotted
Definition units.hpp:306
@ sixty_fourth_triplet
Definition units.hpp:307
@ half
Definition units.hpp:290
@ thirty_second_triplet
Definition units.hpp:304
@ sixteenth
Definition units.hpp:299
@ eighth
Definition units.hpp:296
@ quarter
Definition units.hpp:293
@ sixteenth_triplet
Definition units.hpp:301
@ sixty_fourth
Definition units.hpp:305
@ quarter_triplet
Definition units.hpp:295
@ count
Definition units.hpp:308
@ half_dotted
Definition units.hpp:291
@ eighth_triplet
Definition units.hpp:298
@ sixteenth_dotted
Definition units.hpp:300
@ quarter_dotted
Definition units.hpp:294
T db_to_linear(T db)
Amplitude dB → linear gain. 0 dB → 1, −6.0206 dB → 0.5.
Definition units.hpp:71
T hz_to_midi(T hz)
Hz → MIDI note number.
Definition units.hpp:97
constexpr timebase::BeatDivision to_beat_division(Division division) noexcept
Converts the legacy signal spelling to the canonical timebase vocabulary.
Definition units.hpp:315
constexpr double kMidiA4
MIDI note number of concert A. Fixed by the MIDI specification.
Definition units.hpp:55
T volts_to_semitones(T volts)
Pitch CV volts → semitones, on the 1 V/octave standard.
Definition units.hpp:131
constexpr double kA4Hz
Frequency of concert A in Hz — the tuning reference for midi_to_hz.
Definition units.hpp:58
constexpr int kDivisionCount
Definition units.hpp:311
@ ratio
base_frequency_hz * ratio.
Definition fm_operator_engine.hpp:20
@ u
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ linear
2-point. Cheapest; audible imaging when pitching up.
Definition granular.hpp:133
BeatDivision
Definition beat_division.hpp:15
@ Count
Definition beat_division.hpp:37
constexpr BeatFraction beat_fraction_or(BeatDivision division, BeatFraction fallback) noexcept
Returns the exact fraction for division, or fallback for an invalid ordinal.
Definition beat_division.hpp:67