low-cut audio input filtering

* 40Hz low-cut and DC blocker filter - will remove any signal offsets and also removes rumbling noise up to 12db
* DC blocker set as default for all sources (prerequisite for later measuring sound pressure)

additional filtering options are in the making :-)
This commit is contained in:
Frank
2023-04-06 17:56:32 +02:00
parent 00e9c59295
commit b09077627e

View File

@@ -121,7 +121,7 @@ const float agcSampleSmooth[AGC_NUM_PRESETS] = { 1/12.f, 1/6.f, 1/16.f}; //
static AudioSource *audioSource = nullptr;
static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks.
static bool useBandPassFilter = false; // if true, enables a bandpass filter 80Hz-16Khz to remove noise. Applies before FFT.
static uint8_t useInputFilter = 0; // enables low-cut filtering. Applies before FFT.
//WLEDMM add experimental settings
static uint8_t micLevelMethod = 0; // 0=old "floating" miclev, 1=new "freeze" mode, 2=fast freeze mode (mode 2 may not work for you)
@@ -183,7 +183,7 @@ static TaskHandle_t FFT_Task = nullptr;
static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = {
{ 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f }, // 0 default from SR WLED
// { 1.30f, 1.32f, 1.40f, 1.46f, 1.52f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.11f, 2.21f, 2.30f, 2.39f, 3.09f, 4.34f }, // - Line-In Generic -> pink noise adjustment only
{ 1.24f, 1.20f, 1.30f, 1.40f, 1.48f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.14f, 2.26f, 2.60f, 3.00f, 3.70f, 5.20f }, // 1 Line-In CS5343
{ 2.35f, 1.32f, 1.32f, 1.40f, 1.48f, 1.57f, 1.68f, 1.80f, 1.89f, 1.95f, 2.14f, 2.26f, 2.50f, 2.90f, 4.20f, 6.50f }, // 1 Line-In CS5343 + DC blocker
{ 1.82f, 1.72f, 1.70f, 1.50f, 1.52f, 1.57f, 1.68f, 1.80f, 1.89f, 2.00f, 2.11f, 2.21f, 2.30f, 2.90f, 3.86f, 6.29f}, // 2 IMNP441 datasheet response profile * pink noise
{ 2.80f, 2.20f, 1.30f, 1.15f, 1.55f, 2.45f, 4.20f, 2.80f, 3.20f, 3.60f, 4.20f, 4.90f, 5.70f, 6.05f,10.50f,14.85f}, // 3 IMNP441 - big speaker, strong bass
@@ -341,6 +341,23 @@ constexpr bool skipSecondFFT = true;
#else
constexpr bool skipSecondFFT = false;
#endif
// High-Pass "DC blocker" filter
// see https://www.dsprelated.com/freebooks/filters/DC_Blocker.html
static void runDCBlocker(uint_fast16_t numSamples, float *sampleBuffer) {
constexpr float filterR = 0.990f; // around 40hz
static float xm1 = 0.0f;
static float ym1 = 0.0f;
for (unsigned i=0; i < numSamples; i++) {
float value = sampleBuffer[i];
float filtered = value-xm1 + filterR*ym1;
xm1 = value;
ym1 = filtered;
sampleBuffer[i] = filtered;
}
}
//
// FFT main task
//
@@ -410,7 +427,12 @@ void FFTcode(void * parameter)
// band pass filter - can reduce noise floor by a factor of 50
// downside: frequencies below 100Hz will be ignored
if (useBandPassFilter) runMicFilter(samplesFFT, vReal);
if ((useInputFilter > 0) && (useInputFilter < 99)) {
switch(useInputFilter) {
case 1: runMicFilter(samplesFFT, vReal); break; // PDM microphone bandpass
case 2: runDCBlocker(samplesFFT, vReal); break; // generic Low-Cut + DC blocker (~40hz cut-off)
}
}
// find highest sample in the batch
float maxSample = 0.0f; // max sample from FFT batch
@@ -531,7 +553,7 @@ void FFTcode(void * parameter)
if (freqDist == 0) {
/* new mapping, optimized for 22050 Hz by softhack007 --- update: removed overlap */
// bins frequency range
if (useBandPassFilter) {
if (useInputFilter==1) {
// skip frequencies below 100hz
fftCalc[ 0] = 0.8f * fftAddAvg(3,3);
fftCalc[ 1] = 0.9f * fftAddAvg(4,4);
@@ -560,7 +582,7 @@ void FFTcode(void * parameter)
fftCalc[14] = fftAddAvg(104,164) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping
}
else if (freqDist == 1) { //WLEDMM: Rightshft: note ewowi: frequencies in comments are not correct
if (useBandPassFilter) {
if (useInputFilter==1) {
// skip frequencies below 100hz
fftCalc[ 0] = 0.8f * fftAddAvg(1,1);
fftCalc[ 1] = 0.9f * fftAddAvg(2,2);
@@ -1429,7 +1451,7 @@ class AudioReactive : public Usermod {
#endif
delay(100); // Give that poor microphone some time to setup.
useBandPassFilter = false;
useInputFilter = 2; // default: DC blocker
switch (dmType) {
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
// stub cases for not-yet-supported I2S modes on other ESP32 chips
@@ -1447,7 +1469,7 @@ class AudioReactive : public Usermod {
break;
case 2:
DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only)."));
//useBandPassFilter = true;
//useInputFilter = 0; // in case you need to disable low-cut software filtering
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin);
@@ -1460,7 +1482,6 @@ class AudioReactive : public Usermod {
break;
case 4:
DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
//useBandPassFilter = true;
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/24.0f);
//audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/24.0f, false); // I2S SLAVE mode - does not work, unfortunately
delay(100);
@@ -1470,21 +1491,22 @@ class AudioReactive : public Usermod {
case 5:
DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_PDM_MIC_CHANNEL_TEXT));
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/4.0f);
useBandPassFilter = true; // this reduces the noise floor on SPM1423 from 5% Vpp (~380) down to 0.05% Vpp (~5)
useInputFilter = 1; // PDM bandpass filter - this reduces the noise floor on SPM1423 from 5% Vpp (~380) down to 0.05% Vpp (~5)
delay(100);
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
break;
case 51:
DEBUGSR_PRINT(F("AR: Legacy PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_PDM_MIC_CHANNEL_TEXT));
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f);
useBandPassFilter = true;
useInputFilter = 1; // PDM bandpass filter
delay(100);
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
break;
#endif
case 6:
DEBUGSR_PRINTLN(F("AR: ES8388 Source"));
audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE);
audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f);
//useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour
delay(100);
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;
@@ -1494,7 +1516,7 @@ class AudioReactive : public Usermod {
case 0:
default:
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
useBandPassFilter = true;
useInputFilter = 1; // PDM bandpass filter seems to work well for analog, too
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
if (audioSource) audioSource->initialize(audioPin);
@@ -2102,8 +2124,8 @@ class AudioReactive : public Usermod {
if (dmType == 51) dmType = SR_DMTYPE; // MCU does not support legacy PDM
#endif
#else
if (dmType == 5) useBandPassFilter = true; // enable filter for PDM
if (dmType == 51) useBandPassFilter = true /*false*/; // switch on filter for legacy PDM
if (dmType == 5) useInputFilter = 1; // enable filter for PDM
if (dmType == 51) useInputFilter = 1; // switch on filter for legacy PDM
#endif
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][0], i2ssdPin);