diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 6607d7c5..93a3ca25 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -47,11 +47,12 @@ constexpr i2s_port_t I2S_PORT = I2S_NUM_0; constexpr int BLOCK_SIZE = 128; -//constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms -constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms +constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms +//constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms //constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz - standard. Physical sample time -> 50ms -#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Must be less than time needed to read 512 samples at SAMPLE_RATE -> not the same as I2S time!! +//#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Use with 20Khz sampling +#define FFT_MIN_CYCLE 18 // minimum time before FFT task is repeated. Use with 22Khz sampling // globals static uint8_t inputLevel = 128; // UI slider value @@ -65,7 +66,7 @@ static bool limiterOn = true; // bool: enable / disable dynamics static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec // user settable options for FFTResult scaling -static uint8_t FFTScalingMode = 2; // 0 none; 1 optimized logarithmic; 2 optimized linear +static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized sqare root // // AGC presets @@ -121,7 +122,10 @@ static float vReal[samplesFFT] = {0.0f}; static float vImag[samplesFFT] = {0.0f}; static float fftBin[samplesFFT_2] = {0.0f}; -#define FFT_DOWNSCALE 0.65f // downscaling factor for FFT results - "Flat-Top" window +// the following are observed values, supported by a bit of "educated guessing" +//#define FFT_DOWNSCALE 0.65f // 20kHz - downscaling factor for FFT results - "Flat-Top" window @20Khz, old freq channels + +#define FFT_DOWNSCALE 0.46f // downscaling factor for FFT results - for "Flat-Top" window @22Khz, new freq channels #define LOG_256 5.54517744 #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT @@ -269,7 +273,7 @@ void FFTcode(void * parameter) * Multiplier = 1.320367784 */ if (sampleAvg > 1) { // noise gate open - // Range +#if 0 // Range fftCalc[ 0] = fftAddAvg(2,4); // 60 - 100 fftCalc[ 1] = fftAddAvg(4,5); // 80 - 120 fftCalc[ 2] = fftAddAvg(5,7); // 100 - 160 @@ -286,6 +290,26 @@ void FFTcode(void * parameter) fftCalc[13] = fftAddAvg(111,147); // 2220 - 2960 fftCalc[14] = fftAddAvg(147,194); // 2940 - 3900 fftCalc[15] = fftAddAvg(194,250); // 3880 - 5000 // avoid the last 5 bins, which are usually inaccurate +#else + // optimized for 22050 Hz by softhack007 bins frequency range + fftCalc[ 0] = fftAddAvg(1,2); // 1 43 - 86 sub-bass + fftCalc[ 1] = fftAddAvg(2,3); // 1 86 - 129 bass + fftCalc[ 2] = fftAddAvg(3,5); // 2 129 - 216 bass + fftCalc[ 3] = fftAddAvg(5,7); // 2 216 - 301 bass + midrange + fftCalc[ 4] = fftAddAvg(7,10); // 3 301 - 430 midrange + fftCalc[ 5] = fftAddAvg(10,13); // 3 430 - 560 midrange + fftCalc[ 6] = fftAddAvg(13,19); // 5 560 - 818 midrange + fftCalc[ 7] = fftAddAvg(19,26); // 7 818 - 1120 midrange -- 1Khz should always be the center ! + fftCalc[ 8] = fftAddAvg(26,33); // 7 1120 - 1421 midrange + fftCalc[ 9] = fftAddAvg(33,44); // 9 1421 - 1895 midrange + fftCalc[10] = fftAddAvg(44,56); // 12 1895 - 2412 midrange + high mid + fftCalc[11] = fftAddAvg(56,70); // 14 2412 - 3015 high mid + fftCalc[12] = fftAddAvg(70,86); // 16 3015 - 3704 high mid + fftCalc[13] = fftAddAvg(86,104); // 18 3704 - 4479 high mid + fftCalc[14] = fftAddAvg(104,165) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping + fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping + // don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise) +#endif } else { // noise gate closed for (int i=0; i < 16; i++) { @@ -350,7 +374,7 @@ void FFTcode(void * parameter) if (currentResult > 1.0) currentResult = sqrtf(currentResult); else currentResult = 0.0; // special handling, because sqrt(0) = undefined - currentResult *= 0.85f + (float(i)/5.0f); // extra up-scaling for high frequencies + currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255] break; @@ -872,7 +896,8 @@ class AudioReactive : public Usermod { my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f); FFT_Magnitude = my_magnitude; - FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 1.0f); + FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11050.0f); // restrict value to range expected by effects + //DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet"); haveFreshData = true; } diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 2ba78c89..aee00737 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5938,7 +5938,7 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;; uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment bool samplePeak = false; - float FFT_MajorPeak = 0.0; + float FFT_MajorPeak = 1.0; uint8_t *fftResult = nullptr; float *fftBin = nullptr; um_data_t *um_data; @@ -5958,6 +5958,15 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;; } */ +// a few constants needed for AudioReactive effects +// for 22Khz sampling +#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion) +#define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY) + +// for 20Khz sampling +//#define MAX_FREQUENCY 10240 // sample frequency / 2 (as per Nyquist criterion) +//#define MAX_FREQ_LOG10 4.0103f // log10(MAX_FREQUENCY) + ///////////////////////////////// // * Ripple Peak // @@ -6724,12 +6733,12 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. SEGMENT.fade_out(SEGMENT.speed); // int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(3.71f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. - int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(4.0102f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. + int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. if (locn < 1) locn = 0; // avoid underflow if (locn >=SEGLEN) locn = SEGLEN-1; //uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(3.71f-1.78f); // Scale log10 of frequency values to the 255 colour index. - uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(4.0102f-1.78f); // Scale log10 of frequency values to the 255 colour index. + uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. uint16_t bright = (int)my_magnitude; SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); @@ -6769,7 +6778,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch CRGB color = CRGB::Black; //if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0; - if (FFT_MajorPeak > 10240) FFT_MajorPeak = 0; + if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1; // MajorPeak holds the freq. value which is most abundant in the last sample. // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz // we will treat everything with less than 65Hz as 0 @@ -6777,8 +6786,8 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch if (FFT_MajorPeak < 80) { color = CRGB::Black; } else { - int upperLimit = 20 * SEGMENT.custom2; - int lowerLimit = 2 * SEGMENT.custom1; + int upperLimit = 80 + 42 * SEGMENT.custom2; + int lowerLimit = 80 + 3 * SEGMENT.custom1; int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; uint16_t b = 255 * intensity; if (b > 255) b = 255; @@ -6818,7 +6827,7 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. for (int i=0; i < SEGMENT.intensity/32+1; i++) { uint16_t locn = random16(0,SEGLEN); //uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78) * 255.0/(3.71-1.78); // Scale log10 of frequency values to the 255 colour index. - uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(4.0102f-1.78f); // Scale log10 of frequency values to the 255 colour index. + uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); } @@ -6869,7 +6878,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun CRGB color = 0; //if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0.0f; - if (FFT_MajorPeak > 10240) FFT_MajorPeak = 0.0f; + if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1.0f; // MajorPeak holds the freq. value which is most abundant in the last sample. // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz // we will treat everything with less than 65Hz as 0 @@ -6877,8 +6886,8 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun if (FFT_MajorPeak < 80) { color = CRGB::Black; } else { - int upperLimit = 20 * SEGMENT.custom2; - int lowerLimit = 2 * SEGMENT.custom1; + int upperLimit = 80 + 42 * SEGMENT.custom2; + int lowerLimit = 80 + 3 * SEGMENT.custom1; int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; uint16_t b = 255.0 * intensity; if (b > 255) b=255; @@ -6927,7 +6936,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. for (int i=0; i