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
exp_mod.hpp
Go to the documentation of this file.
1// sbl/dsp/math/exp_mod.hpp — Exponential modulation converter (Signal layer)
2//
3// The "exponential converter IC" of the digital world. In analog synths,
4// a dedicated circuit converts linear control voltage to exponential
5// frequency scaling — this is what makes 1V/oct work. Without it,
6// modulation is perceptually broken: ±200 Hz is 2.3 octaves at 300 Hz
7// but 0.03 octaves at 10 kHz.
8//
9// exp_mod() bridges normalized modulation signals [-1.0, 1.0] and
10// engineering-unit parameters (Hz). This is the application point where
11// the Signal principle holds: "Signals follow [-1.0, 1.0]. Configuration
12// uses engineering units."
13//
14// Part of the Audio Stack: Atoms → Signal → Widgets
15// See FDP-031 for architecture rationale, RPT-014 for the triggering bug.
16//
17// Usage:
18// // Scalar — inside a per-sample loop
19// float freq = sbl::dsp::math::exp_mod(cutoff_hz, lfo_value, 2.0f);
20//
21// // Block — apply to an entire modulation buffer
22// float freq_buf[48];
23// sbl::dsp::math::exp_mod_block(lfo_buf, freq_buf, n, cutoff_hz, 2.0f);
24
25#ifndef SBL_DSP_MATH_EXP_MOD_HPP_
26#define SBL_DSP_MATH_EXP_MOD_HPP_
27
28#include <cstdint>
29
31
32namespace sbl::dsp::math {
33
34/// Apply exponential modulation to a base frequency (scalar).
35///
36/// Converts a normalized modulation signal to a frequency via:
37/// freq = base * 2^(mod * depth)
38///
39/// At depth = 2.0 and mod = 1.0, output is base * 4 (2 octaves up).
40/// At depth = 2.0 and mod = -1.0, output is base / 4 (2 octaves down).
41/// The relationship is perceptually uniform — equal mod deltas produce
42/// equal musical intervals regardless of the base frequency.
43///
44/// @param base Center frequency in Hz (e.g., filter cutoff)
45/// @param mod Normalized modulation value, typically [-1.0, 1.0]
46/// @param depth Modulation depth in octaves (e.g., 2.0 = ±2 octaves)
47/// @param lo Minimum output frequency in Hz (default 20)
48/// @param hi Maximum output frequency in Hz (default 20000)
49/// @return Modulated frequency in Hz, clamped to [lo, hi]
50inline float exp_mod(float base, float mod, float depth,
51 float lo = 20.0f, float hi = 20000.0f) {
52 float freq = base * fast_exp2f(mod * depth);
53 if (freq < lo) freq = lo;
54 if (freq > hi) freq = hi;
55 return freq;
56}
57
58/// Apply exponential modulation to an entire block (buffer version).
59///
60/// Writes modulated frequencies to freq_out[] for each sample. Use this
61/// when you need the frequency buffer for direct coefficient computation
62/// (e.g., feeding into a filter's set_cutoff per-sample).
63///
64/// @param mod Normalized modulation signal buffer [-1.0, 1.0]
65/// @param freq_out Output frequency buffer (Hz)
66/// @param n Frame count
67/// @param base Center frequency in Hz
68/// @param depth Modulation depth in octaves
69/// @param lo Minimum output frequency in Hz (default 20)
70/// @param hi Maximum output frequency in Hz (default 20000)
71inline void exp_mod_block(const float* mod, float* freq_out, uint16_t n,
72 float base, float depth,
73 float lo = 20.0f, float hi = 20000.0f) {
74 for (uint16_t i = 0; i < n; ++i) {
75 freq_out[i] = exp_mod(base, mod[i], depth, lo, hi);
76 }
77}
78
79} // namespace sbl::dsp::math
80
81#endif // SBL_DSP_MATH_EXP_MOD_HPP_
Fast analytical approximations (Audio Stack — Atoms)
Cross-cutting math used at every audio layer.
Definition clamp.hpp:10
void exp_mod_block(const float *mod, float *freq_out, uint16_t n, float base, float depth, float lo=20.0f, float hi=20000.0f)
Definition exp_mod.hpp:71
float fast_exp2f(float x)
Definition fast_math.hpp:99
float exp_mod(float base, float mod, float depth, float lo=20.0f, float hi=20000.0f)
Definition exp_mod.hpp:50