Sound Byte Libs 29c5ff3
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#pragma once
13
14#include <cstdint>
15
18
19namespace sbl::dsp {
20
21/// @note All functions in sbl::dsp (pitch) are ISR-safe — bounded computation, no I/O.
22
23/// Convert semitones to frequency ratio using two-table multiplication.
24/// Input range: -128.0 to +127.0 semitones. 0 = unison (ratio 1.0).
25/// Cost: 1 float multiply + table lookups.
26inline float semitones_to_ratio(float semitones) {
27 float pitch = semitones + 128.0f;
28 int32_t integral = static_cast<int32_t>(pitch);
29 float fractional = pitch - static_cast<float>(integral);
30 if (integral < 0) integral = 0;
31 if (integral > 255) integral = 255;
32 return lut::pitch_ratio_high_256[integral] *
33 lut::pitch_ratio_low_256[static_cast<int32_t>(fractional * 256.0f)];
34}
35
36/// Extended range version — handles semitones outside [-128, 127].
37/// Uses octave doubling/halving for extreme values.
38inline float semitones_to_ratio_safe(float semitones) {
39 float scale = 1.0f;
40 while (semitones > 120.0f) { semitones -= 120.0f; scale *= 1024.0f; }
41 while (semitones < -120.0f) { semitones += 120.0f; scale *= (1.0f / 1024.0f); }
42 return scale * semitones_to_ratio(semitones);
43}
44
45/// MIDI note to frequency ratio relative to A4 (note 69).
46/// note_to_ratio(69) = 1.0, note_to_ratio(81) = 2.0.
47inline float note_to_ratio(float midi_note) {
48 return semitones_to_ratio(midi_note - 69.0f);
49}
50
51/// MIDI note to frequency in Hz. A4 = 440 Hz.
52inline float note_to_frequency(float midi_note) {
53 return 440.0f * note_to_ratio(midi_note);
54}
55
56/// MIDI note to phase increment for a given sample rate.
57/// phase_inc = freq / sample_rate * 2^32
58inline uint32_t note_to_phase_increment(float midi_note, float sample_rate) {
59 float freq = note_to_frequency(midi_note);
60 return static_cast<uint32_t>(freq / sample_rate * 4294967296.0f);
61}
62
63/// Fast 2^x approximation via pitch tables. x in [-10.67, +10.58].
64inline float exp2_approx(float x) {
65 return semitones_to_ratio(x * 12.0f);
66}
67
68/// Extended range 2^x.
69inline float exp2_approx_safe(float x) {
70 return semitones_to_ratio_safe(x * 12.0f);
71}
72
73} // namespace sbl::dsp
constexpr float pitch_ratio_high_256[257]
constexpr float pitch_ratio_low_256[257]
DSP atoms for audio signal processing.
Definition allpass.hpp:22
float exp2_approx_safe(float x)
Extended range 2^x.
Definition pitch.hpp:69
float note_to_frequency(float midi_note)
MIDI note to frequency in Hz. A4 = 440 Hz.
Definition pitch.hpp:52
float note_to_ratio(float midi_note)
Definition pitch.hpp:47
float semitones_to_ratio_safe(float semitones)
Definition pitch.hpp:38
float semitones_to_ratio(float semitones)
Definition pitch.hpp:26
float exp2_approx(float x)
Fast 2^x approximation via pitch tables. x in [-10.67, +10.58].
Definition pitch.hpp:64
uint32_t note_to_phase_increment(float midi_note, float sample_rate)
Definition pitch.hpp:58