From 993b123c8e412b513c63b61fc1b82a774598b6c1 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 5 Mar 2024 19:41:19 +0000 Subject: [PATCH 01/83] Basic usermod for detecting silence --- platformio.ini | 1 + .../usermod_v2_auto_playlist.h | 152 ++++++++++++++++++ wled00/const.h | 1 + wled00/usermods_list.cpp | 8 + 4 files changed, 162 insertions(+) create mode 100644 usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h diff --git a/platformio.ini b/platformio.ini index 14235a4a..b1aeeb6b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -958,6 +958,7 @@ build_flags_S = ; -D WLED_DISABLE_2D ;; un-comment to build a firmware without 2D matrix support ; -D WLED_USE_CIE_BRIGHTNESS_TABLE ;; experimental: use different color / brightness lookup table -D USERMOD_AUDIOREACTIVE + -D USERMOD_AUTO_PLAYLIST -D UM_AUDIOREACTIVE_USE_NEW_FFT ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra ; -D USERMOD_ARTIFX ;; WLEDMM usermod - temporarily moved into "_M", due to problems in "_S" when compiling with -O2 -D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h new file mode 100644 index 00000000..c8cb88a2 --- /dev/null +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -0,0 +1,152 @@ +#pragma once + +#include "wled.h" + + +class AutoPlaylistUsermod : public Usermod { + + private: + bool silenceDetected = true; + uint32_t lastSoundTime; + + public: + + // gets called once at boot. Do all initialization that doesn't depend on + // network here + void setup() { + USER_PRINTLN("AutoPlaylistUsermod"); + } + + // gets called every time WiFi is (re-)connected. Initialize own network + // interfaces here + void connected() {} + + /* + * Da loop. + */ + void loop() { + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // No Audio Reactive + silenceDetected = true; + return; + } + + float volumeSmth = *(float*)um_data->u_data[0]; + + if(volumeSmth > 0.1) { + lastSoundTime = millis(); + } + + if(millis() - lastSoundTime > 60000) { + if(!silenceDetected) { + silenceDetected = true; + USER_PRINTLN("Silence"); + } + } + else { + if(silenceDetected) { + silenceDetected = false; + USER_PRINTLN("End of silence"); + } + } + } + + /* + * 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. + * Below it is shown how this could be used for e.g. a light sensor + */ + void addToJsonInfo(JsonObject& root) { + JsonObject user = root["u"]; + if (user.isNull()) { + user = root.createNestedObject("u"); + } + + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name + + } + + /* + * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + */ + //void addToJsonState(JsonObject& root) { + //} + + /* + * 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) { + if (!initDone) return; // prevent crash on boot applyPreset() + bool en = enabled; + JsonObject um = root[FPSTR(_name)]; + if (!um.isNull()) { + if (en != enabled) enabled = en; + } + } + + void appendConfigData() { + oappend(SET_F("addHB('AutoPlaylist');")); + } + + /* + * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. + * It will be called by WLED when settings are actually saved (for example, LED settings are saved) + * If you want to force saving the current state, use serializeConfig() in your loop(). + * + * CAUTION: serializeConfig() will initiate a filesystem write operation. + * It might cause the LEDs to stutter and will cause flash wear if called too often. + * Use it sparingly and always in the loop, never in network callbacks! + * + * addToConfig() will also not yet add your setting to one of the settings pages automatically. + * To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually. + * + * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! + */ + void addToConfig(JsonObject& root) { + // we add JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}} + JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname + // top[FPSTR(_autoSaveEnabled)] = enabled; + // top[FPSTR(_autoSaveAfterSec)] = autoSaveAfterSec; // usermodparam + // top[FPSTR(_autoSavePreset)] = autoSavePreset; // usermodparam + // top[FPSTR(_autoSaveApplyOnBoot)] = applyAutoSaveOnBoot; + DEBUG_PRINTLN(F("AutoPlaylist config saved.")); + } + + /* + * readFromConfig() can be used to read back the custom settings you added with addToConfig(). + * This is called by WLED when settings are loaded (currently this only happens once immediately after boot) + * + * readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes), + * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. + * If you don't know what that is, don't fret. It most likely doesn't affect your use case :) + * + * The function should return true if configuration was successfully loaded or false if there was no configuration. + */ + bool readFromConfig(JsonObject& root) { + // we look for JSON object: {"Autosave": {"enabled": true, "autoSaveAfterSec": 10, "autoSavePreset": 250, ...}} + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } + + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(" config (re)loaded.")); + + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return true; + } + + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() { + return USERMOD_ID_AUTOPLAYLIST; + } +}; + diff --git a/wled00/const.h b/wled00/const.h index ae97beef..89bd3007 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -142,6 +142,7 @@ #define USERMOD_ID_WEATHER 91 //Usermod "usermod_v2_weather.h" #define USERMOD_ID_GAMES 92 //Usermod "usermod_v2_games.h" #define USERMOD_ID_ANIMARTRIX 93 //Usermod "usermod_v2_animartrix.h" +#define USERMOD_ID_AUTOPLAYLIST 94 // Usermod usermod_v2_auto_playlist.h //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 777ca9d3..b6ecfcc7 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -203,6 +203,9 @@ #ifdef USERMOD_ANIMARTRIX #include "../usermods/usermod_v2_animartrix/usermod_v2_animartrix.h" #endif +#ifdef USERMOD_AUTO_PLAYLIST +#include "../usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h" +#endif void registerUsermods() { @@ -402,4 +405,9 @@ void registerUsermods() usermods.add(new AnimartrixUsermod("Animartrix", false)); #endif +#ifdef USERMOD_AUTO_PLAYLIST + usermods.add(new AutoPlaylistUsermod()); +#endif + + } From b1611796366de41a24379396bd72bb8e7f9df3f2 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 5 Mar 2024 21:05:05 +0000 Subject: [PATCH 02/83] add preferences --- .../usermod_v2_auto_playlist.h | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index c8cb88a2..b8a25797 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -7,10 +7,19 @@ class AutoPlaylistUsermod : public Usermod { private: bool silenceDetected = true; - uint32_t lastSoundTime; + uint32_t lastSoundTime = 0; + byte ambientPlaylist = 1; + byte musicPlaylist = 2; + int timeout = 10; + + static const char _enabled[]; + static const char _ambientPlaylist[]; + static const char _musicPlaylist[]; public: + AutoPlaylistUsermod(const char *name, bool enabled):Usermod(name, enabled) {} + // gets called once at boot. Do all initialization that doesn't depend on // network here void setup() { @@ -25,6 +34,9 @@ class AutoPlaylistUsermod : public Usermod { * Da loop. */ void loop() { + + if(!enabled) return; + um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // No Audio Reactive @@ -38,16 +50,22 @@ class AutoPlaylistUsermod : public Usermod { lastSoundTime = millis(); } - if(millis() - lastSoundTime > 60000) { + if(millis() - lastSoundTime > (timeout * 1000)) { if(!silenceDetected) { silenceDetected = true; - USER_PRINTLN("Silence"); + String name = ""; + getPresetName(ambientPlaylist, name); + USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); + applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); } } else { if(silenceDetected) { silenceDetected = false; - USER_PRINTLN("End of silence"); + String name = ""; + getPresetName(musicPlaylist, name); + USER_PRINTF("AutoPlaylist: End of silence - apply %s\n", name.c_str()); + applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); } } } @@ -64,7 +82,12 @@ class AutoPlaylistUsermod : public Usermod { } JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name - + if(!enabled) { + infoArr.add("disabled"); + } + else { + infoArr.add(lastSoundTime); + } } /* @@ -106,12 +129,10 @@ class AutoPlaylistUsermod : public Usermod { * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! */ void addToConfig(JsonObject& root) { - // we add JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}} JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - // top[FPSTR(_autoSaveEnabled)] = enabled; - // top[FPSTR(_autoSaveAfterSec)] = autoSaveAfterSec; // usermodparam - // top[FPSTR(_autoSavePreset)] = autoSavePreset; // usermodparam - // top[FPSTR(_autoSaveApplyOnBoot)] = applyAutoSaveOnBoot; + // top[FPSTR(_enabled)] = enabled; + top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam + top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam DEBUG_PRINTLN(F("AutoPlaylist config saved.")); } @@ -126,7 +147,6 @@ class AutoPlaylistUsermod : public Usermod { * The function should return true if configuration was successfully loaded or false if there was no configuration. */ bool readFromConfig(JsonObject& root) { - // we look for JSON object: {"Autosave": {"enabled": true, "autoSaveAfterSec": 10, "autoSavePreset": 250, ...}} JsonObject top = root[FPSTR(_name)]; if (top.isNull()) { DEBUG_PRINT(FPSTR(_name)); @@ -135,6 +155,9 @@ class AutoPlaylistUsermod : public Usermod { } DEBUG_PRINT(FPSTR(_name)); + getJsonValue(top["ambientPlaylist"], ambientPlaylist); + getJsonValue(top["musicPlaylist"], musicPlaylist); + DEBUG_PRINTLN(F(" config (re)loaded.")); // use "return !top["newestParameter"].isNull();" when updating Usermod with new features @@ -150,3 +173,6 @@ class AutoPlaylistUsermod : public Usermod { } }; +const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; +const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; +const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; From 4f9675bf9ffb5b231c490f5aa3a1b646b3b03a1c Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 5 Mar 2024 21:14:45 +0000 Subject: [PATCH 03/83] add preferences --- .../usermod_v2_auto_playlist.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index b8a25797..fd1d2863 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -10,11 +10,12 @@ class AutoPlaylistUsermod : public Usermod { uint32_t lastSoundTime = 0; byte ambientPlaylist = 1; byte musicPlaylist = 2; - int timeout = 10; + int timeout = 60; static const char _enabled[]; static const char _ambientPlaylist[]; static const char _musicPlaylist[]; + static const char _timeout[]; public: @@ -130,7 +131,7 @@ class AutoPlaylistUsermod : public Usermod { */ void addToConfig(JsonObject& root) { JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - // top[FPSTR(_enabled)] = enabled; + top[FPSTR(_enabled)] = enabled; top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam DEBUG_PRINTLN(F("AutoPlaylist config saved.")); @@ -155,8 +156,10 @@ class AutoPlaylistUsermod : public Usermod { } DEBUG_PRINT(FPSTR(_name)); - getJsonValue(top["ambientPlaylist"], ambientPlaylist); - getJsonValue(top["musicPlaylist"], musicPlaylist); + getJsonValue(top[_enabled], enabled); + getJsonValue(top[_timeout], timeout); + getJsonValue(top[_ambientPlaylist], ambientPlaylist); + getJsonValue(top[_musicPlaylist], musicPlaylist); DEBUG_PRINTLN(F(" config (re)loaded.")); @@ -173,6 +176,7 @@ class AutoPlaylistUsermod : public Usermod { } }; -const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; -const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; -const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; +const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; +const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; +const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; +const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout"; From 43e5a12b1d0db485da017e16f73bffe9b8d6995d Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 5 Mar 2024 21:23:19 +0000 Subject: [PATCH 04/83] name --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 1 + wled00/usermods_list.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index fd1d2863..e797862b 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -132,6 +132,7 @@ class AutoPlaylistUsermod : public Usermod { void addToConfig(JsonObject& root) { JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname top[FPSTR(_enabled)] = enabled; + top[FPSTR(_timeout)] = timeout; top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam DEBUG_PRINTLN(F("AutoPlaylist config saved.")); diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index b6ecfcc7..334d3e59 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -406,7 +406,7 @@ void registerUsermods() #endif #ifdef USERMOD_AUTO_PLAYLIST - usermods.add(new AutoPlaylistUsermod()); + usermods.add(new AutoPlaylistUsermod("Auto Playlist", true)); #endif From 6550504f605bd99ec56bf9913ea28fec08732dab Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 5 Mar 2024 23:35:23 +0000 Subject: [PATCH 05/83] Troy's extra data --- usermods/audioreactive/audio_reactive.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 6c7fe132..b74cd9c7 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,6 +141,10 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) +int zcr = 0; +int energy = 0; +int lfc = 0; + // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -551,7 +555,18 @@ void FFTcode(void * parameter) // pick our our current mic sample - we take the max value from all samples that go into FFT if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]); + + // WLED-MM/TroyHacks: Calculate zero crossings + if (i < samplesFFT-2) { + if (vReal[i] * vReal[i+1] < 0) { + zcr++; + } + } + // WLED-MM/TroyHacks: Calculate energy + energy += vReal[i] * vReal[i]; } + energy /= 100000; // WLED-MM/TroyHacks: scale this down becasue we're gonna make it bigger later + // release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function // early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results. micDataReal = maxSample; @@ -646,6 +661,8 @@ void FFTcode(void * parameter) vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. } // for() + lfc = fftAddAvg(1,9); // WLED-MM/TroyHacks: Calculate Low-Frequency Content + // mapping of FFT result bins to frequency channels //if (fabsf(sampleAvg) > 0.25f) { // noise gate open if (fabsf(volumeSmth) > 0.25f) { // noise gate open @@ -1746,6 +1763,10 @@ class AudioReactive : public Usermod { um_data->u_type[9] = UMT_FLOAT; um_data->u_data[10] = &agcSensitivity; // used (New) um_data->u_type[10] = UMT_FLOAT; + int extra[3] = {zcr, energy, lfc}; + um_data->u_data[11] = extra; // + um_data->u_type[11] = UMT_INT16_ARR; + #else // ESP8266 // See https://github.com/MoonModules/WLED/pull/60#issuecomment-1666972133 for explanation of these alternative sources of data From 730714b0d633fc985978db7e981f900fc5f2fb4a Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 6 Mar 2024 10:10:16 +0000 Subject: [PATCH 06/83] Add TroyHacks auto change --- .../usermod_v2_auto_playlist.h | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index e797862b..26a3f63c 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -11,11 +11,36 @@ class AutoPlaylistUsermod : public Usermod { byte ambientPlaylist = 1; byte musicPlaylist = 2; int timeout = 60; + bool autoChange = false; + + + int avg_long_energy = 10000; + int avg_long_lfc = 1000; + int avg_long_zcr = 100; + + int avg_short_energy = 10000; + int avg_short_lfc = 1000; + int avg_short_zcr = 100; + + int vector_energy = 0; + int vector_lfc = 0; + int vector_zcr = 0; + + int squared_distance = 0; + int lastchange = millis(); + + int last_beat_interval = millis(); + int change_threshold = 10; + + int change_lockout = 100; // never change below this numnber of millis. I think of this more like a debounce, but opinions may vary. + int ideal_change_max = 5000; // ideally change patterns no more than this number of millis + int ideal_change_min = 2000; // ideally change patterns no less than this number of millis static const char _enabled[]; static const char _ambientPlaylist[]; static const char _musicPlaylist[]; static const char _timeout[]; + static const char _autoChange[]; public: @@ -31,6 +56,53 @@ class AutoPlaylistUsermod : public Usermod { // interfaces here void connected() {} + void change(um_data_t *um_data) { + + int *extra = (int*) um_data->u_data[11]; + + int zcr = extra[0]; + int energy = extra[1]; + int lfc = extra[2]; + + // WLED-MM/TroyHacks: Calculate the long- and short-running averages + // and the squared_distance for the vector. + + avg_long_energy = avg_long_energy * 0.99 + energy * 0.01; + avg_long_lfc = avg_long_lfc * 0.99 + lfc * 0.01; + avg_long_zcr = avg_long_zcr * 0.99 + zcr * 0.01; + + avg_short_energy = avg_short_energy * 0.9 + energy * 0.1; + avg_short_lfc = avg_short_lfc * 0.9 + lfc * 0.1; + avg_short_zcr = avg_short_zcr * 0.9 + zcr * 0.1; + + // allegedly this is faster than pow(whatever,2) + vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); + vector_energy = (avg_short_energy-avg_long_energy)*(avg_short_energy-avg_long_energy); + vector_zcr = (avg_short_zcr-avg_long_zcr)*(avg_short_zcr-avg_long_zcr); + + squared_distance = vector_lfc + vector_energy + vector_zcr; + squared_distance = squared_distance * squared_distance / 10000000; // shorten just because it's a big number + + // WLED-MM/TroyHacks - Change pattern testing + // + int change_interval = millis()-lastchange; + if (squared_distance <= change_threshold && change_interval > change_lockout) { + if (change_interval > ideal_change_max) { + change_threshold += 1; + } else if (change_interval < ideal_change_min) { + change_threshold -= 1; + } + if (change_threshold < 0) change_threshold = 0; + + int newpreset = random(2,30); + while (currentPreset == newpreset || newpreset == 8 || newpreset == 16 || newpreset == 27) { // 8 is a playlist for me so skip it and the other two just suck :D + newpreset = random(2,30); // make sure we get a different preset + } + applyPreset(newpreset); + USER_PRINTF("*** CHANGE! Squared distance = %d - change interval was %d ms - next change min is %d\n",squared_distance, change_interval, change_threshold); + lastchange = millis(); + } + } /* * Da loop. */ @@ -59,6 +131,7 @@ class AutoPlaylistUsermod : public Usermod { USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); } + if(autoChange) change(um_data); } else { if(silenceDetected) { @@ -135,6 +208,7 @@ class AutoPlaylistUsermod : public Usermod { top[FPSTR(_timeout)] = timeout; top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam + top[FPSTR(_autoChange)] = autoChange; DEBUG_PRINTLN(F("AutoPlaylist config saved.")); } @@ -161,6 +235,7 @@ class AutoPlaylistUsermod : public Usermod { getJsonValue(top[_timeout], timeout); getJsonValue(top[_ambientPlaylist], ambientPlaylist); getJsonValue(top[_musicPlaylist], musicPlaylist); + getJsonValue(top[_autoChange], autoChange); DEBUG_PRINTLN(F(" config (re)loaded.")); @@ -181,3 +256,4 @@ const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout"; +const char AutoPlaylistUsermod::_autoChange[] PROGMEM = "autoChange"; From a8661b512555e643f7d0dbd38f4724e783626c22 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 6 Mar 2024 10:43:16 +0000 Subject: [PATCH 07/83] Set autochange in correct state --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 26a3f63c..06a4b85e 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -131,7 +131,6 @@ class AutoPlaylistUsermod : public Usermod { USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); } - if(autoChange) change(um_data); } else { if(silenceDetected) { @@ -141,6 +140,7 @@ class AutoPlaylistUsermod : public Usermod { USER_PRINTF("AutoPlaylist: End of silence - apply %s\n", name.c_str()); applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); } + if(autoChange) change(um_data); } } From 265339cc3fb1927d253d58dfadea9b7f0e7f235a Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 6 Mar 2024 18:38:27 +0000 Subject: [PATCH 08/83] Trying to add enable button to info panel --- .../usermod_v2_auto_playlist.h | 19 ++++++++++++++++--- wled00/usermods_list.cpp | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 06a4b85e..02d4bff8 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -44,7 +44,7 @@ class AutoPlaylistUsermod : public Usermod { public: - AutoPlaylistUsermod(const char *name, bool enabled):Usermod(name, enabled) {} + AutoPlaylistUsermod(bool enabled):Usermod("AutoPlaylist", enabled) {} // gets called once at boot. Do all initialization that doesn't depend on // network here @@ -156,6 +156,18 @@ class AutoPlaylistUsermod : public Usermod { } JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name + String uiDomString = F(""); + infoArr.add(uiDomString); + + infoArr = user.createNestedArray(F("")); if(!enabled) { infoArr.add("disabled"); } @@ -177,10 +189,11 @@ class AutoPlaylistUsermod : public Usermod { */ void readFromJsonState(JsonObject& root) { if (!initDone) return; // prevent crash on boot applyPreset() - bool en = enabled; JsonObject um = root[FPSTR(_name)]; if (!um.isNull()) { - if (en != enabled) enabled = en; + if (um[FPSTR(_enabled)].is()) { + enabled = um[FPSTR(_enabled)].as(); + } } } diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 334d3e59..3cb5c19c 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -406,7 +406,7 @@ void registerUsermods() #endif #ifdef USERMOD_AUTO_PLAYLIST - usermods.add(new AutoPlaylistUsermod("Auto Playlist", true)); + usermods.add(new AutoPlaylistUsermod(true)); #endif From 45d2ae7744a010d146413262e7b35c6341a6b86e Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 7 Mar 2024 19:21:03 +0000 Subject: [PATCH 09/83] Clearer variable names --- usermods/audioreactive/audio_reactive.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index b74cd9c7..96d3d7a6 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,9 +141,9 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -int zcr = 0; +int zeroCrossingCount = 0; int energy = 0; -int lfc = 0; +int lowFreqencyContent = 0; // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -559,7 +559,7 @@ void FFTcode(void * parameter) // WLED-MM/TroyHacks: Calculate zero crossings if (i < samplesFFT-2) { if (vReal[i] * vReal[i+1] < 0) { - zcr++; + zeroCrossingCount++; } } // WLED-MM/TroyHacks: Calculate energy @@ -661,7 +661,7 @@ void FFTcode(void * parameter) vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. } // for() - lfc = fftAddAvg(1,9); // WLED-MM/TroyHacks: Calculate Low-Frequency Content + lowFreqencyContent = fftAddAvg(1,9); // WLED-MM/TroyHacks: Calculate Low-Frequency Content // mapping of FFT result bins to frequency channels //if (fabsf(sampleAvg) > 0.25f) { // noise gate open @@ -1763,7 +1763,7 @@ class AudioReactive : public Usermod { um_data->u_type[9] = UMT_FLOAT; um_data->u_data[10] = &agcSensitivity; // used (New) um_data->u_type[10] = UMT_FLOAT; - int extra[3] = {zcr, energy, lfc}; + int extra[3] = {zeroCrossingCount, energy, lowFreqencyContent}; um_data->u_data[11] = extra; // um_data->u_type[11] = UMT_INT16_ARR; From 95e94a99b9b8bfa85a1fb68b86e708d7ddd21b39 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 8 Mar 2024 00:35:22 +0000 Subject: [PATCH 10/83] Try to use existing FTT data --- .../usermod_v2_auto_playlist.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 02d4bff8..71df7625 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -60,9 +60,9 @@ class AutoPlaylistUsermod : public Usermod { int *extra = (int*) um_data->u_data[11]; - int zcr = extra[0]; + unsigned int zcr = extra[0]; int energy = extra[1]; - int lfc = extra[2]; + int lfc = getFFTFromRange(um_data, 0 , 3); // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the squared_distance for the vector. @@ -103,6 +103,15 @@ class AutoPlaylistUsermod : public Usermod { lastchange = millis(); } } + uint8_t getFFTFromRange(um_data_t *data, uint8_t from, uint8_t to) { + uint8_t *fftResult = (uint8_t*) data->u_data[2]; + uint8_t result = 0; + for (int i = from; i <= to; i++) { + result += fftResult[i] * fftResult[i]; + } + return sqrt(result / (to - from + 1)); + } + /* * Da loop. */ From 9923a7a7d8a294682dcd7ce1bdcdada77a341eb6 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 8 Mar 2024 00:51:12 +0000 Subject: [PATCH 11/83] Swap to unsigned and pointers --- usermods/audioreactive/audio_reactive.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 96d3d7a6..da6005db 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,9 +141,9 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -int zeroCrossingCount = 0; -int energy = 0; -int lowFreqencyContent = 0; +unsigned int zeroCrossingCount = 0; +unsigned int energy = 0; +unsigned int lowFreqencyContent = 0; // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -1763,7 +1763,7 @@ class AudioReactive : public Usermod { um_data->u_type[9] = UMT_FLOAT; um_data->u_data[10] = &agcSensitivity; // used (New) um_data->u_type[10] = UMT_FLOAT; - int extra[3] = {zeroCrossingCount, energy, lowFreqencyContent}; + unsigned int* extra[3] = {&zeroCrossingCount, &energy, &lowFreqencyContent}; um_data->u_data[11] = extra; // um_data->u_type[11] = UMT_INT16_ARR; From 5c4c72d068dc0ea559bc01390e09138c298799de Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 12 Mar 2024 20:27:40 +0000 Subject: [PATCH 12/83] Do not auto-select preset if the lamp is off --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 71df7625..e57ab5ba 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -138,7 +138,7 @@ class AutoPlaylistUsermod : public Usermod { String name = ""; getPresetName(ambientPlaylist, name); USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); - applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); + if(bri > 0) applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); } } else { @@ -147,7 +147,7 @@ class AutoPlaylistUsermod : public Usermod { String name = ""; getPresetName(musicPlaylist, name); USER_PRINTF("AutoPlaylist: End of silence - apply %s\n", name.c_str()); - applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); + if(bri > 0) applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); } if(autoChange) change(um_data); } From c0f2f747556d34eeb1ded4ea9f43449d9fadf509 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 14 Mar 2024 20:14:40 +0000 Subject: [PATCH 13/83] Load presets from playlist --- .../usermod_v2_auto_playlist.h | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index e57ab5ba..60c1ea68 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -36,6 +36,8 @@ class AutoPlaylistUsermod : public Usermod { int ideal_change_max = 5000; // ideally change patterns no more than this number of millis int ideal_change_min = 2000; // ideally change patterns no less than this number of millis + std::vector autoChangeIds; + static const char _enabled[]; static const char _ambientPlaylist[]; static const char _musicPlaylist[]; @@ -94,10 +96,23 @@ class AutoPlaylistUsermod : public Usermod { } if (change_threshold < 0) change_threshold = 0; - int newpreset = random(2,30); - while (currentPreset == newpreset || newpreset == 8 || newpreset == 16 || newpreset == 27) { // 8 is a playlist for me so skip it and the other two just suck :D - newpreset = random(2,30); // make sure we get a different preset + if(autoChangeIds.size() == 0) { + USER_PRINTF("Loading presets from playlist:%u\n", currentPlaylist); + JsonObject playtlistOjb = doc.to(); + serializePlaylist(playtlistOjb); + JsonArray playlistArray = playtlistOjb["playlist"]["ps"]; + for(JsonVariant v : playlistArray) { + USER_PRINTF("Adding %u to autoChangeIds\n", v.as()); + autoChangeIds.push_back(v.as()); + } } + + uint8_t newpreset = 0; + do { + newpreset = autoChangeIds.at(random(0, (autoChangeIds.size() - 1))); + } + while (currentPreset == newpreset); + applyPreset(newpreset); USER_PRINTF("*** CHANGE! Squared distance = %d - change interval was %d ms - next change min is %d\n",squared_distance, change_interval, change_threshold); lastchange = millis(); @@ -105,7 +120,7 @@ class AutoPlaylistUsermod : public Usermod { } uint8_t getFFTFromRange(um_data_t *data, uint8_t from, uint8_t to) { uint8_t *fftResult = (uint8_t*) data->u_data[2]; - uint8_t result = 0; + uint16_t result = 0; for (int i = from; i <= to; i++) { result += fftResult[i] * fftResult[i]; } From 3ed5b89efcf3db54642ea324592ef18281f7917a Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 14 Mar 2024 20:16:23 +0000 Subject: [PATCH 14/83] Skip if turned off --- .../usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 60c1ea68..7a90d706 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -134,6 +134,8 @@ class AutoPlaylistUsermod : public Usermod { if(!enabled) return; + if(bri == 0) return; + um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // No Audio Reactive @@ -153,7 +155,7 @@ class AutoPlaylistUsermod : public Usermod { String name = ""; getPresetName(ambientPlaylist, name); USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); - if(bri > 0) applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); + applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); } } else { @@ -162,7 +164,7 @@ class AutoPlaylistUsermod : public Usermod { String name = ""; getPresetName(musicPlaylist, name); USER_PRINTF("AutoPlaylist: End of silence - apply %s\n", name.c_str()); - if(bri > 0) applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); + applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); } if(autoChange) change(um_data); } From c4cb5af78c4c8e4902833f0c62148ef73ac5f33e Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Mon, 18 Mar 2024 14:57:41 +0000 Subject: [PATCH 15/83] Disable auto-playlist if manual selection is made --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 7a90d706..419e241c 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -12,6 +12,7 @@ class AutoPlaylistUsermod : public Usermod { byte musicPlaylist = 2; int timeout = 60; bool autoChange = false; + byte lastAutoPreset = 0; int avg_long_energy = 10000; @@ -136,6 +137,8 @@ class AutoPlaylistUsermod : public Usermod { if(bri == 0) return; + if(lastAutoPreset > 0 && lastAutoPreset != currentPreset) enabled = false; + um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // No Audio Reactive @@ -156,6 +159,7 @@ class AutoPlaylistUsermod : public Usermod { getPresetName(ambientPlaylist, name); USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); + lastAutoPreset = ambientPlaylist; } } else { @@ -165,6 +169,7 @@ class AutoPlaylistUsermod : public Usermod { getPresetName(musicPlaylist, name); USER_PRINTF("AutoPlaylist: End of silence - apply %s\n", name.c_str()); applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); + lastAutoPreset = ambientPlaylist; } if(autoChange) change(um_data); } From 2fdd438d34327358b00f26cde2677532a96021c1 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 21 Mar 2024 20:06:29 +0000 Subject: [PATCH 16/83] Disable AutoPlaylist if user makes manual change --- .../usermod_v2_auto_playlist.h | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 419e241c..e1c017d9 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -12,7 +12,7 @@ class AutoPlaylistUsermod : public Usermod { byte musicPlaylist = 2; int timeout = 60; bool autoChange = false; - byte lastAutoPreset = 0; + byte lastAutoPlaylist = 0; int avg_long_energy = 10000; @@ -135,9 +135,14 @@ class AutoPlaylistUsermod : public Usermod { if(!enabled) return; + if(millis() < 10000) return; // Wait for device to settle + if(bri == 0) return; - if(lastAutoPreset > 0 && lastAutoPreset != currentPreset) enabled = false; + if(lastAutoPlaylist > 0 && lastAutoPlaylist != currentPlaylist) { + USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); + enabled = false; + } um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -155,21 +160,15 @@ class AutoPlaylistUsermod : public Usermod { if(millis() - lastSoundTime > (timeout * 1000)) { if(!silenceDetected) { silenceDetected = true; - String name = ""; - getPresetName(ambientPlaylist, name); - USER_PRINTF("AutoPlaylist: Silence - apply %s\n", name.c_str()); - applyPreset(ambientPlaylist, CALL_MODE_NOTIFICATION); - lastAutoPreset = ambientPlaylist; + USER_PRINTF("AutoPlaylist: Silence "); + changePlaylist(ambientPlaylist); } } else { if(silenceDetected) { silenceDetected = false; - String name = ""; - getPresetName(musicPlaylist, name); - USER_PRINTF("AutoPlaylist: End of silence - apply %s\n", name.c_str()); - applyPreset(musicPlaylist, CALL_MODE_NOTIFICATION); - lastAutoPreset = ambientPlaylist; + USER_PRINTF("AutoPlaylist: End of silence "); + changePlaylist(musicPlaylist); } if(autoChange) change(um_data); } @@ -253,6 +252,7 @@ class AutoPlaylistUsermod : public Usermod { top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam top[FPSTR(_autoChange)] = autoChange; + lastAutoPlaylist = 0; DEBUG_PRINTLN(F("AutoPlaylist config saved.")); } @@ -294,6 +294,18 @@ class AutoPlaylistUsermod : public Usermod { uint16_t getId() { return USERMOD_ID_AUTOPLAYLIST; } + + private: + + void changePlaylist(byte id) { + String name = ""; + getPresetName(id, name); + USER_PRINTF("apply %s\n", name.c_str()); + applyPreset(id, CALL_MODE_NOTIFICATION); + lastAutoPlaylist = id; + } + + }; const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; From cfafa0d358815696f2201e862c35d24998b18b80 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 21 Mar 2024 23:32:44 +0000 Subject: [PATCH 17/83] Auto disengage --- .../usermod_v2_auto_playlist.h | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index e1c017d9..4d5ba8c9 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -133,16 +133,26 @@ class AutoPlaylistUsermod : public Usermod { */ void loop() { - if(!enabled) return; - if(millis() < 10000) return; // Wait for device to settle - if(bri == 0) return; - - if(lastAutoPlaylist > 0 && lastAutoPlaylist != currentPlaylist) { - USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); - enabled = false; + if(lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) { + if(currentPlaylist == musicPlaylist) { + USER_PRINTF("AutoPlaylist: enabled due to manual change of playlist back to %u\n", currentPlaylist); + enabled = true; + lastAutoPlaylist = currentPlaylist; + } + else if(enabled) { + USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); + enabled = false; + } } + if(!enabled && currentPlaylist == musicPlaylist) { + USER_PRINTF("AutoPlaylist: enabled due selecting musicPlaylist(%u)", musicPlaylist); + enabled = true; + } + if(!enabled) return; + + if(bri == 0) return; um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -153,7 +163,7 @@ class AutoPlaylistUsermod : public Usermod { float volumeSmth = *(float*)um_data->u_data[0]; - if(volumeSmth > 0.1) { + if(volumeSmth > 0.5) { lastSoundTime = millis(); } From fd714e3e0f5e394e7a786a2590f580d9ee39c10b Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 22 Mar 2024 00:07:36 +0000 Subject: [PATCH 18/83] Remove buttton --- .../usermod_v2_auto_playlist.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 4d5ba8c9..e39f4c2b 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -196,16 +196,6 @@ class AutoPlaylistUsermod : public Usermod { } JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name - String uiDomString = F(""); - infoArr.add(uiDomString); infoArr = user.createNestedArray(F("")); if(!enabled) { From 734ba393998bd0bc350af156d31a61f5982ebd86 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 23 Mar 2024 09:29:32 -0400 Subject: [PATCH 19/83] Fixes. Auto is too animated. --- usermods/audioreactive/audio_reactive.h | 3 ++- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index da6005db..b234b43c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -36,7 +36,7 @@ #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) -// this applies "pink noise scaling" to FFT results before computing the major peak for effects. +// this applies "pink noise scaling" to FFT results before computing the major peak for effects.0 // currently only for ESP32-S3 and classic ESP32, due to increased runtime #define FFT_MAJORPEAK_HUMAN_EAR #endif @@ -549,6 +549,7 @@ void FFTcode(void * parameter) // find highest sample in the batch float maxSample = 0.0f; // max sample from FFT batch + zeroCrossingCount = 0; for (int i=0; i < samplesFFT; i++) { // set imaginary parts to 0 vImag[i] = 0; diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index e39f4c2b..8be728b1 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -34,8 +34,8 @@ class AutoPlaylistUsermod : public Usermod { int change_threshold = 10; int change_lockout = 100; // never change below this numnber of millis. I think of this more like a debounce, but opinions may vary. - int ideal_change_max = 5000; // ideally change patterns no more than this number of millis int ideal_change_min = 2000; // ideally change patterns no less than this number of millis + int ideal_change_max = 5000; // ideally change patterns no more than this number of millis std::vector autoChangeIds; @@ -65,7 +65,7 @@ class AutoPlaylistUsermod : public Usermod { unsigned int zcr = extra[0]; int energy = extra[1]; - int lfc = getFFTFromRange(um_data, 0 , 3); + int lfc = extra[2]; // getFFTFromRange(um_data, 1 , 3); // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the squared_distance for the vector. From 8d687b3a868cdba5faf953beeb0914994dc70943 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 23 Mar 2024 10:10:09 -0400 Subject: [PATCH 20/83] Debugging --- usermods/audioreactive/audio_reactive.h | 2 +- .../usermod_v2_auto_playlist.h | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index b234b43c..b19b4532 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -662,7 +662,7 @@ void FFTcode(void * parameter) vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. } // for() - lowFreqencyContent = fftAddAvg(1,9); // WLED-MM/TroyHacks: Calculate Low-Frequency Content + lowFreqencyContent = fftAddAvg(1,4); // WLED-MM/TroyHacks: Calculate Low-Frequency Content // mapping of FFT result bins to frequency channels //if (fabsf(sampleAvg) > 0.25f) { // noise gate open diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 8be728b1..2b21691f 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -14,7 +14,6 @@ class AutoPlaylistUsermod : public Usermod { bool autoChange = false; byte lastAutoPlaylist = 0; - int avg_long_energy = 10000; int avg_long_lfc = 1000; int avg_long_zcr = 100; @@ -65,7 +64,9 @@ class AutoPlaylistUsermod : public Usermod { unsigned int zcr = extra[0]; int energy = extra[1]; - int lfc = extra[2]; // getFFTFromRange(um_data, 1 , 3); + int lfc = extra[2]; // getFFTFromRange(um_data, 2 , 4); + + USER_PRINTF("zcr = %d, energy = %d, lfc = %d\n",zcr,energy,lfc); // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the squared_distance for the vector. @@ -78,12 +79,18 @@ class AutoPlaylistUsermod : public Usermod { avg_short_lfc = avg_short_lfc * 0.9 + lfc * 0.1; avg_short_zcr = avg_short_zcr * 0.9 + zcr * 0.1; + energy = 0; + lfc = 0; + zcr = 0; + // allegedly this is faster than pow(whatever,2) vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); vector_energy = (avg_short_energy-avg_long_energy)*(avg_short_energy-avg_long_energy); vector_zcr = (avg_short_zcr-avg_long_zcr)*(avg_short_zcr-avg_long_zcr); squared_distance = vector_lfc + vector_energy + vector_zcr; + // USER_PRINTF("squared_distance = %d\n", squared_distance * squared_distance / 10000000); + squared_distance = squared_distance * squared_distance / 10000000; // shorten just because it's a big number // WLED-MM/TroyHacks - Change pattern testing @@ -119,13 +126,22 @@ class AutoPlaylistUsermod : public Usermod { lastchange = millis(); } } + + // // static float fftAddAvgLin(int from, int to) { + // float result = 0.0f; + // for (int i = from; i <= to; i++) { + // result += vReal[i]; + // } + // return result / float(to - from + 1); + // } + uint8_t getFFTFromRange(um_data_t *data, uint8_t from, uint8_t to) { uint8_t *fftResult = (uint8_t*) data->u_data[2]; uint16_t result = 0; for (int i = from; i <= to; i++) { - result += fftResult[i] * fftResult[i]; + result += fftResult[i]; // * fftResult[i]; } - return sqrt(result / (to - from + 1)); + return result / float(to - from + 1); // sqrt(result / (to - from + 1)); } /* From 2fe86bde5db87bb43f6033f2df0dd2ae7789dbfb Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 23 Mar 2024 17:41:34 -0400 Subject: [PATCH 21/83] Feature complete... and better! --- usermods/audioreactive/audio_reactive.h | 57 +++++--- .../usermod_v2_auto_playlist.h | 138 +++++++++++------- 2 files changed, 120 insertions(+), 75 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index b19b4532..db6f5065 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,9 +141,9 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -unsigned int zeroCrossingCount = 0; -unsigned int energy = 0; -unsigned int lowFreqencyContent = 0; +uint_fast32_t zeroCrossingCount = 0; +uint_fast32_t energy = 0; +uint_fast32_t lowFreqencyContent = 0; // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -498,6 +498,21 @@ void FFTcode(void * parameter) // get a fresh batch of samples from I2S if (audioSource) audioSource->getSamples(vReal, samplesFFT); + + // WLED-MM/TroyHacks: Calculate zero crossings + // + zeroCrossingCount = 0; + energy = 0; + + for (int i=0; i < samplesFFT; i++) { + if (i < (samplesFFT)-2) { + if((vReal[i] >= 0 && vReal[i+1] < 0) || (vReal[i+1] < 0 && vReal[i+1] >= 0)) { + zeroCrossingCount++; + } + } + // WLED-MM/TroyHacks: Calculate energy + energy += (vReal[i] * vReal[i])/10000000; + } #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) // debug info in case that stack usage changes @@ -549,24 +564,13 @@ void FFTcode(void * parameter) // find highest sample in the batch float maxSample = 0.0f; // max sample from FFT batch - zeroCrossingCount = 0; for (int i=0; i < samplesFFT; i++) { // set imaginary parts to 0 vImag[i] = 0; // pick our our current mic sample - we take the max value from all samples that go into FFT if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]); - - // WLED-MM/TroyHacks: Calculate zero crossings - if (i < samplesFFT-2) { - if (vReal[i] * vReal[i+1] < 0) { - zeroCrossingCount++; - } - } - // WLED-MM/TroyHacks: Calculate energy - energy += vReal[i] * vReal[i]; } - energy /= 100000; // WLED-MM/TroyHacks: scale this down becasue we're gonna make it bigger later // release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function // early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results. @@ -641,6 +645,17 @@ void FFTcode(void * parameter) #endif FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects FFT_MajPeakSmth = FFT_MajPeakSmth + 0.42 * (FFT_MajorPeak - FFT_MajPeakSmth); // I like this "swooping peak" look + + // WLED-MM/TroyHacks: Calculate Low-Frequency Content + // + lowFreqencyContent = fftAddAvg(2,4)/1000; + + // USER_PRINT("ZCR = "); + // USER_PRINT(zeroCrossingCount); + // USER_PRINT(" Energy = "); + // USER_PRINT(" LFC = "); + // USER_PRINT(lowFreqencyContent); + // USER_PRINTLN(); } else { // skip second run --> clear fft results, keep peaks memset(vReal, 0, sizeof(vReal)); @@ -662,8 +677,6 @@ void FFTcode(void * parameter) vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. } // for() - lowFreqencyContent = fftAddAvg(1,4); // WLED-MM/TroyHacks: Calculate Low-Frequency Content - // mapping of FFT result bins to frequency channels //if (fabsf(sampleAvg) > 0.25f) { // noise gate open if (fabsf(volumeSmth) > 0.25f) { // noise gate open @@ -1738,7 +1751,7 @@ class AudioReactive : public Usermod { // usermod exchangeable data // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers um_data = new um_data_t; - um_data->u_size = 11; + um_data->u_size = 13; um_data->u_type = new um_types_t[um_data->u_size]; um_data->u_data = new void*[um_data->u_size]; um_data->u_data[0] = &volumeSmth; //*used (New) @@ -1764,10 +1777,12 @@ class AudioReactive : public Usermod { um_data->u_type[9] = UMT_FLOAT; um_data->u_data[10] = &agcSensitivity; // used (New) um_data->u_type[10] = UMT_FLOAT; - unsigned int* extra[3] = {&zeroCrossingCount, &energy, &lowFreqencyContent}; - um_data->u_data[11] = extra; // - um_data->u_type[11] = UMT_INT16_ARR; - + um_data->u_data[11] = &zeroCrossingCount; + um_data->u_type[11] = UMT_UINT32; + um_data->u_data[12] = &energy; + um_data->u_type[12] = UMT_UINT32; + um_data->u_data[13] = &lowFreqencyContent; + um_data->u_type[13] = UMT_UINT32; #else // ESP8266 // See https://github.com/MoonModules/WLED/pull/60#issuecomment-1666972133 for explanation of these alternative sources of data diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 2b21691f..e8061331 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -13,28 +13,32 @@ class AutoPlaylistUsermod : public Usermod { int timeout = 60; bool autoChange = false; byte lastAutoPlaylist = 0; + int change_timer = millis(); - int avg_long_energy = 10000; - int avg_long_lfc = 1000; - int avg_long_zcr = 100; + uint_fast32_t avg_long_energy = 250; + uint_fast32_t avg_long_lfc = 1000; + uint_fast32_t avg_long_zcr = 500; - int avg_short_energy = 10000; - int avg_short_lfc = 1000; - int avg_short_zcr = 100; + uint_fast32_t avg_short_energy = 250; + uint_fast32_t avg_short_lfc = 1000; + uint_fast32_t avg_short_zcr = 500; - int vector_energy = 0; - int vector_lfc = 0; - int vector_zcr = 0; + uint_fast32_t vector_energy = 0; + uint_fast32_t vector_lfc = 0; + uint_fast32_t vector_zcr = 0; + + uint_fast32_t distance = 0; + + // uint_fast64_t squared_distance = 0; - int squared_distance = 0; int lastchange = millis(); int last_beat_interval = millis(); - int change_threshold = 10; + int change_threshold = 50; - int change_lockout = 100; // never change below this numnber of millis. I think of this more like a debounce, but opinions may vary. - int ideal_change_min = 2000; // ideally change patterns no less than this number of millis - int ideal_change_max = 5000; // ideally change patterns no more than this number of millis + int change_lockout = 1000; // never change below this numnber of millis. I think of this more like a debounce, but opinions may vary. + int ideal_change_min = 10000; // ideally change patterns no less than this number of millis + int ideal_change_max = 20000; // ideally change patterns no more than this number of millis std::vector autoChangeIds; @@ -43,10 +47,15 @@ class AutoPlaylistUsermod : public Usermod { static const char _musicPlaylist[]; static const char _timeout[]; static const char _autoChange[]; + static const char _change_lockout[]; + static const char _ideal_change_min[]; + static const char _ideal_change_max[]; public: - AutoPlaylistUsermod(bool enabled):Usermod("AutoPlaylist", enabled) {} + AutoPlaylistUsermod(bool enabled):Usermod("AutoPlaylist", enabled) { + // noop + } // gets called once at boot. Do all initialization that doesn't depend on // network here @@ -56,47 +65,62 @@ class AutoPlaylistUsermod : public Usermod { // gets called every time WiFi is (re-)connected. Initialize own network // interfaces here - void connected() {} + void connected() { + // noop + } void change(um_data_t *um_data) { - int *extra = (int*) um_data->u_data[11]; - - unsigned int zcr = extra[0]; - int energy = extra[1]; - int lfc = extra[2]; // getFFTFromRange(um_data, 2 , 4); - - USER_PRINTF("zcr = %d, energy = %d, lfc = %d\n",zcr,energy,lfc); + uint_fast32_t zcr = *(uint_fast32_t*)um_data->u_data[11]; + uint_fast32_t energy = *(uint_fast32_t*)um_data->u_data[12]; + uint_fast32_t lfc = *(uint_fast32_t*)um_data->u_data[13]; // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the squared_distance for the vector. - avg_long_energy = avg_long_energy * 0.99 + energy * 0.01; - avg_long_lfc = avg_long_lfc * 0.99 + lfc * 0.01; - avg_long_zcr = avg_long_zcr * 0.99 + zcr * 0.01; + if (volumeSmth > 0.1) { - avg_short_energy = avg_short_energy * 0.9 + energy * 0.1; - avg_short_lfc = avg_short_lfc * 0.9 + lfc * 0.1; - avg_short_zcr = avg_short_zcr * 0.9 + zcr * 0.1; + avg_long_energy = avg_long_energy * 0.99 + energy * 0.01; + avg_long_lfc = avg_long_lfc * 0.99 + lfc * 0.01; + avg_long_zcr = avg_long_zcr * 0.99 + zcr * 0.01; - energy = 0; - lfc = 0; - zcr = 0; + avg_short_energy = avg_short_energy * 0.9 + energy * 0.1; + avg_short_lfc = avg_short_lfc * 0.9 + lfc * 0.1; + avg_short_zcr = avg_short_zcr * 0.9 + zcr * 0.1; - // allegedly this is faster than pow(whatever,2) - vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); - vector_energy = (avg_short_energy-avg_long_energy)*(avg_short_energy-avg_long_energy); - vector_zcr = (avg_short_zcr-avg_long_zcr)*(avg_short_zcr-avg_long_zcr); + // allegedly this is faster than pow(whatever,2) + vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); + vector_energy = (avg_short_energy-avg_long_energy)*(avg_short_energy-avg_long_energy); + vector_zcr = (avg_short_zcr-avg_long_zcr)*(avg_short_zcr-avg_long_zcr); - squared_distance = vector_lfc + vector_energy + vector_zcr; + } + + distance = vector_lfc + vector_energy + vector_zcr; // USER_PRINTF("squared_distance = %d\n", squared_distance * squared_distance / 10000000); - squared_distance = squared_distance * squared_distance / 10000000; // shorten just because it's a big number + // squared_distance = distance * distance; + + int change_interval = millis()-lastchange; + + // if (millis() > change_timer + 100) { + // // if (change_interval > ideal_change_max) { + // // USER_PRINTF("Increasing sensitivity to: %d\n",change_threshold++); + // // } + // USER_PRINT("\tDistance: "); + // USER_PRINT(distance); + // USER_PRINT("\tv_lfc: "); + // USER_PRINT(vector_lfc); + // USER_PRINT("\tv_energy: "); + // USER_PRINT(vector_energy); + // USER_PRINT("\tv_zcr: "); + // USER_PRINTLN(vector_zcr); + + // change_timer = millis(); + // } // WLED-MM/TroyHacks - Change pattern testing // - int change_interval = millis()-lastchange; - if (squared_distance <= change_threshold && change_interval > change_lockout) { + if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 0.1) { if (change_interval > ideal_change_max) { change_threshold += 1; } else if (change_interval < ideal_change_min) { @@ -122,19 +146,16 @@ class AutoPlaylistUsermod : public Usermod { while (currentPreset == newpreset); applyPreset(newpreset); - USER_PRINTF("*** CHANGE! Squared distance = %d - change interval was %d ms - next change min is %d\n",squared_distance, change_interval, change_threshold); + USER_PRINT("*** CHANGE! Vector distance = "); + USER_PRINT(distance); + USER_PRINT(" - change interval was "); + USER_PRINT(change_interval); + USER_PRINT("ms - next change min is "); + USER_PRINTLN(change_threshold); lastchange = millis(); } } - // // static float fftAddAvgLin(int from, int to) { - // float result = 0.0f; - // for (int i = from; i <= to; i++) { - // result += vReal[i]; - // } - // return result / float(to - from + 1); - // } - uint8_t getFFTFromRange(um_data_t *data, uint8_t from, uint8_t to) { uint8_t *fftResult = (uint8_t*) data->u_data[2]; uint16_t result = 0; @@ -268,6 +289,9 @@ class AutoPlaylistUsermod : public Usermod { top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam top[FPSTR(_autoChange)] = autoChange; + top[FPSTR(_change_lockout)] = change_lockout; + top[FPSTR(_ideal_change_min)] = ideal_change_min; + top[FPSTR(_ideal_change_max)] = ideal_change_max; lastAutoPlaylist = 0; DEBUG_PRINTLN(F("AutoPlaylist config saved.")); } @@ -296,6 +320,9 @@ class AutoPlaylistUsermod : public Usermod { getJsonValue(top[_ambientPlaylist], ambientPlaylist); getJsonValue(top[_musicPlaylist], musicPlaylist); getJsonValue(top[_autoChange], autoChange); + getJsonValue(top[_change_lockout], change_lockout); + getJsonValue(top[_ideal_change_min], ideal_change_min); + getJsonValue(top[_ideal_change_max], ideal_change_max); DEBUG_PRINTLN(F(" config (re)loaded.")); @@ -324,8 +351,11 @@ class AutoPlaylistUsermod : public Usermod { }; -const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; -const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; -const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; -const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout"; -const char AutoPlaylistUsermod::_autoChange[] PROGMEM = "autoChange"; +const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; +const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; +const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; +const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout"; +const char AutoPlaylistUsermod::_autoChange[] PROGMEM = "autoChange"; +const char AutoPlaylistUsermod::_change_lockout[] PROGMEM = "change_lockout"; +const char AutoPlaylistUsermod::_ideal_change_min[] PROGMEM = "ideal_change_min"; +const char AutoPlaylistUsermod::_ideal_change_max[] PROGMEM = "ideal_change_max"; From 9e9b0149594d790a450d5ff104b623a0bb1736b9 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 23 Mar 2024 18:22:44 -0400 Subject: [PATCH 22/83] change_threshold pushback when window missing. --- .../usermod_v2_auto_playlist.h | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index e8061331..44407616 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -96,27 +96,30 @@ class AutoPlaylistUsermod : public Usermod { } distance = vector_lfc + vector_energy + vector_zcr; - // USER_PRINTF("squared_distance = %d\n", squared_distance * squared_distance / 10000000); - // squared_distance = distance * distance; int change_interval = millis()-lastchange; - // if (millis() > change_timer + 100) { - // // if (change_interval > ideal_change_max) { - // // USER_PRINTF("Increasing sensitivity to: %d\n",change_threshold++); - // // } - // USER_PRINT("\tDistance: "); - // USER_PRINT(distance); - // USER_PRINT("\tv_lfc: "); - // USER_PRINT(vector_lfc); - // USER_PRINT("\tv_energy: "); - // USER_PRINT(vector_energy); - // USER_PRINT("\tv_zcr: "); - // USER_PRINTLN(vector_zcr); + // USER_PRINT("\tDistance: "); + // USER_PRINT(distance); + // USER_PRINT("\tv_lfc: "); + // USER_PRINT(vector_lfc); + // USER_PRINT("\tv_energy: "); + // USER_PRINT(vector_energy); + // USER_PRINT("\tv_zcr: "); + // USER_PRINTLN(vector_zcr); - // change_timer = millis(); - // } + if (millis() > change_timer + ideal_change_min) { + // Make the analysis less sensitive if we miss the window, slowly. + // Sometimes the analysis lowers the change_threshold too much for + // the current music, especially after track changes or during + // sparce intros and breakdowns. + if (change_interval > ideal_change_min) { + change_threshold++; + USER_PRINTF("Increasing change_threshold to: %d\n",change_threshold); + } + change_timer = millis(); + } // WLED-MM/TroyHacks - Change pattern testing // @@ -146,14 +149,17 @@ class AutoPlaylistUsermod : public Usermod { while (currentPreset == newpreset); applyPreset(newpreset); + USER_PRINT("*** CHANGE! Vector distance = "); USER_PRINT(distance); - USER_PRINT(" - change interval was "); + USER_PRINT(" - change_interval was "); USER_PRINT(change_interval); - USER_PRINT("ms - next change min is "); + USER_PRINT("ms - next change_threshold is "); USER_PRINTLN(change_threshold); lastchange = millis(); + } + } uint8_t getFFTFromRange(um_data_t *data, uint8_t from, uint8_t to) { @@ -283,7 +289,9 @@ class AutoPlaylistUsermod : public Usermod { * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! */ void addToConfig(JsonObject& root) { + JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname + top[FPSTR(_enabled)] = enabled; top[FPSTR(_timeout)] = timeout; top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam @@ -292,8 +300,11 @@ class AutoPlaylistUsermod : public Usermod { top[FPSTR(_change_lockout)] = change_lockout; top[FPSTR(_ideal_change_min)] = ideal_change_min; top[FPSTR(_ideal_change_max)] = ideal_change_max; + lastAutoPlaylist = 0; + DEBUG_PRINTLN(F("AutoPlaylist config saved.")); + } /* @@ -307,7 +318,9 @@ class AutoPlaylistUsermod : public Usermod { * The function should return true if configuration was successfully loaded or false if there was no configuration. */ bool readFromConfig(JsonObject& root) { + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); @@ -328,6 +341,7 @@ class AutoPlaylistUsermod : public Usermod { // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; + } /* @@ -348,7 +362,6 @@ class AutoPlaylistUsermod : public Usermod { lastAutoPlaylist = id; } - }; const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; From 901e4fa83ea8d9d6e971cff2b6373b1221933688 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:19:10 -0400 Subject: [PATCH 23/83] Better automatic change_intervals --- .../usermod_v2_auto_playlist.h | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 44407616..39b14049 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -28,7 +28,7 @@ class AutoPlaylistUsermod : public Usermod { uint_fast32_t vector_zcr = 0; uint_fast32_t distance = 0; - + uint_fast32_t distance_tracker = UINT_FAST32_MAX; // uint_fast64_t squared_distance = 0; int lastchange = millis(); @@ -78,7 +78,7 @@ class AutoPlaylistUsermod : public Usermod { // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the squared_distance for the vector. - if (volumeSmth > 0.1) { + if (volumeSmth > 1) { avg_long_energy = avg_long_energy * 0.99 + energy * 0.01; avg_long_lfc = avg_long_lfc * 0.99 + lfc * 0.01; @@ -95,11 +95,17 @@ class AutoPlaylistUsermod : public Usermod { } + // distance is linear, squared_distance is magnitude. + // linear is easier to fine-tune, IMHO. distance = vector_lfc + vector_energy + vector_zcr; // squared_distance = distance * distance; int change_interval = millis()-lastchange; + if (distance < distance_tracker && change_interval > change_lockout) { + distance_tracker = distance; + } + // USER_PRINT("\tDistance: "); // USER_PRINT(distance); // USER_PRINT("\tv_lfc: "); @@ -114,9 +120,13 @@ class AutoPlaylistUsermod : public Usermod { // Sometimes the analysis lowers the change_threshold too much for // the current music, especially after track changes or during // sparce intros and breakdowns. - if (change_interval > ideal_change_min) { - change_threshold++; + if (change_interval > ideal_change_min && distance_tracker < 1000) { + // change_threshold++; + change_threshold += distance_tracker/10; USER_PRINTF("Increasing change_threshold to: %d\n",change_threshold); + USER_PRINT (" lowest recorded distance was: "); + USER_PRINTLN(distance_tracker); + distance_tracker = UINT_FAST32_MAX; } change_timer = millis(); } @@ -124,11 +134,17 @@ class AutoPlaylistUsermod : public Usermod { // WLED-MM/TroyHacks - Change pattern testing // if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 0.1) { + if (change_interval > ideal_change_max) { - change_threshold += 1; + // change_threshold += 1; + change_threshold += distance/10; } else if (change_interval < ideal_change_min) { - change_threshold -= 1; + // change_threshold -= 1; + change_threshold -= distance/10; } + + distance_tracker = UINT_FAST32_MAX; + if (change_threshold < 0) change_threshold = 0; if(autoChangeIds.size() == 0) { @@ -144,7 +160,7 @@ class AutoPlaylistUsermod : public Usermod { uint8_t newpreset = 0; do { - newpreset = autoChangeIds.at(random(0, (autoChangeIds.size() - 1))); + newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. } while (currentPreset == newpreset); From 5ff44755d81a7f1431eb7c1aff2dfa47fb0487a5 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 08:19:05 -0400 Subject: [PATCH 24/83] calculations based on normalized FFT results --- usermods/audioreactive/audio_reactive.h | 34 +++++++++---------- .../usermod_v2_auto_playlist.h | 19 ++++++----- wled00/FX_2Dfcn.cpp | 2 +- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index db6f5065..0d302db4 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -498,20 +498,16 @@ void FFTcode(void * parameter) // get a fresh batch of samples from I2S if (audioSource) audioSource->getSamples(vReal, samplesFFT); - + // WLED-MM/TroyHacks: Calculate zero crossings // zeroCrossingCount = 0; - energy = 0; - for (int i=0; i < samplesFFT; i++) { if (i < (samplesFFT)-2) { if((vReal[i] >= 0 && vReal[i+1] < 0) || (vReal[i+1] < 0 && vReal[i+1] >= 0)) { zeroCrossingCount++; } } - // WLED-MM/TroyHacks: Calculate energy - energy += (vReal[i] * vReal[i])/10000000; } #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) @@ -645,17 +641,6 @@ void FFTcode(void * parameter) #endif FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects FFT_MajPeakSmth = FFT_MajPeakSmth + 0.42 * (FFT_MajorPeak - FFT_MajPeakSmth); // I like this "swooping peak" look - - // WLED-MM/TroyHacks: Calculate Low-Frequency Content - // - lowFreqencyContent = fftAddAvg(2,4)/1000; - - // USER_PRINT("ZCR = "); - // USER_PRINT(zeroCrossingCount); - // USER_PRINT(" Energy = "); - // USER_PRINT(" LFC = "); - // USER_PRINT(lowFreqencyContent); - // USER_PRINTLN(); } else { // skip second run --> clear fft results, keep peaks memset(vReal, 0, sizeof(vReal)); @@ -801,7 +786,22 @@ void FFTcode(void * parameter) // run peak detection autoResetPeak(); detectSamplePeak(); - + + // WLED-MM/TroyHacks: Calculate Energy & Low-Frequency Content + // + energy = 0; + for (int i=0; i < NUM_GEQ_CHANNELS; i++) { + energy += fftResult[i]; + } + energy *= energy; + energy /= 10000; + lowFreqencyContent = fftResult[0]; + + // WLED-MM/TroyHacks: Ideally these numbers are roughly in the same rations + // ...but more importantly hitting a zero point at a regular interval + // + // USER_PRINTF("ZCR: %3lu Energy: %5lu LFC: %4lu\n",(unsigned long)zeroCrossingCount,(unsigned long)energy,(unsigned long)lowFreqencyContent) + // we have new results - notify UDP sound send haveNewFFTResult = true; diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 39b14049..dc23135c 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -121,12 +121,15 @@ class AutoPlaylistUsermod : public Usermod { // the current music, especially after track changes or during // sparce intros and breakdowns. if (change_interval > ideal_change_min && distance_tracker < 1000) { - // change_threshold++; - change_threshold += distance_tracker/10; - USER_PRINTF("Increasing change_threshold to: %d\n",change_threshold); - USER_PRINT (" lowest recorded distance was: "); + + change_threshold += distance_tracker>10?distance_tracker/10:1; + + USER_PRINT ("The lowest recorded distance was: "); USER_PRINTLN(distance_tracker); + USER_PRINTF("Increasing change_threshold to: %d\n",change_threshold); + distance_tracker = UINT_FAST32_MAX; + } change_timer = millis(); } @@ -136,11 +139,9 @@ class AutoPlaylistUsermod : public Usermod { if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 0.1) { if (change_interval > ideal_change_max) { - // change_threshold += 1; - change_threshold += distance/10; + change_threshold += distance_tracker>10?distance_tracker/10:1; } else if (change_interval < ideal_change_min) { - // change_threshold -= 1; - change_threshold -= distance/10; + change_threshold -= distance_tracker>10?distance_tracker/10:1; } distance_tracker = UINT_FAST32_MAX; @@ -162,7 +163,7 @@ class AutoPlaylistUsermod : public Usermod { do { newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. } - while (currentPreset == newpreset); + while (currentPreset == newpreset); // make sure we get a different random preset. applyPreset(newpreset); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 999684b1..166536e1 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -64,7 +64,7 @@ void WS2812FX::setUpMatrix() { return; } - USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); + // TroyHacks temp commented out, FIXME and put back: USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); //WLEDMM recreate customMappingTable if more space needed if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { From b52773c1d31484d362cd2af99c33c909b66be5f1 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 08:27:12 -0400 Subject: [PATCH 25/83] comments and some temp removal of other debug --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 4 ++-- wled00/FX_fcn.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index dc23135c..36cece72 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -34,9 +34,9 @@ class AutoPlaylistUsermod : public Usermod { int lastchange = millis(); int last_beat_interval = millis(); - int change_threshold = 50; + int change_threshold = 50; // arbitrary starting point. - int change_lockout = 1000; // never change below this numnber of millis. I think of this more like a debounce, but opinions may vary. + int change_lockout = 1000; // never change below this number of millis. I think of this more like a debounce, but opinions may vary. int ideal_change_min = 10000; // ideally change patterns no less than this number of millis int ideal_change_max = 20000; // ideally change patterns no more than this number of millis diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index fa464320..d70096ad 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -124,7 +124,7 @@ void Segment::allocLeds() { } // softhack007 clean up buffer } if ((size > 0) && (!ledsrgb || size > ledsrgbSize)) { //softhack dont allocate zero bytes - USER_PRINTF("allocLeds (%d,%d to %d,%d), %u from %u\n", start, startY, stop, stopY, size, ledsrgb?ledsrgbSize:0); + // TroyHacks: FIXME reneable this later: USER_PRINTF("allocLeds (%d,%d to %d,%d), %u from %u\n", start, startY, stop, stopY, size, ledsrgb?ledsrgbSize:0); if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first ledsrgb = (CRGB*)calloc(size, 1); ledsrgbSize = ledsrgb?size:0; From 977002f30c0ee5218960b5dfae012e398e5bce3b Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 08:58:34 -0400 Subject: [PATCH 26/83] better debug messages --- .../usermod_v2_auto_playlist.h | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 36cece72..2a4bfe6a 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -124,9 +124,7 @@ class AutoPlaylistUsermod : public Usermod { change_threshold += distance_tracker>10?distance_tracker/10:1; - USER_PRINT ("The lowest recorded distance was: "); - USER_PRINTLN(distance_tracker); - USER_PRINTF("Increasing change_threshold to: %d\n",change_threshold); + USER_PRINTF("The lowest recorded distance was: %3lu - change_threshold increased by %2u to %3u\n", (unsigned long)distance_tracker,(distance_tracker>10?distance_tracker/10:1),change_threshold); distance_tracker = UINT_FAST32_MAX; @@ -149,12 +147,12 @@ class AutoPlaylistUsermod : public Usermod { if (change_threshold < 0) change_threshold = 0; if(autoChangeIds.size() == 0) { - USER_PRINTF("Loading presets from playlist:%u\n", currentPlaylist); + USER_PRINTF("Loading presets from playlist: %3u\n", currentPlaylist); JsonObject playtlistOjb = doc.to(); serializePlaylist(playtlistOjb); JsonArray playlistArray = playtlistOjb["playlist"]["ps"]; for(JsonVariant v : playlistArray) { - USER_PRINTF("Adding %u to autoChangeIds\n", v.as()); + USER_PRINTF("Adding %3u to autoChangeIds\n", v.as()); autoChangeIds.push_back(v.as()); } } @@ -167,12 +165,8 @@ class AutoPlaylistUsermod : public Usermod { applyPreset(newpreset); - USER_PRINT("*** CHANGE! Vector distance = "); - USER_PRINT(distance); - USER_PRINT(" - change_interval was "); - USER_PRINT(change_interval); - USER_PRINT("ms - next change_threshold is "); - USER_PRINTLN(change_threshold); + USER_PRINTF("*** CHANGE! Vector distance =%4lu - change_interval was %5dms - next change_threshold is %3d\n",(unsigned long)distance,change_interval,change_threshold); + lastchange = millis(); } From cc01e38cadd336486d4788ab510a91e7497fab02 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:15:31 -0400 Subject: [PATCH 27/83] Comments added. --- usermods/audioreactive/audio_reactive.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 0d302db4..959ab54c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -794,11 +794,11 @@ void FFTcode(void * parameter) energy += fftResult[i]; } energy *= energy; - energy /= 10000; + energy /= 10000; // scale down so we get 0 sometimes lowFreqencyContent = fftResult[0]; - // WLED-MM/TroyHacks: Ideally these numbers are roughly in the same rations - // ...but more importantly hitting a zero point at a regular interval + // WLED-MM/TroyHacks: Ideally these numbers are roughly in the same ratios + // ...but most importantly all values need to be hitting zero at a regular interval // // USER_PRINTF("ZCR: %3lu Energy: %5lu LFC: %4lu\n",(unsigned long)zeroCrossingCount,(unsigned long)energy,(unsigned long)lowFreqencyContent) From 22be977df8a2a373cec99db8c554142aab87fb24 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:19:13 -0400 Subject: [PATCH 28/83] IfDefs to quiet some other debug lines during debugging. --- wled00/FX_2Dfcn.cpp | 4 +++- wled00/FX_fcn.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 166536e1..b013457e 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -64,7 +64,9 @@ void WS2812FX::setUpMatrix() { return; } - // TroyHacks temp commented out, FIXME and put back: USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); + #ifndef USERMOD_AUTO_PLAYLIST // TroyHacks - FIXME: just tidy output temporarily while debugging + USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); + #endif //WLEDMM recreate customMappingTable if more space needed if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index d70096ad..68c0d7dc 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -124,7 +124,9 @@ void Segment::allocLeds() { } // softhack007 clean up buffer } if ((size > 0) && (!ledsrgb || size > ledsrgbSize)) { //softhack dont allocate zero bytes - // TroyHacks: FIXME reneable this later: USER_PRINTF("allocLeds (%d,%d to %d,%d), %u from %u\n", start, startY, stop, stopY, size, ledsrgb?ledsrgbSize:0); + #ifndef USERMOD_AUTO_PLAYLIST // TroyHacks - FIXME: just tidy output temporarily while debugging + USER_PRINTF("allocLeds (%d,%d to %d,%d), %u from %u\n", start, startY, stop, stopY, size, ledsrgb?ledsrgbSize:0); + #endif if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first ledsrgb = (CRGB*)calloc(size, 1); ledsrgbSize = ledsrgb?size:0; From 793b1089ac55dd92634d2564c924b15bff22658b Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:42:12 -0400 Subject: [PATCH 29/83] Better debug --- .../usermod_v2_auto_playlist.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 2a4bfe6a..c8060764 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -102,18 +102,11 @@ class AutoPlaylistUsermod : public Usermod { int change_interval = millis()-lastchange; - if (distance < distance_tracker && change_interval > change_lockout) { + if (distance < distance_tracker && change_interval > change_lockout && volumeSmth > 1) { distance_tracker = distance; } - // USER_PRINT("\tDistance: "); - // USER_PRINT(distance); - // USER_PRINT("\tv_lfc: "); - // USER_PRINT(vector_lfc); - // USER_PRINT("\tv_energy: "); - // USER_PRINT(vector_energy); - // USER_PRINT("\tv_zcr: "); - // USER_PRINTLN(vector_zcr); + // USER_PRINTF("Distance: %3lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr); if (millis() > change_timer + ideal_change_min) { // Make the analysis less sensitive if we miss the window, slowly. From 3279134a4bdb7ffda12fa64c7dfd7ae283772e54 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:52:33 -0400 Subject: [PATCH 30/83] Tidy --- .../usermod_v2_auto_playlist.h | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index c8060764..e4cc5216 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -2,10 +2,10 @@ #include "wled.h" - class AutoPlaylistUsermod : public Usermod { private: + bool silenceDetected = true; uint32_t lastSoundTime = 0; byte ambientPlaylist = 1; @@ -36,7 +36,7 @@ class AutoPlaylistUsermod : public Usermod { int last_beat_interval = millis(); int change_threshold = 50; // arbitrary starting point. - int change_lockout = 1000; // never change below this number of millis. I think of this more like a debounce, but opinions may vary. + int change_lockout = 1000; // never change below this number of millis. I think of this more like a debounce, but opinions may vary. int ideal_change_min = 10000; // ideally change patterns no less than this number of millis int ideal_change_max = 20000; // ideally change patterns no more than this number of millis @@ -109,10 +109,12 @@ class AutoPlaylistUsermod : public Usermod { // USER_PRINTF("Distance: %3lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr); if (millis() > change_timer + ideal_change_min) { + // Make the analysis less sensitive if we miss the window, slowly. // Sometimes the analysis lowers the change_threshold too much for // the current music, especially after track changes or during // sparce intros and breakdowns. + if (change_interval > ideal_change_min && distance_tracker < 1000) { change_threshold += distance_tracker>10?distance_tracker/10:1; @@ -122,11 +124,11 @@ class AutoPlaylistUsermod : public Usermod { distance_tracker = UINT_FAST32_MAX; } + change_timer = millis(); + } - // WLED-MM/TroyHacks - Change pattern testing - // if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 0.1) { if (change_interval > ideal_change_max) { @@ -139,18 +141,23 @@ class AutoPlaylistUsermod : public Usermod { if (change_threshold < 0) change_threshold = 0; - if(autoChangeIds.size() == 0) { + if (autoChangeIds.size() == 0) { + USER_PRINTF("Loading presets from playlist: %3u\n", currentPlaylist); + JsonObject playtlistOjb = doc.to(); serializePlaylist(playtlistOjb); JsonArray playlistArray = playtlistOjb["playlist"]["ps"]; + for(JsonVariant v : playlistArray) { USER_PRINTF("Adding %3u to autoChangeIds\n", v.as()); autoChangeIds.push_back(v.as()); } + } uint8_t newpreset = 0; + do { newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. } @@ -166,68 +173,60 @@ class AutoPlaylistUsermod : public Usermod { } - uint8_t getFFTFromRange(um_data_t *data, uint8_t from, uint8_t to) { - uint8_t *fftResult = (uint8_t*) data->u_data[2]; - uint16_t result = 0; - for (int i = from; i <= to; i++) { - result += fftResult[i]; // * fftResult[i]; - } - return result / float(to - from + 1); // sqrt(result / (to - from + 1)); - } - /* * Da loop. */ void loop() { - if(millis() < 10000) return; // Wait for device to settle + if (millis() < 10000) return; // Wait for device to settle - if(lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) { - if(currentPlaylist == musicPlaylist) { + if (lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) { + if (currentPlaylist == musicPlaylist) { USER_PRINTF("AutoPlaylist: enabled due to manual change of playlist back to %u\n", currentPlaylist); enabled = true; lastAutoPlaylist = currentPlaylist; - } - else if(enabled) { + } else if (enabled) { USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); enabled = false; } } - if(!enabled && currentPlaylist == musicPlaylist) { + + if (!enabled && currentPlaylist == musicPlaylist) { USER_PRINTF("AutoPlaylist: enabled due selecting musicPlaylist(%u)", musicPlaylist); enabled = true; } - if(!enabled) return; - if(bri == 0) return; + if (!enabled) return; + + if (bri == 0) return; um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // No Audio Reactive silenceDetected = true; return; } - float volumeSmth = *(float*)um_data->u_data[0]; + float volumeSmth = *(float*)um_data->u_data[0]; - if(volumeSmth > 0.5) { + if (volumeSmth > 0.5) { lastSoundTime = millis(); } - if(millis() - lastSoundTime > (timeout * 1000)) { - if(!silenceDetected) { + if (millis() - lastSoundTime > (timeout * 1000)) { + if (!silenceDetected) { silenceDetected = true; USER_PRINTF("AutoPlaylist: Silence "); changePlaylist(ambientPlaylist); } - } - else { - if(silenceDetected) { + } else { + if (silenceDetected) { silenceDetected = false; USER_PRINTF("AutoPlaylist: End of silence "); changePlaylist(musicPlaylist); } - if(autoChange) change(um_data); + if (autoChange) change(um_data); } } @@ -237,7 +236,9 @@ class AutoPlaylistUsermod : public Usermod { * Below it is shown how this could be used for e.g. a light sensor */ void addToJsonInfo(JsonObject& root) { + JsonObject user = root["u"]; + if (user.isNull()) { user = root.createNestedObject("u"); } @@ -245,12 +246,13 @@ class AutoPlaylistUsermod : public Usermod { JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name infoArr = user.createNestedArray(F("")); - if(!enabled) { + + if (!enabled) { infoArr.add("disabled"); - } - else { + } else { infoArr.add(lastSoundTime); } + } /* @@ -358,12 +360,12 @@ class AutoPlaylistUsermod : public Usermod { private: - void changePlaylist(byte id) { - String name = ""; - getPresetName(id, name); - USER_PRINTF("apply %s\n", name.c_str()); - applyPreset(id, CALL_MODE_NOTIFICATION); - lastAutoPlaylist = id; + void changePlaylist(byte id) { + String name = ""; + getPresetName(id, name); + USER_PRINTF("apply %s\n", name.c_str()); + applyPreset(id, CALL_MODE_NOTIFICATION); + lastAutoPlaylist = id; } }; From 13bfe6018dd6904be656aa628183c902eec56848 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Tue, 26 Mar 2024 08:13:56 -0400 Subject: [PATCH 31/83] Faster change_threshold catch-up --- .../usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index e4cc5216..0208ec7c 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -76,7 +76,7 @@ class AutoPlaylistUsermod : public Usermod { uint_fast32_t lfc = *(uint_fast32_t*)um_data->u_data[13]; // WLED-MM/TroyHacks: Calculate the long- and short-running averages - // and the squared_distance for the vector. + // and the individual vector distances. if (volumeSmth > 1) { @@ -117,9 +117,11 @@ class AutoPlaylistUsermod : public Usermod { if (change_interval > ideal_change_min && distance_tracker < 1000) { - change_threshold += distance_tracker>10?distance_tracker/10:1; + // change_threshold += distance_tracker>10?distance_tracker/10:1; + change_threshold += ((distance_tracker-change_threshold)/2)>1?(distance_tracker-change_threshold)/2:1; - USER_PRINTF("The lowest recorded distance was: %3lu - change_threshold increased by %2u to %3u\n", (unsigned long)distance_tracker,(distance_tracker>10?distance_tracker/10:1),change_threshold); + // USER_PRINTF("The lowest recorded distance was: %3lu - change_threshold increased by %2u to %3u\n", (unsigned long)distance_tracker,(distance_tracker>10?distance_tracker/10:1),change_threshold); + USER_PRINTF("The lowest recorded distance was: %3lu - change_threshold increased by %2u to %3u\n", (unsigned long)distance_tracker,((distance_tracker-change_threshold)/2)>1?(distance_tracker-change_threshold)/2:1,change_threshold); distance_tracker = UINT_FAST32_MAX; From d922374e92794a69a0e7e4443dbcbdf727ae2fc4 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:48:55 -0400 Subject: [PATCH 32/83] Better change window tracking. --- .../usermod_v2_auto_playlist.h | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 0208ec7c..eddddb3c 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -33,10 +33,10 @@ class AutoPlaylistUsermod : public Usermod { int lastchange = millis(); - int last_beat_interval = millis(); - int change_threshold = 50; // arbitrary starting point. + int_fast16_t change_threshold = 50; // arbitrary starting point. + uint_fast16_t change_threshold_change = 0; - int change_lockout = 1000; // never change below this number of millis. I think of this more like a debounce, but opinions may vary. + int change_lockout = 1000; // never change below this number of millis. Ideally 60000/your_average_bpm*beats_to_skip = change_lockout (1000 = skip 2 beats at 120bpm) int ideal_change_min = 10000; // ideally change patterns no less than this number of millis int ideal_change_max = 20000; // ideally change patterns no more than this number of millis @@ -117,11 +117,10 @@ class AutoPlaylistUsermod : public Usermod { if (change_interval > ideal_change_min && distance_tracker < 1000) { - // change_threshold += distance_tracker>10?distance_tracker/10:1; - change_threshold += ((distance_tracker-change_threshold)/2)>1?(distance_tracker-change_threshold)/2:1; + change_threshold_change = (distance_tracker)-change_threshold; + change_threshold = distance_tracker; - // USER_PRINTF("The lowest recorded distance was: %3lu - change_threshold increased by %2u to %3u\n", (unsigned long)distance_tracker,(distance_tracker>10?distance_tracker/10:1),change_threshold); - USER_PRINTF("The lowest recorded distance was: %3lu - change_threshold increased by %2u to %3u\n", (unsigned long)distance_tracker,((distance_tracker-change_threshold)/2)>1?(distance_tracker-change_threshold)/2:1,change_threshold); + USER_PRINTF("--- lowest distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); distance_tracker = UINT_FAST32_MAX; @@ -133,15 +132,21 @@ class AutoPlaylistUsermod : public Usermod { if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 0.1) { + change_threshold_change = change_threshold-(distance*0.9); + + if (change_threshold_change < 1) change_threshold_change = 1; + if (change_interval > ideal_change_max) { - change_threshold += distance_tracker>10?distance_tracker/10:1; + change_threshold += change_threshold_change; } else if (change_interval < ideal_change_min) { - change_threshold -= distance_tracker>10?distance_tracker/10:1; + change_threshold -= change_threshold_change; + } else { + change_threshold_change = 0; } - distance_tracker = UINT_FAST32_MAX; + if (change_threshold < 1) change_threshold = 0; // we need change_threshold to be signed becasue otherwise this wraps to UINT_FAST16_MAX - if (change_threshold < 0) change_threshold = 0; + distance_tracker = UINT_FAST32_MAX; if (autoChangeIds.size() == 0) { @@ -167,7 +172,7 @@ class AutoPlaylistUsermod : public Usermod { applyPreset(newpreset); - USER_PRINTF("*** CHANGE! Vector distance =%4lu - change_interval was %5dms - next change_threshold is %3d\n",(unsigned long)distance,change_interval,change_threshold); + USER_PRINTF("*** CHANGE distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); lastchange = millis(); From 022b3100f119c61473c01aa9c093a680ef466805 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:15:03 -0400 Subject: [PATCH 33/83] Comments and debug faff. --- .../usermod_v2_auto_playlist.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index eddddb3c..5a1ec6d9 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -110,7 +110,7 @@ class AutoPlaylistUsermod : public Usermod { if (millis() > change_timer + ideal_change_min) { - // Make the analysis less sensitive if we miss the window, slowly. + // Make the analysis less sensitive if we miss the window. // Sometimes the analysis lowers the change_threshold too much for // the current music, especially after track changes or during // sparce intros and breakdowns. @@ -120,7 +120,7 @@ class AutoPlaylistUsermod : public Usermod { change_threshold_change = (distance_tracker)-change_threshold; change_threshold = distance_tracker; - USER_PRINTF("--- lowest distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("--- lowest distance =%4lu - no changes done in %6ums - next change_threshold is %3u (%3u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); distance_tracker = UINT_FAST32_MAX; @@ -129,22 +129,22 @@ class AutoPlaylistUsermod : public Usermod { change_timer = millis(); } - - if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 0.1) { + + if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1) { change_threshold_change = change_threshold-(distance*0.9); if (change_threshold_change < 1) change_threshold_change = 1; if (change_interval > ideal_change_max) { - change_threshold += change_threshold_change; + change_threshold += change_threshold_change; // make changes more sensitive } else if (change_interval < ideal_change_min) { - change_threshold -= change_threshold_change; + change_threshold -= change_threshold_change; // make changes less sensitive } else { - change_threshold_change = 0; + change_threshold_change = 0; // change was within our window, no sensitivity change } - if (change_threshold < 1) change_threshold = 0; // we need change_threshold to be signed becasue otherwise this wraps to UINT_FAST16_MAX + if (change_threshold < 1) change_threshold = 0; // we need change_threshold to be signed becasue otherwise this wraps to UINT_FAST16_MAX distance_tracker = UINT_FAST32_MAX; From 58184a5a74ab3ec3e5ff1d25a9f28e4c0743d39e Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:45:32 -0400 Subject: [PATCH 34/83] Moved calcs into usermod except zero crossings --- usermods/audioreactive/audio_reactive.h | 26 ++----------------- .../usermod_v2_auto_playlist.h | 16 +++++++++--- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 959ab54c..8a5610be 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,9 +141,7 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -uint_fast32_t zeroCrossingCount = 0; -uint_fast32_t energy = 0; -uint_fast32_t lowFreqencyContent = 0; +uint_fast16_t zeroCrossingCount = 0; // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -787,22 +785,6 @@ void FFTcode(void * parameter) autoResetPeak(); detectSamplePeak(); - // WLED-MM/TroyHacks: Calculate Energy & Low-Frequency Content - // - energy = 0; - for (int i=0; i < NUM_GEQ_CHANNELS; i++) { - energy += fftResult[i]; - } - energy *= energy; - energy /= 10000; // scale down so we get 0 sometimes - lowFreqencyContent = fftResult[0]; - - // WLED-MM/TroyHacks: Ideally these numbers are roughly in the same ratios - // ...but most importantly all values need to be hitting zero at a regular interval - // - // USER_PRINTF("ZCR: %3lu Energy: %5lu LFC: %4lu\n",(unsigned long)zeroCrossingCount,(unsigned long)energy,(unsigned long)lowFreqencyContent) - - // we have new results - notify UDP sound send haveNewFFTResult = true; #if !defined(I2S_GRAB_ADC1_COMPLETELY) @@ -1778,11 +1760,7 @@ class AudioReactive : public Usermod { um_data->u_data[10] = &agcSensitivity; // used (New) um_data->u_type[10] = UMT_FLOAT; um_data->u_data[11] = &zeroCrossingCount; - um_data->u_type[11] = UMT_UINT32; - um_data->u_data[12] = &energy; - um_data->u_type[12] = UMT_UINT32; - um_data->u_data[13] = &lowFreqencyContent; - um_data->u_type[13] = UMT_UINT32; + um_data->u_type[11] = UMT_UINT16; #else // ESP8266 // See https://github.com/MoonModules/WLED/pull/60#issuecomment-1666972133 for explanation of these alternative sources of data diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 5a1ec6d9..b07d1178 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -15,6 +15,8 @@ class AutoPlaylistUsermod : public Usermod { byte lastAutoPlaylist = 0; int change_timer = millis(); + uint_fast32_t energy = 0; + uint_fast32_t avg_long_energy = 250; uint_fast32_t avg_long_lfc = 1000; uint_fast32_t avg_long_zcr = 500; @@ -71,9 +73,17 @@ class AutoPlaylistUsermod : public Usermod { void change(um_data_t *um_data) { - uint_fast32_t zcr = *(uint_fast32_t*)um_data->u_data[11]; - uint_fast32_t energy = *(uint_fast32_t*)um_data->u_data[12]; - uint_fast32_t lfc = *(uint_fast32_t*)um_data->u_data[13]; + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + for (int i=0; i < NUM_GEQ_CHANNELS; i++) { + energy += fftResult[i]; + } + + energy *= energy; + energy /= 10000; // scale down so we get 0 sometimes + + uint8_t lfc = fftResult[0]; + uint_fast16_t zcr = *(uint_fast16_t*)um_data->u_data[11]; // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the individual vector distances. From 9b8f0ba873c3019a133a2a9c46390c079a2cdc44 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:12:38 -0400 Subject: [PATCH 35/83] Bug fix with um_data length --- usermods/audioreactive/audio_reactive.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 8a5610be..c938c465 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1733,7 +1733,7 @@ class AudioReactive : public Usermod { // usermod exchangeable data // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers um_data = new um_data_t; - um_data->u_size = 13; + um_data->u_size = 11; um_data->u_type = new um_types_t[um_data->u_size]; um_data->u_data = new void*[um_data->u_size]; um_data->u_data[0] = &volumeSmth; //*used (New) From cd8884dbf7dc11cff9dc61628c9668c8c8f2a7a2 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Thu, 28 Mar 2024 08:55:18 -0400 Subject: [PATCH 36/83] Skip change logic for distance freefall --- .../usermod_v2_auto_playlist.h | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index b07d1178..9eb438d8 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -180,11 +180,26 @@ class AutoPlaylistUsermod : public Usermod { } while (currentPreset == newpreset); // make sure we get a different random preset. - applyPreset(newpreset); + if (change_interval > change_lockout+3) { - USER_PRINTF("*** CHANGE distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + // Make sure we have a statistically significant change and we aren't + // just bouncing off change_lockout. That's valid for changing the + // thresholds, but might be a bit crazy for lighting changes. + // When the music changes quite a bit, the distance calculation can + // go into freefall - this logic stops that from triggering right + // after change_lockout. Better for smaller change_lockout values. - lastchange = millis(); + applyPreset(newpreset); + + USER_PRINTF("*** CHANGE distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + + } else { + + USER_PRINTF("*** SKIP!! distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + + } + + lastchange = millis(); } From 494646a21e3b4e85be6d9a4b0f254fb60bd7fa0a Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Tue, 26 Mar 2024 20:08:22 +0000 Subject: [PATCH 37/83] Tweak status --- .../usermod_v2_auto_playlist.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 9eb438d8..debb7e57 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -278,11 +278,11 @@ class AutoPlaylistUsermod : public Usermod { JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name infoArr = user.createNestedArray(F("")); - - if (!enabled) { - infoArr.add("disabled"); - } else { - infoArr.add(lastSoundTime); + if(!enabled) { + infoArr.add(""); + } + else { + infoArr.add("Active"); } } From 9eabbf3789356f11ac97601c791a71f6e3dbc094 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 28 Mar 2024 19:40:59 +0000 Subject: [PATCH 38/83] Sync audio_rective changes from auto-playlist branch --- usermods/audioreactive/audio_reactive.h | 33 +++++++++++-------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index da6005db..19e59519 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,9 +141,7 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -unsigned int zeroCrossingCount = 0; -unsigned int energy = 0; -unsigned int lowFreqencyContent = 0; +uint_fast16_t zeroCrossingCount = 0; // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -499,6 +497,17 @@ void FFTcode(void * parameter) // get a fresh batch of samples from I2S if (audioSource) audioSource->getSamples(vReal, samplesFFT); + // WLED-MM/TroyHacks: Calculate zero crossings + // + zeroCrossingCount = 0; + for (int i=0; i < samplesFFT; i++) { + if (i < (samplesFFT)-2) { + if((vReal[i] >= 0 && vReal[i+1] < 0) || (vReal[i+1] < 0 && vReal[i+1] >= 0)) { + zeroCrossingCount++; + } + } + } + #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) // debug info in case that stack usage changes static unsigned int minStackFree = UINT32_MAX; @@ -555,17 +564,7 @@ void FFTcode(void * parameter) // pick our our current mic sample - we take the max value from all samples that go into FFT if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]); - - // WLED-MM/TroyHacks: Calculate zero crossings - if (i < samplesFFT-2) { - if (vReal[i] * vReal[i+1] < 0) { - zeroCrossingCount++; - } - } - // WLED-MM/TroyHacks: Calculate energy - energy += vReal[i] * vReal[i]; } - energy /= 100000; // WLED-MM/TroyHacks: scale this down becasue we're gonna make it bigger later // release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function // early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results. @@ -661,8 +660,6 @@ void FFTcode(void * parameter) vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. } // for() - lowFreqencyContent = fftAddAvg(1,9); // WLED-MM/TroyHacks: Calculate Low-Frequency Content - // mapping of FFT result bins to frequency channels //if (fabsf(sampleAvg) > 0.25f) { // noise gate open if (fabsf(volumeSmth) > 0.25f) { // noise gate open @@ -1763,10 +1760,8 @@ class AudioReactive : public Usermod { um_data->u_type[9] = UMT_FLOAT; um_data->u_data[10] = &agcSensitivity; // used (New) um_data->u_type[10] = UMT_FLOAT; - unsigned int* extra[3] = {&zeroCrossingCount, &energy, &lowFreqencyContent}; - um_data->u_data[11] = extra; // - um_data->u_type[11] = UMT_INT16_ARR; - + um_data->u_data[11] = &zeroCrossingCount; + um_data->u_type[11] = UMT_UINT16; #else // ESP8266 // See https://github.com/MoonModules/WLED/pull/60#issuecomment-1666972133 for explanation of these alternative sources of data From cce7d4c79e964497ff5410acf7201e643a771f93 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 28 Mar 2024 19:57:42 +0000 Subject: [PATCH 39/83] Cleanup --- usermods/audioreactive/audio_reactive.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index c938c465..191e60d0 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -36,7 +36,7 @@ #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) -// this applies "pink noise scaling" to FFT results before computing the major peak for effects.0 +// this applies "pink noise scaling" to FFT results before computing the major peak for effects. // currently only for ESP32-S3 and classic ESP32, due to increased runtime #define FFT_MAJORPEAK_HUMAN_EAR #endif From d9dceb64ede9ae4da29e47f6defb40e666cc9079 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Thu, 28 Mar 2024 20:06:47 +0000 Subject: [PATCH 40/83] Attempt at adding zeroCrossingCount to audio-sync --- usermods/audioreactive/audio_reactive.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 19e59519..d068d7cc 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1021,7 +1021,7 @@ class AudioReactive : public Usermod { uint8_t samplePeak; // 01 Bytes offset 16 - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude uint8_t frameCounter; // 01 Bytes offset 17 - track duplicate/out of order packets uint8_t fftResult[16]; // 16 Bytes offset 18 - uint8_t gap2[2]; // gap added by compiler: 02 Bytes, offset 34 + uint16_t zeroCrossingCount; // 02 Bytes, offset 34 float FFT_Magnitude; // 04 Bytes offset 36 float FFT_MajorPeak; // 04 Bytes offset 40 }; @@ -1561,6 +1561,7 @@ class AudioReactive : public Usermod { transmitData.samplePeak = udpSamplePeak ? 1:0; udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it transmitData.frameCounter = frameCounter; + transmitData.zeroCrossingCount = zeroCrossingCount; for (int i = 0; i < NUM_GEQ_CHANNELS; i++) { transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254); @@ -1633,6 +1634,7 @@ class AudioReactive : public Usermod { FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects soundPressure = volumeSmth; // substitute - V2 format does not (yet) include this value agcSensitivity = 128.0f; // substitute - V2 format does not (yet) include this value + zeroCrossingCount = receivedPacket->zeroCrossingCount; return true; } From 1e934d342604417039dee50c88491bafa906b9a9 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 29 Mar 2024 12:47:51 +0000 Subject: [PATCH 41/83] Prevent crash trying to load playlist -1 --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index debb7e57..deead6d6 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -159,8 +159,9 @@ class AutoPlaylistUsermod : public Usermod { distance_tracker = UINT_FAST32_MAX; if (autoChangeIds.size() == 0) { + if(currentPlaylist < 1) return; - USER_PRINTF("Loading presets from playlist: %3u\n", currentPlaylist); + USER_PRINTF("Loading presets from playlist: %3d\n", currentPlaylist); JsonObject playtlistOjb = doc.to(); serializePlaylist(playtlistOjb); From ebab56ada5fd1cd3adc921ff312d35d8af8728cc Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 29 Mar 2024 13:39:09 +0000 Subject: [PATCH 42/83] Update logger --- usermods/audioreactive/audio_reactive.h | 2 +- wled00/data/update.htm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index d068d7cc..430b85c6 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1939,7 +1939,7 @@ class AudioReactive : public Usermod { USER_PRINTF("| uint8_t samplePeak offset = %2d size = %2d\n", offsetof(audioSyncPacket, samplePeak), sizeof(data.samplePeak)); // offset 16 size 1 USER_PRINTF("| uint8_t frameCounter offset = %2d size = %2d\n", offsetof(audioSyncPacket, frameCounter), sizeof(data.frameCounter)); // offset 17 size 1 USER_PRINTF("| uint8_t fftResult[16] offset = %2d size = %2d\n", offsetof(audioSyncPacket, fftResult[0]), sizeof(data.fftResult)); // offset 18 size 16 - USER_PRINTF("| uint8_t gap2[2] offset = %2d size = %2d\n", offsetof(audioSyncPacket, gap2[0]), sizeof(data.gap2)); // offset 34 size 2 + USER_PRINTF("| uint16_t zeroCrossingCount offset = %2d size = %2d\n", offsetof(audioSyncPacket, zeroCrossingCount), sizeof(data.zeroCrossingCount)); // offset 34 size 2 USER_PRINTF("| float FFT_Magnitude offset = %2d size = %2d\n", offsetof(audioSyncPacket, FFT_Magnitude), sizeof(data.FFT_Magnitude));// offset 36 size 4 USER_PRINTF("| float FFT_MajorPeak offset = %2d size = %2d\n", offsetof(audioSyncPacket, FFT_MajorPeak), sizeof(data.FFT_MajorPeak));// offset 40 size 4 USER_PRINTLN(); USER_FLUSH(); diff --git a/wled00/data/update.htm b/wled00/data/update.htm index a4cee007..1bab1d9b 100644 --- a/wled00/data/update.htm +++ b/wled00/data/update.htm @@ -17,8 +17,8 @@

MoonMod WLED Software Update

##VERSION##
- Download the latest release: -
+ Download the latest release: +


From 04a8681f717c802b909309ea9f28c8f044b80fd2 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 30 Mar 2024 11:09:14 -0400 Subject: [PATCH 43/83] WM8978 fix for IDF below 4.2.0 --- usermods/audioreactive/audio_source.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index bd026968..0e0a2153 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -649,7 +649,13 @@ class WM8978Source : public I2SSource { _wm8978I2cWrite( 1,0b000111110); // Power Management 1 - power off most things, but enable mic bias and I/O tie-off to help mitigate mic leakage. _wm8978I2cWrite( 2,0b110111111); // Power Management 2 - enable output and amp stages (amps may lift signal but it works better on the ADCs) _wm8978I2cWrite( 3,0b000001100); // Power Management 3 - enable L&R output mixers + + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) _wm8978I2cWrite( 4,0b001010000); // Audio Interface - standard I2S, 24-bit + #else + _wm8978I2cWrite( 4,0b001001000); // Audio Interface - left-justified I2S, 24-bit + #endif + _wm8978I2cWrite( 6,0b000000000); // Clock generation control - use external mclk _wm8978I2cWrite( 7,0b000000100); // Sets sample rate to ~24kHz (only used for internal calculations, not I2S) _wm8978I2cWrite(14,0b010001000); // 128x ADC oversampling - high pass filter disabled as it kills the bass response From c36adbdad07ec4e365e0414f484a1292da70a4d9 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 30 Mar 2024 12:30:32 -0400 Subject: [PATCH 44/83] Button added to Info panel. Logic needs work. --- .../usermod_v2_auto_playlist.h | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index deead6d6..c9339809 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -5,7 +5,8 @@ class AutoPlaylistUsermod : public Usermod { private: - + + bool initDone = false; bool silenceDetected = true; uint32_t lastSoundTime = 0; byte ambientPlaylist = 1; @@ -44,7 +45,8 @@ class AutoPlaylistUsermod : public Usermod { std::vector autoChangeIds; - static const char _enabled[]; + static const char _name[]; + static const char _autoPlaylistEnabled[]; static const char _ambientPlaylist[]; static const char _musicPlaylist[]; static const char _timeout[]; @@ -63,6 +65,7 @@ class AutoPlaylistUsermod : public Usermod { // network here void setup() { USER_PRINTLN("AutoPlaylistUsermod"); + initDone = true; } // gets called every time WiFi is (re-)connected. Initialize own network @@ -264,48 +267,46 @@ class AutoPlaylistUsermod : public Usermod { } /* - * 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. - * Below it is shown how this could be used for e.g. a light sensor + * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients */ void addToJsonInfo(JsonObject& root) { - JsonObject user = root["u"]; - if (user.isNull()) { user = root.createNestedObject("u"); } JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name - infoArr = user.createNestedArray(F("")); - if(!enabled) { - infoArr.add(""); - } - else { - infoArr.add("Active"); - } - + String uiDomString = F(""); + infoArr.add(uiDomString); } - /* - * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). - * Values in the state object may be modified by connected clients - */ - //void addToJsonState(JsonObject& root) { - //} - /* * 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) { if (!initDone) return; // prevent crash on boot applyPreset() + bool en = enabled; JsonObject um = root[FPSTR(_name)]; if (!um.isNull()) { - if (um[FPSTR(_enabled)].is()) { - enabled = um[FPSTR(_enabled)].as(); + if (um[FPSTR(_autoPlaylistEnabled)].is()) { + en = um[FPSTR(_autoPlaylistEnabled)].as(); + } else { + String str = um[FPSTR(_autoPlaylistEnabled)]; // checkbox -> off or on + en = (bool)(str!="off"); // off is guaranteed to be present } + if (en != enabled) enabled = en; + USER_PRINT("AutoPlaylist enabled = "); + USER_PRINTLN(en); } } @@ -331,18 +332,18 @@ class AutoPlaylistUsermod : public Usermod { JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - top[FPSTR(_enabled)] = enabled; - top[FPSTR(_timeout)] = timeout; - top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam - top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam - top[FPSTR(_autoChange)] = autoChange; - top[FPSTR(_change_lockout)] = change_lockout; - top[FPSTR(_ideal_change_min)] = ideal_change_min; - top[FPSTR(_ideal_change_max)] = ideal_change_max; + top[FPSTR(_autoPlaylistEnabled)] = enabled; + top[FPSTR(_timeout)] = timeout; + top[FPSTR(_ambientPlaylist)] = ambientPlaylist; // usermodparam + top[FPSTR(_musicPlaylist)] = musicPlaylist; // usermodparam + top[FPSTR(_autoChange)] = autoChange; + top[FPSTR(_change_lockout)] = change_lockout; + top[FPSTR(_ideal_change_min)] = ideal_change_min; + top[FPSTR(_ideal_change_max)] = ideal_change_max; lastAutoPlaylist = 0; - DEBUG_PRINTLN(F("AutoPlaylist config saved.")); + USER_PRINTLN(F("AutoPlaylist config saved.")); } @@ -361,22 +362,22 @@ class AutoPlaylistUsermod : public Usermod { JsonObject top = root[FPSTR(_name)]; if (top.isNull()) { - DEBUG_PRINT(FPSTR(_name)); - DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + USER_PRINT(FPSTR(_name)); + USER_PRINTLN(F(": No config found. (Using defaults.)")); return false; } - DEBUG_PRINT(FPSTR(_name)); - getJsonValue(top[_enabled], enabled); - getJsonValue(top[_timeout], timeout); - getJsonValue(top[_ambientPlaylist], ambientPlaylist); - getJsonValue(top[_musicPlaylist], musicPlaylist); - getJsonValue(top[_autoChange], autoChange); - getJsonValue(top[_change_lockout], change_lockout); - getJsonValue(top[_ideal_change_min], ideal_change_min); - getJsonValue(top[_ideal_change_max], ideal_change_max); + enabled = top[FPSTR(_autoPlaylistEnabled)] | enabled; + timeout = top[FPSTR(_timeout)] | timeout; + ambientPlaylist = top[FPSTR(_ambientPlaylist)] | ambientPlaylist; + musicPlaylist = top[FPSTR(_musicPlaylist)] | musicPlaylist; + autoChange = top[FPSTR(_autoChange)] | autoChange; + change_lockout = top[FPSTR(_change_lockout)] | change_lockout; + ideal_change_min = top[FPSTR(_ideal_change_min)] | ideal_change_min; + ideal_change_max = top[FPSTR(_ideal_change_max)] | ideal_change_max; - DEBUG_PRINTLN(F(" config (re)loaded.")); + USER_PRINT(FPSTR(_name)); + USER_PRINTLN(F(" config (re)loaded.")); // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; @@ -403,11 +404,12 @@ class AutoPlaylistUsermod : public Usermod { }; -const char AutoPlaylistUsermod::_enabled[] PROGMEM = "enabled"; -const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; -const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; -const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout"; -const char AutoPlaylistUsermod::_autoChange[] PROGMEM = "autoChange"; -const char AutoPlaylistUsermod::_change_lockout[] PROGMEM = "change_lockout"; -const char AutoPlaylistUsermod::_ideal_change_min[] PROGMEM = "ideal_change_min"; -const char AutoPlaylistUsermod::_ideal_change_max[] PROGMEM = "ideal_change_max"; +const char AutoPlaylistUsermod::_name[] PROGMEM = "AutoPlaylist"; +const char AutoPlaylistUsermod::_autoPlaylistEnabled[] PROGMEM = "enabled"; +const char AutoPlaylistUsermod::_ambientPlaylist[] PROGMEM = "ambientPlaylist"; +const char AutoPlaylistUsermod::_musicPlaylist[] PROGMEM = "musicPlaylist"; +const char AutoPlaylistUsermod::_timeout[] PROGMEM = "timeout"; +const char AutoPlaylistUsermod::_autoChange[] PROGMEM = "autoChange"; +const char AutoPlaylistUsermod::_change_lockout[] PROGMEM = "change_lockout"; +const char AutoPlaylistUsermod::_ideal_change_min[] PROGMEM = "ideal_change_min"; +const char AutoPlaylistUsermod::_ideal_change_max[] PROGMEM = "ideal_change_max"; From 1f659aa1bfc87c09ec395f75e7999b3b9f81db75 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 30 Mar 2024 12:54:28 -0400 Subject: [PATCH 45/83] "Enabled" mostly working --- .../usermod_v2_auto_playlist.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index c9339809..4237aabc 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -214,6 +214,8 @@ class AutoPlaylistUsermod : public Usermod { */ void loop() { + if (!enabled) return; + if (millis() < 10000) return; // Wait for device to settle if (lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) { @@ -232,7 +234,7 @@ class AutoPlaylistUsermod : public Usermod { enabled = true; } - if (!enabled) return; + if (bri == 0) return; @@ -305,8 +307,8 @@ class AutoPlaylistUsermod : public Usermod { en = (bool)(str!="off"); // off is guaranteed to be present } if (en != enabled) enabled = en; - USER_PRINT("AutoPlaylist enabled = "); - USER_PRINTLN(en); + DEBUG_PRINT("AutoPlaylist enabled = "); + DEBUG_PRINTLN(en); } } @@ -343,7 +345,7 @@ class AutoPlaylistUsermod : public Usermod { lastAutoPlaylist = 0; - USER_PRINTLN(F("AutoPlaylist config saved.")); + DEBUG_PRINTLN(F("AutoPlaylist config saved.")); } @@ -376,8 +378,8 @@ class AutoPlaylistUsermod : public Usermod { ideal_change_min = top[FPSTR(_ideal_change_min)] | ideal_change_min; ideal_change_max = top[FPSTR(_ideal_change_max)] | ideal_change_max; - USER_PRINT(FPSTR(_name)); - USER_PRINTLN(F(" config (re)loaded.")); + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(" config (re)loaded.")); // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; From c3bf851613d1b6406f163638f8fc11cb78090be0 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:09:59 -0400 Subject: [PATCH 46/83] Higher possible change threshold --- .../usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 4237aabc..d9211c43 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -128,12 +128,12 @@ class AutoPlaylistUsermod : public Usermod { // the current music, especially after track changes or during // sparce intros and breakdowns. - if (change_interval > ideal_change_min && distance_tracker < 1000) { + if (change_interval > ideal_change_min && distance_tracker <= 9999) { change_threshold_change = (distance_tracker)-change_threshold; change_threshold = distance_tracker; - USER_PRINTF("--- lowest distance =%4lu - no changes done in %6ums - next change_threshold is %3u (%3u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ums - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); distance_tracker = UINT_FAST32_MAX; @@ -195,15 +195,16 @@ class AutoPlaylistUsermod : public Usermod { applyPreset(newpreset); - USER_PRINTF("*** CHANGE distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ums - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); } else { - USER_PRINTF("*** SKIP!! distance =%4lu - change_interval was %5ums - next change_threshold is %3u (%3u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ums - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); } lastchange = millis(); + change_timer = millis(); } From 42df965088b5bc0a711d3b55fbf8de78b179adb7 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 3 Apr 2024 19:02:22 +0100 Subject: [PATCH 47/83] Revert change to update --- wled00/data/update.htm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/data/update.htm b/wled00/data/update.htm index 1bab1d9b..a4cee007 100644 --- a/wled00/data/update.htm +++ b/wled00/data/update.htm @@ -17,8 +17,8 @@

MoonMod WLED Software Update

##VERSION##
- Download the latest release: -
+ Download the latest release: +


From 33b46b820fbdb1a4b0052fc4a7142bad867013be Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 4 Apr 2024 23:52:31 +0200 Subject: [PATCH 48/83] optimizations and bugfixing * calculate zero crossings after filtering out DC offsets * update effect variable only once * fix a logic error when detecting zero crossings * fix array bounds violation when creating umdata entries --- usermods/audioreactive/audio_reactive.h | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 430b85c6..070fba00 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -141,7 +141,7 @@ static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. chann static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter) static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -uint_fast16_t zeroCrossingCount = 0; +static uint16_t zeroCrossingCount = 0; // number of zero crossings in the current batch of 512 samples // TODO: probably best not used by receive nodes static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255 @@ -497,17 +497,6 @@ void FFTcode(void * parameter) // get a fresh batch of samples from I2S if (audioSource) audioSource->getSamples(vReal, samplesFFT); - // WLED-MM/TroyHacks: Calculate zero crossings - // - zeroCrossingCount = 0; - for (int i=0; i < samplesFFT; i++) { - if (i < (samplesFFT)-2) { - if((vReal[i] >= 0 && vReal[i+1] < 0) || (vReal[i+1] < 0 && vReal[i+1] >= 0)) { - zeroCrossingCount++; - } - } - } - #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) // debug info in case that stack usage changes static unsigned int minStackFree = UINT32_MAX; @@ -556,15 +545,25 @@ void FFTcode(void * parameter) } } - // find highest sample in the batch + // find highest sample in the batch, and count zero crossings float maxSample = 0.0f; // max sample from FFT batch + uint_fast16_t newZeroCrossingCount = 0; for (int i=0; i < samplesFFT; i++) { // set imaginary parts to 0 vImag[i] = 0; // pick our our current mic sample - we take the max value from all samples that go into FFT if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]); + + // WLED-MM/TroyHacks: Calculate zero crossings + // + if (i < (samplesFFT-1)) { + if((vReal[i] >= 0 && vReal[i+1] < 0) || (vReal[i] < 0 && vReal[i+1] >= 0)) { + newZeroCrossingCount++; + } + } } + zeroCrossingCount = newZeroCrossingCount; // update only once, to avoid that effects pick up an intermediate value // release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function // early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results. @@ -1736,7 +1735,7 @@ class AudioReactive : public Usermod { // usermod exchangeable data // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers um_data = new um_data_t; - um_data->u_size = 11; + um_data->u_size = 12; um_data->u_type = new um_types_t[um_data->u_size]; um_data->u_data = new void*[um_data->u_size]; um_data->u_data[0] = &volumeSmth; //*used (New) @@ -1778,6 +1777,8 @@ class AudioReactive : public Usermod { um_data->u_type[9] = UMT_FLOAT; um_data->u_data[10] = &agcSensitivity; // used (New) - dummy value (128 => 50%) um_data->u_type[10] = UMT_FLOAT; + um_data->u_data[11] = &zeroCrossingCount; + um_data->u_type[11] = UMT_UINT16; #endif } From b72e402a2dea3feac69c05a558654d193a6d0f2c Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 5 Apr 2024 00:31:14 +0200 Subject: [PATCH 49/83] bugfixing * make filters work (requires float instead of integer) * avoid "expression propagation to double" by using float constants * use unsigned long for all variables that depend on millis() --- .../usermod_v2_auto_playlist.h | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index d9211c43..94885e47 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -8,23 +8,23 @@ class AutoPlaylistUsermod : public Usermod { bool initDone = false; bool silenceDetected = true; - uint32_t lastSoundTime = 0; + unsigned long lastSoundTime = 0; byte ambientPlaylist = 1; byte musicPlaylist = 2; int timeout = 60; bool autoChange = false; byte lastAutoPlaylist = 0; - int change_timer = millis(); + unsigned long change_timer = millis(); uint_fast32_t energy = 0; - uint_fast32_t avg_long_energy = 250; - uint_fast32_t avg_long_lfc = 1000; - uint_fast32_t avg_long_zcr = 500; + float avg_long_energy = 250; + float avg_long_lfc = 1000; + float avg_long_zcr = 500; - uint_fast32_t avg_short_energy = 250; - uint_fast32_t avg_short_lfc = 1000; - uint_fast32_t avg_short_zcr = 500; + float avg_short_energy = 250; + float avg_short_lfc = 1000; + float avg_short_zcr = 500; uint_fast32_t vector_energy = 0; uint_fast32_t vector_lfc = 0; @@ -34,7 +34,7 @@ class AutoPlaylistUsermod : public Usermod { uint_fast32_t distance_tracker = UINT_FAST32_MAX; // uint_fast64_t squared_distance = 0; - int lastchange = millis(); + unsigned long lastchange = millis(); int_fast16_t change_threshold = 50; // arbitrary starting point. uint_fast16_t change_threshold_change = 0; @@ -86,20 +86,21 @@ class AutoPlaylistUsermod : public Usermod { energy /= 10000; // scale down so we get 0 sometimes uint8_t lfc = fftResult[0]; - uint_fast16_t zcr = *(uint_fast16_t*)um_data->u_data[11]; + uint16_t zcr = *(uint16_t*)um_data->u_data[11]; // WLED-MM/TroyHacks: Calculate the long- and short-running averages // and the individual vector distances. - if (volumeSmth > 1) { + if (volumeSmth > 1.0f) { - avg_long_energy = avg_long_energy * 0.99 + energy * 0.01; - avg_long_lfc = avg_long_lfc * 0.99 + lfc * 0.01; - avg_long_zcr = avg_long_zcr * 0.99 + zcr * 0.01; + // softhack007: original code used 0.998f as decay factor + avg_long_energy = avg_long_energy * 0.99f + energy * 0.01f; + avg_long_lfc = avg_long_lfc * 0.99f + lfc * 0.01f; + avg_long_zcr = avg_long_zcr * 0.99f + zcr * 0.01f; - avg_short_energy = avg_short_energy * 0.9 + energy * 0.1; - avg_short_lfc = avg_short_lfc * 0.9 + lfc * 0.1; - avg_short_zcr = avg_short_zcr * 0.9 + zcr * 0.1; + avg_short_energy = avg_short_energy * 0.9f + energy * 0.1f; + avg_short_lfc = avg_short_lfc * 0.9f + lfc * 0.1f; + avg_short_zcr = avg_short_zcr * 0.9f + zcr * 0.1f; // allegedly this is faster than pow(whatever,2) vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); @@ -113,15 +114,15 @@ class AutoPlaylistUsermod : public Usermod { distance = vector_lfc + vector_energy + vector_zcr; // squared_distance = distance * distance; - int change_interval = millis()-lastchange; + long change_interval = millis()-lastchange; - if (distance < distance_tracker && change_interval > change_lockout && volumeSmth > 1) { + if (distance < distance_tracker && change_interval > change_lockout && volumeSmth > 1.0f) { distance_tracker = distance; } // USER_PRINTF("Distance: %3lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr); - if (millis() > change_timer + ideal_change_min) { + if ((millis() - change_timer) > ideal_change_min) { // softhack007 same result as "millis() > change_timer + ideal_change_min", but more robust against unsigned overflow // Make the analysis less sensitive if we miss the window. // Sometimes the analysis lowers the change_threshold too much for @@ -133,7 +134,7 @@ class AutoPlaylistUsermod : public Usermod { change_threshold_change = (distance_tracker)-change_threshold; change_threshold = distance_tracker; - USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ums - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); distance_tracker = UINT_FAST32_MAX; @@ -143,9 +144,9 @@ class AutoPlaylistUsermod : public Usermod { } - if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1) { + if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) { - change_threshold_change = change_threshold-(distance*0.9); + change_threshold_change = change_threshold-(distance*0.9f); if (change_threshold_change < 1) change_threshold_change = 1; @@ -157,7 +158,7 @@ class AutoPlaylistUsermod : public Usermod { change_threshold_change = 0; // change was within our window, no sensitivity change } - if (change_threshold < 1) change_threshold = 0; // we need change_threshold to be signed becasue otherwise this wraps to UINT_FAST16_MAX + if (change_threshold < 1) change_threshold = 0; // we need change_threshold to be signed because otherwise this wraps to UINT_FAST16_MAX distance_tracker = UINT_FAST32_MAX; @@ -195,11 +196,11 @@ class AutoPlaylistUsermod : public Usermod { applyPreset(newpreset); - USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ums - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); } else { - USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ums - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); } @@ -226,7 +227,7 @@ class AutoPlaylistUsermod : public Usermod { lastAutoPlaylist = currentPlaylist; } else if (enabled) { USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); - enabled = false; + enabled = false; // softhack007: warning this state is _not_ intermediate, due to line 219 "if (!enabled) return;" } } @@ -249,11 +250,11 @@ class AutoPlaylistUsermod : public Usermod { float volumeSmth = *(float*)um_data->u_data[0]; - if (volumeSmth > 0.5) { + if (volumeSmth > 0.5f) { lastSoundTime = millis(); } - if (millis() - lastSoundTime > (timeout * 1000)) { + if (millis() - lastSoundTime > (long(timeout) * 1000)) { if (!silenceDetected) { silenceDetected = true; USER_PRINTF("AutoPlaylist: Silence "); From 39f7a2e1872c2ee120dd80b6c3791599d9b291c1 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:32:26 -0400 Subject: [PATCH 50/83] Removed button, bettr Info pane, debug flag. --- .../usermod_v2_auto_playlist.h | 86 ++++++++++++------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 94885e47..c62c7c5f 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -1,5 +1,11 @@ #pragma once +#ifdef WLED_DEBUG + #ifndef USERMOD_AUTO_PLAYLIST_DEBUG + #define USERMOD_AUTO_PLAYLIST_DEBUG + #endif +#endif + #include "wled.h" class AutoPlaylistUsermod : public Usermod { @@ -134,7 +140,9 @@ class AutoPlaylistUsermod : public Usermod { change_threshold_change = (distance_tracker)-change_threshold; change_threshold = distance_tracker; + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); + #endif distance_tracker = UINT_FAST32_MAX; @@ -165,14 +173,18 @@ class AutoPlaylistUsermod : public Usermod { if (autoChangeIds.size() == 0) { if(currentPlaylist < 1) return; + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("Loading presets from playlist: %3d\n", currentPlaylist); + #endif JsonObject playtlistOjb = doc.to(); serializePlaylist(playtlistOjb); JsonArray playlistArray = playtlistOjb["playlist"]["ps"]; for(JsonVariant v : playlistArray) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("Adding %3u to autoChangeIds\n", v.as()); + #endif autoChangeIds.push_back(v.as()); } @@ -195,12 +207,16 @@ class AutoPlaylistUsermod : public Usermod { // after change_lockout. Better for smaller change_lockout values. applyPreset(newpreset); - + + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + #endif } else { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + #endif } @@ -216,27 +232,31 @@ class AutoPlaylistUsermod : public Usermod { */ void loop() { - if (!enabled) return; - if (millis() < 10000) return; // Wait for device to settle if (lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) { if (currentPlaylist == musicPlaylist) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("AutoPlaylist: enabled due to manual change of playlist back to %u\n", currentPlaylist); + #endif enabled = true; lastAutoPlaylist = currentPlaylist; } else if (enabled) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); + #endif enabled = false; // softhack007: warning this state is _not_ intermediate, due to line 219 "if (!enabled) return;" } } if (!enabled && currentPlaylist == musicPlaylist) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("AutoPlaylist: enabled due selecting musicPlaylist(%u)", musicPlaylist); + #endif enabled = true; } - + if (!enabled) return; if (bri == 0) return; @@ -282,15 +302,30 @@ class AutoPlaylistUsermod : public Usermod { JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name - String uiDomString = F(""); + String uiDomString = enabled ? F("AutoPlaylist is Enabled") : F("AutoPlaylist is Disabled"); + + uiDomString += F("
"); + + if (autoChange && currentPlaylist == musicPlaylist) { + uiDomString += F("AutoChange is Active"); + } else if (autoChange && currentPlaylist != musicPlaylist) { + uiDomString += F("AutoChange on Stand-by"); + } else if (!autoChange) { + uiDomString += F("AutoChange is Disabled"); + } + + uiDomString += F("
"); + + if (currentPlaylist == musicPlaylist && currentPlaylist > 0) { + uiDomString += F("Music Playlist"); + } else if (currentPlaylist == ambientPlaylist && currentPlaylist > 0) { + uiDomString += F("Ambient Playlist"); + } else { + uiDomString += F("Playlist Overridden"); + } + infoArr.add(uiDomString); + } /* @@ -298,20 +333,7 @@ class AutoPlaylistUsermod : public Usermod { * Values in the state object may be modified by connected clients */ void readFromJsonState(JsonObject& root) { - if (!initDone) return; // prevent crash on boot applyPreset() - bool en = enabled; - JsonObject um = root[FPSTR(_name)]; - if (!um.isNull()) { - if (um[FPSTR(_autoPlaylistEnabled)].is()) { - en = um[FPSTR(_autoPlaylistEnabled)].as(); - } else { - String str = um[FPSTR(_autoPlaylistEnabled)]; // checkbox -> off or on - en = (bool)(str!="off"); // off is guaranteed to be present - } - if (en != enabled) enabled = en; - DEBUG_PRINT("AutoPlaylist enabled = "); - DEBUG_PRINTLN(en); - } + return; } void appendConfigData() { @@ -347,7 +369,9 @@ class AutoPlaylistUsermod : public Usermod { lastAutoPlaylist = 0; - DEBUG_PRINTLN(F("AutoPlaylist config saved.")); + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTLN(F("AutoPlaylist config saved.")); + #endif } @@ -380,8 +404,10 @@ class AutoPlaylistUsermod : public Usermod { ideal_change_min = top[FPSTR(_ideal_change_min)] | ideal_change_min; ideal_change_max = top[FPSTR(_ideal_change_max)] | ideal_change_max; - DEBUG_PRINT(FPSTR(_name)); - DEBUG_PRINTLN(F(" config (re)loaded.")); + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINT(FPSTR(_name)); + USER_PRINTLN(F(" config (re)loaded.")); + #endif // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; @@ -401,7 +427,9 @@ class AutoPlaylistUsermod : public Usermod { void changePlaylist(byte id) { String name = ""; getPresetName(id, name); + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("apply %s\n", name.c_str()); + #endif applyPreset(id, CALL_MODE_NOTIFICATION); lastAutoPlaylist = id; } From a90c9f0f2a6ebd0fad5e2574a33308b368284876 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:39:58 -0400 Subject: [PATCH 51/83] Update FX_fcn.cpp Removed temp debug suppression --- wled00/FX_fcn.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 68c0d7dc..561f2380 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -115,7 +115,7 @@ Segment::Segment(const Segment &orig) { //WLEDMM: recreate ledsrgb if more space needed (will not free ledsrgb!) void Segment::allocLeds() { - size_t size = sizeof(CRGB)*max((size_t) length(), ledmapMaxSize); //TroyHack + size_t size = sizeof(CRGB)*max((size_t) length(), ledmapMaxSize); //Hack if ((size < sizeof(CRGB)) || (size > 164000)) { //softhack too small (<3) or too large (>160Kb) DEBUG_PRINTF("allocLeds warning: size == %u !!\n", size); if (ledsrgb && (ledsrgbSize == 0)) { @@ -124,9 +124,7 @@ void Segment::allocLeds() { } // softhack007 clean up buffer } if ((size > 0) && (!ledsrgb || size > ledsrgbSize)) { //softhack dont allocate zero bytes - #ifndef USERMOD_AUTO_PLAYLIST // TroyHacks - FIXME: just tidy output temporarily while debugging USER_PRINTF("allocLeds (%d,%d to %d,%d), %u from %u\n", start, startY, stop, stopY, size, ledsrgb?ledsrgbSize:0); - #endif if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first ledsrgb = (CRGB*)calloc(size, 1); ledsrgbSize = ledsrgb?size:0; @@ -1538,7 +1536,7 @@ void WS2812FX::enumerateLedmaps() { USER_PRINTF("enumerateLedmaps %s \"%s\"", fileName, name); if (isMatrix) { - //WLEDMM calc ledmapMaxSize (TroyHack) + //WLEDMM calc ledmapMaxSize (Hack) char dim[34] = { '\0' }; f.find("\"width\":"); f.readBytesUntil('\n', dim, sizeof(dim)-1); //hack: use fileName as we have this allocated already @@ -2389,7 +2387,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { //WLEDMM recreate customMappingTable if more space needed if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { - size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight));//TroyHack + size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight));//Hack USER_PRINTF("deserializemap customMappingTable alloc %u from %u\n", size, customMappingTableSize); //if (customMappingTable != nullptr) delete[] customMappingTable; //customMappingTable = new uint16_t[size]; From 1f9e800957035e43e7a11c4f613c891a223d034c Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:45:46 -0400 Subject: [PATCH 52/83] Update FX_fcn.cpp Accidental find/replace --- wled00/FX_fcn.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 561f2380..b9c37e8b 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -115,7 +115,7 @@ Segment::Segment(const Segment &orig) { //WLEDMM: recreate ledsrgb if more space needed (will not free ledsrgb!) void Segment::allocLeds() { - size_t size = sizeof(CRGB)*max((size_t) length(), ledmapMaxSize); //Hack + size_t size = sizeof(CRGB)*max((size_t) length(), ledmapMaxSize); // TroyHacks if ((size < sizeof(CRGB)) || (size > 164000)) { //softhack too small (<3) or too large (>160Kb) DEBUG_PRINTF("allocLeds warning: size == %u !!\n", size); if (ledsrgb && (ledsrgbSize == 0)) { @@ -1536,7 +1536,7 @@ void WS2812FX::enumerateLedmaps() { USER_PRINTF("enumerateLedmaps %s \"%s\"", fileName, name); if (isMatrix) { - //WLEDMM calc ledmapMaxSize (Hack) + //WLEDMM calc ledmapMaxSize (TroyHacks) char dim[34] = { '\0' }; f.find("\"width\":"); f.readBytesUntil('\n', dim, sizeof(dim)-1); //hack: use fileName as we have this allocated already @@ -2387,7 +2387,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { //WLEDMM recreate customMappingTable if more space needed if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { - size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight));//Hack + size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks USER_PRINTF("deserializemap customMappingTable alloc %u from %u\n", size, customMappingTableSize); //if (customMappingTable != nullptr) delete[] customMappingTable; //customMappingTable = new uint16_t[size]; From 6e23b09a93beac8e641e1ff612528c6c85ad40a7 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:46:35 -0400 Subject: [PATCH 53/83] Update FX_2Dfcn.cpp removed temp debugging --- wled00/FX_2Dfcn.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index b013457e..c4e0ac8e 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -64,13 +64,11 @@ void WS2812FX::setUpMatrix() { return; } - #ifndef USERMOD_AUTO_PLAYLIST // TroyHacks - FIXME: just tidy output temporarily while debugging USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); - #endif - + //WLEDMM recreate customMappingTable if more space needed if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { - size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight));//TroyHack + size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", size, customMappingTableSize); //if (customMappingTable != nullptr) delete[] customMappingTable; //customMappingTable = new uint16_t[size]; From bf6932ecb79e8fdab73505dddd147f442121afb8 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:57:55 +0200 Subject: [PATCH 54/83] ar_energy: reduced zeroCrossingCount magnitude by 1/3 zero crossing counter is often larger than in the prototype by @troyhacks - due to calculation after filtering (catches more crossings), and due to the fixed condition for detecting zero crossings (doubles the number of crossings). This patch reduces the final value to 2/3, so it typically stays below 256. --- usermods/audioreactive/audio_reactive.h | 1 + 1 file changed, 1 insertion(+) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 070fba00..402ad29e 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -563,6 +563,7 @@ void FFTcode(void * parameter) } } } + newZeroCrossingCount = (newZeroCrossingCount*2)/3; // reduce value so it typicially stays below 256 zeroCrossingCount = newZeroCrossingCount; // update only once, to avoid that effects pick up an intermediate value // release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function From abc811484d9394113f873872f2f588009f2958b5 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:31:08 -0400 Subject: [PATCH 55/83] Added new internal functionality enabled tracking --- .../usermod_v2_auto_playlist.h | 92 +++++++++++-------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index c62c7c5f..3859c8ed 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -13,6 +13,7 @@ class AutoPlaylistUsermod : public Usermod { private: bool initDone = false; + bool functionality_enabled = false; bool silenceDetected = true; unsigned long lastSoundTime = 0; byte ambientPlaylist = 1; @@ -100,13 +101,13 @@ class AutoPlaylistUsermod : public Usermod { if (volumeSmth > 1.0f) { // softhack007: original code used 0.998f as decay factor - avg_long_energy = avg_long_energy * 0.99f + energy * 0.01f; - avg_long_lfc = avg_long_lfc * 0.99f + lfc * 0.01f; - avg_long_zcr = avg_long_zcr * 0.99f + zcr * 0.01f; + avg_long_energy = avg_long_energy * 0.99f + energy * 0.01f; + avg_long_lfc = avg_long_lfc * 0.99f + lfc * 0.01f; + avg_long_zcr = avg_long_zcr * 0.99f + zcr * 0.01f; - avg_short_energy = avg_short_energy * 0.9f + energy * 0.1f; - avg_short_lfc = avg_short_lfc * 0.9f + lfc * 0.1f; - avg_short_zcr = avg_short_zcr * 0.9f + zcr * 0.1f; + avg_short_energy = avg_short_energy * 0.90f + energy * 0.10f; + avg_short_lfc = avg_short_lfc * 0.90f + lfc * 0.10f; + avg_short_zcr = avg_short_zcr * 0.90f + zcr * 0.10f; // allegedly this is faster than pow(whatever,2) vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); @@ -135,14 +136,18 @@ class AutoPlaylistUsermod : public Usermod { // the current music, especially after track changes or during // sparce intros and breakdowns. - if (change_interval > ideal_change_min && distance_tracker <= 9999) { - - change_threshold_change = (distance_tracker)-change_threshold; + if (change_interval > ideal_change_min && distance_tracker <= 1000) { + + change_threshold_change = distance_tracker-change_threshold; change_threshold = distance_tracker; - #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); - #endif + if (change_threshold_change > 9999999) change_threshold_change = 0; + + if (functionality_enabled) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); + #endif + } distance_tracker = UINT_FAST32_MAX; @@ -151,8 +156,8 @@ class AutoPlaylistUsermod : public Usermod { change_timer = millis(); } - - if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) { + + if (functionality_enabled && distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) { change_threshold_change = change_threshold-(distance*0.9f); @@ -194,8 +199,7 @@ class AutoPlaylistUsermod : public Usermod { do { newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. - } - while (currentPreset == newpreset); // make sure we get a different random preset. + } while (currentPreset == newpreset); // make sure we get a different random preset. if (change_interval > change_lockout+3) { @@ -232,31 +236,33 @@ class AutoPlaylistUsermod : public Usermod { */ void loop() { + if (!enabled) return; + if (millis() < 10000) return; // Wait for device to settle if (lastAutoPlaylist > 0 && currentPlaylist != lastAutoPlaylist && currentPreset != 0) { - if (currentPlaylist == musicPlaylist) { - #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("AutoPlaylist: enabled due to manual change of playlist back to %u\n", currentPlaylist); - #endif - enabled = true; - lastAutoPlaylist = currentPlaylist; - } else if (enabled) { + if (functionality_enabled) { #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("AutoPlaylist: disable due to manual change of playlist from %u to %d, preset:%u\n", lastAutoPlaylist, currentPlaylist, currentPreset); #endif - enabled = false; // softhack007: warning this state is _not_ intermediate, due to line 219 "if (!enabled) return;" + functionality_enabled = false; + } else if (currentPlaylist == musicPlaylist) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTF("AutoPlaylist: enabled due to manual change of playlist back to %u\n", currentPlaylist); + #endif + functionality_enabled = true; + lastAutoPlaylist = currentPlaylist; } } - if (!enabled && currentPlaylist == musicPlaylist) { + if (!functionality_enabled && currentPlaylist == musicPlaylist) { #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("AutoPlaylist: enabled due selecting musicPlaylist(%u)", musicPlaylist); + USER_PRINTF("AutoPlaylist: enabled due selecting musicPlaylist(%u)\n", musicPlaylist); #endif - enabled = true; + functionality_enabled = true; } - if (!enabled) return; + // if (!functionality_enabled) return; if (bri == 0) return; @@ -277,13 +283,13 @@ class AutoPlaylistUsermod : public Usermod { if (millis() - lastSoundTime > (long(timeout) * 1000)) { if (!silenceDetected) { silenceDetected = true; - USER_PRINTF("AutoPlaylist: Silence "); + USER_PRINTLN("AutoPlaylist: Silence"); changePlaylist(ambientPlaylist); } } else { if (silenceDetected) { silenceDetected = false; - USER_PRINTF("AutoPlaylist: End of silence "); + USER_PRINTLN("AutoPlaylist: End of silence"); changePlaylist(musicPlaylist); } if (autoChange) change(um_data); @@ -302,13 +308,21 @@ class AutoPlaylistUsermod : public Usermod { JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name - String uiDomString = enabled ? F("AutoPlaylist is Enabled") : F("AutoPlaylist is Disabled"); + String uiDomString = F(""); + + if (enabled && functionality_enabled) { + uiDomString += F("AutoPlaylist is Running"); + } else if (!enabled) { + uiDomString += F("AutoPlaylist is Disabled"); + } else { + uiDomString += F("AutoPlaylist is Suspended"); + } uiDomString += F("
"); - if (autoChange && currentPlaylist == musicPlaylist) { + if (autoChange && currentPlaylist == musicPlaylist && functionality_enabled) { uiDomString += F("AutoChange is Active"); - } else if (autoChange && currentPlaylist != musicPlaylist) { + } else if (autoChange && (currentPlaylist != musicPlaylist || !functionality_enabled || !enabled)) { uiDomString += F("AutoChange on Stand-by"); } else if (!autoChange) { uiDomString += F("AutoChange is Disabled"); @@ -317,12 +331,18 @@ class AutoPlaylistUsermod : public Usermod { uiDomString += F("
"); if (currentPlaylist == musicPlaylist && currentPlaylist > 0) { - uiDomString += F("Music Playlist"); + uiDomString += F("Playlist: Music Playlist"); } else if (currentPlaylist == ambientPlaylist && currentPlaylist > 0) { - uiDomString += F("Ambient Playlist"); + uiDomString += F("Playlist: Ambient Playlist"); } else { - uiDomString += F("Playlist Overridden"); + uiDomString += F("Playlist: Overridden"); } + + // #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + // uiDomString += F("
"); + // uiDomString += F("Change Threshold: "); + // uiDomString += String(change_threshold); + // #endif infoArr.add(uiDomString); From f9eb6d23006dfe6e488d5da06d41a63519048cf6 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:04:39 -0400 Subject: [PATCH 56/83] functionality_enabled logic --- .../usermod_v2_auto_playlist.h | 86 +++++++++++-------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 3859c8ed..1d23770b 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -22,6 +22,7 @@ class AutoPlaylistUsermod : public Usermod { bool autoChange = false; byte lastAutoPlaylist = 0; unsigned long change_timer = millis(); + unsigned long autochange_timer = millis(); uint_fast32_t energy = 0; @@ -85,6 +86,8 @@ class AutoPlaylistUsermod : public Usermod { uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + energy = 0; + for (int i=0; i < NUM_GEQ_CHANNELS; i++) { energy += fftResult[i]; } @@ -136,12 +139,12 @@ class AutoPlaylistUsermod : public Usermod { // the current music, especially after track changes or during // sparce intros and breakdowns. - if (change_interval > ideal_change_min && distance_tracker <= 1000) { + if (change_interval > ideal_change_min && distance_tracker <= 100) { change_threshold_change = distance_tracker-change_threshold; change_threshold = distance_tracker; - if (change_threshold_change > 9999999) change_threshold_change = 0; + if (change_threshold_change > 9999) change_threshold_change = 0; // cosmetic for debug if (functionality_enabled) { #ifdef USERMOD_AUTO_PLAYLIST_DEBUG @@ -157,7 +160,7 @@ class AutoPlaylistUsermod : public Usermod { } - if (functionality_enabled && distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) { + if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) { change_threshold_change = change_threshold-(distance*0.9f); @@ -175,52 +178,56 @@ class AutoPlaylistUsermod : public Usermod { distance_tracker = UINT_FAST32_MAX; - if (autoChangeIds.size() == 0) { - if(currentPlaylist < 1) return; + if (functionality_enabled) { + + if (autoChangeIds.size() == 0) { + if(currentPlaylist < 1) return; - #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("Loading presets from playlist: %3d\n", currentPlaylist); - #endif - - JsonObject playtlistOjb = doc.to(); - serializePlaylist(playtlistOjb); - JsonArray playlistArray = playtlistOjb["playlist"]["ps"]; - - for(JsonVariant v : playlistArray) { #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("Adding %3u to autoChangeIds\n", v.as()); + USER_PRINTF("Loading presets from playlist: %3d\n", currentPlaylist); #endif - autoChangeIds.push_back(v.as()); + + JsonObject playtlistOjb = doc.to(); + serializePlaylist(playtlistOjb); + JsonArray playlistArray = playtlistOjb["playlist"]["ps"]; + + for(JsonVariant v : playlistArray) { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTF("Adding %3u to autoChangeIds\n", v.as()); + #endif + autoChangeIds.push_back(v.as()); + } + } - } + uint8_t newpreset = 0; - uint8_t newpreset = 0; + do { + newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. + } while (currentPreset == newpreset); // make sure we get a different random preset. - do { - newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. - } while (currentPreset == newpreset); // make sure we get a different random preset. + if (change_interval > change_lockout+3) { - if (change_interval > change_lockout+3) { + // Make sure we have a statistically significant change and we aren't + // just bouncing off change_lockout. That's valid for changing the + // thresholds, but might be a bit crazy for lighting changes. + // When the music changes quite a bit, the distance calculation can + // go into freefall - this logic stops that from triggering right + // after change_lockout. Better for smaller change_lockout values. - // Make sure we have a statistically significant change and we aren't - // just bouncing off change_lockout. That's valid for changing the - // thresholds, but might be a bit crazy for lighting changes. - // When the music changes quite a bit, the distance calculation can - // go into freefall - this logic stops that from triggering right - // after change_lockout. Better for smaller change_lockout values. + applyPreset(newpreset); + + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + #endif - applyPreset(newpreset); - - #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); - #endif + } else { - } else { + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); + #endif - #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("^^^ SKIP!! distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); - #endif + } } @@ -292,7 +299,10 @@ class AutoPlaylistUsermod : public Usermod { USER_PRINTLN("AutoPlaylist: End of silence"); changePlaylist(musicPlaylist); } - if (autoChange) change(um_data); + if (autoChange && millis() >= autochange_timer+22) { + change(um_data); + autochange_timer = millis(); + } } } From c5a71af679d618a1b5664211e42a4fadd6a6b39a Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:10:38 -0400 Subject: [PATCH 57/83] Update usermods_list.cpp Disabled by default --- wled00/usermods_list.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 3cb5c19c..a6e7ba7a 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -406,7 +406,7 @@ void registerUsermods() #endif #ifdef USERMOD_AUTO_PLAYLIST - usermods.add(new AutoPlaylistUsermod(true)); + usermods.add(new AutoPlaylistUsermod(false)); #endif From 02b464f2836d7a674128a5de1468945da7c7718b Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:45:21 -0400 Subject: [PATCH 58/83] Smoothing sound detection --- .../usermod_v2_auto_playlist.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 1d23770b..74c6472d 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -15,14 +15,15 @@ class AutoPlaylistUsermod : public Usermod { bool initDone = false; bool functionality_enabled = false; bool silenceDetected = true; - unsigned long lastSoundTime = 0; byte ambientPlaylist = 1; byte musicPlaylist = 2; int timeout = 60; bool autoChange = false; byte lastAutoPlaylist = 0; + unsigned long lastSoundTime = millis()-(timeout*1000)-100; unsigned long change_timer = millis(); unsigned long autochange_timer = millis(); + float avg_volumeSmth = 0; uint_fast32_t energy = 0; @@ -283,20 +284,22 @@ class AutoPlaylistUsermod : public Usermod { float volumeSmth = *(float*)um_data->u_data[0]; - if (volumeSmth > 0.5f) { + avg_volumeSmth = avg_volumeSmth * 0.99f + volumeSmth * 0.01f; + + if (avg_volumeSmth >= 1.0f) { lastSoundTime = millis(); } if (millis() - lastSoundTime > (long(timeout) * 1000)) { if (!silenceDetected) { silenceDetected = true; - USER_PRINTLN("AutoPlaylist: Silence"); + USER_PRINTLN("AutoPlaylist: Silence detected"); changePlaylist(ambientPlaylist); } } else { if (silenceDetected) { silenceDetected = false; - USER_PRINTLN("AutoPlaylist: End of silence"); + USER_PRINTLN("AutoPlaylist: Sound detected"); changePlaylist(musicPlaylist); } if (autoChange && millis() >= autochange_timer+22) { @@ -330,7 +333,7 @@ class AutoPlaylistUsermod : public Usermod { uiDomString += F("
"); - if (autoChange && currentPlaylist == musicPlaylist && functionality_enabled) { + if (enabled && autoChange && currentPlaylist == musicPlaylist && functionality_enabled) { uiDomString += F("AutoChange is Active"); } else if (autoChange && (currentPlaylist != musicPlaylist || !functionality_enabled || !enabled)) { uiDomString += F("AutoChange on Stand-by"); @@ -458,7 +461,7 @@ class AutoPlaylistUsermod : public Usermod { String name = ""; getPresetName(id, name); #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("apply %s\n", name.c_str()); + USER_PRINTF("AutoPlaylist: Applying \"%s\"\n", name.c_str()); #endif applyPreset(id, CALL_MODE_NOTIFICATION); lastAutoPlaylist = id; From 5186c0fdb67ff60600d81917b589826c0547255a Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 7 Apr 2024 21:23:01 +0200 Subject: [PATCH 59/83] autoplaylist bugfix for playlists with only one preset * prevent infinite loop in case that there is only one preset in a playlist * fixing two typos --- .../usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 74c6472d..0e420eae 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -138,7 +138,7 @@ class AutoPlaylistUsermod : public Usermod { // Make the analysis less sensitive if we miss the window. // Sometimes the analysis lowers the change_threshold too much for // the current music, especially after track changes or during - // sparce intros and breakdowns. + // sparse intros and breakdowns. if (change_interval > ideal_change_min && distance_tracker <= 100) { @@ -149,7 +149,7 @@ class AutoPlaylistUsermod : public Usermod { if (functionality_enabled) { #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff aprox)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); + USER_PRINTF("--- lowest distance = %4lu - no changes done in %6ldms - next change_threshold is %4u (%4u diff approx)\n", (unsigned long)distance_tracker,change_interval,change_threshold,change_threshold_change); #endif } @@ -205,7 +205,7 @@ class AutoPlaylistUsermod : public Usermod { do { newpreset = autoChangeIds.at(random(0, autoChangeIds.size())); // random() is *exclusive* of the last value, so it's OK to use the full size. - } while (currentPreset == newpreset); // make sure we get a different random preset. + } while ((currentPreset == newpreset) && (autoChangeIds.size() > 1)); // make sure we get a different random preset. Unless there is only one. if (change_interval > change_lockout+3) { From 2a4810f43716f0502ec98f288e637d29f348d5a4 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 7 Apr 2024 21:31:53 +0200 Subject: [PATCH 60/83] autoplaylist filter improvements * initialize slow and fast filters with current values (first call only) - reduces time for filter stabilization * use slightly optimized variant of "rolling average" filter (result is exactly the same) --> replaced `xX = xX * (1-a) + newX * a;` by `xX = xX + a * (newX - Xx);` * use constants for filter parameters (for tinkering and improved readability) --- .../usermod_v2_auto_playlist.h | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 0e420eae..06bbc2ff 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -12,6 +12,25 @@ class AutoPlaylistUsermod : public Usermod { private: + #if 0 + // experimental parameters by softhack007 - more balanced but need testing + const uint_fast32_t ENERGY_SCALE = 24000; + const float FILTER_SLOW1 = 0.0075f; // for "slow" energy + const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr + const float FILTER_FAST1 = 0.2f; // for "fast" energy + const float FILTER_FAST2 = 0.25f; // for "fast" lfc / zcr + const float FILTER_VOLUME = 0.03f; // for volumeSmth averaging - takes 8-10sec until "silence" + #else + // parameters used by TroyHacks / netmindz - behaviour is mainly driven by "energy" + const uint_fast32_t ENERGY_SCALE = 10000; + // softhack007: original code used FILTER_SLOW = 0.002f + const float FILTER_SLOW1 = 0.01f; // for "slow" energy + const float FILTER_SLOW2 = 0.01f; // for "slow" lfc / zcr + const float FILTER_FAST1 = 0.1f; // for "fast" energy + const float FILTER_FAST2 = 0.1f; // for "fast" lfc / zcr + const float FILTER_VOLUME = 0.01f; // for volumeSmth averaging - takes 12-15sec until "silence" + #endif + bool initDone = false; bool functionality_enabled = false; bool silenceDetected = true; @@ -35,6 +54,7 @@ class AutoPlaylistUsermod : public Usermod { float avg_short_lfc = 1000; float avg_short_zcr = 500; + bool resetFilters = true; // to (re)initialize filters on first run uint_fast32_t vector_energy = 0; uint_fast32_t vector_lfc = 0; uint_fast32_t vector_zcr = 0; @@ -94,7 +114,7 @@ class AutoPlaylistUsermod : public Usermod { } energy *= energy; - energy /= 10000; // scale down so we get 0 sometimes + energy /= ENERGY_SCALE; // scale down so we get 0 sometimes uint8_t lfc = fftResult[0]; uint16_t zcr = *(uint16_t*)um_data->u_data[11]; @@ -103,15 +123,24 @@ class AutoPlaylistUsermod : public Usermod { // and the individual vector distances. if (volumeSmth > 1.0f) { + // initialize filters on first run + if (resetFilters) { + avg_short_energy = avg_long_energy = energy; + avg_short_lfc = avg_long_lfc = lfc; + avg_short_zcr = avg_long_zcr = zcr; + resetFilters = false; + #ifdef USERMOD_AUTO_PLAYLIST_DEBUG + USER_PRINTLN("autoplaylist: filters reset."); + #endif + } - // softhack007: original code used 0.998f as decay factor - avg_long_energy = avg_long_energy * 0.99f + energy * 0.01f; - avg_long_lfc = avg_long_lfc * 0.99f + lfc * 0.01f; - avg_long_zcr = avg_long_zcr * 0.99f + zcr * 0.01f; + avg_long_energy = avg_long_energy + FILTER_SLOW1 * (float(energy) - avg_long_energy); + avg_long_lfc = avg_long_lfc + FILTER_SLOW2 * (float(lfc) - avg_long_lfc); + avg_long_zcr = avg_long_zcr + FILTER_SLOW2 * (float(zcr) - avg_long_zcr); - avg_short_energy = avg_short_energy * 0.90f + energy * 0.10f; - avg_short_lfc = avg_short_lfc * 0.90f + lfc * 0.10f; - avg_short_zcr = avg_short_zcr * 0.90f + zcr * 0.10f; + avg_short_energy = avg_short_energy + FILTER_FAST1 * (float(energy) - avg_short_energy); + avg_short_lfc = avg_short_lfc + FILTER_FAST2 * (float(lfc) - avg_short_lfc); + avg_short_zcr = avg_short_zcr + FILTER_FAST2 * (float(zcr) - avg_short_zcr); // allegedly this is faster than pow(whatever,2) vector_lfc = (avg_short_lfc-avg_long_lfc)*(avg_short_lfc-avg_long_lfc); @@ -284,7 +313,7 @@ class AutoPlaylistUsermod : public Usermod { float volumeSmth = *(float*)um_data->u_data[0]; - avg_volumeSmth = avg_volumeSmth * 0.99f + volumeSmth * 0.01f; + avg_volumeSmth = avg_volumeSmth + FILTER_VOLUME * (volumeSmth - avg_volumeSmth); if (avg_volumeSmth >= 1.0f) { lastSoundTime = millis(); From 0ddfb25ad0dd623d9463398b76fdbb292d4a79c5 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 22 Oct 2023 17:54:00 +0200 Subject: [PATCH 61/83] Possible fix for: - #3382 - #3312 --- wled00/led.cpp | 3 ++- wled00/wled_server.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/wled00/led.cpp b/wled00/led.cpp index c6857c4a..cee883f3 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -163,6 +163,8 @@ void updateInterfaces(uint8_t callMode) { sendDataWs(); lastInterfaceUpdate = millis(); + interfaceUpdateCallMode = 0; //disable + if (callMode == CALL_MODE_WS_SEND) return; #ifndef WLED_DISABLE_ALEXA @@ -172,7 +174,6 @@ void updateInterfaces(uint8_t callMode) } #endif doPublishMqtt = true; - interfaceUpdateCallMode = 0; //disable } diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index 8751119e..a08e9ea6 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -220,6 +220,8 @@ void initServer() if (verboseResponse) { if (!isConfig) { + lastInterfaceUpdate = millis(); // prevent WS update until cooldown + interfaceUpdateCallMode = CALL_MODE_WS_SEND; // schedule WS update serveJson(request); return; //if JSON contains "v" } else { doSerializeConfig = true; //serializeConfig(); //Save new settings to FS From e94064addeb04a19745a71378010fa514ccdf7c9 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 28 Aug 2023 17:58:30 +0200 Subject: [PATCH 62/83] Bugfix. - feed WDT even if strip is updating - provide custom palette names - handle interface cooldown properly - rotary encoder ALT fix for custom palettes --- wled00/led.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wled00/led.cpp b/wled00/led.cpp index cee883f3..fd334872 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -161,6 +161,8 @@ void stateUpdated(byte callMode) { void updateInterfaces(uint8_t callMode) { + if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return; + sendDataWs(); lastInterfaceUpdate = millis(); interfaceUpdateCallMode = 0; //disable @@ -180,7 +182,7 @@ void updateInterfaces(uint8_t callMode) void handleTransitions() { //handle still pending interface update - if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode); + updateInterfaces(interfaceUpdateCallMode); #ifndef WLED_DISABLE_MQTT if (doPublishMqtt) publishMqtt(); #endif From aff4de34b449206e7d0f72c2b5719c8dc879b8f3 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 18 Jun 2023 01:07:50 +0200 Subject: [PATCH 63/83] WS logic: No resending, improved ESP8266 stability Update ESP8266 core to 3.1.2 --- CHANGELOG.md | 6 ++++++ wled00/const.h | 5 ++++- wled00/ws.cpp | 17 +++++++++-------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11799d26..7866801e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,12 @@ - Improved ESP8266 stability by reducing WebSocket response resends - Updated ESP8266 core to 3.1.2 +#### Build 2306180 + +- Added client-side option for applying effect defaults from metadata +- Improved ESP8266 stability by reducing WebSocket response resends +- Updated ESP8266 core to 3.1.2 + #### Build 2306141 - Lissajous improvements - Scrolling Text improvements (leading 0) diff --git a/wled00/const.h b/wled00/const.h index 578e1ab0..fa3ded65 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -494,7 +494,10 @@ #define DEFAULT_LED_COUNT 30 #endif -#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates +#define INTERFACE_UPDATE_COOLDOWN 1200 // time in ms to wait between websockets, alexa, and MQTT updates + +#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct +#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes // HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves // which GPIO pins are actually used in a hardware layout (controller board) diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 85f75406..be5e0687 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -62,14 +62,15 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp } releaseJSONBufferLock(); // will clean fileDoc - // force broadcast in 500ms after updating client - if (verboseResponse) { - sendDataWs(client); - lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); - } else { - // we have to send something back otherwise WS connection closes - client->text(F("{\"success\":true}")); - lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); + if (!interfaceUpdateCallMode) { // individual client response only needed if no WS broadcast soon + if (verboseResponse) { + sendDataWs(client); + } else { + // we have to send something back otherwise WS connection closes + client->text(F("{\"success\":true}")); + } + // force broadcast in 500ms after updating client + //lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); // ESP8266 does not like this } } } else { From bc249379f070ece81f7b75da680d477875296e56 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:43:46 +0200 Subject: [PATCH 64/83] minor improvements from upstream * add checkSettingsPIN() and get_random_wheel_index() functions * add on/off state to UDP data * small robustness improvements --- wled00/fcn_declare.h | 2 ++ wled00/led.cpp | 8 +++++-- wled00/udp.cpp | 1 + wled00/util.cpp | 55 ++++++++++++++++++++++++++++++++++---------- wled00/wled.h | 2 +- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index dcf4019e..f9dbd8ac 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -370,9 +370,11 @@ void releaseJSONBufferLock(); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr); int16_t extractModeDefaults(uint8_t mode, const char *segVar); +void checkSettingsPIN(const char *pin); uint16_t __attribute__((pure)) crc16(const unsigned char* data_p, size_t length); // WLEDMM: added attribute pure um_data_t* simulateSound(uint8_t simulationId); // WLEDMM enumerateLedmaps(); moved to FX.h +uint8_t get_random_wheel_index(uint8_t pos); CRGB getCRGBForBand(int x, uint8_t *fftResult, int pal); //WLEDMM netmindz ar palette char *cleanUpName(char *in); // to clean up a name that was read from file diff --git a/wled00/led.cpp b/wled00/led.cpp index fd334872..0e245c9d 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -105,6 +105,7 @@ void stateUpdated(byte callMode) { if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode); + if (bri != briOld && nodeBroadcastEnabled) sendSysInfoUDP(); // update on state //set flag to update ws and mqtt interfaceUpdateCallMode = callMode; @@ -193,12 +194,15 @@ void handleTransitions() if (tper >= 1.0f) { strip.setTransitionMode(false); + // restore (global) transition time if not called from UDP notifier or single/temporary transition from JSON (also playlist) + if (jsonTransitionOnce) strip.setTransition(transitionDelay); transitionActive = false; + jsonTransitionOnce = false; tperLast = 0; applyFinalBri(); return; } - if (tper - tperLast < 0.004) return; + if (tper - tperLast < 0.004f) return; tperLast = tper; briT = briOld + ((bri - briOld) * tper); @@ -208,7 +212,7 @@ void handleTransitions() // legacy method, applies values from col, effectCurrent, ... to selected segments -void colorUpdated(byte callMode){ +void colorUpdated(byte callMode) { applyValuesToSelectedSegs(); stateUpdated(callMode); } diff --git a/wled00/udp.cpp b/wled00/udp.cpp index c2395357..0a2d5332 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -712,6 +712,7 @@ void sendSysInfoUDP() #else data[38] = NODE_TYPE_ID_UNDEFINED; #endif + if (bri) data[38] |= 0x80U; // add on/off state data[39] = ip[3]; // unit ID == last IP number uint32_t build = VERSION; diff --git a/wled00/util.cpp b/wled00/util.cpp index 31f9716c..ceefcc2f 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -204,7 +204,7 @@ bool requestJSONBufferLock(uint8_t module) { unsigned long now = millis(); - while (jsonBufferLock && millis()-now < 1200) delay(1); // wait for fraction for buffer lock + while (jsonBufferLock && millis()-now < 1100) delay(1); // wait for fraction for buffer lock if (jsonBufferLock) { USER_PRINT(F("ERROR: Locking JSON buffer failed! (still locked by ")); @@ -239,9 +239,10 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe { if (src == JSON_mode_names || src == nullptr) { if (mode < strip.getModeCount()) { - char lineBuffer[256]; + char lineBuffer[256] = { '\0' }; //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); - strcpy_P(lineBuffer, strip.getModeData(mode)); + strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1); + lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string size_t len = strlen(lineBuffer); size_t j = 0; for (; j < maxLen && j < len; j++) { @@ -253,6 +254,12 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe } else return 0; } + if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) { + snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode); + dest[maxLen-1] = '\0'; + return strlen(dest); + } + uint8_t qComma = 0; bool insideQuotes = false; uint8_t printedChars = 0; @@ -363,9 +370,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL int16_t extractModeDefaults(uint8_t mode, const char *segVar) { if (mode < strip.getModeCount()) { - char lineBuffer[128] = ""; - strncpy_P(lineBuffer, strip.getModeData(mode), 127); - lineBuffer[127] = '\0'; // terminate string + char lineBuffer[256] = { '\0' }; + strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1); + lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data if (!startPtr) return -1; @@ -381,6 +388,16 @@ int16_t extractModeDefaults(uint8_t mode, const char *segVar) } +void checkSettingsPIN(const char* pin) { + if (!pin) return; + if (!correctPIN && millis() - lastEditTime < PIN_RETRY_COOLDOWN) return; // guard against PIN brute force + bool correctBefore = correctPIN; + correctPIN = (strlen(settingsPIN) == 0 || strncmp(settingsPIN, pin, 4) == 0); + if (correctBefore != correctPIN) createEditHandler(correctPIN); + lastEditTime = millis(); +} + + uint16_t crc16(const unsigned char* data_p, size_t length) { uint8_t x; uint16_t crc = 0xFFFF; @@ -401,9 +418,9 @@ uint16_t crc16(const unsigned char* data_p, size_t length) { // (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type) typedef enum UM_SoundSimulations { UMS_BeatSin = 0, - UMS_WeWillRockYou - //UMS_10_13, - //UMS_14_3 + UMS_WeWillRockYou, + UMS_10_13, + UMS_14_3 } um_soundSimulations_t; um_data_t* simulateSound(uint8_t simulationId) @@ -491,7 +508,7 @@ um_data_t* simulateSound(uint8_t simulationId) fftResult[i] = 0; } break; - /*case UMS_10_3: + case UMS_10_13: for (int i = 0; i<16; i++) fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); volumeSmth = fftResult[8]; @@ -500,11 +517,11 @@ um_data_t* simulateSound(uint8_t simulationId) for (int i = 0; i<16; i++) fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); volumeSmth = fftResult[8]; - break;*/ + break; } samplePeak = random8() > 250; - FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // WLEDMM 21hz...8200hz + FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // walk thru full range of 21hz...8200hz maxVol = 31; // this gets feedback fro UI binNum = 8; // this gets feedback fro UI volumeRaw = volumeSmth; @@ -545,6 +562,20 @@ CRGB getCRGBForBand(int x, uint8_t *fftResult, int pal) { return value; } +/* + * Returns a new, random color wheel index with a minimum distance of 42 from pos. + */ +uint8_t get_random_wheel_index(uint8_t pos) { + uint8_t r = 0, x = 0, y = 0, d = 0; + while (d < 42) { + r = random8(); + x = abs(pos - r); + y = 255 - x; + d = MIN(x, y); + } + return r; +} + // WLEDMM extended "trim string" function to support enumerateLedmaps // The function takes char* as input, and removes all leading and trailing "decorations" like spaces, tabs, line endings, quotes, colons // The conversion is "in place" (destructive). diff --git a/wled00/wled.h b/wled00/wled.h index 5008c1af..30772955 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2402252 +#define VERSION 2404090 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ From 03cbc52c65ef6f7d4e99a8f227a15dd88ab69488 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 Apr 2024 19:23:28 +0200 Subject: [PATCH 65/83] ar_energy small improvements * use named constant instead of "100" * make change_threshold_change more robust against negatives * unloadPlaylist() before activating a new playlist --- .../usermod_v2_auto_playlist.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 06bbc2ff..83407e5f 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -14,6 +14,7 @@ class AutoPlaylistUsermod : public Usermod { #if 0 // experimental parameters by softhack007 - more balanced but need testing + const uint_fast32_t MAX_DISTANCE_TRACKER = 184; // maximum accepted distance_tracker const uint_fast32_t ENERGY_SCALE = 24000; const float FILTER_SLOW1 = 0.0075f; // for "slow" energy const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr @@ -22,6 +23,7 @@ class AutoPlaylistUsermod : public Usermod { const float FILTER_VOLUME = 0.03f; // for volumeSmth averaging - takes 8-10sec until "silence" #else // parameters used by TroyHacks / netmindz - behaviour is mainly driven by "energy" + const uint_fast32_t MAX_DISTANCE_TRACKER = 128; // maximum accepted distance_tracker const uint_fast32_t ENERGY_SCALE = 10000; // softhack007: original code used FILTER_SLOW = 0.002f const float FILTER_SLOW1 = 0.01f; // for "slow" energy @@ -169,7 +171,7 @@ class AutoPlaylistUsermod : public Usermod { // the current music, especially after track changes or during // sparse intros and breakdowns. - if (change_interval > ideal_change_min && distance_tracker <= 100) { + if (change_interval > ideal_change_min && distance_tracker <= MAX_DISTANCE_TRACKER) { change_threshold_change = distance_tracker-change_threshold; change_threshold = distance_tracker; @@ -192,9 +194,7 @@ class AutoPlaylistUsermod : public Usermod { if (distance <= change_threshold && change_interval > change_lockout && volumeSmth > 1.0f) { - change_threshold_change = change_threshold-(distance*0.9f); - - if (change_threshold_change < 1) change_threshold_change = 1; + change_threshold_change = max(1.0f, roundf(change_threshold-(distance*0.9f))); // exclude negatives, ensure change_threshold_change is always >= 1 if (change_interval > ideal_change_max) { change_threshold += change_threshold_change; // make changes more sensitive @@ -245,6 +245,8 @@ class AutoPlaylistUsermod : public Usermod { // go into freefall - this logic stops that from triggering right // after change_lockout. Better for smaller change_lockout values. + // SH7: this method is sub-optimal, as its interfering with the "playlist" engine + // we shoud find a better method for triggering playlist changes applyPreset(newpreset); #ifdef USERMOD_AUTO_PLAYLIST_DEBUG @@ -492,7 +494,10 @@ class AutoPlaylistUsermod : public Usermod { #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("AutoPlaylist: Applying \"%s\"\n", name.c_str()); #endif - applyPreset(id, CALL_MODE_NOTIFICATION); + // if (currentPlaylist != id) { // un-comment to only change on "real" changes + unloadPlaylist(); // applying a preset requires to unload previous playlist + applyPreset(id, CALL_MODE_NOTIFICATION); + // } lastAutoPlaylist = id; } From 7b87c27402780327043a9ccf282857896fd6d645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Fri, 1 Mar 2024 16:28:19 +0100 Subject: [PATCH 66/83] Merge pull request #3616 from demophoon/dmx-segment-options-expanded Add additional segment options when controlling over e1.31 --- wled00/e131.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 18c1e3cf..dcb1e314 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -231,11 +231,16 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8 if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3]; if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]); - uint8_t segOption = (uint8_t)floor(e131_data[dataOffset+5]/64.0); - if (segOption == 0 && (seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, false);} - if (segOption == 1 && (seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, true);} - if (segOption == 2 && (!seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, false);} - if (segOption == 3 && (!seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, true);} + if ((e131_data[dataOffset+5] & 0b00000010) != seg.reverse_y) { seg.setOption(SEG_OPTION_REVERSED_Y, e131_data[dataOffset+5] & 0b00000010); } + if ((e131_data[dataOffset+5] & 0b00000100) != seg.mirror_y) { seg.setOption(SEG_OPTION_MIRROR_Y, e131_data[dataOffset+5] & 0b00000100); } + if ((e131_data[dataOffset+5] & 0b00001000) != seg.transpose) { seg.setOption(SEG_OPTION_TRANSPOSED, e131_data[dataOffset+5] & 0b00001000); } + if ((e131_data[dataOffset+5] & 0b00110000) / 8 != seg.map1D2D) { + seg.map1D2D = (e131_data[dataOffset+5] & 0b00110000) / 8; + } + // To maintain backwards compatibility with prior e1.31 values, reverse is fixed to mask 0x01000000 + if ((e131_data[dataOffset+5] & 0b01000000) != seg.reverse) { seg.setOption(SEG_OPTION_REVERSED, e131_data[dataOffset+5] & 0b01000000); } + // To maintain backwards compatibility with prior e1.31 values, mirror is fixed to mask 0x10000000 + if ((e131_data[dataOffset+5] & 0b10000000) != seg.mirror) { seg.setOption(SEG_OPTION_MIRROR, e131_data[dataOffset+5] & 0b10000000); } uint32_t colors[3]; byte whites[3] = {0,0,0}; From 0bb0b7f0364a4bcbed5d7a3878540b7e067552bd Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:02:06 +0200 Subject: [PATCH 67/83] Custom palette gamma (fixes #126) based on upstream https://github.com/Aircoookie/WLED/commit/7f6486c77dd4a8fc340e61ffa74023873c32e25b --- wled00/FX_fcn.cpp | 14 ++++++++------ wled00/wled.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index fa464320..66ce1643 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -2273,16 +2273,16 @@ void WS2812FX::loadCustomPalettes() { if (readObjectFromFile(fileName, nullptr, &pDoc)) { JsonArray pal = pDoc[F("palette")]; - if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries) + if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries) if (pal[0].is() && pal[1].is()) { // we have an array of index & hex strings size_t palSize = min(pal.size(), (size_t)36); // WLEDMM use native min/max palSize -= palSize % 2; // make sure size is multiple of 2 - for (size_t i=0, j=0; i()<256; i+=2, j+=4) { + for (unsigned i=0, j=0; i()<256; i+=2, j+=4) { uint8_t rgbw[] = {0,0,0,0}; tcp[ j ] = (uint8_t) pal[ i ].as(); // index colorFromHexString(rgbw, pal[i+1].as()); // will catch non-string entires - for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component + for (unsigned c=0; c<3; c++) tcp[j+1+c] = gamma8(rgbw[c]); // only use RGB component DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3])); } } else { @@ -2290,13 +2290,15 @@ void WS2812FX::loadCustomPalettes() { palSize -= palSize % 4; // make sure size is multiple of 4 for (size_t i=0; i()<256; i+=4) { tcp[ i ] = (uint8_t) pal[ i ].as(); // index - tcp[i+1] = (uint8_t) pal[i+1].as(); // R - tcp[i+2] = (uint8_t) pal[i+2].as(); // G - tcp[i+3] = (uint8_t) pal[i+3].as(); // B + tcp[i+1] = gamma8((uint8_t) pal[i+1].as()); // R + tcp[i+2] = gamma8((uint8_t) pal[i+2].as()); // G + tcp[i+3] = gamma8((uint8_t) pal[i+3].as()); // B DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3])); } } customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp)); + } else { + DEBUG_PRINTLN(F("Wrong palette format.")); } } } else { diff --git a/wled00/wled.h b/wled00/wled.h index 30772955..546a1c90 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2404090 +#define VERSION 2404091 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ From 98b5ad12684da3f33425453e33392dd14e845b0b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 14 Mar 2024 20:27:03 +0100 Subject: [PATCH 68/83] Update CONTRIBUTING.md Trying to explain why any PR should have a`description` --- CONTRIBUTING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fdf1388c..05c60b8b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,16 @@ Here are a few suggestions to make it easier for you to contribute! +### Describe your PR + +Please add a description of your proposed code changes. It does not need to be an exhaustive essay, however a PR with no description or just a few words might not get accepted, simply because very basic information is missing. + +A good description helps us to review and understand your proposed changes. For example, you could say a few words about +* what you try to achieve (new feature, fixing a bug, refactoring, security enhancements, etc.) +* how your code works (short technical summary - focus on important aspects that might not be obvious when reading the code) +* testing you performed, known limitations, open ends you possibly could not solve. +* any areas where you like to get help from an experienced maintainer (yes WLEDMM has become big 😉) + ### Code style When in doubt, it is easiest to replicate the code style you find in the files you want to edit :) @@ -79,4 +89,5 @@ Good: There is no set character limit for a comment within a line, though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window. + Inline comments are OK if they describe that line only and are not exceedingly wide. From c946b52974ab66166c29d02a3aa2729ed1c46acf Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 17 Mar 2024 11:52:09 +0100 Subject: [PATCH 69/83] WLED 0.14.2 release --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7866801e..5a4bb1cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ ## [WLED upstream](https://github.com/Aircoookie/WLED/tree/0_14_1) changelog +#### Build 2403170 +- WLED 0.14.2 release + +#### Build 2403110 +- Beta WLED 0.14.2-b2 +- Fixing a potential array bounds violation in ESPDMX +- LockedJsonResponse: Release early if possible (by @willmmiles) + +#### Build 2402120 +- Beta WLED 0.14.2-b1 +- Possible fix for #3589 & partial fix for #3605 +- Prevent JSON buffer clear after failed lock attempt +- Multiple analog button fix for #3549 +- UM Audioreactive: add two compiler options (#3732 by @wled-install) +- Fix for #3693 + +#### Build 2401141 +- Official release of WLED 0.14.1 +- Fix for #3566, #3665, #3672 +- Sorting of palettes in custom palette editor (#3674 by @WoodyLetsCode) + +#### Build 2401060 +- Version bump: 0.14.1-b3 +- Global JSON buffer guarding (#3648 by @willmmiles, resolves #3641, #3312, #3367, #3637, #3646, #3447) +- Fix for #3632 +- Custom palette editor mobile UI enhancement (#3617 by @imeszaros) +- changelog update + #### Build 2312290 - Fix for #3622 - NB: fix for #3613 #3609 are not needed in MoonModules fork From 4093df1de50599b16405171561ce729b6e0dda53 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 29 Mar 2024 20:05:56 +0100 Subject: [PATCH 70/83] WLED 0.14.3 release - Fix for transition 0 (#3854, #3832, #3720) - copyright year update - updated AsyncWebServer to v2.2.0 --- CHANGELOG.md | 4 ++++ platformio.ini | 6 +++--- wled00/led.cpp | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a4bb1cd..6369804d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [WLED upstream](https://github.com/Aircoookie/WLED/tree/0_14_1) changelog +#### Build 2403290 +- WLED 0.14.3 release +- Fix for transition 0 (#3854, #3832, #3720) + #### Build 2403170 - WLED 0.14.2 release diff --git a/platformio.ini b/platformio.ini index 107ab6fe..2bddd037 100644 --- a/platformio.ini +++ b/platformio.ini @@ -113,7 +113,7 @@ arduino_core_2_7_4 = espressif8266@2.6.2 arduino_core_3_0_0 = espressif8266@3.0.0 arduino_core_3_2_0 = espressif8266@3.2.0 arduino_core_4_1_0 = espressif8266@4.1.0 -arduino_core_3_1_2 = espressif8266@4.2.0 +arduino_core_3_1_2 = espressif8266@4.2.1 # Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop @@ -123,8 +123,7 @@ arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/ platform_wled_default = ${common.arduino_core_3_1_2} # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization #platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 -platform_packages = platformio/framework-arduinoespressif8266 - platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502 +platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502 platformio/tool-esptool #@ ~1.413.0 platformio/tool-esptoolpy #@ ~1.30000.0 @@ -241,6 +240,7 @@ lib_deps = ;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections ;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit + ;; https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.2.1 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For compatible OLED display uncomment following diff --git a/wled00/led.cpp b/wled00/led.cpp index 0e245c9d..70863396 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -140,6 +140,8 @@ void stateUpdated(byte callMode) { jsonTransitionOnce = false; strip.setTransition(transitionDelayTemp); if (transitionDelayTemp == 0) { + jsonTransitionOnce = false; + transitionActive = false; applyFinalBri(); strip.trigger(); return; From 8e84dba6447bc0c016bf012aa1e3dba3014bf743 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 Apr 2024 21:01:44 +0200 Subject: [PATCH 71/83] post-merge this one was almost lost --- wled00/FX_fcn.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 66ce1643..12f81497 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -482,6 +482,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t && (!grp || (grouping == grp && spacing == spc)) && (ofs == UINT16_MAX || ofs == offset)) return; + stateChanged = true; // send UDP/WS broadcast + if (stop>start) fill(BLACK); //turn old segment range off // WLEDMM stop > start if (i2 <= i1) { //disable segment stop = 0; From 1d1a53b70320bafdc14ffc4cf7ff6ab6143d0d6e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 Apr 2024 21:14:35 +0200 Subject: [PATCH 72/83] align with upstream platformio.ini adding 160Mhz 8266 builds NB: MoonModules 8266 build are always using 160Mhz, so nothing new for us ;-) --- platformio.ini | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/platformio.ini b/platformio.ini index 2bddd037..59eb9e59 100644 --- a/platformio.ini +++ b/platformio.ini @@ -452,6 +452,11 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED lib_deps = ${esp8266.lib_deps} monitor_filters = esp8266_exception_decoder +[env:nodemcuv2_160] +extends = env:nodemcuv2 +board_build.f_cpu = 160000000L +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D + [env:esp8266_2m] board = esp_wroom_02 platform = ${common.platform_wled_default} @@ -461,6 +466,11 @@ build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 lib_deps = ${esp8266.lib_deps} +[env:esp8266_2m_160] +extends = env:esp8266_2m +board_build.f_cpu = 160000000L +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02_160 + ;WLEDMM: see below ; [env:esp01_1m_full] ; board = esp01_1m @@ -471,6 +481,11 @@ lib_deps = ${esp8266.lib_deps} ; build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA ; ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM ; lib_deps = ${esp8266.lib_deps} +; [env:esp01_1m_full_160] +; extends = env:esp01_1m_full +; board_build.f_cpu = 160000000L +; build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA +; ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM [env:esp07] board = esp07 From 164c9a12cc4ba5473e2c57205229b683a636d2e0 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 Apr 2024 21:39:51 +0200 Subject: [PATCH 73/83] minor version upgrade to -b30.37 --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- package-lock.json | 4 ++-- package.json | 2 +- wled00/improv.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 60b00809..9f6250cb 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -48,7 +48,7 @@ body: attributes: label: What version/release of MM WLED? description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message" - placeholder: "e.g. build 2401290, WLEDMM_0.14.1-b30.36_esp32_4MB_M.bin" + placeholder: "e.g. build 2401290, WLEDMM_0.14.1-b30.37_esp32_4MB_M.bin" validations: required: true - type: dropdown diff --git a/package-lock.json b/package-lock.json index f8c04b00..eead5461 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wled", - "version": "0.14.1-b30.36", + "version": "0.14.1-b30.37", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "wled", - "version": "0.14.1-b30.36", + "version": "0.14.1-b30.37", "license": "GPL-3.0-or-later", "dependencies": { "clean-css": "^4.2.3", diff --git a/package.json b/package.json index 476577f1..920024ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.1-b30.36", + "version": "0.14.1-b30.37", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/wled00/improv.cpp b/wled00/improv.cpp index c84406e8..af30065d 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -210,7 +210,7 @@ void sendImprovInfoResponse() { //Use serverDescription if it has been changed from the default "WLED", else mDNS name bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); char vString[32]; - snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b30.36/%i"),VERSION); + snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b30.37/%i"),VERSION); const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); From b27686bd2b506eacfaca9c92b630d3cc68f7efe9 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:34:32 +0200 Subject: [PATCH 74/83] audioreactive small optimization * clear vImag[] using memset * zerocrossing detection: directly check sign bit --- usermods/audioreactive/audio_reactive.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 402ad29e..4a7b7a24 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -7,6 +7,7 @@ #include #include +#include #endif #if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG)) @@ -545,12 +546,13 @@ void FFTcode(void * parameter) } } + // set imaginary parts to 0 + memset(vImag, 0, sizeof(vImag)); + // find highest sample in the batch, and count zero crossings float maxSample = 0.0f; // max sample from FFT batch uint_fast16_t newZeroCrossingCount = 0; for (int i=0; i < samplesFFT; i++) { - // set imaginary parts to 0 - vImag[i] = 0; // pick our our current mic sample - we take the max value from all samples that go into FFT if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]); @@ -558,9 +560,8 @@ void FFTcode(void * parameter) // WLED-MM/TroyHacks: Calculate zero crossings // if (i < (samplesFFT-1)) { - if((vReal[i] >= 0 && vReal[i+1] < 0) || (vReal[i] < 0 && vReal[i+1] >= 0)) { + if (__builtin_signbit(vReal[i]) != __builtin_signbit(vReal[i+1])) // test sign bit: sign changed -> zero crossing newZeroCrossingCount++; - } } } newZeroCrossingCount = (newZeroCrossingCount*2)/3; // reduce value so it typicially stays below 256 From 70bfbd5a4334c3e96b6fdd973f732b469e65c1ec Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:42:21 +0200 Subject: [PATCH 75/83] ar_energy: fix a math error fixing mis-optimized math when calculating energy. energy = sum(amplitude^2). this is not the same as sum(amplitude)^2. Example: 1+5+7 = 13; 13 * 13 = 169 1*1 + 5*5 + 7*7 = 75 --- .../usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 83407e5f..56ed93c2 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -15,7 +15,7 @@ class AutoPlaylistUsermod : public Usermod { #if 0 // experimental parameters by softhack007 - more balanced but need testing const uint_fast32_t MAX_DISTANCE_TRACKER = 184; // maximum accepted distance_tracker - const uint_fast32_t ENERGY_SCALE = 24000; + const uint_fast32_t ENERGY_SCALE = 14000; const float FILTER_SLOW1 = 0.0075f; // for "slow" energy const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr const float FILTER_FAST1 = 0.2f; // for "fast" energy @@ -112,10 +112,10 @@ class AutoPlaylistUsermod : public Usermod { energy = 0; for (int i=0; i < NUM_GEQ_CHANNELS; i++) { - energy += fftResult[i]; + uint_fast32_t amplitude = fftResult[i]; + energy += amplitude * amplitude; } - energy *= energy; energy /= ENERGY_SCALE; // scale down so we get 0 sometimes uint8_t lfc = fftResult[0]; From 6e24850073da97e6c234b13708024083f37da72e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:56:23 +0200 Subject: [PATCH 76/83] ar_energy: improved energy reconstruction fftResult[] is indended to look good on GEQ, and has a very "twisted" representation of FFT results only. This change improves reconstruction of signal energy, by on-doing some of the "GEQ enhancements" in fftResults[]. --- .../usermod_v2_auto_playlist.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 56ed93c2..acbb3e6d 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -15,7 +15,7 @@ class AutoPlaylistUsermod : public Usermod { #if 0 // experimental parameters by softhack007 - more balanced but need testing const uint_fast32_t MAX_DISTANCE_TRACKER = 184; // maximum accepted distance_tracker - const uint_fast32_t ENERGY_SCALE = 14000; + const uint_fast32_t ENERGY_SCALE = 1500; const float FILTER_SLOW1 = 0.0075f; // for "slow" energy const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr const float FILTER_FAST1 = 0.2f; // for "fast" energy @@ -24,7 +24,8 @@ class AutoPlaylistUsermod : public Usermod { #else // parameters used by TroyHacks / netmindz - behaviour is mainly driven by "energy" const uint_fast32_t MAX_DISTANCE_TRACKER = 128; // maximum accepted distance_tracker - const uint_fast32_t ENERGY_SCALE = 10000; + //const uint_fast32_t ENERGY_SCALE = 10000; + const uint_fast32_t ENERGY_SCALE = 2000; // softhack007: original code used FILTER_SLOW = 0.002f const float FILTER_SLOW1 = 0.01f; // for "slow" energy const float FILTER_SLOW2 = 0.01f; // for "slow" lfc / zcr @@ -46,6 +47,9 @@ class AutoPlaylistUsermod : public Usermod { unsigned long autochange_timer = millis(); float avg_volumeSmth = 0; + // fftesult de-scaling factors: 2.8f / fftResultPink[] + const float fftDeScaler[NUM_GEQ_CHANNELS] = {2.8/2.35, 2.8/1.32, 2.8/1.32, 2.8/1.40, 2.8/1.48, 2.8/1.57, 2.8/1.68, 2.8/1.80, 2.8/1.89, 2.8/1.95, 2.8/2.14, 2.8/2.26, 2.8/2.50, 2.8/2.90, 2.8/4.20, 2.8/6.50}; + uint_fast32_t energy = 0; float avg_long_energy = 250; @@ -112,8 +116,15 @@ class AutoPlaylistUsermod : public Usermod { energy = 0; for (int i=0; i < NUM_GEQ_CHANNELS; i++) { +#if 1 + // make an attempt to undo some "trying to look better" FFT manglings in AudioReactive postProcessFFTResults() + float amplitude = float(fftResult[i]) * fftDeScaler[i]; // undo "pink noise" scaling + amplitude /= 0.85f + (float(i)/4.5f); // undo extra up-scaling for high frequencies + energy += roundf(amplitude * amplitude); // calc energy from amplitude +#else uint_fast32_t amplitude = fftResult[i]; energy += amplitude * amplitude; +#endif } energy /= ENERGY_SCALE; // scale down so we get 0 sometimes From af028b58908a609be1f94850dbdb1ed7dc88ebcd Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sun, 14 Apr 2024 14:39:15 -0400 Subject: [PATCH 77/83] Accepting some Softhack007 suggestions --- .../usermod_v2_auto_playlist.h | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index acbb3e6d..6655c749 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -12,7 +12,7 @@ class AutoPlaylistUsermod : public Usermod { private: - #if 0 + #if 1 // experimental parameters by softhack007 - more balanced but need testing const uint_fast32_t MAX_DISTANCE_TRACKER = 184; // maximum accepted distance_tracker const uint_fast32_t ENERGY_SCALE = 1500; @@ -27,11 +27,11 @@ class AutoPlaylistUsermod : public Usermod { //const uint_fast32_t ENERGY_SCALE = 10000; const uint_fast32_t ENERGY_SCALE = 2000; // softhack007: original code used FILTER_SLOW = 0.002f - const float FILTER_SLOW1 = 0.01f; // for "slow" energy - const float FILTER_SLOW2 = 0.01f; // for "slow" lfc / zcr - const float FILTER_FAST1 = 0.1f; // for "fast" energy - const float FILTER_FAST2 = 0.1f; // for "fast" lfc / zcr - const float FILTER_VOLUME = 0.01f; // for volumeSmth averaging - takes 12-15sec until "silence" + const float FILTER_SLOW1 = 0.01f; // for "slow" energy + const float FILTER_SLOW2 = 0.01f; // for "slow" lfc / zcr + const float FILTER_FAST1 = 0.1f; // for "fast" energy + const float FILTER_FAST2 = 0.1f; // for "fast" lfc / zcr + const float FILTER_VOLUME = 0.03f; // for volumeSmth averaging - takes 8-10sec until "silence" #endif bool initDone = false; @@ -67,7 +67,6 @@ class AutoPlaylistUsermod : public Usermod { uint_fast32_t distance = 0; uint_fast32_t distance_tracker = UINT_FAST32_MAX; - // uint_fast64_t squared_distance = 0; unsigned long lastchange = millis(); @@ -116,15 +115,13 @@ class AutoPlaylistUsermod : public Usermod { energy = 0; for (int i=0; i < NUM_GEQ_CHANNELS; i++) { -#if 1 + // make an attempt to undo some "trying to look better" FFT manglings in AudioReactive postProcessFFTResults() + float amplitude = float(fftResult[i]) * fftDeScaler[i]; // undo "pink noise" scaling amplitude /= 0.85f + (float(i)/4.5f); // undo extra up-scaling for high frequencies energy += roundf(amplitude * amplitude); // calc energy from amplitude -#else - uint_fast32_t amplitude = fftResult[i]; - energy += amplitude * amplitude; -#endif + } energy /= ENERGY_SCALE; // scale down so we get 0 sometimes @@ -136,6 +133,7 @@ class AutoPlaylistUsermod : public Usermod { // and the individual vector distances. if (volumeSmth > 1.0f) { + // initialize filters on first run if (resetFilters) { avg_short_energy = avg_long_energy = energy; @@ -143,7 +141,7 @@ class AutoPlaylistUsermod : public Usermod { avg_short_zcr = avg_long_zcr = zcr; resetFilters = false; #ifdef USERMOD_AUTO_PLAYLIST_DEBUG - USER_PRINTLN("autoplaylist: filters reset."); + USER_PRINTLN("AutoPlaylist: Filters reset."); #endif } @@ -162,10 +160,7 @@ class AutoPlaylistUsermod : public Usermod { } - // distance is linear, squared_distance is magnitude. - // linear is easier to fine-tune, IMHO. distance = vector_lfc + vector_energy + vector_zcr; - // squared_distance = distance * distance; long change_interval = millis()-lastchange; @@ -173,7 +168,7 @@ class AutoPlaylistUsermod : public Usermod { distance_tracker = distance; } - // USER_PRINTF("Distance: %3lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr); + // USER_PRINTF("Distance: %5lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr); if ((millis() - change_timer) > ideal_change_min) { // softhack007 same result as "millis() > change_timer + ideal_change_min", but more robust against unsigned overflow @@ -312,8 +307,6 @@ class AutoPlaylistUsermod : public Usermod { functionality_enabled = true; } - // if (!functionality_enabled) return; - if (bri == 0) return; um_data_t *um_data; From dfab0ca402eeceb379ccc0972187af40e2636f03 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sun, 14 Apr 2024 15:21:07 -0400 Subject: [PATCH 78/83] Accepting more Softhack007 suggestings --- .../usermod_v2_auto_playlist.h | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 6655c749..ffd057da 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -12,27 +12,14 @@ class AutoPlaylistUsermod : public Usermod { private: - #if 1 // experimental parameters by softhack007 - more balanced but need testing const uint_fast32_t MAX_DISTANCE_TRACKER = 184; // maximum accepted distance_tracker const uint_fast32_t ENERGY_SCALE = 1500; - const float FILTER_SLOW1 = 0.0075f; // for "slow" energy - const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr - const float FILTER_FAST1 = 0.2f; // for "fast" energy - const float FILTER_FAST2 = 0.25f; // for "fast" lfc / zcr + const float FILTER_SLOW1 = 0.0075f; // for "slow" energy - was 0.01f + const float FILTER_SLOW2 = 0.005f; // for "slow" lfc / zcr - was 0.01f + const float FILTER_FAST1 = 0.2f; // for "fast" energy - was 0.10f + const float FILTER_FAST2 = 0.25f; // for "fast" lfc / zcr - was 0.10f const float FILTER_VOLUME = 0.03f; // for volumeSmth averaging - takes 8-10sec until "silence" - #else - // parameters used by TroyHacks / netmindz - behaviour is mainly driven by "energy" - const uint_fast32_t MAX_DISTANCE_TRACKER = 128; // maximum accepted distance_tracker - //const uint_fast32_t ENERGY_SCALE = 10000; - const uint_fast32_t ENERGY_SCALE = 2000; - // softhack007: original code used FILTER_SLOW = 0.002f - const float FILTER_SLOW1 = 0.01f; // for "slow" energy - const float FILTER_SLOW2 = 0.01f; // for "slow" lfc / zcr - const float FILTER_FAST1 = 0.1f; // for "fast" energy - const float FILTER_FAST2 = 0.1f; // for "fast" lfc / zcr - const float FILTER_VOLUME = 0.03f; // for volumeSmth averaging - takes 8-10sec until "silence" - #endif bool initDone = false; bool functionality_enabled = false; @@ -126,7 +113,7 @@ class AutoPlaylistUsermod : public Usermod { energy /= ENERGY_SCALE; // scale down so we get 0 sometimes - uint8_t lfc = fftResult[0]; + uint8_t lfc = float(fftResult[0]) * fftDeScaler[0] / 0.85f; // might as well undo pink noise here too. uint16_t zcr = *(uint16_t*)um_data->u_data[11]; // WLED-MM/TroyHacks: Calculate the long- and short-running averages @@ -168,6 +155,7 @@ class AutoPlaylistUsermod : public Usermod { distance_tracker = distance; } + // Debug for adjusting formulas, etc: // USER_PRINTF("Distance: %5lu - v_lfc: %5lu v_energy: %5lu v_zcr: %5lu\n",(unsigned long)distance,(unsigned long)vector_lfc,(unsigned long)vector_energy,(unsigned long)vector_zcr); if ((millis() - change_timer) > ideal_change_min) { // softhack007 same result as "millis() > change_timer + ideal_change_min", but more robust against unsigned overflow From 11315a81b5c11a82c422015469c747357bd82547 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sun, 14 Apr 2024 15:32:13 -0400 Subject: [PATCH 79/83] unsigned wraparound prevention --- .../usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index ffd057da..afe5852f 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -167,7 +167,12 @@ class AutoPlaylistUsermod : public Usermod { if (change_interval > ideal_change_min && distance_tracker <= MAX_DISTANCE_TRACKER) { - change_threshold_change = distance_tracker-change_threshold; + if (distance_tracker >= change_threshold) { + change_threshold_change = distance_tracker-change_threshold; + } else { + change_threshold_change = change_threshold-distance_tracker; + } + change_threshold = distance_tracker; if (change_threshold_change > 9999) change_threshold_change = 0; // cosmetic for debug From acfbe890b316cd0242c76e4a3ce46089eed7cf1b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:25:21 +0200 Subject: [PATCH 80/83] suspend playlist engine while auto-change is active * adding suspendPlaylist() to playlist engine code * autoplaylist usermod calls suspendPlaylist() before switching to another preset * fix a potential overflow on `lfc` (uint8_t -> uint16_t) --- .../usermod_v2_auto_playlist.h | 5 ++--- wled00/fcn_declare.h | 1 + wled00/playlist.cpp | 12 ++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index afe5852f..4ac05c24 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -113,7 +113,7 @@ class AutoPlaylistUsermod : public Usermod { energy /= ENERGY_SCALE; // scale down so we get 0 sometimes - uint8_t lfc = float(fftResult[0]) * fftDeScaler[0] / 0.85f; // might as well undo pink noise here too. + uint16_t lfc = float(fftResult[0]) * fftDeScaler[0] / 0.85f; // might as well undo pink noise here too. uint16_t zcr = *(uint16_t*)um_data->u_data[11]; // WLED-MM/TroyHacks: Calculate the long- and short-running averages @@ -244,8 +244,7 @@ class AutoPlaylistUsermod : public Usermod { // go into freefall - this logic stops that from triggering right // after change_lockout. Better for smaller change_lockout values. - // SH7: this method is sub-optimal, as its interfering with the "playlist" engine - // we shoud find a better method for triggering playlist changes + suspendPlaylist(); // suspend the playlist engine before changing to another preset applyPreset(newpreset); #ifdef USERMOD_AUTO_PLAYLIST_DEBUG diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index dcf4019e..a818d3fa 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -208,6 +208,7 @@ void _overlayAnalogCountdown(); void _overlayAnalogClock(); //playlist.cpp +void suspendPlaylist(); // WLEDMM support function for auto playlist usermod void shufflePlaylist(); void unloadPlaylist(); int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0); diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index 8a6227e1..236cc324 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -41,6 +41,12 @@ void shufflePlaylist() { DEBUG_PRINTLN(F("Playlist shuffle.")); } +// WLEDMM supporting function for auto_playlist usermod +// prevents the active playlist from progressing (until it gets unloaded) +static bool playlistSuspended = false; +void suspendPlaylist() { + playlistSuspended = true; +} void unloadPlaylist() { if (playlistEntries != nullptr) { @@ -49,6 +55,7 @@ void unloadPlaylist() { } currentPlaylist = playlistIndex = -1; playlistLen = playlistEntryDur = playlistOptions = 0; + playlistSuspended = false; // WLEDMM DEBUG_PRINTLN(F("Playlist unloaded.")); } @@ -125,6 +132,11 @@ void handlePlaylist() { // if fileDoc is not null JSON buffer is in use so just quit if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return; + if (playlistSuspended) { // WLEDMM + if (millis() - presetCycledTime > (100*playlistEntryDur)) presetCycledTime = millis(); // keep updating timer + return; // but don't progress to next extry, and don't shuffle + } + if (millis() - presetCycledTime > (100*playlistEntryDur)) { presetCycledTime = millis(); if (bri == 0 || nightlightActive) return; From d42148d9944df471855d4da06d2c64aeea296b22 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:57:31 +0200 Subject: [PATCH 81/83] Info page optimizations * reduce from 3 lines to two lines * don't add to info when usermod is disabled in UM settings * show current playlist id --- .../usermod_v2_auto_playlist.h | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 4ac05c24..c15f116e 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -346,16 +346,26 @@ class AutoPlaylistUsermod : public Usermod { user = root.createNestedObject("u"); } - JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name + if (!enabled) return; // usermod disabled -> don't add to info page - String uiDomString = F(""); - + String uiNameString = FPSTR(_name); if (enabled && functionality_enabled) { - uiDomString += F("AutoPlaylist is Running"); + uiNameString += F(" Running"); } else if (!enabled) { - uiDomString += F("AutoPlaylist is Disabled"); + uiNameString += F(" Disabled"); } else { - uiDomString += F("AutoPlaylist is Suspended"); + uiNameString += F(" Suspended"); + } + JsonArray infoArr = user.createNestedArray(uiNameString); // name + status + + String uiDomString = (currentPlaylist > 0) ? String("#") + String(currentPlaylist) + String(" ") : String(""); + + if (currentPlaylist == musicPlaylist && currentPlaylist > 0) { + uiDomString += F("Music Playlist"); + } else if (currentPlaylist == ambientPlaylist && currentPlaylist > 0) { + uiDomString += F("Ambient Playlist"); + } else { + uiDomString += F("Playlist Overridden"); } uiDomString += F("
"); @@ -367,16 +377,6 @@ class AutoPlaylistUsermod : public Usermod { } else if (!autoChange) { uiDomString += F("AutoChange is Disabled"); } - - uiDomString += F("
"); - - if (currentPlaylist == musicPlaylist && currentPlaylist > 0) { - uiDomString += F("Playlist: Music Playlist"); - } else if (currentPlaylist == ambientPlaylist && currentPlaylist > 0) { - uiDomString += F("Playlist: Ambient Playlist"); - } else { - uiDomString += F("Playlist: Overridden"); - } // #ifdef USERMOD_AUTO_PLAYLIST_DEBUG // uiDomString += F("
"); From 019cafc5e073072305a5926d4d94555ed9914f48 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:10:46 +0200 Subject: [PATCH 82/83] report out-of-memory situations to the user --- wled00/FX_2Dfcn.cpp | 6 +++++- wled00/FX_fcn.cpp | 30 ++++++++++++++++++++++++------ wled00/const.h | 4 +++- wled00/data/index.js | 10 ++++++++-- wled00/wled.h | 2 +- wled00/ws.cpp | 1 + 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 999684b1..aac0ac76 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -80,7 +80,10 @@ void WS2812FX::setUpMatrix() { if ((size > 0) && (customMappingTable == nullptr)) { // second try DEBUG_PRINTLN("setUpMatrix: trying to get fresh memory block."); customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t)); - if (customMappingTable == nullptr) USER_PRINTLN("setUpMatrix: alloc failed"); + if (customMappingTable == nullptr) { + USER_PRINTLN("setUpMatrix: alloc failed"); + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + } } if (customMappingTable != nullptr) customMappingTableSize = size; } @@ -159,6 +162,7 @@ void WS2812FX::setUpMatrix() { } else { // memory allocation error customMappingTableSize = 0; USER_PRINTLN(F("Ledmap alloc error.")); + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag isMatrix = false; //WLEDMM does not like this done in teh background while end users are confused whats happened... panels = 0; panel.clear(); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 12f81497..b9f02ac8 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -128,10 +128,13 @@ void Segment::allocLeds() { if (ledsrgb) free(ledsrgb); // we need a bigger buffer, so free the old one first ledsrgb = (CRGB*)calloc(size, 1); ledsrgbSize = ledsrgb?size:0; - if (ledsrgb == nullptr) USER_PRINTLN("allocLeds failed!!"); + if (ledsrgb == nullptr) { + USER_PRINTLN("allocLeds failed!!"); + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + } } else { - USER_PRINTF("reuse Leds %u from %u\n", size, ledsrgb?ledsrgbSize:0); + //USER_PRINTF("reuse Leds %u from %u\n", size, ledsrgb?ledsrgbSize:0); } } @@ -212,7 +215,11 @@ bool Segment::allocateData(size_t len) { //DEBUG_PRINTF("allocateData(%u) start %d, stop %d, vlen %d\n", len, start, stop, virtualLength()); deallocateData(); if (len == 0) return false; // nothing to do - if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory + if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { + //USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA); + if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag + return false; //not enough memory + } // do not use SPI RAM on ESP32 since it is slow //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) //if (psramFound()) @@ -220,10 +227,17 @@ bool Segment::allocateData(size_t len) { //else //#endif data = (byte*) malloc(len); - if (!data) { _dataLen = 0; return false;} //allocation failed // WLEDMM reset dataLen + if (!data) { + _dataLen = 0; // WLEDMM reset dataLen + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + USER_PRINT(F("Segment::allocateData: FAILED to allocate ")); + USER_PRINT(len); USER_PRINTLN(F(" bytes.")); + return false; + } //allocation failed Segment::addUsedSegmentData(len); _dataLen = len; memset(data, 0, len); + if (errorFlag == ERR_LOW_SEG_MEM) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success return true; } @@ -231,7 +245,7 @@ void Segment::deallocateData() { if (!data) {_dataLen = 0; return;} // WLEDMM reset dataLen free(data); data = nullptr; - //DEBUG_PRINTLN("deallocateData() called free()."); + //USER_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen); Segment::addUsedSegmentData(-_dataLen); _dataLen = 0; } @@ -1656,6 +1670,7 @@ void WS2812FX::finalizeInit(void) //#endif if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0) if ((Segment::_globalLeds != nullptr) && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); // WLEDMM avoid dereferencing nullptr + if ((Segment::_globalLeds == nullptr) && (arrSize > 0)) errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag } //segments are created in makeAutoSegments(); @@ -2403,7 +2418,10 @@ bool WS2812FX::deserializeMap(uint8_t n) { if ((size > 0) && (customMappingTable == nullptr)) { // second try DEBUG_PRINTLN("deserializeMap: trying to get fresh memory block."); customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t)); - if (customMappingTable == nullptr) DEBUG_PRINTLN("deserializeMap: alloc failed!"); + if (customMappingTable == nullptr) { + DEBUG_PRINTLN("deserializeMap: alloc failed!"); + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + } } if (customMappingTable != nullptr) customMappingTableSize = size; } diff --git a/wled00/const.h b/wled00/const.h index fa3ded65..2e155754 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -347,7 +347,9 @@ #define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) #define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented) -#define ERR_LOW_MEM 33 // low memory (RAM) +#define ERR_LOW_MEM 33 // WLEDMM: low memory (RAM) +#define ERR_LOW_SEG_MEM 34 // WLEDMM: low memory (segment data RAM) +#define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws) // Timer mode types #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness diff --git a/wled00/data/index.js b/wled00/data/index.js index 3ea998e3..2a4b51ba 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -1968,8 +1968,14 @@ function readState(s,command=false) errstr = "A filesystem error has occured."; break; case 33: - errstr = "Warning: Low Memory (RAM)."; - break; + errstr = "Low Memory (generic RAM)."; + break; + case 34: + errstr = "Low Memory (effect data)."; + break; + case 35: + errstr = "Low Memory (WS data)."; + break; } showToast('Error ' + s.error + ": " + errstr, true); } diff --git a/wled00/wled.h b/wled00/wled.h index 546a1c90..23321384 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2404091 +#define VERSION 2404161 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ diff --git a/wled00/ws.cpp b/wled00/ws.cpp index be5e0687..351caad4 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -162,6 +162,7 @@ void sendDataWs(AsyncWebSocketClient * client) ws.closeAll(1013); //code 1013 = temporary overload, try again later ws.cleanupClients(0); //disconnect all clients to release memory ws._cleanBuffers(); + errorFlag = ERR_LOW_WS_MEM; return; //out of memory } From 3fd5e190c499a5765a9660d042ec233d82398c5c Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:07:51 +0200 Subject: [PATCH 83/83] reduce memory needs of popcorn effect On a matrix with 52 columns, popcorn was requesting around 30Kb of segment data. This patch reduces the data to the actually necessary amount based on the "intensity" slider. If intensity is increased, it means that the effect will get a bigger chunk of data allocated - zero'd out but this does not hurt much. --- wled00/FX.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c4b1e0e9..5fa0a0fe 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3194,7 +3194,13 @@ static uint16_t mode_popcorn_core(bool useaudio) { if (SEGLEN == 1) return mode_static(); //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); - uint16_t dataSize = sizeof(spark) * maxNumPopcorn; + size_t dataSize = sizeof(spark) * maxNumPopcorn; + uint8_t neededPopcorn = maxNumPopcorn; // WLEDMM + if (strips > 8) { // WLEDMM more than 8 virtual strips --> reduce memory requirements to minimum necessary + neededPopcorn = (SEGMENT.intensity*maxNumPopcorn)/255; + neededPopcorn = min(max(neededPopcorn, uint8_t(2)), uint8_t(maxNumPopcorn)); + dataSize = sizeof(spark) * neededPopcorn; + } if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed Spark* popcorn = reinterpret_cast(SEGENV.data); @@ -3212,7 +3218,7 @@ static uint16_t mode_popcorn_core(bool useaudio) { struct virtualStrip { static void runStrip(uint16_t stripNr, Spark* popcorn, bool useaudio, um_data_t *um_data) { // WLEDMM added useaudio and um_data - float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s + float gravity = -0.0001f - (SEGMENT.speed/200000.0f); // m/s/s gravity *= SEGLEN; uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; @@ -3267,7 +3273,7 @@ static uint16_t mode_popcorn_core(bool useaudio) { }; for (int stripNr=0; stripNr