arduino-audio-tools
Loading...
Searching...
No Matches
Equalizer3Bands.h
1#pragma once
2#include <math.h>
3
4#include "AudioTools/CoreAudio/AudioOutput.h"
5#include "AudioTools/CoreAudio/AudioStreams.h"
6#include "AudioToolsConfig.h"
7
14namespace audio_tools {
15
38 channels = 2;
39 bits_per_sample = 16;
40 sample_rate = 44100;
41 }
42
45 int freq_low = 880;
46
49 int freq_high = 5000;
50
52 float gain_low = 1.0;
53
55 float gain_medium = 1.0;
56
58 float gain_high = 1.0;
59};
60
86 public:
90
94
99 setOutput(out);
100 out.addNotifyAudioChange(*this);
101 }
102
107 setStream(stream);
108 stream.addNotifyAudioChange(*this);
109 }
110
112 if (state != nullptr) delete[] state;
113 }
114
117 void setStream(Stream& io) override {
118 p_print = &io;
119 p_stream = &io;
120 };
121
124 void setOutput(Print& out) override { p_print = &out; }
125
129
133
138 p_cfg = &config;
139 return begin();
140 }
141
142 bool begin() {
144 if (state != nullptr) delete[] state;
145 state = new EQSTATE[p_cfg->channels];
147 }
148
149 // Setup state
150 for (int j = 0; j < max_state_count; j++) {
151 memset(&state[j], 0, sizeof(EQSTATE));
152
153 // Calculate filter cutoff frequencies
154 state[j].lf =
155 2 *
156 sin((float)PI * ((float)p_cfg->freq_low / (float)p_cfg->sample_rate));
157 state[j].hf = 2 * sin((float)PI * ((float)p_cfg->freq_high /
158 (float)p_cfg->sample_rate));
159 }
160 is_active = true;
161 return true;
162 }
163
164 void end() { is_active = false; }
165
168 virtual void setAudioInfo(AudioInfo info) override {
170 p_cfg->channels = info.channels;
172 begin(*p_cfg);
173 }
174
179 size_t write(const uint8_t* data, size_t len) override {
180 filterSamples(data, len);
181 return p_print->write(data, len);
182 }
183
186 int availableForWrite() override { return p_print->availableForWrite(); }
187
192 size_t readBytes(uint8_t* data, size_t len) override {
193 size_t result = 0;
194 if (p_stream != nullptr) {
195 result = p_stream->readBytes(data, len);
196 filterSamples(data, len);
197 }
198 return result;
199 }
200
203 int available() override {
204 return p_stream != nullptr ? p_stream->available() : 0;
205 }
206
207 protected:
208 bool is_active = false;
211 const float vsa =
212 (1.0 / 4294967295.0);
213 Print* p_print = nullptr;
214 Stream* p_stream = nullptr;
216
218 struct EQSTATE {
219 // Filter #1 (Low band) - 4-pole low-pass filter
220 float lf;
221 float f1p0;
222 float f1p1;
223 float f1p2;
224 float f1p3;
225
226 // Filter #2 (High band) - 4-pole high-pass filter
227 float hf;
228 float f2p0;
229 float f2p1;
230 float f2p2;
231 float f2p3;
232
233 // Sample history buffer for filter calculations
234 float sdm1;
235 float sdm2;
236 float sdm3;
237
238 }* state = nullptr;
239
243 void filterSamples(const uint8_t* data, size_t len) {
244 // no filter if not active
245 if (!is_active) return;
246
247 // process samples
248 switch (p_cfg->bits_per_sample) {
249 case 16: {
250 int16_t* p_dataT = (int16_t*)data;
251 size_t sample_count = len / sizeof(int16_t);
252 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
253 for (int ch = 0; ch < p_cfg->channels; ch++) {
254 p_dataT[j + ch] = NumberConverter::fromFloat(
255 sample(state[ch],
256 NumberConverter::toFloat(p_dataT[j + ch], 16)),
257 16);
258 }
259 }
260 } break;
261 case 24: {
262 int24_t* p_dataT = (int24_t*)data;
263 size_t sample_count = len / sizeof(int24_t);
264 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
265 for (int ch = 0; ch < p_cfg->channels; ch++) {
266 p_dataT[j + ch] = NumberConverter::fromFloat(
267 sample(state[ch],
268 NumberConverter::toFloat(p_dataT[j + ch], 24)),
269 24);
270 }
271 }
272 } break;
273 case 32: {
274 int32_t* p_dataT = (int32_t*)data;
275 size_t sample_count = len / sizeof(int32_t);
276 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
277 for (int ch = 0; ch < p_cfg->channels; ch++) {
278 p_dataT[j + ch] = NumberConverter::fromFloat(
279 sample(state[ch],
280 NumberConverter::toFloat(p_dataT[j + ch], 32)),
281 32);
282 }
283 }
284 } break;
285
286 default:
287 LOGE("Only 16 bits supported: %d", p_cfg->bits_per_sample);
288 break;
289 }
290 }
291
296 float sample(EQSTATE& es, float sample) {
297 // Locals
298 float l, m, h; // Low / Mid / High - Sample Values
299 // Filter #1 (lowpass)
300 es.f1p0 += (es.lf * (sample - es.f1p0)) + vsa;
301 es.f1p1 += (es.lf * (es.f1p0 - es.f1p1));
302 es.f1p2 += (es.lf * (es.f1p1 - es.f1p2));
303 es.f1p3 += (es.lf * (es.f1p2 - es.f1p3));
304
305 l = es.f1p3;
306
307 // Filter #2 (highpass)
308 es.f2p0 += (es.hf * (sample - es.f2p0)) + vsa;
309 es.f2p1 += (es.hf * (es.f2p0 - es.f2p1));
310 es.f2p2 += (es.hf * (es.f2p1 - es.f2p2));
311 es.f2p3 += (es.hf * (es.f2p2 - es.f2p3));
312
313 h = es.sdm3 - es.f2p3;
314 // Calculate midrange (signal - (low + high))
315 m = es.sdm3 - (h + l);
316 // Scale, Combine and store
317 l *= p_cfg->gain_low;
318 m *= p_cfg->gain_medium;
319 h *= p_cfg->gain_high;
320
321 // Shuffle history buffer
322 es.sdm3 = es.sdm2;
323 es.sdm2 = es.sdm1;
324 es.sdm1 = sample;
325
326 // Return result
327 return (l + m + h);
328 }
329};
330
340 public:
344
348
356
361 setStream(stream);
362 stream.addNotifyAudioChange(*this);
363 }
364
366 if (state != nullptr) delete[] state;
367 }
368
370 void setStream(Stream& io) override {
371 p_print = &io;
372 p_stream = &io;
373 };
374
376 void setOutput(Print& out) override { p_print = &out; }
377
378 ConfigEqualizer3Bands& config() { return *p_cfg; }
379
380 ConfigEqualizer3Bands defaultConfig() { return cfg; }
381
386 p_cfg = &config;
387 return begin();
388 }
389
392 bool begin() {
393 // Ensure per-channel arrays are allocated
395
396 // Ensure that EQSTATE is allocated
398 if (state != nullptr) delete[] state;
399 state = new EQSTATE[p_cfg->channels];
401 }
402
403 // Setup state for each channel with its own parameters
404 for (int j = 0; j < p_cfg->channels; j++) {
405 memset(&state[j], 0, sizeof(EQSTATE));
406
407 // Calculate filter cutoff frequencies per channel
408 state[j].lf =
409 2 * sin((float)PI * ((float)freq_low[j] / (float)p_cfg->sample_rate));
410 state[j].hf = 2 * sin((float)PI *
411 ((float)freq_high[j] / (float)p_cfg->sample_rate));
412 }
413 is_active = true;
414 return true;
415 }
416
417 void end() override { is_active = false; }
418
419 virtual void setAudioInfo(AudioInfo info) override {
421 p_cfg->channels = info.channels;
423 begin(*p_cfg);
424 }
425
430 void setChannelFrequencies(int channel, int freq_low_val, int freq_high_val) {
432 if (channel >= 0 && channel < p_cfg->channels && !freq_low.empty()) {
433 freq_low[channel] = freq_low_val;
434 freq_high[channel] = freq_high_val;
435
436 // Recalculate filter coefficients for this channel
437 if (state != nullptr) {
438 state[channel].lf =
439 2 *
440 sin((float)PI * ((float)freq_low_val / (float)p_cfg->sample_rate));
441 state[channel].hf =
442 2 *
443 sin((float)PI * ((float)freq_high_val / (float)p_cfg->sample_rate));
444 }
445 }
446 }
447
453 void setChannelGains(int channel, float gain_low_val, float gain_medium_val,
454 float gain_high_val) {
456 if (channel >= 0 && channel < p_cfg->channels && !gain_low.empty()) {
457 gain_low[channel] = gain_low_val;
458 gain_medium[channel] = gain_medium_val;
459 gain_high[channel] = gain_high_val;
460 }
461 }
462
468 bool getChannelFrequencies(int channel, int& freq_low_val,
469 int& freq_high_val) {
470 if (channel >= 0 && channel < p_cfg->channels && !freq_low.empty()) {
471 freq_low_val = freq_low[channel];
472 freq_high_val = freq_high[channel];
473 return true;
474 }
475 return false;
476 }
477
484 bool getChannelGains(int channel, float& gain_low_val, float& gain_medium_val,
485 float& gain_high_val) {
486 if (channel >= 0 && channel < p_cfg->channels && !gain_low.empty()) {
487 gain_low_val = gain_low[channel];
488 gain_medium_val = gain_medium[channel];
489 gain_high_val = gain_high[channel];
490 return true;
491 }
492 return false;
493 }
494
499 size_t write(const uint8_t* data, size_t len) override {
500 filterSamples(data, len);
501 return p_print->write(data, len);
502 }
503
506 int availableForWrite() override { return p_print->availableForWrite(); }
507
512 size_t readBytes(uint8_t* data, size_t len) override {
513 size_t result = 0;
514 if (p_stream != nullptr) {
515 result = p_stream->readBytes(data, len);
516 filterSamples(data, len);
517 }
518 return result;
519 }
520
523 int available() override {
524 return p_stream != nullptr ? p_stream->available() : 0;
525 }
526
527 protected:
528 bool is_active = false;
531 const float vsa =
532 (1.0 / 4294967295.0);
533 Print* p_print = nullptr;
534 Stream* p_stream = nullptr;
536
537 // Per-channel frequency and gain settings using Vector containers
543
544 struct EQSTATE {
545 // Filter #1 (Low band)
546 float lf; // Frequency
547 float f1p0; // Poles ...
548 float f1p1;
549 float f1p2;
550 float f1p3;
551
552 // Filter #2 (High band)
553 float hf; // Frequency
554 float f2p0; // Poles ...
555 float f2p1;
556 float f2p2;
557 float f2p3;
558
559 // Sample history buffer
560 float sdm1; // Sample data minus 1
561 float sdm2; // 2
562 float sdm3; // 3
563
564 }* state = nullptr;
565
568 if (freq_low.size() != p_cfg->channels) {
570 }
571 }
572
575 void allocateChannelArrays(int num_channels) {
576 // Resize all vectors to accommodate the number of channels
577 freq_low.resize(num_channels);
578 freq_high.resize(num_channels);
579 gain_low.resize(num_channels);
580 gain_medium.resize(num_channels);
581 gain_high.resize(num_channels);
582
583 // Initialize with config default values
584 for (int i = 0; i < num_channels; i++) {
585 freq_low[i] = p_cfg->freq_low;
587 gain_low[i] = p_cfg->gain_low;
590 }
591 }
592
596 void filterSamples(const uint8_t* data, size_t len) {
597 if (!is_active) return;
598 switch (p_cfg->bits_per_sample) {
599 case 16: {
600 int16_t* p_dataT = (int16_t*)data;
601 size_t sample_count = len / sizeof(int16_t);
602 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
603 for (int ch = 0; ch < p_cfg->channels; ch++) {
604 p_dataT[j + ch] = NumberConverter::fromFloat(
605 sample(ch, NumberConverter::toFloat(p_dataT[j + ch], 16)), 16);
606 }
607 }
608 } break;
609 case 24: {
610 int24_t* p_dataT = (int24_t*)data;
611 size_t sample_count = len / sizeof(int24_t);
612 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
613 for (int ch = 0; ch < p_cfg->channels; ch++) {
614 p_dataT[j + ch] = NumberConverter::fromFloat(
615 sample(ch, NumberConverter::toFloat(p_dataT[j + ch], 24)), 24);
616 }
617 }
618 } break;
619 case 32: {
620 int32_t* p_dataT = (int32_t*)data;
621 size_t sample_count = len / sizeof(int32_t);
622 for (size_t j = 0; j < sample_count; j += p_cfg->channels) {
623 for (int ch = 0; ch < p_cfg->channels; ch++) {
624 p_dataT[j + ch] = NumberConverter::fromFloat(
625 sample(ch, NumberConverter::toFloat(p_dataT[j + ch], 32)), 32);
626 }
627 }
628 } break;
629
630 default:
631 LOGE("Only 16 bits supported: %d", p_cfg->bits_per_sample);
632 break;
633 }
634 }
635
641 float sample(int channel, float sample_val) {
642 EQSTATE& es = state[channel];
643
644 // Locals
645 float l, m, h; // Low / Mid / High - Sample Values
646
647 // Filter #1 (lowpass)
648 es.f1p0 += (es.lf * (sample_val - es.f1p0)) + vsa;
649 es.f1p1 += (es.lf * (es.f1p0 - es.f1p1));
650 es.f1p2 += (es.lf * (es.f1p1 - es.f1p2));
651 es.f1p3 += (es.lf * (es.f1p2 - es.f1p3));
652
653 l = es.f1p3;
654
655 // Filter #2 (highpass)
656 es.f2p0 += (es.hf * (sample_val - es.f2p0)) + vsa;
657 es.f2p1 += (es.hf * (es.f2p0 - es.f2p1));
658 es.f2p2 += (es.hf * (es.f2p1 - es.f2p2));
659 es.f2p3 += (es.hf * (es.f2p2 - es.f2p3));
660
661 h = es.sdm3 - es.f2p3;
662
663 // Calculate midrange (signal - (low + high))
664 m = es.sdm3 - (h + l);
665
666 // Scale with per-channel gains
667 l *= gain_low[channel];
668 m *= gain_medium[channel];
669 h *= gain_high[channel];
670
671 // Shuffle history buffer
672 es.sdm3 = es.sdm2;
673 es.sdm2 = es.sdm1;
674 es.sdm1 = sample_val;
675
676 // Return result
677 return (l + m + h);
678 }
679};
680
681} // namespace audio_tools
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition AudioTypes.h:153
Abstract Audio Ouptut class.
Definition AudioOutput.h:25
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 Equalizer3Bands.h:85
int max_state_count
Maximum number of allocated channel states.
Definition Equalizer3Bands.h:215
void setOutput(Print &out) override
Definition Equalizer3Bands.h:124
Equalizer3Bands(AudioStream &stream)
Definition Equalizer3Bands.h:106
void filterSamples(const uint8_t *data, size_t len)
Definition Equalizer3Bands.h:243
bool is_active
Indicates if the equalizer is active.
Definition Equalizer3Bands.h:208
size_t readBytes(uint8_t *data, size_t len) override
Definition Equalizer3Bands.h:192
const float vsa
Very small amount for denormal fix.
Definition Equalizer3Bands.h:211
float sample(EQSTATE &es, float sample)
Definition Equalizer3Bands.h:296
bool begin(ConfigEqualizer3Bands &config)
Definition Equalizer3Bands.h:137
int available() override
Definition Equalizer3Bands.h:203
size_t write(const uint8_t *data, size_t len) override
Definition Equalizer3Bands.h:179
ConfigEqualizer3Bands * p_cfg
Pointer to active configuration.
Definition Equalizer3Bands.h:210
ConfigEqualizer3Bands defaultConfig()
Definition Equalizer3Bands.h:132
Stream * p_stream
Input/output stream for read operations.
Definition Equalizer3Bands.h:214
int availableForWrite() override
Definition Equalizer3Bands.h:186
Equalizer3Bands(Print &out)
Definition Equalizer3Bands.h:89
virtual void setAudioInfo(AudioInfo info) override
Definition Equalizer3Bands.h:168
Equalizer3Bands(Stream &in)
Definition Equalizer3Bands.h:93
Print * p_print
Output stream for write operations.
Definition Equalizer3Bands.h:213
void setStream(Stream &io) override
Definition Equalizer3Bands.h:117
ConfigEqualizer3Bands cfg
Default configuration instance.
Definition Equalizer3Bands.h:209
Equalizer3Bands(AudioOutput &out)
Definition Equalizer3Bands.h:98
ConfigEqualizer3Bands & config()
Definition Equalizer3Bands.h:128
3 Band Equalizer with per-channel frequency and gain control Allows independent frequency and gain se...
Definition Equalizer3Bands.h:339
int max_state_count
Maximum number of allocated channel states.
Definition Equalizer3Bands.h:535
void setOutput(Print &out) override
Defines/Changes the output target.
Definition Equalizer3Bands.h:376
bool getChannelGains(int channel, float &gain_low_val, float &gain_medium_val, float &gain_high_val)
Definition Equalizer3Bands.h:484
bool getChannelFrequencies(int channel, int &freq_low_val, int &freq_high_val)
Definition Equalizer3Bands.h:468
float sample(int channel, float sample_val)
Definition Equalizer3Bands.h:641
Equalizer3BandsPerChannel(Print &out)
Definition Equalizer3Bands.h:343
Equalizer3BandsPerChannel(AudioOutput &out)
Definition Equalizer3Bands.h:352
void allocateChannelArrays(int num_channels)
Definition Equalizer3Bands.h:575
void filterSamples(const uint8_t *data, size_t len)
Definition Equalizer3Bands.h:596
size_t readBytes(uint8_t *data, size_t len) override
Definition Equalizer3Bands.h:512
const float vsa
Very small amount for denormal fix.
Definition Equalizer3Bands.h:531
Vector< int > freq_high
High frequency cutoffs per channel (Hz)
Definition Equalizer3Bands.h:539
bool begin()
Definition Equalizer3Bands.h:392
bool begin(ConfigEqualizer3Bands &config)
Definition Equalizer3Bands.h:385
int available() override
Definition Equalizer3Bands.h:523
size_t write(const uint8_t *data, size_t len) override
Definition Equalizer3Bands.h:499
ConfigEqualizer3Bands * p_cfg
Pointer to active configuration.
Definition Equalizer3Bands.h:530
Stream * p_stream
Input/output stream for read operations.
Definition Equalizer3Bands.h:534
int availableForWrite() override
Definition Equalizer3Bands.h:506
Vector< float > gain_high
High frequency gains per channel.
Definition Equalizer3Bands.h:542
Vector< float > gain_medium
Medium frequency gains per channel.
Definition Equalizer3Bands.h:541
Vector< float > gain_low
Low frequency gains per channel.
Definition Equalizer3Bands.h:540
void setChannelFrequencies(int channel, int freq_low_val, int freq_high_val)
Definition Equalizer3Bands.h:430
virtual void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition Equalizer3Bands.h:419
Equalizer3BandsPerChannel(AudioStream &stream)
Definition Equalizer3Bands.h:360
void ensureChannelArraysAllocated()
Ensures that per-channel arrays are allocated and properly sized.
Definition Equalizer3Bands.h:567
Print * p_print
Output stream for write operations.
Definition Equalizer3Bands.h:533
Vector< int > freq_low
Low frequency cutoffs per channel (Hz)
Definition Equalizer3Bands.h:538
void setStream(Stream &io) override
Defines/Changes the input & output.
Definition Equalizer3Bands.h:370
ConfigEqualizer3Bands cfg
Default configuration instance.
Definition Equalizer3Bands.h:529
void setChannelGains(int channel, float gain_low_val, float gain_medium_val, float gain_high_val)
Definition Equalizer3Bands.h:453
Equalizer3BandsPerChannel(Stream &in)
Definition Equalizer3Bands.h:347
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:376
static int32_t fromFloat(float value, int bits)
Convert a float (with max 1.0) to an integer autio type.
Definition AudioTypes.h:381
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:55
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61
Configuration for 3 Band Equalizer.
Definition Equalizer3Bands.h:36
int freq_high
Definition Equalizer3Bands.h:49
int freq_low
Definition Equalizer3Bands.h:45
float gain_low
Gain multiplier for low frequencies (0.0-2.0, where 1.0 = unity gain)
Definition Equalizer3Bands.h:52
float gain_medium
Gain multiplier for medium frequencies (0.0-2.0, where 1.0 = unity gain)
Definition Equalizer3Bands.h:55
float gain_high
Gain multiplier for high frequencies (0.0-2.0, where 1.0 = unity gain)
Definition Equalizer3Bands.h:58
Filter state for each channel.
Definition Equalizer3Bands.h:218
float f2p1
Filter pole 1.
Definition Equalizer3Bands.h:229
float hf
High frequency cutoff coefficient.
Definition Equalizer3Bands.h:227
float f1p2
Filter pole 2.
Definition Equalizer3Bands.h:223
float sdm1
Sample data minus 1 (previous sample)
Definition Equalizer3Bands.h:234
float f2p3
Filter pole 3.
Definition Equalizer3Bands.h:231
float f2p0
Filter pole 0.
Definition Equalizer3Bands.h:228
float lf
Low frequency cutoff coefficient.
Definition Equalizer3Bands.h:220
float sdm3
Sample data minus 3.
Definition Equalizer3Bands.h:236
float f1p1
Filter pole 1.
Definition Equalizer3Bands.h:222
float f1p0
Filter pole 0.
Definition Equalizer3Bands.h:221
float f1p3
Filter pole 3.
Definition Equalizer3Bands.h:224
float f2p2
Filter pole 2.
Definition Equalizer3Bands.h:230
float sdm2
Sample data minus 2.
Definition Equalizer3Bands.h:235
Definition Equalizer3Bands.h:544