14
14
namespace audio_tools {
15
15
16
16
/* *
17
- * @brief Configuration for 3 Band Equalizer: Set
18
- * channels,bits_per_sample,sample_rate. Set and update gain_low, gain_medium
19
- * and gain_high to value between 0 and 1.0
17
+ * @brief Configuration for 3 Band Equalizer
18
+ *
19
+ * Configure the basic audio parameters (channels, bits_per_sample, sample_rate)
20
+ * and the equalizer settings. The frequency and gain parameters apply to all
21
+ * channels identically in the basic Equalizer3Bands class.
22
+ *
23
+ * Frequency bands:
24
+ * - Low band: DC to freq_low Hz
25
+ * - Medium band: freq_low to freq_high Hz
26
+ * - High band: freq_high to Nyquist frequency
27
+ *
28
+ * Gain values should typically range from 0.0 to 2.0:
29
+ * - 0.0 = complete attenuation (silence)
30
+ * - 1.0 = no change (unity gain)
31
+ * - 2.0 = 6dB boost
32
+ *
20
33
* @ingroup equilizer
21
34
* @author pschatzmann
22
35
*/
@@ -27,33 +40,59 @@ struct ConfigEqualizer3Bands : public AudioInfo {
27
40
sample_rate = 44100 ;
28
41
}
29
42
30
- // Frequencies
43
+ // / Low-pass filter cutoff frequency in Hz. Frequencies below this are considered "low"
31
44
int freq_low = 880 ;
45
+
46
+ // / High-pass filter cutoff frequency in Hz. Frequencies above this are considered "high"
32
47
int freq_high = 5000 ;
33
48
34
- // Gain Controls
49
+ // / Gain multiplier for low frequencies (0.0-2.0, where 1.0 = unity gain)
35
50
float gain_low = 1.0 ;
51
+
52
+ // / Gain multiplier for medium frequencies (0.0-2.0, where 1.0 = unity gain)
36
53
float gain_medium = 1.0 ;
54
+
55
+ // / Gain multiplier for high frequencies (0.0-2.0, where 1.0 = unity gain)
37
56
float gain_high = 1.0 ;
38
57
};
39
58
40
59
/* *
41
- * @brief 3 Band Equalizer inspired from
60
+ * @brief 3 Band Equalizer with identical settings for all channels
61
+ *
62
+ * Digital 3-band equalizer implementation inspired from
42
63
* https://www.musicdsp.org/en/latest/Filters/236-3-band-equaliser.html
64
+ *
65
+ * This equalizer applies the same frequency response and gain settings
66
+ * to all audio channels. The audio spectrum is divided into three bands:
67
+ * - Low: DC to freq_low Hz (controlled by gain_low)
68
+ * - Medium: freq_low to freq_high Hz (controlled by gain_medium)
69
+ * - High: freq_high to Nyquist frequency (controlled by gain_high)
70
+ *
71
+ * Each band uses a 4-pole filter implementation for smooth frequency response.
72
+ * If you need different settings per channel, use Equalizer3BandsPerChannel instead.
73
+ *
43
74
* @ingroup equilizer
44
75
* @author pschatzmann
45
76
*/
46
77
class Equalizer3Bands : public ModifyingStream {
47
78
public:
79
+ // / Constructor with Print output stream
80
+ // / @param out Print stream where processed audio will be written
48
81
Equalizer3Bands (Print &out) { setOutput (out); }
49
82
83
+ // / Constructor with bidirectional Stream
84
+ // / @param in Stream for both input and output
50
85
Equalizer3Bands (Stream &in) { setStream (in); }
51
86
87
+ // / Constructor with AudioOutput (includes automatic audio format notifications)
88
+ // / @param out AudioOutput where processed audio will be written
52
89
Equalizer3Bands (AudioOutput &out) {
53
90
setOutput (out);
54
91
out.addNotifyAudioChange (*this );
55
92
}
56
93
94
+ // / Constructor with AudioStream (includes automatic audio format notifications)
95
+ // / @param stream AudioStream for both input and output
57
96
Equalizer3Bands (AudioStream &stream) {
58
97
setStream (stream);
59
98
stream.addNotifyAudioChange (*this );
@@ -63,19 +102,28 @@ class Equalizer3Bands : public ModifyingStream {
63
102
if (state != nullptr ) delete[] state;
64
103
}
65
104
66
- // / Defines/Changes the input & output
105
+ // / Defines/Changes the input & output stream
106
+ // / @param io Stream to use for both reading and writing audio data
67
107
void setStream (Stream &io) override {
68
108
p_print = &io;
69
109
p_stream = &io;
70
110
};
71
111
72
112
// / Defines/Changes the output target
113
+ // / @param out Print stream where processed audio will be written
73
114
void setOutput (Print &out) override { p_print = &out; }
74
115
116
+ // / Access to the current configuration
117
+ // / @return Reference to the configuration object
75
118
ConfigEqualizer3Bands &config () { return cfg; }
76
119
120
+ // / Access to the default configuration
121
+ // / @return Reference to the default configuration object
77
122
ConfigEqualizer3Bands &defaultConfig () { return config (); }
78
123
124
+ // / Initialize the equalizer with the provided configuration
125
+ // / @param config Configuration settings including frequencies and gains
126
+ // / @return true if initialization was successful
79
127
bool begin (ConfigEqualizer3Bands &config) {
80
128
p_cfg = &config;
81
129
if (p_cfg->channels > max_state_count) {
@@ -98,21 +146,32 @@ class Equalizer3Bands : public ModifyingStream {
98
146
return true ;
99
147
}
100
148
149
+ // / Called automatically when audio format changes
150
+ // / @param info New audio format information
101
151
virtual void setAudioInfo (AudioInfo info) override {
102
152
p_cfg->sample_rate = info.sample_rate ;
103
153
p_cfg->channels = info.channels ;
104
154
p_cfg->bits_per_sample = info.bits_per_sample ;
105
155
begin (*p_cfg);
106
156
}
107
157
158
+ // / Process and write audio data through the equalizer
159
+ // / @param data Pointer to audio data buffer
160
+ // / @param len Length of data in bytes
161
+ // / @return Number of bytes written
108
162
size_t write (const uint8_t *data, size_t len) override {
109
163
filterSamples (data, len);
110
164
return p_print->write (data, len);
111
165
}
112
166
167
+ // / Get available space for writing
168
+ // / @return Number of bytes available for writing
113
169
int availableForWrite () override { return p_print->availableForWrite (); }
114
170
115
- // / Provides the data from all streams mixed together
171
+ // / Read and process audio data through the equalizer
172
+ // / @param data Buffer to store processed audio data
173
+ // / @param len Maximum number of bytes to read
174
+ // / @return Number of bytes actually read and processed
116
175
size_t readBytes (uint8_t *data, size_t len) override {
117
176
size_t result = 0 ;
118
177
if (p_stream != nullptr ) {
@@ -122,42 +181,46 @@ class Equalizer3Bands : public ModifyingStream {
122
181
return result;
123
182
}
124
183
184
+ // / Get available data for reading
185
+ // / @return Number of bytes available for reading
125
186
int available () override {
126
187
return p_stream != nullptr ? p_stream->available () : 0 ;
127
188
}
128
189
129
190
protected:
130
- ConfigEqualizer3Bands cfg;
131
- ConfigEqualizer3Bands *p_cfg = &cfg;
132
- const float vsa = (1.0 / 4294967295.0 ); // Very small amount (Denormal Fix)
133
- Print *p_print = nullptr ; // support for write
134
- Stream *p_stream = nullptr ; // support for write
135
- // AudioOutput *p_out=nullptr; // support for write
136
- // AudioStream *p_in=nullptr; // support for readBytes
137
- int max_state_count = 0 ;
138
-
191
+ ConfigEqualizer3Bands cfg; // /< Default configuration instance
192
+ ConfigEqualizer3Bands *p_cfg = &cfg; // /< Pointer to active configuration
193
+ const float vsa = (1.0 / 4294967295.0 ); // /< Very small amount for denormal fix
194
+ Print *p_print = nullptr ; // /< Output stream for write operations
195
+ Stream *p_stream = nullptr ; // /< Input/output stream for read operations
196
+ int max_state_count = 0 ; // /< Maximum number of allocated channel states
197
+
198
+ // / Filter state for each channel
139
199
struct EQSTATE {
140
- // Filter #1 (Low band)
141
- float lf; // Frequency
142
- float f1p0; // Poles ...
143
- float f1p1;
144
- float f1p2;
145
- float f1p3;
146
-
147
- // Filter #2 (High band)
148
- float hf; // Frequency
149
- float f2p0; // Poles ...
150
- float f2p1;
151
- float f2p2;
152
- float f2p3;
153
-
154
- // Sample history buffer
155
- float sdm1; // Sample data minus 1
156
- float sdm2; // 2
157
- float sdm3; // 3
200
+ // Filter #1 (Low band) - 4-pole low-pass filter
201
+ float lf; // /< Low frequency cutoff coefficient
202
+ float f1p0; // /< Filter pole 0
203
+ float f1p1; // /< Filter pole 1
204
+ float f1p2; // /< Filter pole 2
205
+ float f1p3; // /< Filter pole 3
206
+
207
+ // Filter #2 (High band) - 4-pole high-pass filter
208
+ float hf; // /< High frequency cutoff coefficient
209
+ float f2p0; // /< Filter pole 0
210
+ float f2p1; // /< Filter pole 1
211
+ float f2p2; // /< Filter pole 2
212
+ float f2p3; // /< Filter pole 3
213
+
214
+ // Sample history buffer for filter calculations
215
+ float sdm1; // /< Sample data minus 1 (previous sample)
216
+ float sdm2; // /< Sample data minus 2
217
+ float sdm3; // /< Sample data minus 3
158
218
159
219
} *state = nullptr ;
160
220
221
+ // / Apply 3-band equalization to audio samples
222
+ // / @param data Pointer to audio data buffer (modified in-place)
223
+ // / @param len Length of data buffer in bytes
161
224
void filterSamples (const uint8_t *data, size_t len) {
162
225
if (state == nullptr ){
163
226
LOGE (" You need to call begin() before using the equalizer" );
@@ -198,8 +261,10 @@ class Equalizer3Bands : public ModifyingStream {
198
261
}
199
262
}
200
263
201
-
202
- // calculates a single sample using the indicated state
264
+ // / Process a single audio sample through the 3-band equalizer
265
+ // / @param es Reference to the filter state for this channel
266
+ // / @param sample Input sample value (normalized float)
267
+ // / @return Processed sample value with equalization applied
203
268
float sample (EQSTATE &es, float sample) {
204
269
// Locals
205
270
float l, m, h; // Low / Mid / High - Sample Values
@@ -324,6 +389,9 @@ class Equalizer3BandsPerChannel : public ModifyingStream {
324
389
}
325
390
326
391
// / Set frequency parameters for a specific channel
392
+ // / @param channel Channel index (0-based)
393
+ // / @param freq_low_val Low frequency cutoff in Hz
394
+ // / @param freq_high_val High frequency cutoff in Hz
327
395
void setChannelFrequencies (int channel, int freq_low_val, int freq_high_val) {
328
396
ensureChannelArraysAllocated ();
329
397
if (channel >= 0 && channel < p_cfg->channels && !freq_low.empty ()) {
@@ -339,6 +407,10 @@ class Equalizer3BandsPerChannel : public ModifyingStream {
339
407
}
340
408
341
409
// / Set gain parameters for a specific channel
410
+ // / @param channel Channel index (0-based)
411
+ // / @param gain_low_val Gain multiplier for low frequencies (0.0-2.0)
412
+ // / @param gain_medium_val Gain multiplier for medium frequencies (0.0-2.0)
413
+ // / @param gain_high_val Gain multiplier for high frequencies (0.0-2.0)
342
414
void setChannelGains (int channel, float gain_low_val, float gain_medium_val, float gain_high_val) {
343
415
ensureChannelArraysAllocated ();
344
416
if (channel >= 0 && channel < p_cfg->channels && !gain_low.empty ()) {
@@ -349,6 +421,10 @@ class Equalizer3BandsPerChannel : public ModifyingStream {
349
421
}
350
422
351
423
// / Get frequency parameters for a specific channel
424
+ // / @param channel Channel index (0-based)
425
+ // / @param freq_low_val Reference to store the low frequency cutoff
426
+ // / @param freq_high_val Reference to store the high frequency cutoff
427
+ // / @return true if successful, false if channel index is invalid
352
428
bool getChannelFrequencies (int channel, int &freq_low_val, int &freq_high_val) {
353
429
if (channel >= 0 && channel < p_cfg->channels && !freq_low.empty ()) {
354
430
freq_low_val = freq_low[channel];
@@ -359,6 +435,11 @@ class Equalizer3BandsPerChannel : public ModifyingStream {
359
435
}
360
436
361
437
// / Get gain parameters for a specific channel
438
+ // / @param channel Channel index (0-based)
439
+ // / @param gain_low_val Reference to store the low frequency gain
440
+ // / @param gain_medium_val Reference to store the medium frequency gain
441
+ // / @param gain_high_val Reference to store the high frequency gain
442
+ // / @return true if successful, false if channel index is invalid
362
443
bool getChannelGains (int channel, float &gain_low_val, float &gain_medium_val, float &gain_high_val) {
363
444
if (channel >= 0 && channel < p_cfg->channels && !gain_low.empty ()) {
364
445
gain_low_val = gain_low[channel];
@@ -369,14 +450,23 @@ class Equalizer3BandsPerChannel : public ModifyingStream {
369
450
return false ;
370
451
}
371
452
453
+ // / Process and write audio data through the per-channel equalizer
454
+ // / @param data Pointer to audio data buffer
455
+ // / @param len Length of data in bytes
456
+ // / @return Number of bytes written
372
457
size_t write (const uint8_t *data, size_t len) override {
373
458
filterSamples (data, len);
374
459
return p_print->write (data, len);
375
460
}
376
461
462
+ // / Get available space for writing
463
+ // / @return Number of bytes available for writing
377
464
int availableForWrite () override { return p_print->availableForWrite (); }
378
465
379
- // / Provides the data from all streams mixed together
466
+ // / Read and process audio data through the per-channel equalizer
467
+ // / @param data Buffer to store processed audio data
468
+ // / @param len Maximum number of bytes to read
469
+ // / @return Number of bytes actually read and processed
380
470
size_t readBytes (uint8_t *data, size_t len) override {
381
471
size_t result = 0 ;
382
472
if (p_stream != nullptr ) {
@@ -386,26 +476,26 @@ class Equalizer3BandsPerChannel : public ModifyingStream {
386
476
return result;
387
477
}
388
478
479
+ // / Get available data for reading
480
+ // / @return Number of bytes available for reading
389
481
int available () override {
390
482
return p_stream != nullptr ? p_stream->available () : 0 ;
391
483
}
392
484
393
485
protected:
394
- ConfigEqualizer3Bands cfg;
395
- ConfigEqualizer3Bands *p_cfg = &cfg;
396
- const float vsa = (1.0 / 4294967295.0 ); // Very small amount (Denormal Fix)
397
- Print *p_print = nullptr ; // support for write
398
- Stream *p_stream = nullptr ; // support for write
399
- int max_state_count = 0 ;
400
-
401
- // Per-channel frequency settings using Vector
402
- Vector<int > freq_low;
403
- Vector<int > freq_high;
404
-
405
- // Per-channel gain controls using Vector
406
- Vector<float > gain_low;
407
- Vector<float > gain_medium;
408
- Vector<float > gain_high;
486
+ ConfigEqualizer3Bands cfg; // /< Default configuration instance
487
+ ConfigEqualizer3Bands *p_cfg = &cfg; // /< Pointer to active configuration
488
+ const float vsa = (1.0 / 4294967295.0 ); // /< Very small amount for denormal fix
489
+ Print *p_print = nullptr ; // /< Output stream for write operations
490
+ Stream *p_stream = nullptr ; // /< Input/output stream for read operations
491
+ int max_state_count = 0 ; // /< Maximum number of allocated channel states
492
+
493
+ // Per-channel frequency and gain settings using Vector containers
494
+ Vector<int > freq_low; // /< Low frequency cutoffs per channel (Hz)
495
+ Vector<int > freq_high; // /< High frequency cutoffs per channel (Hz)
496
+ Vector<float > gain_low; // /< Low frequency gains per channel
497
+ Vector<float > gain_medium; // /< Medium frequency gains per channel
498
+ Vector<float > gain_high; // /< High frequency gains per channel
409
499
410
500
struct EQSTATE {
411
501
// Filter #1 (Low band)
0 commit comments