7#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h"
8#include "AudioTools/CoreAudio/AudioOutput.h"
9#include "AudioTools/CoreAudio/AudioStreams.h"
10#include "AudioToolsConfig.h"
43template <
typename SampleT = int16_t,
typename AccT = int64_t,
44 int NUM_TAPS = 128,
int NUM_BANDS = 12>
93 if (currentSampleRate <= 0) {
94 LOGE(
"Invalid sample rate: %d", currentSampleRate);
97 setupFrequencies(currentSampleRate);
101 activeKernel = kernelA;
102 updateKernel = kernelB;
105 initializeKernel(kernelA);
106 initializeKernel(kernelB);
121 bool rc = updateFIRKernel();
124 for (
int band = 0; band < NUM_BANDS; band++) {
133 currentSampleRate = 0;
142 float vol_db = volume < 0 ? map<float>(volume, -1.0f, 0.0f, -90.0f, 0.0f) : map<float>(volume, 0.0f, 1.0f, 0.0f, 12.0f);
151 if (band < 0 || band >= NUM_BANDS)
return false;
152 float db = min(gainDb, 12.0f);
153 db = max(db, -90.0f);
154 pendingGains[band] = db;
161 for (
int band = 0; band < NUM_BANDS; band++) {
169 if (band < 0 || band >= NUM_BANDS)
return 0.0f;
170 return map<float>(pendingGains[band], -12.0f, 12.0f, -1.0f, 1.0f);
177 if (band < 0 || band >= NUM_BANDS)
return 0.0f;
178 return pendingGains[band];
185 if (band < 0 || band >= NUM_BANDS)
return 0.0f;
186 return centerFreqs[band];
197 bool update() {
return updateFIRKernel(); }
199 size_t write(
const uint8_t* data,
size_t len)
override {
201 return filtered.write(data, len);
204 size_t readBytes(uint8_t* data,
size_t len)
override {
206 return filtered.readBytes(data, len);
212 volatile bool isUpdating =
false;
214 volatile bool gainsDirty =
false;
217 bool autoUpdate =
false;
224 void setKernel(
volatile int16_t* kernel) { activeKernel = kernel; }
226 SampleT process(SampleT sample)
override {
227 if (activeKernel ==
nullptr) {
228 LOGE(
"Kernel not set!");
232 xHistory[idxHist] = sample;
238 for (
int n = 0; n < NUM_TAPS; n++) {
240 acc += (AccT)xHistory[idx] * (AccT)activeKernel[n];
241 if (--idx < 0) idx = NUM_TAPS - 1;
244 if (++idxHist >= NUM_TAPS) idxHist = 0;
251 SampleT xHistory[NUM_TAPS] = {(SampleT)0};
253 volatile int16_t* activeKernel =
nullptr;
255 static inline SampleT fromQ15(AccT acc) {
257 if constexpr (std::numeric_limits<SampleT>::is_integer) {
259 if constexpr (std::numeric_limits<AccT>::is_integer) {
262 acc = acc / (AccT)(1 << 15);
266 const AccT hi = (AccT)std::numeric_limits<SampleT>::max();
267 const AccT lo = (AccT)std::numeric_limits<SampleT>::min();
268 if (acc > hi) acc = hi;
269 if (acc < lo) acc = lo;
273 return (SampleT)(acc / (AccT)(1 << 15));
279 static constexpr float Q15_SCALE = 32767.0f;
280 float centerFreqs[NUM_BANDS];
282 float gains[NUM_BANDS] = {0};
284 float pendingGains[NUM_BANDS] = {0};
288 float tempFloat[NUM_TAPS] = {0};
291 int16_t kernelA[NUM_TAPS];
292 int16_t kernelB[NUM_TAPS];
293 volatile int16_t* activeKernel;
294 volatile int16_t* updateKernel;
296 float windowCoeffs[NUM_TAPS];
297 int currentSampleRate = 0;
305 inline void enterCritical() {
311 inline void exitCritical() {
318 inline void maybeUpdateKernel() {
319 if (!autoUpdate)
return;
321 if (updateFIRKernel()) {
327 template <
typename T>
328 float map(T x, T in_min, T in_max, T out_min, T out_max) {
329 return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
333 float sinc(
float x) {
334 if (fabsf(x) < 1e-8f)
return 1.0f;
335 return sinf(PI * x) / (PI * x);
339 void setupFrequencies(
int sampleRate) {
340 if (NUM_BANDS <= 0)
return;
341 float fMin = log10f(20.0f);
342 float fMax = log10f(sampleRate / 2.0f);
343 if (NUM_BANDS == 1) {
344 centerFreqs[0] = powf(10.0f, (fMin + fMax) * 0.5f);
345 LOGD(
"Only one band: center frequency set to %.2f Hz", centerFreqs[0]);
348 float step = (fMax - fMin) / (
float)(NUM_BANDS - 1);
349 for (
int i = 0; i < NUM_BANDS; i++) {
350 centerFreqs[i] = powf(10.0f, fMin + step * (
float)i);
351 LOGD(
"Band %d: center frequency = %.2f Hz", i, centerFreqs[i]);
356 void preCalculateWindow() {
357 const float N_minus_1 = (float)(NUM_TAPS - 1);
358 for (
int n = 0; n < NUM_TAPS; n++) {
359 windowCoeffs[n] = 0.42f - 0.5f * cosf(2.0f * PI * n / N_minus_1) +
360 0.08f * cosf(4.0f * PI * n / N_minus_1);
365 void initializeKernel(
volatile int16_t* kernel) {
366 const int M = (NUM_TAPS - 1) / 2;
367 for (
int i = 0; i < NUM_TAPS; i++) {
369 kernel[i] = (int16_t)Q15_SCALE;
376 bool updateFIRKernel() {
377 if (currentSampleRate <= 0) {
378 LOGE(
"Invalid sample rate: %d", currentSampleRate);
395 memcpy(gains, pendingGains,
sizeof(gains));
398 memset(tempFloat, 0,
sizeof(tempFloat));
400 const int M = (NUM_TAPS - 1) / 2;
401 const float sampleRateFloat = (float)currentSampleRate;
406 for (
int i = 0; i < NUM_BANDS; i++) {
408 if (fabs(gains[i]) < 0.1f)
continue;
412 float linGain = powf(10.0f, gains[i] / 20.0f) - 1.0f;
415 float fL_hz = centerFreqs[i] * 0.707f;
416 float fH_hz = centerFreqs[i] * 1.414f;
423 float minBwHz = 4.0f * sampleRateFloat / (float)NUM_TAPS;
424 float actualBwHz = fH_hz - fL_hz;
425 if (actualBwHz < minBwHz) {
426 float expand = (minBwHz - actualBwHz) * 0.5f;
427 fL_hz = fL_hz - expand;
428 fH_hz = fH_hz + expand;
429 if (fL_hz < 1.0f) fL_hz = 1.0f;
432 float fL = fL_hz / sampleRateFloat;
433 float fH = fH_hz / sampleRateFloat;
436 if (fL < 0.0f) fL = 0.0f;
437 if (fH < 0.0f) fH = 0.0f;
438 if (fL > 0.5f) fL = 0.5f;
439 if (fH > 0.5f) fH = 0.5f;
440 if (fH <= fL)
continue;
446 float wCenter = 2.0f * PI * centerFreqs[i] / sampleRateFloat;
449 for (
int n = 0; n < NUM_TAPS; n++) {
450 float nM = (float)(n - M);
451 float bpW = ((2.0f * fH * sinc(2.0f * fH * nM)) -
452 (2.0f * fL * sinc(2.0f * fL * nM))) *
454 hReal += bpW * cosf(wCenter * n);
455 hImag -= bpW * sinf(wCenter * n);
457 float bpMag = sqrtf(hReal * hReal + hImag * hImag);
458 float normFactor = (bpMag > 1e-6f) ? (1.0f / bpMag) : 1.0f;
460 for (
int n = 0; n < NUM_TAPS; n++) {
461 float nM = (float)(n - M);
464 float window = windowCoeffs[n];
467 float bp = (2.0f * fH * sinc(2.0f * fH * nM)) -
468 (2.0f * fL * sinc(2.0f * fL * nM));
471 tempFloat[n] += bp * window * normFactor * linGain;
476 for (
int i = 0; i < NUM_TAPS; i++) {
479 int32_t q = (int32_t)(tempFloat[i] * Q15_SCALE);
482 if (q > 32767) q = 32767;
483 if (q < -32768) q = -32768;
484 updateKernel[i] = (int16_t)q;
490 volatile int16_t* temp = activeKernel;
491 activeKernel = updateKernel;
496 fir.setKernel(activeKernel);
506 LOGI(
"FIR kernel updated with new gains for %d bands /%d taps.", NUM_BANDS,