Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
envelope_follower.hpp
Go to the documentation of this file.
1// sbl/dsp/envelope_follower.hpp — Amplitude envelope extraction
2//
3// Rectifies and smooths an audio signal to extract its amplitude envelope.
4// Full-wave rectification with asymmetric one-pole smoothing (fast attack,
5// slow release). Output is a slowly-varying positive float representing
6// "how loud the input is."
7//
8// Domain: Amplitude — makes the amplitude contour of a signal explicit.
9// See docs/design-philosophy.md for the domain manipulation framework.
10//
11// Usage:
12// sbl::dsp::EnvelopeFollower ef;
13// ef.set_times(1.0f, 100.0f, 48000); // 1ms attack, 100ms release
14// float level = ef.process(sample); // Single sample
15// ef.process(in, out, frames); // Block: in → envelope out
16
17#pragma once
18
19#include <cstdint>
20
21namespace sbl::dsp {
22
24public:
25 /// @note All public methods are ISR-safe — bounded computation, no I/O.
26
27 /**
28 * @brief Set attack and release times in milliseconds
29 *
30 * Computes one-pole coefficients from time constants.
31 * Attack: how fast the follower rises to meet a transient.
32 * Release: how fast it falls after the signal drops.
33 *
34 * @param attack_ms Attack time in milliseconds (typically 0.1–10)
35 * @param release_ms Release time in milliseconds (typically 50–500)
36 * @param sample_rate Audio sample rate (e.g. 48000)
37 */
38 void set_times(float attack_ms, float release_ms, uint32_t sample_rate) {
39 float sr = static_cast<float>(sample_rate);
40 // Time constant → coefficient: a = 1 - exp(-1 / (t * sr))
41 // For t in seconds: t = ms / 1000
42 // Approximation: 1 - exp(-x) ≈ x / (1 + x) for reasonable accuracy
43 float attack_samples = (attack_ms / 1000.0f) * sr;
44 float release_samples = (release_ms / 1000.0f) * sr;
45
46 if (attack_samples > 0.0f) {
47 float x = 1.0f / attack_samples;
48 attack_coeff_ = x / (1.0f + x);
49 } else {
50 attack_coeff_ = 1.0f; // Instant attack
51 }
52
53 if (release_samples > 0.0f) {
54 float x = 1.0f / release_samples;
55 release_coeff_ = x / (1.0f + x);
56 } else {
57 release_coeff_ = 1.0f; // Instant release
58 }
59 }
60
61 /**
62 * @brief Set attack and release coefficients directly
63 *
64 * Coefficient range [0.0, 1.0). Higher = faster response.
65 * 0.0 = no change (hold forever), approaching 1.0 = instant.
66 *
67 * @param attack Attack coefficient
68 * @param release Release coefficient
69 */
70 void set_coefficients(float attack, float release) {
71 attack_coeff_ = attack;
72 release_coeff_ = release;
73 }
74
75 /**
76 * @brief Process a single sample, return envelope value
77 *
78 * Full-wave rectifies the input, then applies asymmetric smoothing:
79 * if |input| > state: use attack coefficient (rise fast)
80 * if |input| < state: use release coefficient (fall slow)
81 *
82 * @param sample Input audio sample
83 * @return Current envelope value (always >= 0)
84 */
85 float process(float sample) {
86 // Full-wave rectification
87 float rectified = sample < 0.0f ? -sample : sample;
88
89 // Asymmetric one-pole: choose coefficient based on direction
90 float coeff = rectified > envelope_ ? attack_coeff_ : release_coeff_;
91 envelope_ += coeff * (rectified - envelope_);
92
93 return envelope_;
94 }
95
96 /**
97 * @brief Process a block, write envelope to output buffer
98 *
99 * Input and output can be the same buffer (in-place).
100 *
101 * @param in Input audio buffer
102 * @param out Output envelope buffer (always >= 0)
103 * @param frames Number of samples
104 */
105 void process(const float* in, float* out, uint16_t frames) {
106 for (uint16_t i = 0; i < frames; ++i) {
107 out[i] = process(in[i]);
108 }
109 }
110
111 /** @brief Current envelope value (can be read without processing) */
112 float value() const { return envelope_; }
113
114 /** @brief Reset envelope state to zero */
115 void reset() { envelope_ = 0.0f; }
116
117 /** @brief Reset envelope state to a specific value */
118 void reset(float initial) { envelope_ = initial; }
119
120private:
121 float envelope_ = 0.0f;
122 float attack_coeff_ = 0.0f;
123 float release_coeff_ = 0.0f;
124};
125
126} // namespace sbl::dsp
float process(float sample)
Process a single sample, return envelope value.
void reset()
Reset envelope state to zero.
void reset(float initial)
Reset envelope state to a specific value.
void set_coefficients(float attack, float release)
Set attack and release coefficients directly.
float value() const
Current envelope value (can be read without processing)
void set_times(float attack_ms, float release_ms, uint32_t sample_rate)
Set attack and release times in milliseconds.
void process(const float *in, float *out, uint16_t frames)
Process a block, write envelope to output buffer.
DSP atoms for audio signal processing.
Definition allpass.hpp:22