Sound Byte Libs 0.5.1-121-g3358a44
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
pitch.hpp
Go to the documentation of this file.
1// sbl/dsp/pitch.hpp — Semitone-to-frequency-ratio conversion
2//
3// Two-table multiplication approach: split 2^(semitones/12) into coarse
4// (per-semitone) and fine (sub-semitone) tables. 2^(a+b) = 2^a * 2^b.
5// Cost: 1 float multiply + table lookups. ~2KB flash, <0.4 cent error.
6//
7// Adapted from Mutable Instruments stmlib/dsp/units.h (MIT license).
8//
9// Note: Requires hardware FPU. STM32H7 (Cortex-M7) and RP2350 (Cortex-M33)
10// have FPU — fine. RP2040 (Cortex-M0+) does not — avoid on M0+ targets.
11
12#ifndef SBL_DSP_MATH_PITCH_HPP_
13#define SBL_DSP_MATH_PITCH_HPP_
14
15#include <cstdint>
16
20
21namespace sbl::dsp::math {
22
23/// @note All functions in sbl::dsp (pitch) are ISR-safe — bounded computation, no I/O.
24
25/// Convert semitones to frequency ratio using two-table multiplication.
26/// Input range: -128.0 to +127.0 semitones. 0 = unison (ratio 1.0).
27/// Cost: 1 float multiply + table lookups.
28inline float semitones_to_ratio(float semitones) {
29 float pitch = semitones + 128.0f;
30 int32_t integral = static_cast<int32_t>(pitch);
31 float fractional = pitch - static_cast<float>(integral);
32 if (integral < 0) integral = 0;
33 if (integral > 255) integral = 255;
34 return lut::pitch_ratio_high_256[integral] *
35 lut::pitch_ratio_low_256[static_cast<int32_t>(fractional * 256.0f)];
36}
37
38/// Extended range version — handles semitones outside [-128, 127].
39/// Uses octave doubling/halving for extreme values.
40inline float semitones_to_ratio_safe(float semitones) {
41 // ±10 octaves = ±120 semitones before folding
42 constexpr float FOLD_RANGE = 120.0f;
43 // 2^10 = 1024 (10 octaves)
44 constexpr float FOLD_SCALE = 1024.0f;
45 float scale = 1.0f;
46 while (semitones > FOLD_RANGE) { semitones -= FOLD_RANGE; scale *= FOLD_SCALE; }
47 while (semitones < -FOLD_RANGE) { semitones += FOLD_RANGE; scale *= (1.0f / FOLD_SCALE); }
48 return scale * semitones_to_ratio(semitones);
49}
50
51/// MIDI note to frequency ratio relative to A4 (note 69).
52/// note_to_ratio(69) = 1.0, note_to_ratio(81) = 2.0.
53inline float note_to_ratio(float midi_note) {
54 return semitones_to_ratio(midi_note - 69.0f);
55}
56
57/// MIDI note to frequency in Hz. A4 = 440 Hz.
58inline float note_to_frequency(float midi_note) {
59 return 440.0f * note_to_ratio(midi_note);
60}
61
62/// MIDI note to phase increment for a given sample rate.
63/// phase_inc = freq / sample_rate * 2^32
64inline uint32_t note_to_phase_increment(float midi_note, float sample_rate) {
65 float freq = note_to_frequency(midi_note);
66 return static_cast<uint32_t>(freq / sample_rate * PHASE_ACCUMULATOR_RANGE);
67}
68
69/// The pitch-su convention: a unipolar signal spans C1..C9 (MIDI 24..120),
70/// the mapping MorphOsc set and every pitched widget follows.
71inline constexpr float PITCH_SU_MIN_NOTE = 24.0f;
72inline constexpr float PITCH_SU_MAX_NOTE = 120.0f;
74
75inline constexpr float note_of_su(float su) { return PITCH_SU_MIN_NOTE + su * PITCH_SU_NOTE_RANGE; }
76inline constexpr float su_of_note(float note) { return (note - PITCH_SU_MIN_NOTE) / PITCH_SU_NOTE_RANGE; }
77
78} // namespace sbl::dsp::math
79
80#endif // SBL_DSP_MATH_PITCH_HPP_
The numbers every layer reaches for.
constexpr float pitch_ratio_high_256[257]
constexpr float pitch_ratio_low_256[257]
Cross-cutting math used at every audio layer.
Definition clamp.hpp:10
constexpr float PITCH_SU_MIN_NOTE
Definition pitch.hpp:71
constexpr float note_of_su(float su)
Definition pitch.hpp:75
uint32_t note_to_phase_increment(float midi_note, float sample_rate)
Definition pitch.hpp:64
float semitones_to_ratio_safe(float semitones)
Definition pitch.hpp:40
float semitones_to_ratio(float semitones)
Definition pitch.hpp:28
float note_to_frequency(float midi_note)
MIDI note to frequency in Hz. A4 = 440 Hz.
Definition pitch.hpp:58
float note_to_ratio(float midi_note)
Definition pitch.hpp:53
constexpr float su_of_note(float note)
Definition pitch.hpp:76
constexpr float PITCH_SU_MAX_NOTE
Definition pitch.hpp:72
constexpr float PHASE_ACCUMULATOR_RANGE
The phase accumulator's full range: uint32_t [0, 2^32) is one cycle.
Definition constants.hpp:16
constexpr float PITCH_SU_NOTE_RANGE
Definition pitch.hpp:73
Generated by LUTE — do not edit.
Generated by LUTE — do not edit.