-
-
Notifications
You must be signed in to change notification settings - Fork 12
FastLED audio #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
FastLED audio #130
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,208 @@ | ||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| @title MoonLight | ||||||||||||||||||||||||||
| @file D_FastLEDAudio.h | ||||||||||||||||||||||||||
| @repo https://github.com/MoonModules/MoonLight, submit changes to this file as PRs | ||||||||||||||||||||||||||
| @Authors https://github.com/MoonModules/MoonLight/commits/main | ||||||||||||||||||||||||||
| @Doc https://moonmodules.org/MoonLight/moonlight/overview/ | ||||||||||||||||||||||||||
| @Copyright © 2026 Github MoonLight Commit Authors | ||||||||||||||||||||||||||
| @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 | ||||||||||||||||||||||||||
| @license For non GPL-v3 usage, commercial licenses must be purchased. Contact us for more information. | ||||||||||||||||||||||||||
| **/ | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #pragma once | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #if FT_MOONLIGHT | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #include "fl/audio.h" | ||||||||||||||||||||||||||
| #include "fl/audio/audio_processor.h" | ||||||||||||||||||||||||||
| #include "fl/audio_input.h" | ||||||||||||||||||||||||||
| #include "fl/time_alpha.h" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // https://github.com/FastLED/FastLED/blob/master/src/fl/audio/README.md | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| class FastLEDAudioDriver : public Node { | ||||||||||||||||||||||||||
| private: | ||||||||||||||||||||||||||
| // Member variables for audio configuration | ||||||||||||||||||||||||||
| fl::AudioConfigI2S* i2sConfig = nullptr; | ||||||||||||||||||||||||||
| fl::AudioConfig* config = nullptr; | ||||||||||||||||||||||||||
| fl::shared_ptr<fl::IAudioInput> audioInput; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public: | ||||||||||||||||||||||||||
| static const char* name() { return "FastLED Audio"; } | ||||||||||||||||||||||||||
| static uint8_t dim() { return _NoD; } | ||||||||||||||||||||||||||
| static const char* tags() { return "☸️"; } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| fl::AudioProcessor audioProcessor; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| bool signalConditioning = true; | ||||||||||||||||||||||||||
| bool autoGain = false; | ||||||||||||||||||||||||||
| bool noiseFloorTracking = false; | ||||||||||||||||||||||||||
| uint8_t channel = fl::Left; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void setup() override { | ||||||||||||||||||||||||||
| addControl(signalConditioning, "signalConditioning", "checkbox"); | ||||||||||||||||||||||||||
| addControl(autoGain, "autoGain", "checkbox"); | ||||||||||||||||||||||||||
| addControl(noiseFloorTracking, "noiseFloorTracking", "checkbox"); | ||||||||||||||||||||||||||
| addControl(channel, "channel", "select"); | ||||||||||||||||||||||||||
| addControlValue("Left"); | ||||||||||||||||||||||||||
| addControlValue("Right"); | ||||||||||||||||||||||||||
| addControlValue("Both"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| moduleIO->addUpdateHandler([this](const String& originId) { readPins(); }, false); | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check handler API definitions and usage patterns
rg -n "addUpdateHandler\|removeUpdateHandler" --type=h --type=cpp -A 3 -B 1 | head -100Repository: MoonModules/MoonLight Length of output: 47 🏁 Script executed: #!/bin/bash
# Find and examine the D_FastLEDAudio class definition and destructor
fd "D_FastLEDAudio" --type f | xargs cat -n | head -200Repository: MoonModules/MoonLight Length of output: 7523 🏁 Script executed: #!/bin/bash
# Search for all destructor implementations in driver files to see the pattern
rg -n "~D_\w+\(\)" --type=h --type=cpp -A 5 | head -150Repository: MoonModules/MoonLight Length of output: 47 🏁 Script executed: #!/bin/bash
# Get full line count of the file and check if it continues beyond line 200
wc -l src/MoonLight/Nodes/Drivers/D_FastLEDAudio.hRepository: MoonModules/MoonLight Length of output: 113 🏁 Script executed: #!/bin/bash
# Get lines 200 onwards to see if destructor exists
tail -100 src/MoonLight/Nodes/Drivers/D_FastLEDAudio.hRepository: MoonModules/MoonLight Length of output: 2729 🏁 Script executed: #!/bin/bash
# Find the Node base class definition
fd "Node\.h" --type f -x cat -n {} \; | head -200Repository: MoonModules/MoonLight Length of output: 47 🏁 Script executed: #!/bin/bash
# Search for moduleIO type and handler methods across the codebase
rg "moduleIO.*addUpdateHandler\|class.*Node.*{" --type=h --type=cpp -B 2 -A 2 | head -150Repository: MoonModules/MoonLight Length of output: 212 🏁 Script executed: #!/bin/bash
# Find Node class definition
fd "Node" --type f | grep -E "\.(h|cpp)$" | head -20Repository: MoonModules/MoonLight Length of output: 135 🏁 Script executed: #!/bin/bash
# Search for Node class definition directly
rg "class Node" --type=h -A 10 | head -100Repository: MoonModules/MoonLight Length of output: 2220 🏁 Script executed: #!/bin/bash
# Search for addUpdateHandler definition or declaration
rg "addUpdateHandler" --type=h --type=cpp -B 3 -A 5 | head -200Repository: MoonModules/MoonLight Length of output: 14747 🏁 Script executed: #!/bin/bash
# Search for removeUpdateHandler or similar removal mechanism
rg "removeUpdateHandler\|remove.*Handler\|erase.*Handler" --type=h --type=cpp -B 2 -A 3 | head -150Repository: MoonModules/MoonLight Length of output: 47 🏁 Script executed: #!/bin/bash
# Look at StatefulService to understand handler lifecycle
cat -n lib/framework/StatefulService.h | head -150Repository: MoonModules/MoonLight Length of output: 5876 🏁 Script executed: #!/bin/bash
# Search for Node destruction and module cleanup
rg "~Node|destroy.*node|NodeManager.*destroy" --type=h --type=cpp -A 5 | head -150Repository: MoonModules/MoonLight Length of output: 589 🏁 Script executed: #!/bin/bash
# Check if there's cleanup logic for handlers when modules are destroyed
rg "callUpdateHandlers\|_updateHandlers" --type=h --type=cpp -B 2 -A 5 | head -200Repository: MoonModules/MoonLight Length of output: 47 🏁 Script executed: #!/bin/bash
# Check destructor patterns in other driver files
rg "~D_\w+\(\)" --type=h -A 10 | head -250Repository: MoonModules/MoonLight Length of output: 47 🏁 Script executed: #!/bin/bash
# Look for Module base class and its lifecycle
fd "Module\.h" --type f -x head -100 {} \;Repository: MoonModules/MoonLight Length of output: 3437 Prevent use-after-free when The Either store the handler ID and call 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| readPins(); // Node added at runtime so initial IO update not received so run explicitly | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onBeat([]() { | ||||||||||||||||||||||||||
| sharedData.beat = true; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onBeat"); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onVocalStart([]() { | ||||||||||||||||||||||||||
| sharedData.vocalsActive = true; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onVocalStart"); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onVocalEnd([]() { | ||||||||||||||||||||||||||
| sharedData.vocalsActive = false; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onVocalEnd"); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onVocalConfidence([](float confidence) { | ||||||||||||||||||||||||||
| sharedData.vocalConfidence = sharedData.vocalsActive ? confidence : 0.0; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onVocalConfidence %d", confidence); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onBass([](float level) { | ||||||||||||||||||||||||||
| if (level > 0.01f) { | ||||||||||||||||||||||||||
| sharedData.bassLevel = level; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onBass: %f", level); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onMid([](float level) { | ||||||||||||||||||||||||||
| if (level > 0.01f) { | ||||||||||||||||||||||||||
| sharedData.midLevel = level; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onBass: %f", level); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
Comment on lines
+81
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: copy-paste log label in Line 84 reads 🔧 Proposed fix audioProcessor.onMid([](float level) {
if (level > 0.01f) {
sharedData.midLevel = level;
- // EXT_LOGD(ML_TAG, "onBass: %f", level);
+ // EXT_LOGD(ML_TAG, "onMid: %f", level);
}
}); audioProcessor.onVocalConfidence([](float confidence) {
sharedData.vocalConfidence = sharedData.vocalsActive ? confidence : 0.0;
- // EXT_LOGD(ML_TAG, "onVocalConfidence %d", confidence);
+ // EXT_LOGD(ML_TAG, "onVocalConfidence %f", confidence);
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| audioProcessor.onTreble([](float level) { | ||||||||||||||||||||||||||
| if (level > 0.01f) { | ||||||||||||||||||||||||||
| sharedData.trebleLevel = level; | ||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "onTreble: %f", level); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| audioProcessor.onPercussion([](fl::PercussionType type) { | ||||||||||||||||||||||||||
| EXT_LOGD(ML_TAG, "onPercussion: %d", type); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void onUpdate(const Char<20>& oldValue, const JsonObject& control) override { | ||||||||||||||||||||||||||
| if (control["name"] == "signalConditioning") { | ||||||||||||||||||||||||||
| audioProcessor.setSignalConditioningEnabled(signalConditioning); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (control["name"] == "autoGain") { | ||||||||||||||||||||||||||
| audioProcessor.setAutoGainEnabled(autoGain); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (control["name"] == "noiseFloorTracking") { | ||||||||||||||||||||||||||
| audioProcessor.setNoiseFloorTrackingEnabled(noiseFloorTracking); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (control["name"] == "channel" && oldValue != "") { // not on boot as readPins will do it then | ||||||||||||||||||||||||||
| // recreate with the new channel | ||||||||||||||||||||||||||
| stopAudio(); | ||||||||||||||||||||||||||
| startAudio(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| uint8_t pinI2SSD = UINT8_MAX; | ||||||||||||||||||||||||||
| uint8_t pinI2SWS = UINT8_MAX; | ||||||||||||||||||||||||||
| uint8_t pinI2SSCK = UINT8_MAX; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void readPins() { | ||||||||||||||||||||||||||
| if (safeModeMB) { | ||||||||||||||||||||||||||
| EXT_LOGW(ML_TAG, "Safe mode enabled, not adding pins"); | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| moduleIO->read( | ||||||||||||||||||||||||||
| [&](ModuleState& state) { | ||||||||||||||||||||||||||
| bool i2sPinsChanged = false; | ||||||||||||||||||||||||||
| for (JsonObject pinObject : state.data["pins"].as<JsonArray>()) { | ||||||||||||||||||||||||||
| uint8_t usage = pinObject["usage"]; | ||||||||||||||||||||||||||
| switch (usage) { | ||||||||||||||||||||||||||
| case pin_I2S_SD: | ||||||||||||||||||||||||||
| if (pinI2SSD != pinObject["GPIO"]) { | ||||||||||||||||||||||||||
| pinI2SSD = pinObject["GPIO"]; | ||||||||||||||||||||||||||
| i2sPinsChanged = true; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| case pin_I2S_WS: | ||||||||||||||||||||||||||
| if (pinI2SWS != pinObject["GPIO"]) { | ||||||||||||||||||||||||||
| pinI2SWS = pinObject["GPIO"]; | ||||||||||||||||||||||||||
| i2sPinsChanged = true; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| case pin_I2S_SCK: | ||||||||||||||||||||||||||
| if (pinI2SSCK != pinObject["GPIO"]) { | ||||||||||||||||||||||||||
| pinI2SSCK = pinObject["GPIO"]; | ||||||||||||||||||||||||||
| i2sPinsChanged = true; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (i2sPinsChanged && pinI2SSD != UINT8_MAX && pinI2SWS != UINT8_MAX && pinI2SSCK != UINT8_MAX) { | ||||||||||||||||||||||||||
| EXT_LOGI(ML_TAG, "(re)creating audioInput)"); | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: stray closing parenthesis in log string.
🔧 Proposed fix- EXT_LOGI(ML_TAG, "(re)creating audioInput)");
+ EXT_LOGI(ML_TAG, "(re)creating audioInput");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| stopAudio(); | ||||||||||||||||||||||||||
| startAudio(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| name()); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void loop20ms() override { | ||||||||||||||||||||||||||
| if (!audioInput) return; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| sharedData.beat = false; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while (fl::AudioSample sample = audioInput->read()) { | ||||||||||||||||||||||||||
| audioProcessor.update(sample); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void startAudio() { | ||||||||||||||||||||||||||
| // Create configuration objects | ||||||||||||||||||||||||||
| i2sConfig = new fl::AudioConfigI2S(pinI2SWS, pinI2SSD, pinI2SSCK, 0, channel == 1 ? fl::Right : channel == 2 ? fl::Both : fl::Left, 44100, 16, fl::Philips); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| config = new fl::AudioConfig(*i2sConfig); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| fl::string errorMsg; | ||||||||||||||||||||||||||
| audioInput = fl::IAudioInput::create(*config, &errorMsg); | ||||||||||||||||||||||||||
| if (!audioInput) { | ||||||||||||||||||||||||||
| EXT_LOGE(ML_TAG, "Failed to create audio input: %s", errorMsg.c_str()); | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| audioInput->start(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void stopAudio() { | ||||||||||||||||||||||||||
| if (audioInput) { | ||||||||||||||||||||||||||
| audioInput->stop(); | ||||||||||||||||||||||||||
| audioInput.reset(); // Explicitly release shared_ptr, even makes it a nullptr... | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Clean up raw pointers | ||||||||||||||||||||||||||
| if (config) { | ||||||||||||||||||||||||||
| delete config; | ||||||||||||||||||||||||||
| config = nullptr; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (i2sConfig) { | ||||||||||||||||||||||||||
| delete i2sConfig; | ||||||||||||||||||||||||||
| i2sConfig = nullptr; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ~FastLEDAudioDriver() override { stopAudio(); } | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,4 +37,50 @@ class RainbowEffect : public Node { | |||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| class FLAudioEffect : public Node { | ||||||||||||||||||||||||||||||||||||||||||
| public: | ||||||||||||||||||||||||||||||||||||||||||
| static const char* name() { return "FLAudio"; } | ||||||||||||||||||||||||||||||||||||||||||
| static uint8_t dim() { return _2D; } | ||||||||||||||||||||||||||||||||||||||||||
| static const char* tags() { return "⚡️🎵"; } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| void setup() {} | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| uint16_t hue = 0; | ||||||||||||||||||||||||||||||||||||||||||
| uint8_t beatLevel = 0; | ||||||||||||||||||||||||||||||||||||||||||
| float maxBass = 0; | ||||||||||||||||||||||||||||||||||||||||||
| float maxMid = 0; | ||||||||||||||||||||||||||||||||||||||||||
| float maxTreble = 0; | ||||||||||||||||||||||||||||||||||||||||||
| float maxVocal = 0; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| void loop() override { | ||||||||||||||||||||||||||||||||||||||||||
| layer->fadeToBlackBy(70); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // calculate max levels | ||||||||||||||||||||||||||||||||||||||||||
| if (sharedData.bassLevel > maxBass) maxBass = sharedData.bassLevel; | ||||||||||||||||||||||||||||||||||||||||||
| if (sharedData.midLevel > maxMid) maxMid = sharedData.midLevel; | ||||||||||||||||||||||||||||||||||||||||||
| if (sharedData.trebleLevel > maxTreble) maxTreble = sharedData.trebleLevel; | ||||||||||||||||||||||||||||||||||||||||||
| if (sharedData.vocalConfidence > maxVocal) maxVocal = sharedData.vocalConfidence; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if (sharedData.beat) beatLevel = 255; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // EXT_LOGD(ML_TAG, "%f-%d %f-%d %d-%d %f-%d", sharedData.bassLevel, bassLevel, sharedData.trebleLevel, trebleLevel, sharedData.beat, beatLevel, sharedData.vocalsActive ? sharedData.vocalConfidence : 0, vocalsLevel); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| uint8_t columnNr = 0; | ||||||||||||||||||||||||||||||||||||||||||
| layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.bassLevel / maxBass, CRGB::Red); | ||||||||||||||||||||||||||||||||||||||||||
| layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.midLevel / maxMid, CRGB::Orange); | ||||||||||||||||||||||||||||||||||||||||||
| layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.trebleLevel / maxTreble, CRGB::Green); | ||||||||||||||||||||||||||||||||||||||||||
| layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.vocalConfidence / maxVocal, CRGB::Blue); | ||||||||||||||||||||||||||||||||||||||||||
| layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * beatLevel / 255, CRGB::Purple); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+68
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Each Separate the increment from the call: 🐛 Proposed fix- uint8_t columnNr = 0;
- layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.bassLevel / maxBass, CRGB::Red);
- layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.midLevel / maxMid, CRGB::Orange);
- layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.trebleLevel / maxTreble, CRGB::Green);
- layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * sharedData.vocalConfidence / maxVocal, CRGB::Blue);
- layer->drawLine(columnNr, layer->size.y - 1, columnNr++, layer->size.y - 1 - layer->size.y * beatLevel / 255, CRGB::Purple);
+ uint8_t col = 0;
+ if (maxBass > 0.0f)
+ layer->drawLine(col, layer->size.y - 1, col, layer->size.y - 1 - layer->size.y * sharedData.bassLevel / maxBass, CRGB::Red);
+ ++col;
+ if (maxMid > 0.0f)
+ layer->drawLine(col, layer->size.y - 1, col, layer->size.y - 1 - layer->size.y * sharedData.midLevel / maxMid, CRGB::Orange);
+ ++col;
+ if (maxTreble > 0.0f)
+ layer->drawLine(col, layer->size.y - 1, col, layer->size.y - 1 - layer->size.y * sharedData.trebleLevel / maxTreble, CRGB::Green);
+ ++col;
+ if (maxVocal > 0.0f)
+ layer->drawLine(col, layer->size.y - 1, col, layer->size.y - 1 - layer->size.y * sharedData.vocalConfidence / maxVocal, CRGB::Blue);
+ ++col;
+ layer->drawLine(col, layer->size.y - 1, col, layer->size.y - 1 - layer->size.y * beatLevel / 255, CRGB::Purple);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // correct for lower output | ||||||||||||||||||||||||||||||||||||||||||
| if (maxBass > 0) maxBass -= 0.01; | ||||||||||||||||||||||||||||||||||||||||||
| if (maxMid > 0) maxMid -= 0.01; | ||||||||||||||||||||||||||||||||||||||||||
| if (maxTreble > 0) maxTreble -= 0.01; | ||||||||||||||||||||||||||||||||||||||||||
| if (maxVocal > 0) maxVocal -= 0.01; | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Division by zero when max levels are zero, and decay undershoots zero. Two related issues:
🐛 Proposed fix- if (sharedData.bassLevel > maxBass) maxBass = sharedData.bassLevel;
- if (sharedData.midLevel > maxMid) maxMid = sharedData.midLevel;
- if (sharedData.trebleLevel > maxTreble) maxTreble = sharedData.trebleLevel;
- if (sharedData.vocalConfidence > maxVocal) maxVocal = sharedData.vocalConfidence;
+ if (sharedData.bassLevel > maxBass) maxBass = sharedData.bassLevel;
+ if (sharedData.midLevel > maxMid) maxMid = sharedData.midLevel;
+ if (sharedData.trebleLevel > maxTreble) maxTreble = sharedData.trebleLevel;
+ if (sharedData.vocalConfidence > maxVocal) maxVocal = sharedData.vocalConfidence;
...
- if (maxBass > 0) maxBass -= 0.01;
- if (maxMid > 0) maxMid -= 0.01;
- if (maxTreble > 0) maxTreble -= 0.01;
- if (maxVocal > 0) maxVocal -= 0.01;
+ maxBass = fmaxf(0.0f, maxBass - 0.01f);
+ maxMid = fmaxf(0.0f, maxMid - 0.01f);
+ maxTreble = fmaxf(0.0f, maxTreble - 0.01f);
+ maxVocal = fmaxf(0.0f, maxVocal - 0.01f);And guard the + if (maxBass > 0.0f)
layer->drawLine(columnNr, layer->size.y - 1, columnNr, layer->size.y - 1 - layer->size.y * sharedData.bassLevel / maxBass, CRGB::Red);
+ columnNr++;(apply the same pattern to mid, treble, vocal) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // beat delay | ||||||||||||||||||||||||||||||||||||||||||
| if (beatLevel) beatLevel -= MIN(255/layer->size.y, beatLevel); | ||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: integer division by zero if
🛡️ Proposed fix- if (beatLevel) beatLevel -= MIN(255/layer->size.y, beatLevel);
+ if (beatLevel && layer->size.y > 0)
+ beatLevel -= MIN(255 / layer->size.y, beatLevel);🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 3265
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 92
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 92
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 811
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 2738
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 181
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 2966
🏁 Script executed:
Repository: MoonModules/MoonLight
Length of output: 3009
Add
-D FASTLED_RMT5=0to force legacy RMT4 driver and prevent ISR handler degradation.The FASTLED_BUILD string aligns correctly with the pinned commit. However, platform 55.03.37 uses ESP-IDF 5.5+ with Arduino core 3.x, which causes FastLED to default to the RMT5 driver. RMT5 has documented ISR handler lifecycle issues (see FastLED issue
#1903) causing repeated "Handler detached" messages and severe performance drops (~2 FPS). Add-D FASTLED_RMT5=0to the [moonlight] build_flags to force the stable legacy RMT4 driver.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not true on newest FastLED
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
💡 Result:
I couldn’t find any FastLED change/issue (through the public repo + recent searches) specifically tied to a “handler detached” message for the ESP32 RMT5 driver in February 2026—so there doesn’t appear to be a known “Feb 2026” targeted fix labeled that way. (If you have the exact log line + platform/core version, I can search more precisely.)
What is documented/fixed recently around ESP32 RMT5 stability:
-D FASTLED_RMT5=0(typically requires older ESP32 Arduino core / IDF4-era setups; also note RMT4 and RMT5 can’t coexist). [3]Most actionable next step: confirm whether your “handler detached” happens only with DMA enabled. If yes, try updating FastLED (>= 3.10.1) first. [1] If it persists, try
FASTLED_RMT5=0(RMT4) or switch to a non-RMT driver as above. [3]Sources:
[1] FastLED releases (3.10.1 notes: DMA mode accidentally set true, breaking RMT5) https://github.com/fastled/fastled/releases
[2] FastLED issue
#2082(RMT5 + Wi‑Fi flicker; buffering/priority limits/knobs discussion) FastLED/FastLED#2082[3] FastLED repository documentation (RMT4 vs RMT5 selection;
FASTLED_RMT5=0; alternative drivers) https://github.com/FastLED/FastLED@ewowi, thank you for the correction! I appreciate you letting me know that the RMT5 "Handler detached" issues have been resolved in the newest FastLED version.Since you originally provided the learning about RMT5 issues (learning ID
9bb17d59-f1a1-4505-b40f-39a68c64be29) and you're now confirming it's no longer applicable with the latest FastLED, would you like me to remove that outdated learning from the knowledge base? This will prevent it from being referenced in future reviews.✏️ Learnings added
🧠 Learnings used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes remove it please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ewowi, I've removed the outdated learning about the FastLED RMT5 driver issues. The knowledge base now reflects the current state: that RMT5 issues have been resolved in FastLED as of commit 27c99130c83ab666c4c6dbf389425fec5f27db05 (master 20260222), and the-D FASTLED_RMT5=0workaround is no longer needed. Thank you for keeping the information up to date!🐰💭
(Learning tidied up!)
/) /)
( •.• )
/>📝✨
⛔ Learnings removed
🧠 Learnings used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabling RMT5 should not be necessary now? The RMT5 driver now allocates more memory during WIFI active mode. Please let me know if this isn't the case. RMT5 will have stress testing either this week if ai credits sustain. Otherwise post friday on refresh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.