From f3364e1327b891482001130f664c66a556c2d07b Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 16 Jun 2022 21:52:14 +0200 Subject: [PATCH] Scrolling text #DATETIME bugfix. Cosmetic changes. --- usermods/audioreactive/audio_reactive.h | 138 ++++++++++++++---------- wled00/FX.cpp | 10 +- 2 files changed, 89 insertions(+), 59 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 2f9dfa8b..0bfc37cb 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -144,7 +144,8 @@ float fftAdd(int from, int to) { } // FFT main code -void FFTcode(void * parameter) { +void FFTcode(void * parameter) +{ DEBUGSR_PRINT("FFT running on core: "); DEBUGSR_PRINTLN(xPortGetCoreID()); #ifdef MAJORPEAK_SUPPRESS_NOISE static double xtemp[24] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; @@ -367,17 +368,16 @@ class AudioReactive : public Usermod { int8_t mclkPin = MLCK_PIN; #endif - #define UDP_SYNC_HEADER "00001" struct audioSyncPacket { - char header[6]; + char header[6]; uint8_t myVals[32]; // 32 Bytes - int sampleAgc; // 04 Bytes - int sample; // 04 Bytes - float sampleAvg; // 04 Bytes - bool samplePeak; // 01 Bytes + int sampleAgc; // 04 Bytes + int sample; // 04 Bytes + float sampleAvg; // 04 Bytes + bool samplePeak; // 01 Bytes uint8_t fftResult[16]; // 16 Bytes - double FFT_Magnitude; // 08 Bytes - double FFT_MajorPeak; // 08 Bytes + double FFT_Magnitude; // 08 Bytes + double FFT_MajorPeak; // 08 Bytes }; WiFiUDP fftUdp; @@ -416,20 +416,21 @@ class AudioReactive : public Usermod { bool agcEffect = false; int last_soundAgc = -1; float control_integrated = 0.0f; // "integrator control" = accumulated error + unsigned long last_update_time = 0; + unsigned long last_kick_time = 0; + uint8_t last_user_inputLevel = 0; // strings to reduce flash memory usage (used more than twice) static const char _name[]; + static const char _enabled[]; static const char _analogmic[]; static const char _digitalmic[]; + static const char UDP_SYNC_HEADER[]; // private methods - bool isValidUdpSyncVersion(const char *header) { - return strncmp(header, UDP_SYNC_HEADER, 6) == 0; - } - - - void logAudio() { + void logAudio() + { #ifdef MIC_LOGGER //Serial.print("micData:"); Serial.print(micData); Serial.print("\t"); //Serial.print("micDataSm:"); Serial.print(micDataSm); Serial.print("\t"); @@ -522,7 +523,8 @@ class AudioReactive : public Usermod { * a) normal zone - very slow adjustment * b) emergency zome (<10% or >90%) - very fast adjustment */ - void agcAvg() { + void agcAvg() + { const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function float lastMultAgc = multAgc; // last muliplier used @@ -605,9 +607,8 @@ class AudioReactive : public Usermod { } // agcAvg() - void getSample() { - static long peakTime; - + void getSample() + { const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function #ifdef WLED_DISABLE_SOUND @@ -677,24 +678,24 @@ class AudioReactive : public Usermod { if (userVar1 == 0) samplePeak = 0; // Poor man's beat detection by seeing if sample > Average + some value. // Serial.print(binNum); Serial.print("\t"); Serial.print(fftBin[binNum]); Serial.print("\t"); Serial.print(fftAvg[binNum/16]); Serial.print("\t"); Serial.print(maxVol); Serial.print("\t"); Serial.println(samplePeak); - if ((fftBin[binNum] > maxVol) && (millis() > (peakTime + 100))) { // This goe through ALL of the 255 bins - // if (sample > (sampleAvg + maxVol) && millis() > (peakTime + 200)) { + if ((fftBin[binNum] > maxVol) && (millis() > (timeOfPeak + 100))) { // This goe through ALL of the 255 bins + // if (sample > (sampleAvg + maxVol) && millis() > (timeOfPeak + 200)) { // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync. - samplePeak = 1; - timeOfPeak = millis(); + samplePeak = 1; + timeOfPeak = millis(); udpSamplePeak = 1; - userVar1 = samplePeak; - peakTime=millis(); + userVar1 = samplePeak; } } // getSample() - void transmitAudioData() { + void transmitAudioData() + { if (!udpSyncConnected) return; //DEBUGSR_PRINTLN("Transmitting UDP Mic Packet"); audioSyncPacket transmitData; - strncpy(transmitData.header, UDP_SYNC_HEADER, 6); + strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6); for (int i = 0; i < 32; i++) { transmitData.myVals[i] = myVals[i]; @@ -720,7 +721,13 @@ class AudioReactive : public Usermod { } // transmitAudioData() - void receiveAudioData() { + bool isValidUdpSyncVersion(const char *header) { + return strncmp_P(header, PSTR(UDP_SYNC_HEADER), 6) == 0; + } + + + void receiveAudioData() + { if (!udpSyncConnected) return; //DEBUGSR_PRINTLN("Checking for UDP Microphone Packet"); @@ -764,8 +771,8 @@ class AudioReactive : public Usermod { * You can use it to initialize variables, sensors or similar. * It is called *AFTER* readFromConfig() */ - void setup() { - + void setup() + { if (!initDone) { // usermod exchangeable data // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers @@ -859,6 +866,7 @@ class AudioReactive : public Usermod { } delay(250); // give mictophone enough time to initialise + if (!audioSource) enabled = false; // audio failed to initialise if (enabled) onUpdateBegin(false); // create FFT task initDone = true; @@ -869,7 +877,8 @@ class AudioReactive : public Usermod { * connected() is called every time the WiFi is (re)connected * Use it to initialize network interfaces */ - void connected() { + void connected() + { if (audioSyncPort > 0 || (audioSyncEnabled & 0x03)) { #ifndef ESP8266 udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort); @@ -890,7 +899,10 @@ class AudioReactive : public Usermod { * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds. * Instead, use a timer check as shown here. */ - void loop() { + void loop() + { + if (!enabled || strip.isUpdating()) return; + if (!(audioSyncEnabled & 0x02)) { // Only run the sampling code IF we're not in Receive mode if (soundAgc > AGC_NUM_PRESETS) soundAgc = 0; // make sure that AGC preset is valid (to avoid array bounds violation) getSample(); // Sample the microphone @@ -918,9 +930,6 @@ class AudioReactive : public Usermod { // update inputLevel Slider based on current AGC gain if ((soundAgc>0) && agcEffect) { - static unsigned long last_update_time = 0; - static unsigned long last_kick_time = 0; - static int last_user_inputLevel = 0; unsigned long now_time = millis(); // "user kick" feature - if user has moved the slider by at least 32 units, we "kick" AGC gain by 30% (up or down) @@ -938,9 +947,9 @@ class AudioReactive : public Usermod { new_user_inputLevel = MIN(MAX(new_user_inputLevel, 0),255); // update user interfaces - restrict frequency to avoid flooding UI's with small changes - if ( ( ((now_time - last_update_time > 3500) && (abs(new_user_inputLevel - inputLevel) > 2)) // small change - every 3.5 sec (max) - ||((now_time - last_update_time > 2200) && (abs(new_user_inputLevel - inputLevel) > 15)) // medium change - ||((now_time - last_update_time > 1200) && (abs(new_user_inputLevel - inputLevel) > 31)) )) // BIG change - every second + if ( (((now_time - last_update_time > 3500) && (abs(new_user_inputLevel - inputLevel) > 2)) // small change - every 3.5 sec (max) + || ((now_time - last_update_time > 2200) && (abs(new_user_inputLevel - inputLevel) > 15)) // medium change + || ((now_time - last_update_time > 1200) && (abs(new_user_inputLevel - inputLevel) > 31))) ) // BIG change - every second { inputLevel = new_user_inputLevel; // change of least 3 units -> update user variable updateInterfaces(CALL_MODE_WS_SEND); // is this the correct way to notify UIs ? Yes says blazoncek @@ -970,14 +979,16 @@ class AudioReactive : public Usermod { } - bool getUMData(um_data_t **data) { + bool getUMData(um_data_t **data) + { if (!data || !enabled) return false; // no pointer provided by caller or not enabled -> exit *data = um_data; return true; } - void onUpdateBegin(bool init) { + void onUpdateBegin(bool init) + { if (init) vTaskDelete(FFT_Task); // update is about to begin, remove task to prevent crash else { // update has failed or create task requested // Define the FFT Task and lock it to core 0 @@ -998,19 +1009,30 @@ class AudioReactive : public Usermod { * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Below it is shown how this could be used for e.g. a light sensor */ - /* void addToJsonInfo(JsonObject& root) { - int reading = 20; - //this code adds "u":{"Light":[20," lux"]} to the info object JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); - JsonArray lightArr = user.createNestedArray("Light"); //name - lightArr.add(reading); //value - lightArr.add(" lux"); //unit + String uiDomString = F(""); + + JsonArray infoArr = user.createNestedArray(uiDomString); + if (enabled) { + infoArr.add(FPSTR(_enabled)); + } else { + infoArr.add(F("disabled")); + } } - */ /* @@ -1029,13 +1051,18 @@ class AudioReactive : public Usermod { * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients */ - /* void readFromJsonState(JsonObject& root) { - userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value - //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); + if (!initDone) return; // prevent crash on boot applyPreset() + bool prevEnabled = enabled; + JsonObject usermod = root[FPSTR(_name)]; + if (!usermod.isNull()) { + if (usermod[FPSTR(_enabled)].is()) { + enabled = usermod[FPSTR(_enabled)].as(); + if (prevEnabled != enabled) onUpdateBegin(!enabled); + } + } } - */ /* @@ -1076,7 +1103,7 @@ class AudioReactive : public Usermod { void addToConfig(JsonObject& root) { JsonObject top = root.createNestedObject(FPSTR(_name)); - top[F("enabled")] = enabled; + top[FPSTR(_enabled)] = enabled; JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); amic["pin"] = audioPin; @@ -1125,7 +1152,7 @@ class AudioReactive : public Usermod { bool configComplete = !top.isNull(); bool prevEnabled = enabled; - configComplete &= getJsonValue(top[F("enabled")], enabled); + configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); if (initDone && prevEnabled != enabled) { onUpdateBegin(!enabled); // create or remove FFT task } @@ -1156,7 +1183,8 @@ class AudioReactive : public Usermod { } - void appendConfigData() { + void appendConfigData() + { oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');")); oappend(SET_F("addOption(dd,'Generic Analog',0);")); oappend(SET_F("addOption(dd,'Generic I2S',1);")); @@ -1201,5 +1229,7 @@ class AudioReactive : public Usermod { // strings to reduce flash memory usage (used more than twice) const char AudioReactive::_name[] PROGMEM = "AudioReactive"; +const char AudioReactive::_enabled[] PROGMEM = "enabled"; const char AudioReactive::_analogmic[] PROGMEM = "analogmic"; const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic"; +const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00001"; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 19c73055..854a7334 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5872,17 +5872,17 @@ uint16_t WS2812FX::mode_2Dscrollingtext(void) { const char *text = PSTR("Use segment name"); // fallback if empty segment name if (SEGMENT.name && strlen(SEGMENT.name)) text = SEGMENT.name; - char lineBuffer[17]; - if (!strstr_P(text, PSTR("#DATETIME"))) { + char lineBuffer[17], sec[3]; + if (strstr_P(text, PSTR("#DATETIME"))) { byte AmPmHour = hour(localTime); boolean isitAM = true; if (useAMPM) { if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } if (AmPmHour == 0) { AmPmHour = 12; } } - sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime)); - if (useAMPM) sprintf_P(lineBuffer,PSTR("%2d:%02d %s"), AmPmHour, minute(localTime), (isitAM ? "AM" : "PM")); - else sprintf_P(lineBuffer,PSTR("%2d:%02d:%02d"), AmPmHour, minute(localTime), second(localTime)); + if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); + else sprintf_P(sec, PSTR(":%02d"), second(localTime)); + sprintf_P(lineBuffer,PSTR("%s %2d %2d:%02d%s"), monthShortStr(month(localTime)), day(localTime), AmPmHour, minute(localTime), sec); text = lineBuffer; } const int numberOfLetters = strlen(text);