arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Modules Pages
Equalizer.h
1#pragma once
2#include <math.h>
3
4#include "AudioToolsConfig.h"
5#include "AudioTools/CoreAudio/AudioOutput.h"
6#include "AudioTools/CoreAudio/AudioStreams.h"
7
14namespace audio_tools {
15
38 channels = 2;
39 bits_per_sample = 16;
40 sample_rate = 44100;
41 }
42
44 int freq_low = 880;
45
47 int freq_high = 5000;
48
50 float gain_low = 1.0;
51
53 float gain_medium = 1.0;
54
56 float gain_high = 1.0;
57};
58
81 public:
85
89
93 setOutput(out);
94 out.addNotifyAudioChange(*this);
95 }
96
100 setStream(stream);
101 stream.addNotifyAudioChange(*this);
102 }
103
105 if (state != nullptr) delete[] state;
106 }
107
110 void setStream(Stream &io) override {
111 p_print = &io;
112 p_stream = &io;
113 };
114
117 void setOutput(Print &out) override { p_print = &out; }
118
122
126
131 p_cfg = &config;
132 return begin();
133 }
134
135 bool begin(){
137 if (state != nullptr) delete[] state;
138 state = new EQSTATE[p_cfg->channels];
140 }
141
142 // Setup state
143 for (int j = 0; j < max_state_count; j++) {
144 memset(&state[j], 0, sizeof(EQSTATE));
145
146 // Calculate filter cutoff frequencies
147 state[j].lf =
148 2 *
149 sin((float)PI * ((float)p_cfg->freq_low / (float)p_cfg->sample_rate));
150 state[j].hf = 2 * sin((float)PI * ((float)p_cfg->freq_high /
151 (float)p_cfg->sample_rate));
152 }
153 is_active = true;
154 return true;
155 }
156
157 void end(){
158 is_active = false;
159 }
160
163 virtual void setAudioInfo(AudioInfo info) override {
165 p_cfg->channels = info.channels;
167 begin(*p_cfg);
168 }
169
174 size_t write(const uint8_t *data, size_t len) override {
175 filterSamples(data, len);
176 return p_print->write(data, len);
177 }
178
181 int availableForWrite() override { return p_print->availableForWrite(); }
182
187 size_t readBytes(uint8_t *data, size_t len) override {
188 size_t result = 0;
189 if (p_stream != nullptr) {
190 result = p_stream->readBytes(data, len);
191 filterSamples(data, len);
192 }
193 return result;
194 }
195
198 int available() override {
199 return p_stream != nullptr ? p_stream->available() : 0;
200 }
201
202 protected:
203 bool is_active = false;
206 const float vsa = (1.0 / 4294967295.0);
207 Print *p_print = nullptr;
208 Stream *p_stream = nullptr;
210
212 struct EQSTATE {
213 // Filter #1 (Low band) - 4-pole low-pass filter
214 float lf;
215 float f1p0;
216 float f1p1;
217 float f1p2;
218 float f1p3;
219
220 // Filter #2 (High band) - 4-pole high-pass filter
221 float hf;
222 float f2p0;
223 float f2p1;
224 float f2p2;
225 float f2p3;
226
227 // Sample history buffer for filter calculations
228 float sdm1;
229 float sdm2;
230 float sdm3;
231
232 } *state = nullptr;
233
237 void filterSamples(const uint8_t *data, size_t len) {
238 // no filter if not active
239 if (!is_active) return;
240
241
242 // process samples
243 switch (p_cfg->bits_per_sample) {
244 case 16: {
245 int16_t *p_dataT = (int16_t *)data;
246 size_t sample_count = len / sizeof(int16_t);
247 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
248 for (int ch = 0; ch < p_cfg->channels; ch++) {
249 p_dataT[j + ch] = NumberConverter::fromFloat(sample(state[ch], NumberConverter::toFloat(p_dataT[j + ch], 16)), 16);
250 }
251 }
252 } break;
253 case 24: {
254 int24_t *p_dataT = (int24_t *)data;
255 size_t sample_count = len / sizeof(int24_t);
256 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
257 for (int ch = 0; ch < p_cfg->channels; ch++) {
258 p_dataT[j + ch] = NumberConverter::fromFloat(sample(state[ch], NumberConverter::toFloat(p_dataT[j + ch], 24)), 24);
259 }
260 }
261 } break;
262 case 32: {
263 int32_t *p_dataT = (int32_t *)data;
264 size_t sample_count = len / sizeof(int32_t);
265 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
266 for (int ch = 0; ch < p_cfg->channels; ch++) {
267 p_dataT[j + ch] = NumberConverter::fromFloat(sample(state[ch], NumberConverter::toFloat(p_dataT[j + ch], 32)), 32);
268 }
269 }
270 } break;
271
272 default:
273 LOGE("Only 16 bits supported: %d", p_cfg->bits_per_sample);
274 break;
275 }
276 }
277
282 float sample(EQSTATE &es, float sample) {
283 // Locals
284 float l, m, h; // Low / Mid / High - Sample Values
285 // Filter #1 (lowpass)
286 es.f1p0 += (es.lf * (sample - es.f1p0)) + vsa;
287 es.f1p1 += (es.lf * (es.f1p0 - es.f1p1));
288 es.f1p2 += (es.lf * (es.f1p1 - es.f1p2));
289 es.f1p3 += (es.lf * (es.f1p2 - es.f1p3));
290
291 l = es.f1p3;
292
293 // Filter #2 (highpass)
294 es.f2p0 += (es.hf * (sample - es.f2p0)) + vsa;
295 es.f2p1 += (es.hf * (es.f2p0 - es.f2p1));
296 es.f2p2 += (es.hf * (es.f2p1 - es.f2p2));
297 es.f2p3 += (es.hf * (es.f2p2 - es.f2p3));
298
299 h = es.sdm3 - es.f2p3;
300 // Calculate midrange (signal - (low + high))
301 m = es.sdm3 - (h + l);
302 // Scale, Combine and store
303 l *= p_cfg->gain_low;
304 m *= p_cfg->gain_medium;
305 h *= p_cfg->gain_high;
306
307 // Shuffle history buffer
308 es.sdm3 = es.sdm2;
309 es.sdm2 = es.sdm1;
310 es.sdm1 = sample;
311
312 // Return result
313 return (l + m + h);
314 }
315};
316
326 public:
330
334
341
345 setStream(stream);
346 stream.addNotifyAudioChange(*this);
347 }
348
350 if (state != nullptr) delete[] state;
351 }
352
354 void setStream(Stream &io) override {
355 p_print = &io;
356 p_stream = &io;
357 };
358
360 void setOutput(Print &out) override { p_print = &out; }
361
362 ConfigEqualizer3Bands &config() { return cfg; }
363
364 ConfigEqualizer3Bands &defaultConfig() { return config(); }
365
370 p_cfg = &config;
371 return begin();
372 }
373
376 bool begin(){
377 // Ensure per-channel arrays are allocated
379
380 // Ensure that EQSTATE is allocated
382 if (state != nullptr) delete[] state;
383 state = new EQSTATE[p_cfg->channels];
385 }
386
387 // Setup state for each channel with its own parameters
388 for (int j = 0; j < p_cfg->channels; j++) {
389 memset(&state[j], 0, sizeof(EQSTATE));
390
391 // Calculate filter cutoff frequencies per channel
392 state[j].lf = 2 * sin((float)PI * ((float)freq_low[j] / (float)p_cfg->sample_rate));
393 state[j].hf = 2 * sin((float)PI * ((float)freq_high[j] / (float)p_cfg->sample_rate));
394 }
395 is_active = true;
396 return true;
397 }
398
399 void end() override {
400 is_active = false;
401 }
402
403 virtual void setAudioInfo(AudioInfo info) override {
405 p_cfg->channels = info.channels;
407 begin(*p_cfg);
408 }
409
414 void setChannelFrequencies(int channel, int freq_low_val, int freq_high_val) {
416 if (channel >= 0 && channel < p_cfg->channels && !freq_low.empty()) {
417 freq_low[channel] = freq_low_val;
418 freq_high[channel] = freq_high_val;
419
420 // Recalculate filter coefficients for this channel
421 if (state != nullptr) {
422 state[channel].lf = 2 * sin((float)PI * ((float)freq_low_val / (float)p_cfg->sample_rate));
423 state[channel].hf = 2 * sin((float)PI * ((float)freq_high_val / (float)p_cfg->sample_rate));
424 }
425 }
426 }
427
433 void setChannelGains(int channel, float gain_low_val, float gain_medium_val, float gain_high_val) {
435 if (channel >= 0 && channel < p_cfg->channels && !gain_low.empty()) {
436 gain_low[channel] = gain_low_val;
437 gain_medium[channel] = gain_medium_val;
438 gain_high[channel] = gain_high_val;
439 }
440 }
441
447 bool getChannelFrequencies(int channel, int &freq_low_val, int &freq_high_val) {
448 if (channel >= 0 && channel < p_cfg->channels && !freq_low.empty()) {
449 freq_low_val = freq_low[channel];
450 freq_high_val = freq_high[channel];
451 return true;
452 }
453 return false;
454 }
455
462 bool getChannelGains(int channel, float &gain_low_val, float &gain_medium_val, float &gain_high_val) {
463 if (channel >= 0 && channel < p_cfg->channels && !gain_low.empty()) {
464 gain_low_val = gain_low[channel];
465 gain_medium_val = gain_medium[channel];
466 gain_high_val = gain_high[channel];
467 return true;
468 }
469 return false;
470 }
471
476 size_t write(const uint8_t *data, size_t len) override {
477 filterSamples(data, len);
478 return p_print->write(data, len);
479 }
480
483 int availableForWrite() override { return p_print->availableForWrite(); }
484
489 size_t readBytes(uint8_t *data, size_t len) override {
490 size_t result = 0;
491 if (p_stream != nullptr) {
492 result = p_stream->readBytes(data, len);
493 filterSamples(data, len);
494 }
495 return result;
496 }
497
500 int available() override {
501 return p_stream != nullptr ? p_stream->available() : 0;
502 }
503
504 protected:
505 bool is_active = false;
508 const float vsa = (1.0 / 4294967295.0);
509 Print *p_print = nullptr;
510 Stream *p_stream = nullptr;
512
513 // Per-channel frequency and gain settings using Vector containers
519
520 struct EQSTATE {
521 // Filter #1 (Low band)
522 float lf; // Frequency
523 float f1p0; // Poles ...
524 float f1p1;
525 float f1p2;
526 float f1p3;
527
528 // Filter #2 (High band)
529 float hf; // Frequency
530 float f2p0; // Poles ...
531 float f2p1;
532 float f2p2;
533 float f2p3;
534
535 // Sample history buffer
536 float sdm1; // Sample data minus 1
537 float sdm2; // 2
538 float sdm3; // 3
539
540 } *state = nullptr;
541
544 if (freq_low.size() != p_cfg->channels) {
546 }
547 }
548
551 void allocateChannelArrays(int num_channels) {
552 // Resize all vectors to accommodate the number of channels
553 freq_low.resize(num_channels);
554 freq_high.resize(num_channels);
555 gain_low.resize(num_channels);
556 gain_medium.resize(num_channels);
557 gain_high.resize(num_channels);
558
559 // Initialize with config default values
560 for (int i = 0; i < num_channels; i++) {
561 freq_low[i] = p_cfg->freq_low;
563 gain_low[i] = p_cfg->gain_low;
566 }
567 }
568
572 void filterSamples(const uint8_t *data, size_t len) {
573 if (!is_active) return;
574 switch (p_cfg->bits_per_sample) {
575 case 16: {
576 int16_t *p_dataT = (int16_t *)data;
577 size_t sample_count = len / sizeof(int16_t);
578 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
579 for (int ch = 0; ch < p_cfg->channels; ch++) {
580 p_dataT[j + ch] = NumberConverter::fromFloat(sample(ch, NumberConverter::toFloat(p_dataT[j + ch], 16)), 16);
581 }
582 }
583 } break;
584 case 24: {
585 int24_t *p_dataT = (int24_t *)data;
586 size_t sample_count = len / sizeof(int24_t);
587 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
588 for (int ch = 0; ch < p_cfg->channels; ch++) {
589 p_dataT[j + ch] = NumberConverter::fromFloat(sample(ch, NumberConverter::toFloat(p_dataT[j + ch], 24)), 24);
590 }
591 }
592 } break;
593 case 32: {
594 int32_t *p_dataT = (int32_t *)data;
595 size_t sample_count = len / sizeof(int32_t);
596 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
597 for (int ch = 0; ch < p_cfg->channels; ch++) {
598 p_dataT[j + ch] = NumberConverter::fromFloat(sample(ch, NumberConverter::toFloat(p_dataT[j + ch], 32)), 32);
599 }
600 }
601 } break;
602
603 default:
604 LOGE("Only 16 bits supported: %d", p_cfg->bits_per_sample);
605 break;
606 }
607 }
608
613 float sample(int channel, float sample_val) {
614 EQSTATE &es = state[channel];
615
616 // Locals
617 float l, m, h; // Low / Mid / High - Sample Values
618
619 // Filter #1 (lowpass)
620 es.f1p0 += (es.lf * (sample_val - es.f1p0)) + vsa;
621 es.f1p1 += (es.lf * (es.f1p0 - es.f1p1));
622 es.f1p2 += (es.lf * (es.f1p1 - es.f1p2));
623 es.f1p3 += (es.lf * (es.f1p2 - es.f1p3));
624
625 l = es.f1p3;
626
627 // Filter #2 (highpass)
628 es.f2p0 += (es.hf * (sample_val - es.f2p0)) + vsa;
629 es.f2p1 += (es.hf * (es.f2p0 - es.f2p1));
630 es.f2p2 += (es.hf * (es.f2p1 - es.f2p2));
631 es.f2p3 += (es.hf * (es.f2p2 - es.f2p3));
632
633 h = es.sdm3 - es.f2p3;
634
635 // Calculate midrange (signal - (low + high))
636 m = es.sdm3 - (h + l);
637
638 // Scale with per-channel gains
639 l *= gain_low[channel];
640 m *= gain_medium[channel];
641 h *= gain_high[channel];
642
643 // Shuffle history buffer
644 es.sdm3 = es.sdm2;
645 es.sdm2 = es.sdm1;
646 es.sdm1 = sample_val;
647
648 // Return result
649 return (l + m + h);
650 }
651};
652
653} // namespace audio_tools
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition AudioTypes.h:151
Abstract Audio Ouptut class.
Definition AudioOutput.h:22
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:122
3 Band Equalizer with identical settings for all channels
Definition Equalizer.h:80
int max_state_count
Maximum number of allocated channel states.
Definition Equalizer.h:209
void setOutput(Print &out) override
Definition Equalizer.h:117
Equalizer3Bands(AudioStream &stream)
Definition Equalizer.h:99
void filterSamples(const uint8_t *data, size_t len)
Definition Equalizer.h:237
bool is_active
Indicates if the equalizer is active.
Definition Equalizer.h:203
size_t readBytes(uint8_t *data, size_t len) override
Definition Equalizer.h:187
const float vsa
Very small amount for denormal fix.
Definition Equalizer.h:206
float sample(EQSTATE &es, float sample)
Definition Equalizer.h:282
bool begin(ConfigEqualizer3Bands &config)
Definition Equalizer.h:130
int available() override
Definition Equalizer.h:198
size_t write(const uint8_t *data, size_t len) override
Definition Equalizer.h:174
ConfigEqualizer3Bands * p_cfg
Pointer to active configuration.
Definition Equalizer.h:205
Stream * p_stream
Input/output stream for read operations.
Definition Equalizer.h:208
int availableForWrite() override
Definition Equalizer.h:181
Equalizer3Bands(Print &out)
Definition Equalizer.h:84
ConfigEqualizer3Bands & defaultConfig()
Definition Equalizer.h:125
virtual void setAudioInfo(AudioInfo info) override
Definition Equalizer.h:163
Equalizer3Bands(Stream &in)
Definition Equalizer.h:88
Print * p_print
Output stream for write operations.
Definition Equalizer.h:207
void setStream(Stream &io) override
Definition Equalizer.h:110
ConfigEqualizer3Bands cfg
Default configuration instance.
Definition Equalizer.h:204
Equalizer3Bands(AudioOutput &out)
Definition Equalizer.h:92
ConfigEqualizer3Bands & config()
Definition Equalizer.h:121
3 Band Equalizer with per-channel frequency and gain control Allows independent frequency and gain se...
Definition Equalizer.h:325
int max_state_count
Maximum number of allocated channel states.
Definition Equalizer.h:511
void setOutput(Print &out) override
Defines/Changes the output target.
Definition Equalizer.h:360
bool getChannelGains(int channel, float &gain_low_val, float &gain_medium_val, float &gain_high_val)
Definition Equalizer.h:462
bool getChannelFrequencies(int channel, int &freq_low_val, int &freq_high_val)
Definition Equalizer.h:447
float sample(int channel, float sample_val)
Definition Equalizer.h:613
Equalizer3BandsPerChannel(Print &out)
Definition Equalizer.h:329
Equalizer3BandsPerChannel(AudioOutput &out)
Definition Equalizer.h:337
void allocateChannelArrays(int num_channels)
Definition Equalizer.h:551
void filterSamples(const uint8_t *data, size_t len)
Definition Equalizer.h:572
size_t readBytes(uint8_t *data, size_t len) override
Definition Equalizer.h:489
const float vsa
Very small amount for denormal fix.
Definition Equalizer.h:508
Vector< int > freq_high
High frequency cutoffs per channel (Hz)
Definition Equalizer.h:515
bool begin()
Definition Equalizer.h:376
int available() override
Definition Equalizer.h:500
size_t write(const uint8_t *data, size_t len) override
Definition Equalizer.h:476
ConfigEqualizer3Bands * p_cfg
Pointer to active configuration.
Definition Equalizer.h:507
Stream * p_stream
Input/output stream for read operations.
Definition Equalizer.h:510
int availableForWrite() override
Definition Equalizer.h:483
bool begin(ConfigEqualizer3Bands config)
Definition Equalizer.h:369
Vector< float > gain_high
High frequency gains per channel.
Definition Equalizer.h:518
Vector< float > gain_medium
Medium frequency gains per channel.
Definition Equalizer.h:517
Vector< float > gain_low
Low frequency gains per channel.
Definition Equalizer.h:516
void setChannelFrequencies(int channel, int freq_low_val, int freq_high_val)
Definition Equalizer.h:414
virtual void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition Equalizer.h:403
Equalizer3BandsPerChannel(AudioStream &stream)
Definition Equalizer.h:344
void ensureChannelArraysAllocated()
Ensures that per-channel arrays are allocated and properly sized.
Definition Equalizer.h:543
Print * p_print
Output stream for write operations.
Definition Equalizer.h:509
Vector< int > freq_low
Low frequency cutoffs per channel (Hz)
Definition Equalizer.h:514
void setStream(Stream &io) override
Defines/Changes the input & output.
Definition Equalizer.h:354
ConfigEqualizer3Bands cfg
Default configuration instance.
Definition Equalizer.h:506
void setChannelGains(int channel, float gain_low_val, float gain_medium_val, float gain_high_val)
Definition Equalizer.h:433
Equalizer3BandsPerChannel(Stream &in)
Definition Equalizer.h:333
Abstract class: Objects can be put into a pipleline.
Definition AudioStreams.h:68
static float toFloat(int32_t value, int bits)
Convert an integer integer autio type to a float (with max 1.0)
Definition AudioTypes.h:374
static int32_t fromFloat(float value, int bits)
Convert a float (with max 1.0) to an integer autio type.
Definition AudioTypes.h:379
Definition NoArduino.h:62
Definition NoArduino.h:142
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition Int24_4bytes_t.h:16
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59
Configuration for 3 Band Equalizer.
Definition Equalizer.h:36
int freq_high
High-pass filter cutoff frequency in Hz. Frequencies above this are considered "high".
Definition Equalizer.h:47
int freq_low
Low-pass filter cutoff frequency in Hz. Frequencies below this are considered "low".
Definition Equalizer.h:44
float gain_low
Gain multiplier for low frequencies (0.0-2.0, where 1.0 = unity gain)
Definition Equalizer.h:50
float gain_medium
Gain multiplier for medium frequencies (0.0-2.0, where 1.0 = unity gain)
Definition Equalizer.h:53
float gain_high
Gain multiplier for high frequencies (0.0-2.0, where 1.0 = unity gain)
Definition Equalizer.h:56
Filter state for each channel.
Definition Equalizer.h:212
float f2p1
Filter pole 1.
Definition Equalizer.h:223
float hf
High frequency cutoff coefficient.
Definition Equalizer.h:221
float f1p2
Filter pole 2.
Definition Equalizer.h:217
float sdm1
Sample data minus 1 (previous sample)
Definition Equalizer.h:228
float f2p3
Filter pole 3.
Definition Equalizer.h:225
float f2p0
Filter pole 0.
Definition Equalizer.h:222
float lf
Low frequency cutoff coefficient.
Definition Equalizer.h:214
float sdm3
Sample data minus 3.
Definition Equalizer.h:230
float f1p1
Filter pole 1.
Definition Equalizer.h:216
float f1p0
Filter pole 0.
Definition Equalizer.h:215
float f1p3
Filter pole 3.
Definition Equalizer.h:218
float f2p2
Filter pole 2.
Definition Equalizer.h:224
float sdm2
Sample data minus 2.
Definition Equalizer.h:229