AR: special gain for GEO, some bugfixes andparameter tinkering
- new feature: "Input Level" (info page) can be used as global "GEQ gain" - only when AGC is ON (was already possible when AGC=off) - some parameter tweaking in FFT function - hidden feature: FFT decay is slower when setting a high "dynamics Limiter Fall time" (steps: <1000, <2000, <3000, >3000) - FFT_MajorPeak default 1.0f (as log(0.0) is invalid) - FX.cppp: ensure that fftResult[] is always used inside array bounds
This commit is contained in:
parent
753ae51dd5
commit
3c57e2e2b9
@ -113,7 +113,7 @@ static float sampleAgc = 0.0f; // Smoothed AGC sample
|
|||||||
constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2
|
constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2
|
||||||
constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT results - only the "lower half" contains useful information.
|
constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT results - only the "lower half" contains useful information.
|
||||||
|
|
||||||
static float FFT_MajorPeak = 0.0f;
|
static float FFT_MajorPeak = 1.0f;
|
||||||
static float FFT_Magnitude = 0.0f;
|
static float FFT_Magnitude = 0.0f;
|
||||||
|
|
||||||
// These are the input and output vectors. Input vectors receive computed results from FFT.
|
// These are the input and output vectors. Input vectors receive computed results from FFT.
|
||||||
@ -250,9 +250,10 @@ void FFTcode(void * parameter)
|
|||||||
#else
|
#else
|
||||||
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
|
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
|
||||||
#endif
|
#endif
|
||||||
|
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 10240.0f);
|
||||||
|
|
||||||
for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3.
|
for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3.
|
||||||
float t = fabs(vReal[i]); // just to be sure - values in fft bins should be positive any way
|
float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way
|
||||||
fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max.
|
fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max.
|
||||||
} // for()
|
} // for()
|
||||||
|
|
||||||
@ -288,7 +289,8 @@ void FFTcode(void * parameter)
|
|||||||
|
|
||||||
} else { // noise gate closed
|
} else { // noise gate closed
|
||||||
for (int i=0; i < 16; i++) {
|
for (int i=0; i < 16; i++) {
|
||||||
fftCalc[i] *= 0.82f; // decay to zero
|
//fftCalc[i] *= 0.82f; // decay to zero
|
||||||
|
fftCalc[i] *= 0.85f; // decay to zero
|
||||||
if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f;
|
if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,9 +309,12 @@ void FFTcode(void * parameter)
|
|||||||
// smooth results - rise fast, fall slower
|
// smooth results - rise fast, fall slower
|
||||||
if(fftCalc[i] > fftAvg[i]) // rise fast
|
if(fftCalc[i] > fftAvg[i]) // rise fast
|
||||||
fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i]
|
fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i]
|
||||||
else // fall slow
|
else { // fall slow
|
||||||
fftAvg[i] = fftCalc[i]*0.17f + 0.83f*fftAvg[i]; // will need approx 5 cycles (150ms) for converging against fftCalc[i]
|
if (decayTime < 1000) fftAvg[i] = fftCalc[i]*0.22f + 0.78f*fftAvg[i]; // approx 5 cycles (225ms) for falling to zero
|
||||||
|
else if (decayTime < 2000) fftAvg[i] = fftCalc[i]*0.17f + 0.83f*fftAvg[i]; // default - approx 9 cycles (225ms) for falling to zero
|
||||||
|
else if (decayTime < 3000) fftAvg[i] = fftCalc[i]*0.14f + 0.86f*fftAvg[i]; // approx 14 cycles (350ms) for falling to zero
|
||||||
|
else fftAvg[i] = fftCalc[i]*0.1f + 0.9f*fftAvg[i]; // approx 20 cycles (500ms) for falling to zero
|
||||||
|
}
|
||||||
// constrain internal vars - just to be sure
|
// constrain internal vars - just to be sure
|
||||||
fftCalc[i] = constrain(fftCalc[i], 0.0f, 1023.0f);
|
fftCalc[i] = constrain(fftCalc[i], 0.0f, 1023.0f);
|
||||||
fftAvg[i] = constrain(fftAvg[i], 0.0f, 1023.0f);
|
fftAvg[i] = constrain(fftAvg[i], 0.0f, 1023.0f);
|
||||||
@ -344,6 +349,11 @@ void FFTcode(void * parameter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely.
|
// Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely.
|
||||||
|
if (soundAgc > 0) { // apply extra "GEQ Gain" if set by user
|
||||||
|
float post_gain = (float)inputLevel/128.0f;
|
||||||
|
if (post_gain < 1.0f) post_gain = ((post_gain -1.0f) * 0.8f) +1.0f;
|
||||||
|
currentResult *= post_gain;
|
||||||
|
}
|
||||||
fftResult[i] = constrain((int)currentResult, 0, 255);
|
fftResult[i] = constrain((int)currentResult, 0, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -849,7 +859,7 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f);
|
my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f);
|
||||||
FFT_Magnitude = my_magnitude;
|
FFT_Magnitude = my_magnitude;
|
||||||
FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 0.0f);
|
FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 1.0f);
|
||||||
//DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
|
//DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
|
||||||
haveFreshData = true;
|
haveFreshData = true;
|
||||||
}
|
}
|
||||||
@ -990,7 +1000,7 @@ class AudioReactive : public Usermod {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We cannot wait indefinitely before processing audio data
|
// We cannot wait indefinitely before processing audio data
|
||||||
if (strip.isUpdating() && (millis() - lastUMRun < 1)) return; // be nice, but not too nice
|
if (strip.isUpdating() && (millis() - lastUMRun < 2)) return; // be nice, but not too nice
|
||||||
|
|
||||||
// suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET)
|
// suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET)
|
||||||
if ( (realtimeOverride == REALTIME_OVERRIDE_NONE) // please odd other orrides here if needed
|
if ( (realtimeOverride == REALTIME_OVERRIDE_NONE) // please odd other orrides here if needed
|
||||||
@ -1059,6 +1069,9 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
limitSampleDynamics(); // optional - makes volumeSmth very smooth and fluent
|
limitSampleDynamics(); // optional - makes volumeSmth very smooth and fluent
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* currently this is _not_ working. Code relies on "musical note" symbol as second char of the effect name */
|
||||||
|
#error I told you its not working right now
|
||||||
// update WebServer UI
|
// update WebServer UI
|
||||||
uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment
|
uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment
|
||||||
if (lastMode != knownMode) { // only execute if mode changes
|
if (lastMode != knownMode) { // only execute if mode changes
|
||||||
@ -1099,6 +1112,7 @@ class AudioReactive : public Usermod {
|
|||||||
last_user_inputLevel = new_user_inputLevel;
|
last_user_inputLevel = new_user_inputLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1162,13 +1176,14 @@ class AudioReactive : public Usermod {
|
|||||||
volumeRaw = 0; volumeSmth = 0;
|
volumeRaw = 0; volumeSmth = 0;
|
||||||
sampleAgc = 0; sampleAvg = 0;
|
sampleAgc = 0; sampleAvg = 0;
|
||||||
sampleRaw = 0; rawSampleAgc = 0;
|
sampleRaw = 0; rawSampleAgc = 0;
|
||||||
my_magnitude = 0; FFT_Magnitude = 0; FFT_MajorPeak = 0;
|
my_magnitude = 0; FFT_Magnitude = 0; FFT_MajorPeak = 1;
|
||||||
multAgc = 1;
|
multAgc = 1;
|
||||||
// reset FFT data
|
// reset FFT data
|
||||||
memset(fftCalc, 0, sizeof(fftCalc));
|
memset(fftCalc, 0, sizeof(fftCalc));
|
||||||
memset(fftAvg, 0, sizeof(fftAvg));
|
memset(fftAvg, 0, sizeof(fftAvg));
|
||||||
memset(fftResult, 0, sizeof(fftResult));
|
memset(fftResult, 0, sizeof(fftResult));
|
||||||
for(int i=(init?0:1); i<16; i+=2) fftResult[i] = 16; // make a tiny pattern
|
for(int i=(init?0:1); i<16; i+=2) fftResult[i] = 16; // make a tiny pattern
|
||||||
|
inputLevel = 128; // resset level slider to default
|
||||||
|
|
||||||
if (init && FFT_Task) {
|
if (init && FFT_Task) {
|
||||||
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
|
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
|
||||||
@ -1241,15 +1256,22 @@ class AudioReactive : public Usermod {
|
|||||||
infoArr.add(uiDomString);
|
infoArr.add(uiDomString);
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
infoArr = user.createNestedArray(F("Input level"));
|
// Input Level Slider
|
||||||
uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({");
|
if (disableSoundProcessing == false) { // only show slider when audio processing is running
|
||||||
uiDomString += FPSTR(_name);
|
if (soundAgc > 0)
|
||||||
uiDomString += F(":{");
|
infoArr = user.createNestedArray(F("GEQ Input Level")); // if AGC is on, this slider only affects fftResult[] frequencies
|
||||||
uiDomString += FPSTR(_inputLvl);
|
else
|
||||||
uiDomString += F(":parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=255 min=0 type=\"range\" value=");
|
infoArr = user.createNestedArray(F("Audio Input Level"));
|
||||||
uiDomString += inputLevel;
|
uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({");
|
||||||
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
|
uiDomString += FPSTR(_name);
|
||||||
infoArr.add(uiDomString);
|
uiDomString += F(":{");
|
||||||
|
uiDomString += FPSTR(_inputLvl);
|
||||||
|
uiDomString += F(":parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=255 min=0 type=\"range\" value=");
|
||||||
|
uiDomString += inputLevel;
|
||||||
|
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
|
||||||
|
infoArr.add(uiDomString);
|
||||||
|
}
|
||||||
|
//else infoArr.add("<br/> <div> </div>"); // no processing - add empty line
|
||||||
|
|
||||||
// current Audio input
|
// current Audio input
|
||||||
infoArr = user.createNestedArray(F("Audio Source"));
|
infoArr = user.createNestedArray(F("Audio Source"));
|
||||||
@ -1262,7 +1284,7 @@ class AudioReactive : public Usermod {
|
|||||||
else
|
else
|
||||||
infoArr.add(" - idle");
|
infoArr.add(" - idle");
|
||||||
} else {
|
} else {
|
||||||
infoArr.add(" - no network");
|
infoArr.add(" - no connection");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Analog or I2S digital input
|
// Analog or I2S digital input
|
||||||
@ -1319,6 +1341,8 @@ class AudioReactive : public Usermod {
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
infoArr.add("off");
|
infoArr.add("off");
|
||||||
|
if (audioSyncEnabled && !udpSyncConnected) infoArr.add(" <i>(unconnected)</i>");
|
||||||
|
//if (!udpSyncConnected) infoArr.add(" <i>(unconnected)</i>");
|
||||||
|
|
||||||
#ifdef WLED_DEBUG
|
#ifdef WLED_DEBUG
|
||||||
infoArr = user.createNestedArray(F("Sampling time"));
|
infoArr = user.createNestedArray(F("Sampling time"));
|
||||||
|
@ -7093,6 +7093,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
|
|||||||
|
|
||||||
for (int x=0; x < cols; x++) {
|
for (int x=0; x < cols; x++) {
|
||||||
uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1);
|
uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1);
|
||||||
|
band = constrain(band, 0, 15);
|
||||||
uint16_t colorIndex = band * 17;
|
uint16_t colorIndex = band * 17;
|
||||||
uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here
|
uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here
|
||||||
if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up
|
if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up
|
||||||
@ -7153,8 +7154,8 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
|
|||||||
// display values of
|
// display values of
|
||||||
int b = 0;
|
int b = 0;
|
||||||
for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) {
|
for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) {
|
||||||
int hue = fftResult[band];
|
int hue = fftResult[band % 16];
|
||||||
int v = map(fftResult[band], 0, 255, 10, 255);
|
int v = map(fftResult[band % 16], 0, 255, 10, 255);
|
||||||
for (int w = 0; w < barWidth; w++) {
|
for (int w = 0; w < barWidth; w++) {
|
||||||
int xpos = (barWidth * b) + w;
|
int xpos = (barWidth * b) + w;
|
||||||
SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v));
|
SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v));
|
||||||
@ -7269,6 +7270,7 @@ uint16_t mode_2DAkemi(void) {
|
|||||||
if (um_data && fftResult) {
|
if (um_data && fftResult) {
|
||||||
for (int x=0; x < cols/8; x++) {
|
for (int x=0; x < cols/8; x++) {
|
||||||
uint16_t band = x * cols/8;
|
uint16_t band = x * cols/8;
|
||||||
|
band = constrain(band, 0, 15);
|
||||||
uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
|
uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
|
||||||
CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
|
CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user