arduino-audio-tools
Loading...
Searching...
No Matches
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
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy