From 42253aaef9d0571e78728b7fe02d1e16bf36c80c Mon Sep 17 00:00:00 2001 From: Ewowi Date: Wed, 31 Aug 2022 09:43:36 +0200 Subject: [PATCH 1/3] Commit to start audio-reactive branch for this repo --- wled00/wled.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled.h b/wled00/wled.h index 1306b527..102a3b12 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2208222 +#define VERSION 2208311 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From 37ba6499300c5334dd3b49252db5a841bee4ff65 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 1 Sep 2022 14:56:01 +0200 Subject: [PATCH 2/3] audioreactive effect improvements - Info Page: add a small horizontal line below usermod specific part. Improves readability. - updated 2D mapping mode of some 1D soundreactive effects - alllow some effects to fade slowly, even slower that possible with SEGMENT.fade_out(). Looks nice. not sure why - most effects only fade when using SEGMENT.fade_out(), while some need SEGMENT.fadeToBlackBy(). --- usermods/audioreactive/audio_reactive.h | 4 +++ wled00/FX.cpp | 42 ++++++++++++++++--------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index bd3b6ec1..84adc9b6 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1317,6 +1317,10 @@ class AudioReactive : public Usermod { infoArr.add(fftTime-sampleTime); infoArr.add("ms"); #endif + + // add a small horizontal line, for better readability + infoArr = user.createNestedArray(F("
")); + infoArr.add(F("
")); } } diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ce64582c..5ad2ec71 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6254,7 +6254,7 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew return FRAMETIME; } // mode_gravcentric() -static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!;!;ix=128,mp12=3,ssim=0,1d,vo"; // Corner, Beatsin /////////////////////// @@ -6390,7 +6390,7 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. return FRAMETIME; } // mode_midnoise() -static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Maximum length;,!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Maximum length;,!;!;ix=128,mp12=1,ssim=0,1d,vo"; // Bar, Beatsin ////////////////////// @@ -6515,7 +6515,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. } float volumeSmth = *(float*) um_data->u_data[0]; - SEGMENT.fadeToBlackBy(64); + SEGMENT.fadeToBlackBy(32); plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually. plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. @@ -6667,7 +6667,8 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. SEGENV.aux0 = 0; } - SEGMENT.fade_out(SEGMENT.speed); + int fadeoutDelay = (256 - SEGMENT.speed) / 32; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed); SEGENV.step += FRAMETIME; if (SEGENV.step > SPEED_FORMULA_L) { @@ -6735,7 +6736,9 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. float my_magnitude = *(float*) um_data->u_data[5] / 4.0f; if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) - SEGMENT.fade_out(SEGMENT.speed); + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + int fadeoutDelay = (256 - SEGMENT.speed) / 32; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed); 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 @@ -6750,7 +6753,7 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. return FRAMETIME; } // mode_freqmap() -static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;,!;!;mp12=2,ssim=0,1d,fr"; // Circle, Beatsin +static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;,!;!;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin /////////////////////// @@ -6805,7 +6808,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch return FRAMETIME; } // mode_freqmatrix() -static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;mp12=3,ssim=0,1d,fr"; // Corner, Beatsin ////////////////////// @@ -6826,7 +6829,10 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can. - SEGMENT.fade_out(fadeRate); + + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + int fadeoutDelay = (256 - SEGMENT.speed) / 64; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate); for (int i=0; i < SEGMENT.intensity/32+1; i++) { uint16_t locn = random16(0,SEGLEN); @@ -6958,7 +6964,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. return FRAMETIME; } // mode_gravfreq() -static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;,!;!;ix=128,mp12=2,ssim=0,1d,fr"; // Circle, Beatsin +static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;,!;!;ix=128,mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin ////////////////////// @@ -6972,7 +6978,10 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli } uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - SEGMENT.fade_out(224); // Just in case something doesn't get faded. + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + //SEGMENT.fade_out(224); // Just in case something doesn't get faded. + int fadeoutDelay = (256 - SEGMENT.speed) / 96; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4); uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins. for (int i=0; iu_data[4]; float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; - SEGMENT.fadeToBlackBy(64); // Just in case something doesn't get faded. + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded. float frTemp = FFT_MajorPeak; uint8_t octCount = 0; // Octave counter. uint8_t volTemp = 0; - if (my_magnitude > 32) volTemp = 255; // We need to squelch out the background noise. + volTemp = 32.0f + my_magnitude * 1.5f; // brightness = volume (overflows are handled in next lines) + if (my_magnitude < 48) volTemp = 0; // We need to squelch out the background noise. + if (my_magnitude > 144) volTemp = 255; // everything above this is full brightness while ( frTemp > 249 ) { octCount++; // This should go up to 5. @@ -7020,7 +7032,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac return FRAMETIME; } // mode_rocktaves() -static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;,!;!;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;,!;!;mp12=1,ssim=0,1d,fr"; // Bar, Beatsin /////////////////////// @@ -7104,7 +7116,9 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. rippleTime = true; } - SEGMENT.fadeToBlackBy(SEGMENT.speed); + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + int fadeoutDelay = (256 - SEGMENT.speed) / 64; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed); for (int x=0; x < cols; x++) { uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1); From 77ace76e32f86a7b47ad2ab02446cd42c4f4103e Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 2 Sep 2022 13:49:12 +0200 Subject: [PATCH 3/3] Bugfix: make UDP sound sync work in AP mode - the connected() method only get called once a Wifi STA connection is established. UDP Sound Sync should also work when sender is in AP Mode. - added a few comments that should help to understand the code structure. --- usermods/audioreactive/audio_reactive.h | 58 ++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 84adc9b6..e5bda203 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -24,7 +24,7 @@ // #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter) // #define FFT_SAMPLING_LOG // FFT result debugging // #define SR_DEBUG // generic SR DEBUG messages -// #define NO_MIC_LOGGER // exclude MIC_LOGGER from SR_DEBUG + #ifdef SR_DEBUG #define DEBUGSR_PRINT(x) Serial.print(x) @@ -103,6 +103,7 @@ static void autoResetPeak(void); // peak auto-reset function //////////////////// // Begin FFT Code // //////////////////// + #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups @@ -135,7 +136,6 @@ static float windowWeighingFactors[samplesFFT] = {0.0f}; #endif // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. -// Oh, and bins 0,1,2 are no good, so we'll zero them out. static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) #ifdef SR_DEBUG @@ -172,7 +172,7 @@ static float fftAddAvg(int from, int to) { return result / float(to - from + 1); } -// FFT main code +// FFT main task void FFTcode(void * parameter) { DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID()); @@ -417,6 +417,10 @@ static void autoResetPeak(void) { } +//////////////////// +// usermod class // +//////////////////// + //class name. Use something descriptive and leave the ": public Usermod" part :) class AudioReactive : public Usermod { @@ -533,6 +537,10 @@ class AudioReactive : public Usermod { static const char UDP_SYNC_HEADER_v1[]; // private methods + + //////////////////// + // Debug support // + //////////////////// void logAudio() { #ifdef MIC_LOGGER @@ -604,6 +612,10 @@ class AudioReactive : public Usermod { } // logAudio() + ////////////////////// + // Audio Processing // + ////////////////////// + /* * A "PI controller" multiplier to automatically adjust sound sensitivity. * @@ -698,7 +710,7 @@ class AudioReactive : public Usermod { last_soundAgc = soundAgc; } // agcAvg() - + // post-processing and filtering of MIC sample (micDataReal) from FFTcode() void getSample() { float sampleAdj; // Gain adjusted sample value @@ -793,6 +805,26 @@ class AudioReactive : public Usermod { } + ////////////////////// + // UDP Sound Sync // + ////////////////////// + + // try to establish UDP sound sync connection + void connectUDPSoundSync(void) { + // This function tries to establish a UDP sync connection if needed + // necessary as we also want to transmit in "AP Mode", but the standard "connected()" callback only reacts on STA connection + static unsigned long last_connection_attempt = 0; + + if ((audioSyncPort <= 0) || ((audioSyncEnabled & 0x03) == 0)) return; // Sound Sync not enabled + if (udpSyncConnected) return; // already connected + if (!(apActive || interfacesInited)) return; // neither AP nor other connections availeable + if (millis() - last_connection_attempt < 15000) return; // only try once in 15 seconds + + // if we arrive here, we need a UDP connection but don't have one + last_connection_attempt = millis(); + connected(); // try to start UDP + } + void transmitAudioData() { if (!udpSyncConnected) return; @@ -820,12 +852,10 @@ class AudioReactive : public Usermod { return; } // transmitAudioData() - static bool isValidUdpSyncVersion(const char *header) { return strncmp_P(header, PSTR(UDP_SYNC_HEADER), 6) == 0; } - bool receiveAudioData() // check & process new data. return TRUE in case that new audio data was received. { if (!udpSyncConnected) return false; @@ -875,6 +905,10 @@ class AudioReactive : public Usermod { } + ////////////////////// + // usermod functions// + ////////////////////// + public: //Functions called by WLED or other usermods @@ -967,6 +1001,7 @@ class AudioReactive : public Usermod { disableSoundProcessing = true; } + if (enabled) connectUDPSoundSync(); initDone = true; } @@ -977,6 +1012,11 @@ class AudioReactive : public Usermod { */ void connected() { + if (udpSyncConnected) { // clean-up: if open, close old UDP sync connection + udpSyncConnected = false; + fftUdp.stop(); + } + if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) { #ifndef ESP8266 udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort); @@ -1079,6 +1119,8 @@ class AudioReactive : public Usermod { autoResetPeak(); // auto-reset sample peak after strip minShowDelay if (!udpSyncConnected) udpSamplePeak = false; // reset UDP samplePeak while UDP is unconnected + connectUDPSoundSync(); // ensure we have a connection - if needed + // UDP Microphone Sync - receive mode if ((audioSyncEnabled & 0x02) && udpSyncConnected) { // Only run the audio listener code if we're in Receive mode @@ -1196,6 +1238,10 @@ class AudioReactive : public Usermod { } + //////////////////////////// + // Settings and Info Page // + //////////////////////////// + /* * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.