"); // no processing - add empty line
// current Audio input
infoArr = user.createNestedArray(F("Audio Source"));
@@ -1262,7 +1284,7 @@ class AudioReactive : public Usermod {
else
infoArr.add(" - idle");
} else {
- infoArr.add(" - no network");
+ infoArr.add(" - no connection");
}
} else {
// Analog or I2S digital input
@@ -1319,6 +1341,8 @@ class AudioReactive : public Usermod {
}
} else
infoArr.add("off");
+ if (audioSyncEnabled && !udpSyncConnected) infoArr.add(" (unconnected)");
+ //if (!udpSyncConnected) infoArr.add(" (unconnected)");
#ifdef WLED_DEBUG
infoArr = user.createNestedArray(F("Sampling time"));
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index b5a9ab79..2ba78c89 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -7093,6 +7093,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
for (int x=0; x < cols; x++) {
uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1);
+ band = constrain(band, 0, 15);
uint16_t colorIndex = band * 17;
uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here
if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up
@@ -7153,8 +7154,8 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
// display values of
int b = 0;
for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) {
- int hue = fftResult[band];
- int v = map(fftResult[band], 0, 255, 10, 255);
+ int hue = fftResult[band % 16];
+ int v = map(fftResult[band % 16], 0, 255, 10, 255);
for (int w = 0; w < barWidth; w++) {
int xpos = (barWidth * b) + w;
SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v));
@@ -7269,6 +7270,7 @@ uint16_t mode_2DAkemi(void) {
if (um_data && fftResult) {
for (int x=0; x < cols/8; x++) {
uint16_t band = x * cols/8;
+ band = constrain(band, 0, 15);
uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
From f7652bd2ef561a84fb83a2a7e9f5ffc696dedec0 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Fri, 19 Aug 2022 15:17:04 +0200
Subject: [PATCH 04/13] Fix audio sync check
---
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 c7c7e5f8..54314700 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -911,7 +911,7 @@ class AudioReactive : public Usermod {
*/
void connected()
{
- if (audioSyncPort > 0 || (audioSyncEnabled & 0x03)) {
+ if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) {
#ifndef ESP8266
udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort);
#else
From b8db47e528677b545f7b9800aa0f5cf95463809a Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 19 Aug 2022 16:11:50 +0200
Subject: [PATCH 05/13] AR: new freq scaling option "square root"
also looks nice. It's a compromise between log() and linear. OK enough tinkering for today :-)
---
usermods/audioreactive/audio_reactive.h | 30 ++++++++++++++++++-------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index b2b66b08..6607d7c5 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -325,27 +325,40 @@ void FFTcode(void * parameter)
else
currentResult = fftCalc[i];
- if (FFTScalingMode > 0) {
- if (FFTScalingMode == 1) {
+ switch (FFTScalingMode) {
+ case 1:
// Logarithmic scaling
currentResult *= 0.42; // 42 is the answer ;-)
currentResult -= 8.0; // this skips the lowest row, giving some room for peaks
if (currentResult > 1.0)
currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
else currentResult = 0.0; // special handling, because log(1) = 0; log(0) = undefined
-
currentResult *= 0.85f + (float(i)/18.0f); // extra up-scaling for high frequencies
currentResult = mapf(currentResult, 0, LOG_256, 0, 255); // map [log(1) ... log(255)] to [0 ... 255]
-
- } else {
+ break;
+ case 2:
// Linear scaling
currentResult *= 0.30f; // needs a bit more damping, get stay below 255
currentResult -= 4.0; // giving a bit more room for peaks
if (currentResult < 1.0f) currentResult = 0.0f;
currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies
- }
- } else {
- currentResult -= 4; // just a bit more room for peaks
+ break;
+ case 3:
+ // square root scaling
+ currentResult *= 0.38f;
+ currentResult -= 6.0f;
+ if (currentResult > 1.0)
+ currentResult = sqrtf(currentResult);
+ else currentResult = 0.0; // special handling, because sqrt(0) = undefined
+ currentResult *= 0.85f + (float(i)/5.0f); // extra up-scaling for high frequencies
+ currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255]
+ break;
+
+ case 0:
+ default:
+ // no scaling - leave freq bins as-is
+ currentResult -= 4; // just a bit more room for peaks
+ break;
}
// Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely.
@@ -1538,6 +1551,7 @@ class AudioReactive : public Usermod {
oappend(SET_F("dd=addDropdown('AudioReactive','Frequency:Scale');"));
oappend(SET_F("addOption(dd,'None',0);"));
oappend(SET_F("addOption(dd,'Linear (Amplitude)',2);"));
+ oappend(SET_F("addOption(dd,'Square Root (Energy)',3);"));
oappend(SET_F("addOption(dd,'Logarithmic (Loudness)',1);"));
oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');"));
From 44a4b11d369862dbf26e693999dfe89c30a398a3 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Fri, 19 Aug 2022 21:14:49 +0200
Subject: [PATCH 06/13] Replace setOption/getOption
---
wled00/FX.cpp | 6 ++--
wled00/FX.h | 50 +++++++++++++++++--------------
wled00/FX_fcn.cpp | 4 +--
wled00/button.cpp | 4 +--
wled00/json.cpp | 68 +++++++++++++++++++++---------------------
wled00/set.cpp | 18 +++++------
wled00/udp.cpp | 6 ++--
wled00/wled_eeprom.cpp | 10 +++----
8 files changed, 85 insertions(+), 81 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index dd693bb1..91e1da53 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -75,7 +75,7 @@ int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
*/
uint16_t mode_static(void) {
SEGMENT.fill(SEGCOLOR(0));
- return /*(SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME :*/ 350; //update faster if in transition
+ return 350;
}
static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid";
@@ -3986,7 +3986,7 @@ uint16_t mode_flow(void)
{
uint8_t colorIndex = (i * 255 / zoneLen) - counter;
uint16_t led = (z & 0x01) ? i : (zoneLen -1) -i;
- if (SEGMENT.getOption(SEG_OPTION_REVERSED)) led = (zoneLen -1) -led;
+ if (SEGMENT.reverse) led = (zoneLen -1) -led;
SEGMENT.setPixelColor(pos + led, SEGMENT.color_from_palette(colorIndex, false, true, 255));
}
}
@@ -4927,7 +4927,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
SEGENV.aux1 = SEGENV.aux0;
SEGENV.aux0 = crc;
- return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : FRAMETIME_FIXED * (128-(SEGMENT.speed>>1)); // update only when appropriate time passes (in 42 FPS slots)
+ return FRAMETIME_FIXED * (128-(SEGMENT.speed>>1)); // update only when appropriate time passes (in 42 FPS slots)
} // mode_2Dgameoflife()
static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,;!,!;!;2d";
diff --git a/wled00/FX.h b/wled00/FX.h
index 486a07e1..486c9af4 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -344,7 +344,7 @@ typedef enum mapping1D2D {
M12_Block = 3
} mapping1D2D_t;
-// segment, 68 (92 in memory) bytes
+// segment, 72 bytes
typedef struct Segment {
public:
uint16_t start; // start index / start X coordinate 2D (left)
@@ -357,34 +357,38 @@ typedef struct Segment {
union {
uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
struct {
- bool selected : 1; // 0 : selected
- bool reverse : 1; // 1 : reversed
- bool on : 1; // 2 : is On
- bool mirror : 1; // 3 : mirrored
- bool pxs : 1; // 4 : indicates that the effect does not use FRAMETIME or needs getPixelColor (?)
- bool freeze : 1; // 5 : paused/frozen
- bool reset : 1; // 6 : indicates that Segment runtime requires reset
- bool transitional: 1; // 7 : transitional (there is transition occuring)
- bool reverse_y : 1; // 8 : reversed Y (2D)
- bool mirror_y : 1; // 9 : mirrored Y (2D)
- bool transpose : 1; // 10 : transposed (2D, swapped X & Y)
- uint8_t map1D2D : 2; // 11-12 : mapping for 1D effect on 2D (0-strip, 1-expand vertically, 2-circular, 3-rectangular)
- uint8_t soundSim : 3; // 13-15 : 0-7 sound simulation types
+ bool selected : 1; // 0 : selected
+ bool reverse : 1; // 1 : reversed
+ bool on : 1; // 2 : is On
+ bool mirror : 1; // 3 : mirrored
+ bool pxs : 1; // 4 : indicates that the effect does not use FRAMETIME or needs getPixelColor (?)
+ bool freeze : 1; // 5 : paused/frozen
+ bool reset : 1; // 6 : indicates that Segment runtime requires reset
+ bool transitional: 1; // 7 : transitional (there is transition occuring)
+ bool reverse_y : 1; // 8 : reversed Y (2D)
+ bool mirror_y : 1; // 9 : mirrored Y (2D)
+ bool transpose : 1; // 10 : transposed (2D, swapped X & Y)
+ uint8_t map1D2D : 3; // 11-13 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
+ uint8_t soundSim : 2; // 14-15 : 0-3 sound simulation types
};
};
uint8_t grouping, spacing;
+ //struct {
+ // uint8_t grouping : 4; // maximum 15 pixels in a group
+ // uint8_t spacing : 4; // maximum 15 pixels per gap
+ //};
uint8_t opacity;
uint32_t colors[NUM_COLORS];
- uint8_t cct; //0==1900K, 255==10091K
- uint8_t custom1, custom2; // custom FX parameters/sliders
+ uint8_t cct; //0==1900K, 255==10091K
+ uint8_t custom1, custom2; // custom FX parameters/sliders
struct {
- uint8_t custom3 : 5; // reduced range slider (0-31)
- bool check1 : 1; // checkmark 1
- bool check2 : 1; // checkmark 2
- bool check3 : 1; // checkmark 3
+ uint8_t custom3 : 5; // reduced range slider (0-31)
+ bool check1 : 1; // checkmark 1
+ bool check2 : 1; // checkmark 2
+ bool check3 : 1; // checkmark 3
};
- uint16_t startY; // start Y coodrinate 2D (top)
- uint16_t stopY; // stop Y coordinate 2D (bottom)
+ uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows
+ uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows
char *name;
// runtime data
@@ -627,7 +631,7 @@ typedef struct Segment {
void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
#endif
} segment;
-//static int i = sizeof(Segment);
+//static int segSize = sizeof(Segment);
// main "strip" class
class WS2812FX { // 96 bytes
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 13fe77dc..dedff0ef 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1106,7 +1106,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
_brightness = b;
if (_brightness == 0) { //unfreeze all segments on power off
for (segment &seg : _segments) {
- seg.setOption(SEG_OPTION_FREEZE, false);
+ seg.freeze = false;
}
}
if (direct) {
@@ -1319,7 +1319,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
_segments.clear();
for (size_t i = 0; i < s; i++) {
Segment seg = Segment(segStarts[i], segStops[i]);
- seg.setOption(SEG_OPTION_SELECTED, true);
+ seg.selected = true;
_segments.push_back(seg);
}
_mainSegment = 0;
diff --git a/wled00/button.cpp b/wled00/button.cpp
index b472f927..81e3c8c3 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -197,10 +197,10 @@ void handleAnalog(uint8_t b)
// otherwise use "double press" for segment selection
Segment& seg = strip.getSegment(macroDoublePress[b]);
if (aRead == 0) {
- seg.setOption(SEG_OPTION_ON, 0); // off
+ seg.on = false; // off
} else {
seg.setOpacity(aRead);
- seg.setOption(SEG_OPTION_ON, 1);
+ seg.on = true;
}
// this will notify clients of update (websockets,mqtt,etc)
updateInterfaces(CALL_MODE_BUTTON);
diff --git a/wled00/json.cpp b/wled00/json.cpp
index e3ed8a66..a2f654df 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -84,8 +84,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
- seg.map1D2D = map1D2D & 0x03;
- seg.soundSim = soundSim & 0x07;
+ seg.map1D2D = map1D2D & 0x07;
+ seg.soundSim = soundSim & 0x03;
uint16_t len = 1;
if (stop > start) len = stop - start;
@@ -102,15 +102,15 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte segbri = seg.opacity;
if (getVal(elem["bri"], &segbri)) {
if (segbri > 0) seg.setOpacity(segbri);
- seg.setOption(SEG_OPTION_ON, segbri);
+ seg.on = segbri;
}
- bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
+ bool on = elem["on"] | seg.on;
if (elem["on"].is() && elem["on"].as()[0] == 't') on = !on;
- seg.setOption(SEG_OPTION_ON, on);
- bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
- if (elem["frz"].is() && elem["frz"].as()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE);
- seg.setOption(SEG_OPTION_FREEZE, frz);
+ seg.on = on;
+ bool frz = elem["frz"] | seg.freeze;
+ if (elem["frz"].is() && elem["frz"].as()[0] == 't') frz = !seg.freeze;
+ seg.freeze = frz;
seg.setCCT(elem["cct"] | seg.cct);
@@ -162,13 +162,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
}
#endif
- seg.setOption(SEG_OPTION_SELECTED, elem["sel"] | seg.getOption(SEG_OPTION_SELECTED));
- seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
- seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
+ seg.selected = elem["sel"] | seg.selected;
+ seg.reverse = elem["rev"] | seg.reverse;
+ seg.mirror = elem[F("mi")] | seg.mirror;
#ifndef WLED_DISABLE_2D
- seg.setOption(SEG_OPTION_REVERSED_Y, elem[F("rY")] | seg.getOption(SEG_OPTION_REVERSED_Y));
- seg.setOption(SEG_OPTION_MIRROR_Y , elem[F("mY")] | seg.getOption(SEG_OPTION_MIRROR_Y ));
- seg.setOption(SEG_OPTION_TRANSPOSED, elem[F("tp")] | seg.getOption(SEG_OPTION_TRANSPOSED));
+ seg.reverse_y = elem[F("rY")] | seg.reverse_y;
+ seg.mirror_y = elem[F("mY")] | seg.mirror_y;
+ seg.transpose = elem[F("tp")] | seg.transpose;
#endif
byte fx = seg.mode;
@@ -193,8 +193,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
sOpt = extractModeDefaults(fx, SET_F("c1")); if (sOpt >= 0) seg.custom1 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c2")); if (sOpt >= 0) seg.custom2 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c3")); if (sOpt >= 0) seg.custom3 = sOpt;
- sOpt = extractModeDefaults(fx, SET_F("mp12")); if (sOpt >= 0) seg.map1D2D = sOpt & 0x03;
- sOpt = extractModeDefaults(fx, SET_F("ssim")); if (sOpt >= 0) seg.soundSim = sOpt & 0x07;
+ sOpt = extractModeDefaults(fx, SET_F("mp12")); if (sOpt >= 0) seg.map1D2D = sOpt & 0x07;
+ sOpt = extractModeDefaults(fx, SET_F("ssim")); if (sOpt >= 0) seg.soundSim = sOpt & 0x03;
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) seg.reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, SET_F("mi")); if (sOpt >= 0) seg.mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, SET_F("rY")); if (sOpt >= 0) seg.reverse_y = (bool)sOpt;
@@ -238,8 +238,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
strip.setBrightness(scaledBri(bri), true);
// freeze and init to black
- if (!seg.getOption(SEG_OPTION_FREEZE)) {
- seg.setOption(SEG_OPTION_FREEZE, true);
+ if (!seg.freeze) {
+ seg.freeze = true;
seg.fill(BLACK);
}
@@ -285,7 +285,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
}
// send UDP if not in preset and something changed that is not just selection
// send UDP if something changed that is not just selection or segment power/opacity
- if ((seg.differs(prev) & 0x7E) && seg.getOption(SEG_OPTION_ON)==prev.getOption(SEG_OPTION_ON)) stateChanged = true;
+ if ((seg.differs(prev) & 0x7E) && seg.on == prev.on) stateChanged = true;
}
// deserializes WLED state (fileDoc points to doc object if called from web server)
@@ -315,10 +315,10 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (bri && !onBefore) { // unfreeze all segments when turning on
for (size_t s=0; s < strip.getSegmentsNum(); s++) {
- strip.getSegment(s).setOption(SEG_OPTION_FREEZE, false);
+ strip.getSegment(s).freeze = false;
}
if (realtimeMode && !realtimeOverride && useMainSegmentOnly) { // keep live segment frozen if live
- strip.getMainSegment().setOption(SEG_OPTION_FREEZE, true);
+ strip.getMainSegment().freeze = true;
}
}
@@ -371,7 +371,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
realtimeOverride = root[F("lor")] | realtimeOverride;
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) {
- strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride);
+ strip.getMainSegment().freeze = !realtimeOverride;
}
if (root.containsKey("live")) {
@@ -472,14 +472,14 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
}
}
if (!forPreset) root["len"] = seg.stop - seg.start;
- root["grp"] = seg.grouping;
+ root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing;
- root[F("of")] = seg.offset;
- root["on"] = seg.getOption(SEG_OPTION_ON);
- root["frz"] = seg.getOption(SEG_OPTION_FREEZE);
- byte segbri = seg.opacity;
- root["bri"] = (segbri) ? segbri : 255;
- root["cct"] = seg.cct;
+ root[F("of")] = seg.offset;
+ root["on"] = seg.on;
+ root["frz"] = seg.freeze;
+ byte segbri = seg.opacity;
+ root["bri"] = (segbri) ? segbri : 255;
+ root["cct"] = seg.cct;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast(seg.name); //not good practice, but decreases required JSON buffer
@@ -509,12 +509,12 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
root[F("c2")] = seg.custom2;
root[F("c3")] = seg.custom3;
root[F("sel")] = seg.isSelected();
- root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
- root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
+ root["rev"] = seg.reverse;
+ root[F("mi")] = seg.mirror;
if (strip.isMatrix) {
- root[F("rY")] = seg.getOption(SEG_OPTION_REVERSED_Y);
- root[F("mY")] = seg.getOption(SEG_OPTION_MIRROR_Y);
- root[F("tp")] = seg.getOption(SEG_OPTION_TRANSPOSED);
+ root[F("rY")] = seg.reverse_y;
+ root[F("mY")] = seg.mirror_y;
+ root[F("tp")] = seg.transpose;
}
root[F("o1")] = seg.check1;
root[F("o2")] = seg.check2;
diff --git a/wled00/set.cpp b/wled00/set.cpp
index 7dd3d05f..417fe768 100644
--- a/wled00/set.cpp
+++ b/wled00/set.cpp
@@ -665,8 +665,8 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) {
byte t = getNumVal(&req, pos);
- if (t == 2) for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
- selseg.setOption(SEG_OPTION_SELECTED, t);
+ if (t == 2) for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) strip.getSegment(i).selected = false; // unselect other segments
+ selseg.selected = t;
}
// temporary values, write directly to segments, globals are updated by setValuesFromFirstSelectedSeg()
@@ -705,15 +705,15 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse
- if (pos > 0) selseg.setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0');
+ if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';
pos = req.indexOf(F("MI=")); //Segment mirror
- if (pos > 0) selseg.setOption(SEG_OPTION_MIRROR, req.charAt(pos+3) != '0');
+ if (pos > 0) selseg.mirror = req.charAt(pos+3) != '0';
pos = req.indexOf(F("SB=")); //Segment brightness/opacity
if (pos > 0) {
byte segbri = getNumVal(&req, pos);
- selseg.setOption(SEG_OPTION_ON, segbri);
+ selseg.on = segbri;
if (segbri) {
selseg.setOpacity(segbri);
}
@@ -722,9 +722,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("SW=")); //segment power
if (pos > 0) {
switch (getNumVal(&req, pos)) {
- case 0: selseg.setOption(SEG_OPTION_ON, false); break;
- case 1: selseg.setOption(SEG_OPTION_ON, true); break;
- default: selseg.setOption(SEG_OPTION_ON, !selseg.getOption(SEG_OPTION_ON)); break;
+ case 0: selseg.on = false; break;
+ case 1: selseg.on = true; break;
+ default: selseg.on = !selseg.on; break;
}
}
@@ -981,7 +981,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
realtimeOverride = getNumVal(&req, pos);
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) {
- strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride);
+ strip.getMainSegment().freeze = !realtimeOverride;
}
}
diff --git a/wled00/udp.cpp b/wled00/udp.cpp
index aeef791c..9fe03e3a 100644
--- a/wled00/udp.cpp
+++ b/wled00/udp.cpp
@@ -149,7 +149,7 @@ void realtimeLock(uint32_t timeoutMs, byte md)
Segment& mainseg = strip.getMainSegment();
start = mainseg.start;
stop = mainseg.stop;
- mainseg.setOption(SEG_OPTION_FREEZE, true);
+ mainseg.freeze = true;
} else {
start = 0;
stop = strip.getLengthTotal();
@@ -159,7 +159,7 @@ void realtimeLock(uint32_t timeoutMs, byte md)
// if WLED was off and using main segment only, freeze non-main segments so they stay off
if (useMainSegmentOnly && bri == 0) {
for (size_t s=0; s < strip.getSegmentsNum(); s++) {
- strip.getSegment(s).setOption(SEG_OPTION_FREEZE, true);
+ strip.getSegment(s).freeze = true;
}
}
}
@@ -186,7 +186,7 @@ void exitRealtime() {
realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately
realtimeIP[0] = 0;
if (useMainSegmentOnly) { // unfreeze live segment again
- strip.getMainSegment().setOption(SEG_OPTION_FREEZE, false);
+ strip.getMainSegment().freeze = false;
}
updateInterfaces(CALL_MODE_WS_SEND);
}
diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp
index 1fec4e8a..0f2194be 100644
--- a/wled00/wled_eeprom.cpp
+++ b/wled00/wled_eeprom.cpp
@@ -414,10 +414,10 @@ void deEEP() {
for (byte j = 0; j < numChannels; j++) colX.add(EEPROM.read(memloc + j));
}
- segObj["fx"] = EEPROM.read(i+10);
- segObj[F("sx")] = EEPROM.read(i+11);
- segObj[F("ix")] = EEPROM.read(i+16);
- segObj["pal"] = EEPROM.read(i+17);
+ segObj["fx"] = EEPROM.read(i+10);
+ segObj[F("sx")] = EEPROM.read(i+11);
+ segObj[F("ix")] = EEPROM.read(i+16);
+ segObj["pal"] = EEPROM.read(i+17);
} else {
Segment* seg = strip.getSegments();
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
@@ -425,7 +425,7 @@ void deEEP() {
for (byte j = 0; j < strip.getMaxSegments(); j++)
{
strip.getSegment(j).opacity = 255;
- strip.getSegment(j).setOption(SEG_OPTION_ON, 1);
+ strip.getSegment(j).on = true;
}
}
serializeState(pObj, true, false, true);
From 5927332a5f90fa9705d8ca2295a79e3a53ce1800 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Fri, 19 Aug 2022 21:25:44 +0200
Subject: [PATCH 07/13] UI enhancement in PWM Fan usermod.
---
usermods/PWM_fan/usermod_PWM_fan.h | 41 +++++++++++++++++++++---------
1 file changed, 29 insertions(+), 12 deletions(-)
diff --git a/usermods/PWM_fan/usermod_PWM_fan.h b/usermods/PWM_fan/usermod_PWM_fan.h
index 6dfe12fa..fec2e726 100644
--- a/usermods/PWM_fan/usermod_PWM_fan.h
+++ b/usermods/PWM_fan/usermod_PWM_fan.h
@@ -103,6 +103,7 @@ class PWMFanUsermod : public Usermod {
// https://randomnerdtutorials.com/esp32-pwm-arduino-ide/
void initPWMfan(void) {
if (pwmPin < 0 || !pinManager.allocatePin(pwmPin, true, PinOwner::UM_Unspecified)) {
+ enabled = false;
pwmPin = -1;
return;
}
@@ -218,11 +219,23 @@ class PWMFanUsermod : 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");
+
+ JsonArray infoArr = user.createNestedArray(FPSTR(_name));
+ String uiDomString = F("");
+ infoArr.add(uiDomString);
+
if (enabled) {
- JsonObject user = root["u"];
- if (user.isNull()) user = root.createNestedObject("u");
-// if (!tempUM) {
- JsonArray infoArr = user.createNestedArray(F("Fan speed [%]"));
+ JsonArray infoArr = user.createNestedArray(F("Manual"));
String uiDomString = F("
"); //
infoArr.add(uiDomString);
-// }
+
+ JsonArray data = user.createNestedArray(F("Speed"));
if (tachoPin >= 0) {
- JsonArray data = user.createNestedArray(FPSTR(_name));
data.add(last_rpm);
data.add(F("rpm"));
} else {
- JsonArray data = user.createNestedArray(FPSTR(_name));
if (lockFan) data.add(F("locked"));
else data.add(F("auto"));
}
@@ -256,14 +268,19 @@ class PWMFanUsermod : public Usermod {
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject& root) {
- if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
+ if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
- if (!usermod[FPSTR(_speed)].isNull() && usermod[FPSTR(_speed)].is()) {
- int pwmValuePct = usermod[FPSTR(_speed)].as();
- updateFanSpeed((MAX(0,MIN(100,pwmValuePct)) * 255) / 100);
+ if (usermod[FPSTR(_enabled)].is()) {
+ enabled = usermod[FPSTR(_enabled)].as();
+ if (!enabled) updateFanSpeed(0);
}
- if (!usermod[FPSTR(_lock)].isNull() && usermod[FPSTR(_lock)].is()) {
+ if (enabled && !usermod[FPSTR(_speed)].isNull() && usermod[FPSTR(_speed)].is()) {
+ pwmValuePct = usermod[FPSTR(_speed)].as();
+ updateFanSpeed((constrain(pwmValuePct,0,100) * 255) / 100);
+ if (pwmValuePct) lockFan = true;
+ }
+ if (enabled && !usermod[FPSTR(_lock)].isNull() && usermod[FPSTR(_lock)].is()) {
lockFan = usermod[FPSTR(_lock)].as();
}
}
From bbc80498323bf350f8e571c4372742b848c44ef8 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 20 Aug 2022 22:14:54 +0200
Subject: [PATCH 08/13] The Right Thing to Do (makes GEQ look awesome)
... found that stupid commit messages get more attention ;-)
- use 22050 Hz for sampling, as it is a standard frequency. I think this is the best choise.
- redesigned the GEQ channels (fftResult[]) for 22Khz, based on channels found on old HiFi equalizer equipment. 1Kzh is now at the center; Bass/Trebble channels are using 1/4 on left/right side respectively - similar to real equalizers. Looks nice :-)
- adjusted effects that use FFT_MajorPeak so that the maximum frequency is supported.
---
usermods/audioreactive/audio_reactive.h | 41 ++++++++++++++++++++-----
wled00/FX.cpp | 31 ++++++++++++-------
2 files changed, 53 insertions(+), 19 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 6607d7c5..93a3ca25 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -47,11 +47,12 @@
constexpr i2s_port_t I2S_PORT = I2S_NUM_0;
constexpr int BLOCK_SIZE = 128;
-//constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms
-constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms
+constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms
+//constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms
//constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz - standard. Physical sample time -> 50ms
-#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Must be less than time needed to read 512 samples at SAMPLE_RATE -> not the same as I2S time!!
+//#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Use with 20Khz sampling
+#define FFT_MIN_CYCLE 18 // minimum time before FFT task is repeated. Use with 22Khz sampling
// globals
static uint8_t inputLevel = 128; // UI slider value
@@ -65,7 +66,7 @@ static bool limiterOn = true; // bool: enable / disable dynamics
static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec
static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec
// user settable options for FFTResult scaling
-static uint8_t FFTScalingMode = 2; // 0 none; 1 optimized logarithmic; 2 optimized linear
+static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized sqare root
//
// AGC presets
@@ -121,7 +122,10 @@ static float vReal[samplesFFT] = {0.0f};
static float vImag[samplesFFT] = {0.0f};
static float fftBin[samplesFFT_2] = {0.0f};
-#define FFT_DOWNSCALE 0.65f // downscaling factor for FFT results - "Flat-Top" window
+// the following are observed values, supported by a bit of "educated guessing"
+//#define FFT_DOWNSCALE 0.65f // 20kHz - downscaling factor for FFT results - "Flat-Top" window @20Khz, old freq channels
+
+#define FFT_DOWNSCALE 0.46f // downscaling factor for FFT results - for "Flat-Top" window @22Khz, new freq channels
#define LOG_256 5.54517744
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
@@ -269,7 +273,7 @@ void FFTcode(void * parameter)
* Multiplier = 1.320367784
*/
if (sampleAvg > 1) { // noise gate open
- // Range
+#if 0 // Range
fftCalc[ 0] = fftAddAvg(2,4); // 60 - 100
fftCalc[ 1] = fftAddAvg(4,5); // 80 - 120
fftCalc[ 2] = fftAddAvg(5,7); // 100 - 160
@@ -286,6 +290,26 @@ void FFTcode(void * parameter)
fftCalc[13] = fftAddAvg(111,147); // 2220 - 2960
fftCalc[14] = fftAddAvg(147,194); // 2940 - 3900
fftCalc[15] = fftAddAvg(194,250); // 3880 - 5000 // avoid the last 5 bins, which are usually inaccurate
+#else
+ // optimized for 22050 Hz by softhack007 bins frequency range
+ fftCalc[ 0] = fftAddAvg(1,2); // 1 43 - 86 sub-bass
+ fftCalc[ 1] = fftAddAvg(2,3); // 1 86 - 129 bass
+ fftCalc[ 2] = fftAddAvg(3,5); // 2 129 - 216 bass
+ fftCalc[ 3] = fftAddAvg(5,7); // 2 216 - 301 bass + midrange
+ fftCalc[ 4] = fftAddAvg(7,10); // 3 301 - 430 midrange
+ fftCalc[ 5] = fftAddAvg(10,13); // 3 430 - 560 midrange
+ fftCalc[ 6] = fftAddAvg(13,19); // 5 560 - 818 midrange
+ fftCalc[ 7] = fftAddAvg(19,26); // 7 818 - 1120 midrange -- 1Khz should always be the center !
+ fftCalc[ 8] = fftAddAvg(26,33); // 7 1120 - 1421 midrange
+ fftCalc[ 9] = fftAddAvg(33,44); // 9 1421 - 1895 midrange
+ fftCalc[10] = fftAddAvg(44,56); // 12 1895 - 2412 midrange + high mid
+ fftCalc[11] = fftAddAvg(56,70); // 14 2412 - 3015 high mid
+ fftCalc[12] = fftAddAvg(70,86); // 16 3015 - 3704 high mid
+ fftCalc[13] = fftAddAvg(86,104); // 18 3704 - 4479 high mid
+ fftCalc[14] = fftAddAvg(104,165) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping
+ fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping
+ // don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise)
+#endif
} else { // noise gate closed
for (int i=0; i < 16; i++) {
@@ -350,7 +374,7 @@ void FFTcode(void * parameter)
if (currentResult > 1.0)
currentResult = sqrtf(currentResult);
else currentResult = 0.0; // special handling, because sqrt(0) = undefined
- currentResult *= 0.85f + (float(i)/5.0f); // extra up-scaling for high frequencies
+ currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies
currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255]
break;
@@ -872,7 +896,8 @@ class AudioReactive : public Usermod {
my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f);
FFT_Magnitude = my_magnitude;
- FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 1.0f);
+ FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11050.0f); // restrict value to range expected by effects
+
//DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
haveFreshData = true;
}
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 2ba78c89..aee00737 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -5938,7 +5938,7 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;
uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment
bool samplePeak = false;
- float FFT_MajorPeak = 0.0;
+ float FFT_MajorPeak = 1.0;
uint8_t *fftResult = nullptr;
float *fftBin = nullptr;
um_data_t *um_data;
@@ -5958,6 +5958,15 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;
}
*/
+// a few constants needed for AudioReactive effects
+// for 22Khz sampling
+#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion)
+#define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY)
+
+// for 20Khz sampling
+//#define MAX_FREQUENCY 10240 // sample frequency / 2 (as per Nyquist criterion)
+//#define MAX_FREQ_LOG10 4.0103f // log10(MAX_FREQUENCY)
+
/////////////////////////////////
// * Ripple Peak //
@@ -6724,12 +6733,12 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
SEGMENT.fade_out(SEGMENT.speed);
// int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(3.71f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
- int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(4.0102f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
+ int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
if (locn < 1) locn = 0; // avoid underflow
if (locn >=SEGLEN) locn = SEGLEN-1;
//uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(3.71f-1.78f); // Scale log10 of frequency values to the 255 colour index.
- uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(4.0102f-1.78f); // Scale log10 of frequency values to the 255 colour index.
+ uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
uint16_t bright = (int)my_magnitude;
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright));
@@ -6769,7 +6778,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
CRGB color = CRGB::Black;
//if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0;
- if (FFT_MajorPeak > 10240) FFT_MajorPeak = 0;
+ if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1;
// MajorPeak holds the freq. value which is most abundant in the last sample.
// With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz
// we will treat everything with less than 65Hz as 0
@@ -6777,8 +6786,8 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
if (FFT_MajorPeak < 80) {
color = CRGB::Black;
} else {
- int upperLimit = 20 * SEGMENT.custom2;
- int lowerLimit = 2 * SEGMENT.custom1;
+ int upperLimit = 80 + 42 * SEGMENT.custom2;
+ int lowerLimit = 80 + 3 * SEGMENT.custom1;
int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak;
uint16_t b = 255 * intensity;
if (b > 255) b = 255;
@@ -6818,7 +6827,7 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
uint16_t locn = random16(0,SEGLEN);
//uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78) * 255.0/(3.71-1.78); // Scale log10 of frequency values to the 255 colour index.
- uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(4.0102f-1.78f); // Scale log10 of frequency values to the 255 colour index.
+ uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
}
@@ -6869,7 +6878,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
CRGB color = 0;
//if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0.0f;
- if (FFT_MajorPeak > 10240) FFT_MajorPeak = 0.0f;
+ if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1.0f;
// MajorPeak holds the freq. value which is most abundant in the last sample.
// With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz
// we will treat everything with less than 65Hz as 0
@@ -6877,8 +6886,8 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
if (FFT_MajorPeak < 80) {
color = CRGB::Black;
} else {
- int upperLimit = 20 * SEGMENT.custom2;
- int lowerLimit = 2 * SEGMENT.custom1;
+ int upperLimit = 80 + 42 * SEGMENT.custom2;
+ int lowerLimit = 80 + 3 * SEGMENT.custom1;
int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak;
uint16_t b = 255.0 * intensity;
if (b > 255) b=255;
@@ -6927,7 +6936,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
for (int i=0; i
Date: Sun, 21 Aug 2022 09:51:15 +0200
Subject: [PATCH 09/13] Minor cleanup & fix for connected().
---
usermods/audioreactive/audio_reactive.h | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 93a3ca25..7ba25f3b 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -185,16 +185,12 @@ void FFTcode(void * parameter)
delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy.
// taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work.
+ vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
// Only run the FFT computing code if we're not in Receive mode and not in realtime mode
if (disableSoundProcessing || (audioSyncEnabled & 0x02)) {
- //delay(7); // release CPU - delay is implemeted using vTaskDelay(). cannot use yield() because we are out of arduino loop context
- vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, by doing nothing for FFT_MIN_CYCLE millis
continue;
}
- vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
- //vTaskDelayUntil( &xLastWakeTime, xFrequency_2); // release CPU, and let I2S fill its buffers
-
#ifdef WLED_DEBUG
uint64_t start = esp_timer_get_time();
#endif
@@ -401,10 +397,9 @@ void FFTcode(void * parameter)
}
#endif
- //vTaskDelayUntil( &xLastWakeTime, xFrequency_2); // release CPU, by waiting until FFT_MIN_CYCLE is over
// release second sample to volume reactive effects.
// Releasing a second sample now effectively doubles the "sample rate"
- micDataReal = maxSample2;
+ micDataReal = maxSample2; // do we really need this? FFT now takes only about 2ms so no need for this
} // for(;;)
} // FFTcode()
@@ -1008,7 +1003,7 @@ class AudioReactive : public Usermod {
*/
void connected()
{
- if (audioSyncPort > 0 || (audioSyncEnabled & 0x03)) {
+ if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) {
#ifndef ESP8266
udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort);
#else
From d053bc562fadae507ab6ea9c9182e1a741093fae Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sun, 21 Aug 2022 13:10:16 +0200
Subject: [PATCH 10/13] code cleanup, few optimizations, and fixing more
overflows
- code cleanup in audio_reactive.h
- fixing some more under/overflows in fx.cpp
---
usermods/audioreactive/audio_reactive.h | 119 ++++++------------------
wled00/FX.cpp | 33 ++++---
2 files changed, 47 insertions(+), 105 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 7ba25f3b..ea1ca79f 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -23,11 +23,9 @@
// Comment/Uncomment to toggle usb serial debugging
// #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter)
// #define FFT_SAMPLING_LOG // FFT result debugging
-// #define SR_DEBUG // generic SR DEBUG messages (including MIC_LOGGER)
+// #define SR_DEBUG // generic SR DEBUG messages
// #define NO_MIC_LOGGER // exclude MIC_LOGGER from SR_DEBUG
-// hackers corner
-
#ifdef SR_DEBUG
#define DEBUGSR_PRINT(x) Serial.print(x)
#define DEBUGSR_PRINTLN(x) Serial.println(x)
@@ -37,10 +35,6 @@
#define DEBUGSR_PRINTLN(x)
#define DEBUGSR_PRINTF(x...)
#endif
-// legacy support
-// #if defined(SR_DEBUG) && !defined(MIC_LOGGER) && !defined(NO_MIC_LOGGER)
-// #define MIC_LOGGER
-// #endif
#include "audio_source.h"
@@ -49,10 +43,11 @@ constexpr i2s_port_t I2S_PORT = I2S_NUM_0;
constexpr int BLOCK_SIZE = 128;
constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms
//constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms
-//constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz - standard. Physical sample time -> 50ms
+//constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz - previous default. Physical sample time -> 50ms
-//#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Use with 20Khz sampling
#define FFT_MIN_CYCLE 18 // minimum time before FFT task is repeated. Use with 22Khz sampling
+//#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Use with 20Khz sampling
+//#define FFT_MIN_CYCLE 44 // minimum time before FFT task is repeated. Use with 10Khz sampling
// globals
static uint8_t inputLevel = 128; // UI slider value
@@ -204,26 +199,19 @@ void FFTcode(void * parameter)
}
#endif
- const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2
- float maxSample1 = 0.0f; // max sample from first half of FFT batch
- float maxSample2 = 0.0f; // max sample from second half of FFT batch
- for (int i=0; i < halfSamplesFFT; i++) {
+ //const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2
+ float maxSample = 0.0f; // max sample from FFT batch
+ 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]) > maxSample1) maxSample1 = fabsf((float)vReal[i]);
+ if (fabsf((float)vReal[i]) > maxSample) maxSample = fabsf((float)vReal[i]);
}
- for (int i=halfSamplesFFT; 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]) > maxSample2) maxSample2 = fabsf((float)vReal[i]);
- }
- // release first sample to volume reactive effects
- micDataReal = maxSample1;
+ // release sample to volume reactive effects
+ micDataReal = maxSample; // doing this early allows filters (getSample() and agcAvg()) to run on latest values - we'll have up-to-date gain and noise gate values when FFT is done
+ // run FFT
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.dcRemoval(); // remove DC offset
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
@@ -246,11 +234,11 @@ void FFTcode(void * parameter)
//
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
- FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
+ FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
#else
- FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
+ FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
#endif
- FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 10240.0f);
+ FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3.
float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way
@@ -258,18 +246,17 @@ void FFTcode(void * parameter)
} // for()
-/* This FFT post processing is a DIY endeavour. What we really need is someone with sound engineering expertise to do a great job here AND most importantly, that the animations look GREAT as a result.
- *
- *
- * Andrew's updated mapping of 256 bins down to the 16 result bins with Sample Freq = 10240, samplesFFT = 512 and some overlap.
- * Based on testing, the lowest/Start frequency is 60 Hz (with bin 3) and a highest/End frequency of 5120 Hz in bin 255.
- * Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then detetermine the bins.
- * End frequency = Start frequency * multiplier ^ 16
- * Multiplier = (End frequency/ Start frequency) ^ 1/16
- * Multiplier = 1.320367784
- */
if (sampleAvg > 1) { // noise gate open
-#if 0 // Range
+#if 0
+ /* This FFT post processing is a DIY endeavour. What we really need is someone with sound engineering expertise to do a great job here AND most importantly, that the animations look GREAT as a result.
+ *
+ * Andrew's updated mapping of 256 bins down to the 16 result bins with Sample Freq = 10240, samplesFFT = 512 and some overlap.
+ * Based on testing, the lowest/Start frequency is 60 Hz (with bin 3) and a highest/End frequency of 5120 Hz in bin 255.
+ * Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then detetermine the bins.
+ * End frequency = Start frequency * multiplier ^ 16
+ * Multiplier = (End frequency/ Start frequency) ^ 1/16
+ * Multiplier = 1.320367784
+ */ // Range
fftCalc[ 0] = fftAddAvg(2,4); // 60 - 100
fftCalc[ 1] = fftAddAvg(4,5); // 80 - 120
fftCalc[ 2] = fftAddAvg(5,7); // 100 - 160
@@ -287,7 +274,8 @@ void FFTcode(void * parameter)
fftCalc[14] = fftAddAvg(147,194); // 2940 - 3900
fftCalc[15] = fftAddAvg(194,250); // 3880 - 5000 // avoid the last 5 bins, which are usually inaccurate
#else
- // optimized for 22050 Hz by softhack007 bins frequency range
+ /* new mapping, optimized for 22050 Hz by softhack007 */
+ // bins frequency range
fftCalc[ 0] = fftAddAvg(1,2); // 1 43 - 86 sub-bass
fftCalc[ 1] = fftAddAvg(2,3); // 1 86 - 129 bass
fftCalc[ 2] = fftAddAvg(3,5); // 2 129 - 216 bass
@@ -397,10 +385,6 @@ void FFTcode(void * parameter)
}
#endif
- // release second sample to volume reactive effects.
- // Releasing a second sample now effectively doubles the "sample rate"
- micDataReal = maxSample2; // do we really need this? FFT now takes only about 2ms so no need for this
-
} // for(;;)
} // FFTcode()
@@ -816,9 +800,6 @@ class AudioReactive : public Usermod {
audioSyncPacket transmitData;
strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6);
-
- //transmitData.sampleRaw = volumeRaw;
- //transmitData.sampleSmth = volumeSmth;
// transmit samples that were not modified by limitSampleDynamics()
transmitData.sampleRaw = (soundAgc) ? rawSampleAgc: sampleRaw;
transmitData.sampleSmth = (soundAgc) ? sampleAgc : sampleAvg;
@@ -848,7 +829,6 @@ class AudioReactive : public Usermod {
bool receiveAudioData() // check & process new data. return TRUE in case that new audio data was received.
{
if (!udpSyncConnected) return false;
- //DEBUGSR_PRINTLN("Checking for UDP Microphone Packet");
bool haveFreshData = false;
size_t packetSize = fftUdp.parsePacket();
if (packetSize > 5) {
@@ -891,7 +871,7 @@ class AudioReactive : public Usermod {
my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f);
FFT_Magnitude = my_magnitude;
- FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11050.0f); // restrict value to range expected by effects
+ FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
//DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
haveFreshData = true;
@@ -1101,51 +1081,6 @@ class AudioReactive : public Usermod {
if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
limitSampleDynamics(); // optional - makes volumeSmth very smooth and fluent
-
-#if 0
- /* currently this is _not_ working. Code relies on "musical note" symbol as second char of the effect name */
- #error I told you its not working right now
- // update WebServer UI
- uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment
- if (lastMode != knownMode) { // only execute if mode changes
- char lineBuffer[4];
- extractModeName(knownMode, JSON_mode_names, lineBuffer, 3); // use of JSON_mode_names is deprecated, use nullptr
- agcEffect = (lineBuffer[1] == 226 && lineBuffer[2] == 153); // && (lineBuffer[3] == 170 || lineBuffer[3] == 171 ) encoding of ♪ or ♫
- // agcEffect = (lineBuffer[4] == 240 && lineBuffer[5] == 159 && lineBuffer[6] == 142 && lineBuffer[7] == 154 ); //encoding of 🎚 No clue why as not found here https://www.iemoji.com/view/emoji/918/objects/level-slider
- lastMode = knownMode;
- }
-
- // update inputLevel Slider based on current AGC gain
- if ((soundAgc>0) && agcEffect) {
- unsigned long now_time = millis();
-
- // "user kick" feature - if user has moved the slider by at least 32 units, we "kick" AGC gain by 30% (up or down)
- // only once in 3.5 seconds
- if ( (lastMode == knownMode)
- && (abs(last_user_inputLevel - inputLevel) > 31)
- && (now_time - last_kick_time > 3500)) {
- if (last_user_inputLevel > inputLevel) multAgc *= 0.60; // down -> reduce gain
- if (last_user_inputLevel < inputLevel) multAgc *= 1.50; // up -> increase gain
- last_kick_time = now_time;
- }
-
- int new_user_inputLevel = 128.0f * multAgc; // scale AGC multiplier so that "1" is at 128
- if (multAgc > 1.0f) new_user_inputLevel = 128.0f * (((multAgc - 1.0f) / 4.0f) +1.0f); // compress range so we can show values up to 4
- new_user_inputLevel = MIN(MAX(new_user_inputLevel, 0),255);
-
- // update user interfaces - restrict frequency to avoid flooding UI's with small changes
- if (( ((now_time - last_update_time > 3500) && (abs(new_user_inputLevel - inputLevel) > 2)) // small change - every 3.5 sec (max)
- ||((now_time - last_update_time > 2200) && (abs(new_user_inputLevel - inputLevel) > 15)) // medium change
- ||((now_time - last_update_time > 1200) && (abs(new_user_inputLevel - inputLevel) > 31))) // BIG change - every second
- && !strip.isUpdating()) // don't interfere while strip is updating
- {
- inputLevel = new_user_inputLevel; // change of least 3 units -> update user variable
- updateInterfaces(CALL_MODE_WS_SEND); // is this the correct way to notify UIs ? Yes says blazoncek
- last_update_time = now_time;
- last_user_inputLevel = new_user_inputLevel;
- }
- }
-#endif
}
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 6c797748..a57aaafb 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -5958,14 +5958,20 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;
}
*/
+
// a few constants needed for AudioReactive effects
+
// for 22Khz sampling
-#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion)
+#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion)
#define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY)
// for 20Khz sampling
-//#define MAX_FREQUENCY 10240 // sample frequency / 2 (as per Nyquist criterion)
-//#define MAX_FREQ_LOG10 4.0103f // log10(MAX_FREQUENCY)
+//#define MAX_FREQUENCY 10240
+//#define MAX_FREQ_LOG10 4.0103f
+
+// for 10Khz sampling
+//#define MAX_FREQUENCY 5120
+//#define MAX_FREQ_LOG10 3.71f
/////////////////////////////////
@@ -6719,7 +6725,7 @@ static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;mp12=2,ssi
////////////////////
uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate.
// Start frequency = 60 Hz and log10(60) = 1.78
- // End frequency = 5120 Hz and lo10(5120) = 3.71
+ // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
@@ -6732,13 +6738,13 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
SEGMENT.fade_out(SEGMENT.speed);
-// int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(3.71f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
if (locn < 1) locn = 0; // avoid underflow
if (locn >=SEGLEN) locn = SEGLEN-1;
- //uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(3.71f-1.78f); // Scale log10 of frequency values to the 255 colour index.
uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
+ if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
+
uint16_t bright = (int)my_magnitude;
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright));
@@ -6777,7 +6783,6 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
CRGB color = CRGB::Black;
- //if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0;
if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1;
// MajorPeak holds the freq. value which is most abundant in the last sample.
// With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz
@@ -6788,7 +6793,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
} else {
int upperLimit = 80 + 42 * SEGMENT.custom2;
int lowerLimit = 80 + 3 * SEGMENT.custom1;
- int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak;
+ uint8_t i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; // may under/overflow - so we enforce uint8_t
uint16_t b = 255 * intensity;
if (b > 255) b = 255;
color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED
@@ -6826,8 +6831,8 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
uint16_t locn = random16(0,SEGLEN);
- //uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78) * 255.0/(3.71-1.78); // Scale log10 of frequency values to the 255 colour index.
uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
+ if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
}
@@ -6877,7 +6882,6 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
CRGB color = 0;
- //if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0.0f;
if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1.0f;
// MajorPeak holds the freq. value which is most abundant in the last sample.
// With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz
@@ -6888,7 +6892,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
} else {
int upperLimit = 80 + 42 * SEGMENT.custom2;
int lowerLimit = 80 + 3 * SEGMENT.custom1;
- int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak;
+ uint8_t i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; // may under/overflow - so we enforce uint8_t
uint16_t b = 255.0 * intensity;
if (b > 255) b=255;
color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED
@@ -6924,7 +6928,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
float volumeSmth = *(float*) um_data->u_data[0];
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
- SEGMENT.fade_out(240);
+ SEGMENT.fade_out(250);
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
@@ -7012,6 +7016,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
frTemp = fabs(frTemp * 2.1); // Fudge factors to compress octave range starting at 0 and going to 255;
uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
+ i = constrain(i, 0, SEGLEN-1);
SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp));
return FRAMETIME;
@@ -7054,7 +7059,9 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
if (SEGENV.aux0 != secondHand) { // Triggered millis timing.
SEGENV.aux0 = secondHand;
- uint8_t pixCol = (log10f((float)FFT_MajorPeak) - 2.26f) * 177; // log10 frequency range is from 2.26 to 3.7. Let's scale accordingly.
+ //uint8_t pixCol = (log10f((float)FFT_MajorPeak) - 2.26f) * 177; // 10Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.7 (5012hz). Let's scale accordingly.
+ uint8_t pixCol = (log10f(FFT_MajorPeak) - 2.26f) * 150; // 22Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.967 (9260hz). Let's scale accordingly.
+ if (FFT_MajorPeak < 182.0f) pixCol = 0; // handle underflow
if (samplePeak) {
SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92));
From 720fae872014c1e6dbc2cafc8688ae98b5bebf3e Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Sun, 21 Aug 2022 19:15:42 +0200
Subject: [PATCH 11/13] Code sanitation. Default analog pin -1
---
usermods/audioreactive/audio_reactive.h | 164 +++++++++++-------------
1 file changed, 75 insertions(+), 89 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index ea1ca79f..ca8a3287 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -173,7 +173,6 @@ void FFTcode(void * parameter)
// see https://www.freertos.org/vtaskdelayuntil.html
const TickType_t xFrequency = FFT_MIN_CYCLE * portTICK_PERIOD_MS;
- //const TickType_t xFrequency_2 = (FFT_MIN_CYCLE * portTICK_PERIOD_MS) / 2;
for(;;) {
TickType_t xLastWakeTime = xTaskGetTickCount();
@@ -208,8 +207,8 @@ void FFTcode(void * parameter)
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]);
}
- // release sample to volume reactive effects
- micDataReal = maxSample; // doing this early allows filters (getSample() and agcAvg()) to run on latest values - we'll have up-to-date gain and noise gate values when FFT is done
+ // release sample to volume reactive effects (not really necessary as float FFT calculation takes only 2ms)
+ micDataReal = maxSample;
// run FFT
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
@@ -228,10 +227,6 @@ void FFTcode(void * parameter)
FFT.Compute( FFT_FORWARD ); // Compute FFT
FFT.ComplexToMagnitude(); // Compute magnitudes
#endif
- //
- // vReal[3 .. 255] contain useful data, each a 20Hz interval (60Hz - 5120Hz).
- // There could be interesting data at bins 0 to 2, but there are too many artifacts.
- //
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
@@ -240,9 +235,9 @@ void FFTcode(void * parameter)
#endif
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
- for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3.
+ for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3.
float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way
- fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max.
+ fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max.
} // for()
@@ -294,7 +289,6 @@ void FFTcode(void * parameter)
fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping
// don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise)
#endif
-
} else { // noise gate closed
for (int i=0; i < 16; i++) {
//fftCalc[i] *= 0.82f; // decay to zero
@@ -316,7 +310,7 @@ void FFTcode(void * parameter)
// smooth results - rise fast, fall slower
if(fftCalc[i] > fftAvg[i]) // rise fast
- fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i]
+ fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i]
else { // fall slow
if (decayTime < 1000) fftAvg[i] = fftCalc[i]*0.22f + 0.78f*fftAvg[i]; // approx 5 cycles (225ms) for falling to zero
else if (decayTime < 2000) fftAvg[i] = fftCalc[i]*0.17f + 0.83f*fftAvg[i]; // default - approx 9 cycles (225ms) for falling to zero
@@ -336,29 +330,27 @@ void FFTcode(void * parameter)
switch (FFTScalingMode) {
case 1:
// Logarithmic scaling
- currentResult *= 0.42; // 42 is the answer ;-)
- currentResult -= 8.0; // this skips the lowest row, giving some room for peaks
- if (currentResult > 1.0)
- currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
- else currentResult = 0.0; // special handling, because log(1) = 0; log(0) = undefined
- currentResult *= 0.85f + (float(i)/18.0f); // extra up-scaling for high frequencies
+ currentResult *= 0.42; // 42 is the answer ;-)
+ currentResult -= 8.0; // this skips the lowest row, giving some room for peaks
+ if (currentResult > 1.0) currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
+ else currentResult = 0.0; // special handling, because log(1) = 0; log(0) = undefined
+ currentResult *= 0.85f + (float(i)/18.0f); // extra up-scaling for high frequencies
currentResult = mapf(currentResult, 0, LOG_256, 0, 255); // map [log(1) ... log(255)] to [0 ... 255]
break;
case 2:
// Linear scaling
- currentResult *= 0.30f; // needs a bit more damping, get stay below 255
- currentResult -= 4.0; // giving a bit more room for peaks
+ currentResult *= 0.30f; // needs a bit more damping, get stay below 255
+ currentResult -= 4.0; // giving a bit more room for peaks
if (currentResult < 1.0f) currentResult = 0.0f;
- currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies
+ currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies
break;
case 3:
// square root scaling
currentResult *= 0.38f;
currentResult -= 6.0f;
- if (currentResult > 1.0)
- currentResult = sqrtf(currentResult);
- else currentResult = 0.0; // special handling, because sqrt(0) = undefined
- currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies
+ if (currentResult > 1.0) currentResult = sqrtf(currentResult);
+ else currentResult = 0.0; // special handling, because sqrt(0) = undefined
+ currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies
currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255]
break;
@@ -394,7 +386,7 @@ class AudioReactive : public Usermod {
private:
#ifndef AUDIOPIN
- int8_t audioPin = 36;
+ int8_t audioPin = -1;
#else
int8_t audioPin = AUDIOPIN;
#endif
@@ -615,7 +607,6 @@ class AudioReactive : public Usermod {
if((fabs(sampleReal) < 2.0f) || (sampleMax < 1.0f)) {
// MIC signal is "squelched" - deliver silence
- //multAgcTemp = multAgc; // keep old control value (no change)
tmpAgc = 0;
// we need to "spin down" the intgrated error buffer
if (fabs(control_integrated) < 0.01) control_integrated = 0.0;
@@ -623,27 +614,26 @@ class AudioReactive : public Usermod {
} else {
// compute new setpoint
if (tmpAgc <= agcTarget0Up[AGC_preset])
- multAgcTemp = agcTarget0[AGC_preset] / sampleMax; // Make the multiplier so that sampleMax * multiplier = first setpoint
+ multAgcTemp = agcTarget0[AGC_preset] / sampleMax; // Make the multiplier so that sampleMax * multiplier = first setpoint
else
- multAgcTemp = agcTarget1[AGC_preset] / sampleMax; // Make the multiplier so that sampleMax * multiplier = second setpoint
+ multAgcTemp = agcTarget1[AGC_preset] / sampleMax; // Make the multiplier so that sampleMax * multiplier = second setpoint
}
// limit amplification
- //multAgcTemp = constrain(multAgcTemp, 0.015625f, 32.0f); // 1/64 < multAgcTemp < 32
if (multAgcTemp > 32.0f) multAgcTemp = 32.0f;
if (multAgcTemp < 1.0f/64.0f) multAgcTemp = 1.0f/64.0f;
// compute error terms
control_error = multAgcTemp - lastMultAgc;
- if (((multAgcTemp > 0.085f) && (multAgcTemp < 6.5f)) //integrator anti-windup by clamping
- && (multAgc*sampleMax < agcZoneStop[AGC_preset])) //integrator ceiling (>140% of max)
- control_integrated += control_error * 0.002 * 0.25; // 2ms = intgration time; 0.25 for damping
+ if (((multAgcTemp > 0.085f) && (multAgcTemp < 6.5f)) //integrator anti-windup by clamping
+ && (multAgc*sampleMax < agcZoneStop[AGC_preset])) //integrator ceiling (>140% of max)
+ control_integrated += control_error * 0.002 * 0.25; // 2ms = intgration time; 0.25 for damping
else
- control_integrated *= 0.9; // spin down that beasty integrator
+ control_integrated *= 0.9; // spin down that beasty integrator
// apply PI Control
- tmpAgc = sampleReal * lastMultAgc; // check "zone" of the signal using previous gain
- if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower emergy zone
+ tmpAgc = sampleReal * lastMultAgc; // check "zone" of the signal using previous gain
+ if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower emergy zone
multAgcTemp = lastMultAgc + agcFollowFast[AGC_preset] * agcControlKp[AGC_preset] * control_error;
multAgcTemp += agcFollowFast[AGC_preset] * agcControlKi[AGC_preset] * control_integrated;
} else { // "normal zone"
@@ -673,9 +663,6 @@ class AudioReactive : public Usermod {
else
sampleAgc += agcSampleSmooth[AGC_preset] * (tmpAgc - sampleAgc); // smooth path
- //userVar0 = sampleAvg * 4;
- //if (userVar0 > 255) userVar0 = 255;
-
last_soundAgc = soundAgc;
} // agcAvg()
@@ -711,7 +698,7 @@ class AudioReactive : public Usermod {
micLev = ((micLev * 8191.0f) + micDataReal) / 8192.0f; // takes a few seconds to "catch up" with the Mic Input
if(micIn < micLev) micLev = ((micLev * 31.0f) + micDataReal) / 32.0f; // align MicLev to lowest input signal
- micIn -= micLev; // Let's center it to 0 now
+ micIn -= micLev; // Let's center it to 0 now
// Using an exponential filter to smooth out the signal. We'll add controls for this in a future release.
float micInNoDC = fabs(micDataReal - micLev);
expAdjF = (weighting * micInNoDC + (1.0-weighting) * expAdjF);
@@ -725,12 +712,12 @@ class AudioReactive : public Usermod {
sampleAdj = tmpSample * sampleGain / 40.0f * inputLevel/128.0f + tmpSample / 16.0f; // Adjust the gain. with inputLevel adjustment
sampleReal = tmpSample;
- sampleAdj = fmax(fmin(sampleAdj, 255), 0); // Question: why are we limiting the value to 8 bits ???
- sampleRaw = (int16_t)sampleAdj; // ONLY update sample ONCE!!!!
+ sampleAdj = fmax(fmin(sampleAdj, 255), 0); // Question: why are we limiting the value to 8 bits ???
+ sampleRaw = (int16_t)sampleAdj; // ONLY update sample ONCE!!!!
// keep "peak" sample, but decay value if current sample is below peak
if ((sampleMax < sampleReal) && (sampleReal > 0.5f)) {
- sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering
+ sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering
} else {
if ((multAgc*sampleMax > agcZoneStop[AGC_preset]) && (soundAgc > 0))
sampleMax += 0.5f * (sampleReal - sampleMax); // over AGC Zone - get back quickly
@@ -751,13 +738,12 @@ class AudioReactive : public Usermod {
//if (userVar1 == 0) samplePeak = 0;
// Poor man's beat detection by seeing if sample > Average + some value.
- // if (sample > (sampleAvg + maxVol) && millis() > (timeOfPeak + 200)) {
- if ((maxVol > 0) && (binNum > 1) && (fftBin[binNum] > maxVol) && (millis() > (timeOfPeak + 100))) { // This goes through ALL of the 255 bins - but ignores stupid settings
- // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync.
+ if ((maxVol > 0) && (binNum > 1) && (fftBin[binNum] > maxVol) && (millis() > (timeOfPeak + 100))) {
+ // This goes through ALL of the 255 bins - but ignores stupid settings
+ // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync.
samplePeak = true;
timeOfPeak = millis();
udpSamplePeak = true;
- //userVar1 = samplePeak;
}
} // getSample()
@@ -771,7 +757,7 @@ class AudioReactive : public Usermod {
static unsigned long last_time = 0;
static float last_volumeSmth = 0.0f;
- if(limiterOn == false) return;
+ if (limiterOn == false) return;
long delta_time = millis() - last_time;
delta_time = constrain(delta_time , 1, 1000); // below 1ms -> 1ms; above 1sec -> sily lil hick-up
@@ -1043,7 +1029,7 @@ class AudioReactive : public Usermod {
if (audioSyncEnabled & 0x02) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode
if (audioSyncEnabled & 0x01) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode
- if(!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source
+ if (!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source
// Only run the sampling code IF we're not in Receive mode or realtime mode
@@ -1070,7 +1056,7 @@ class AudioReactive : public Usermod {
agcAvg(t_now - userloopDelay); // Calculated the PI adjusted value as sampleAvg
userloopDelay -= 2; // advance "simulated time" by 2ms
} while (userloopDelay > 0);
- lastUMRun = t_now; // update time keeping
+ lastUMRun = t_now; // update time keeping
// update samples for effects (raw, smooth)
volumeSmth = (soundAgc) ? sampleAgc : sampleAvg;
@@ -1078,7 +1064,7 @@ class AudioReactive : public Usermod {
// update FFTMagnitude, taking into account AGC amplification
my_magnitude = FFT_Magnitude; // / 16.0f, 8.0f, 4.0f done in effects
if (soundAgc) my_magnitude *= multAgc;
- if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
+ if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
limitSampleDynamics(); // optional - makes volumeSmth very smooth and fluent
}
@@ -1239,61 +1225,62 @@ class AudioReactive : public Usermod {
uiDomString += F(" />"); //
infoArr.add(uiDomString);
}
- //else infoArr.add("
"); // no processing - add empty line
+
+ // The following can be used for troubleshooting user errors and is so not enclosed in #ifdef WLED_DEBUG
// current Audio input
infoArr = user.createNestedArray(F("Audio Source"));
if (audioSyncEnabled & 0x02) {
- // UDP sound sync - receive mode
- infoArr.add("UDP sound sync");
- if (udpSyncConnected) {
- if (millis() - last_UDPTime < 2500)
- infoArr.add(" - receiving");
- else
- infoArr.add(" - idle");
- } else {
- infoArr.add(" - no connection");
- }
+ // UDP sound sync - receive mode
+ infoArr.add(F("UDP sound sync"));
+ if (udpSyncConnected) {
+ if (millis() - last_UDPTime < 2500)
+ infoArr.add(F(" - receiving"));
+ else
+ infoArr.add(F(" - idle"));
+ } else {
+ infoArr.add(F(" - no connection"));
+ }
} else {
// Analog or I2S digital input
if (audioSource && (audioSource->isInitialized())) {
- // audio source sucessfully configured
- if(audioSource->getType() == AudioSource::Type_I2SAdc) {
- infoArr.add("ADC analog");
- } else {
- infoArr.add("I2S digital");
- }
- // input level or "silence"
- if (maxSample5sec > 1.0) {
- float my_usage = 100.0f * (maxSample5sec / 255.0f);
- snprintf(myStringBuffer, 15, " - peak %3d%%", int(my_usage));
- infoArr.add(myStringBuffer);
- } else {
- infoArr.add(" - quiet");
- }
+ // audio source sucessfully configured
+ if (audioSource->getType() == AudioSource::Type_I2SAdc) {
+ infoArr.add(F("ADC analog"));
+ } else {
+ infoArr.add(F("I2S digital"));
+ }
+ // input level or "silence"
+ if (maxSample5sec > 1.0) {
+ float my_usage = 100.0f * (maxSample5sec / 255.0f);
+ snprintf_P(myStringBuffer, 15, PSTR(" - peak %3d%%"), int(my_usage));
+ infoArr.add(myStringBuffer);
+ } else {
+ infoArr.add(F(" - quiet"));
+ }
} else {
- // error during audio source setup
- infoArr.add("not initialized");
- infoArr.add(" - check GPIO config");
+ // error during audio source setup
+ infoArr.add(F("not initialized"));
+ infoArr.add(F(" - check GPIO config"));
}
}
// Sound processing (FFT and input filters)
infoArr = user.createNestedArray(F("Sound Processing"));
if (audioSource && (disableSoundProcessing == false)) {
- infoArr.add("running");
+ infoArr.add(F("running"));
} else {
- infoArr.add("suspended");
+ infoArr.add(F("suspended"));
}
// AGC or manual Gain
- if((soundAgc==0) && (disableSoundProcessing == false) && !(audioSyncEnabled & 0x02)) {
+ if ((soundAgc==0) && (disableSoundProcessing == false) && !(audioSyncEnabled & 0x02)) {
infoArr = user.createNestedArray(F("Manual Gain"));
float myGain = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // non-AGC gain from presets
infoArr.add(roundf(myGain*100.0f) / 100.0f);
infoArr.add("x");
}
- if(soundAgc && (disableSoundProcessing == false) && !(audioSyncEnabled & 0x02)) {
+ if (soundAgc && (disableSoundProcessing == false) && !(audioSyncEnabled & 0x02)) {
infoArr = user.createNestedArray(F("AGC Gain"));
infoArr.add(roundf(multAgc*100.0f) / 100.0f);
infoArr.add("x");
@@ -1302,15 +1289,14 @@ class AudioReactive : public Usermod {
// UDP Sound Sync status
infoArr = user.createNestedArray(F("UDP Sound Sync"));
if (audioSyncEnabled) {
- if (audioSyncEnabled & 0x01) {
- infoArr.add("send mode");
- } else if (audioSyncEnabled & 0x02) {
- infoArr.add("receive mode");
- }
+ if (audioSyncEnabled & 0x01) {
+ infoArr.add(F("send mode"));
+ } else if (audioSyncEnabled & 0x02) {
+ infoArr.add(F("receive mode"));
+ }
} else
- infoArr.add("off");
+ infoArr.add("off");
if (audioSyncEnabled && !udpSyncConnected) infoArr.add(" (unconnected)");
- //if (!udpSyncConnected) infoArr.add(" (unconnected)");
#ifdef WLED_DEBUG
infoArr = user.createNestedArray(F("Sampling time"));
From b722c618bdfb034ba9c33bb39367b140b578abdc Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Sun, 21 Aug 2022 20:50:24 +0200
Subject: [PATCH 12/13] Fixes in NodeStruct & bus manager.
---
wled00/NodeStruct.h | 1 -
wled00/bus_manager.h | 42 +++++++++++++++++-------------------------
wled00/wled.h | 4 ++--
3 files changed, 19 insertions(+), 28 deletions(-)
diff --git a/wled00/NodeStruct.h b/wled00/NodeStruct.h
index a0fd2f63..bc95d1e8 100644
--- a/wled00/NodeStruct.h
+++ b/wled00/NodeStruct.h
@@ -19,7 +19,6 @@ struct NodeStruct
{
String nodeName;
IPAddress ip;
- uint8_t unit;
uint8_t age;
uint8_t nodeType;
uint32_t build;
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index 405c402a..a5c88daf 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -134,7 +134,12 @@ struct ColorOrderMap {
//parent class of BusDigital, BusPwm, and BusNetwork
class Bus {
public:
- Bus(uint8_t type, uint16_t start, uint8_t aw) {
+ Bus(uint8_t type, uint16_t start, uint8_t aw)
+ : _bri(255)
+ , _len(1)
+ , _valid(false)
+ , _needsRefresh(false)
+ {
_type = type;
_start = start;
_autoWhiteMode = Bus::isRgbw(_type) ? aw : RGBW_MODE_MANUAL_ONLY;
@@ -142,13 +147,13 @@ class Bus {
virtual ~Bus() {} //throw the bus under the bus
- virtual void show() {}
+ virtual void show() = 0;
virtual bool canShow() { return true; }
virtual void setStatusPixel(uint32_t c) {}
- virtual void setPixelColor(uint16_t pix, uint32_t c) {}
+ virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
- virtual void setBrightness(uint8_t b) {}
- virtual void cleanup() {}
+ virtual void setBrightness(uint8_t b) { _bri = b; };
+ virtual void cleanup() = 0;
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
virtual uint16_t getLength() { return _len; }
virtual void setColorOrder() {}
@@ -195,12 +200,12 @@ class Bus {
bool reversed = false;
protected:
- uint8_t _type = TYPE_NONE;
- uint8_t _bri = 255;
- uint16_t _start = 0;
- uint16_t _len = 1;
- bool _valid = false;
- bool _needsRefresh = false;
+ uint8_t _type;
+ uint8_t _bri;
+ uint16_t _start;
+ uint16_t _len;
+ bool _valid;
+ bool _needsRefresh;
uint8_t _autoWhiteMode;
static uint8_t _gAWM; // definition in FX_fcn.cpp
static int16_t _cct; // definition in FX_fcn.cpp
@@ -262,7 +267,7 @@ class BusDigital : public Bus {
if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
}
#endif
- _bri = b;
+ Bus::setBrightness(b);
PolyBus::setBrightness(_busPtr, _iType, b);
}
@@ -448,10 +453,6 @@ class BusPwm : public Bus {
}
}
- inline void setBrightness(uint8_t b) {
- _bri = b;
- }
-
uint8_t getPins(uint8_t* pinArray) {
if (!_valid) return 0;
uint8_t numPins = NUM_PWM_PINS(_type);
@@ -531,10 +532,6 @@ class BusOnOff : public Bus {
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data);
}
- inline void setBrightness(uint8_t b) {
- _bri = b;
- }
-
uint8_t getPins(uint8_t* pinArray) {
if (!_valid) return 0;
pinArray[0] = _pin;
@@ -623,10 +620,6 @@ class BusNetwork : public Bus {
return !_broadcastLock;
}
- inline void setBrightness(uint8_t b) {
- _bri = b;
- }
-
uint8_t getPins(uint8_t* pinArray) {
for (uint8_t i = 0; i < 4; i++) {
pinArray[i] = _client[i];
@@ -655,7 +648,6 @@ class BusNetwork : public Bus {
private:
IPAddress _client;
- uint8_t _bri = 255;
uint8_t _UDPtype;
uint8_t _UDPchannels;
bool _rgbw;
diff --git a/wled00/wled.h b/wled00/wled.h
index bff5b49a..ade76c86 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
-#define VERSION 2208191
+#define VERSION 2208211
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -27,7 +27,7 @@
//#define WLED_DISABLE_ALEXA // saves 11kb
//#define WLED_DISABLE_BLYNK // saves 6kb
//#define WLED_DISABLE_HUESYNC // saves 4kb
-//#define WLED_DISABLE_INFRARED // there is no pin left for this on ESP8266-01, saves 12kb
+//#define WLED_DISABLE_INFRARED // saves 12kb
#ifndef WLED_DISABLE_MQTT
#define WLED_ENABLE_MQTT // saves 12kb
#endif
From be7e7ac2746573bc8244a1bd13492930281678a7 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 22 Aug 2022 10:08:22 +0200
Subject: [PATCH 13/13] AR: documentation
- clarified a misleading comment in FFTCode
- added a few more comments to describe steps of the processing
- removed some commented-out code
---
usermods/audioreactive/audio_reactive.h | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index ca8a3287..826ad7f4 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -189,6 +189,7 @@ void FFTcode(void * parameter)
uint64_t start = esp_timer_get_time();
#endif
+ // get a fresh batch of samples from I2S
if (audioSource) audioSource->getSamples(vReal, samplesFFT);
#ifdef WLED_DEBUG
@@ -198,7 +199,7 @@ void FFTcode(void * parameter)
}
#endif
- //const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2
+ // find highest sample in the batch
float maxSample = 0.0f; // max sample from FFT batch
for (int i=0; i < samplesFFT; i++) {
// set imaginary parts to 0
@@ -207,10 +208,11 @@ void FFTcode(void * parameter)
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]);
}
- // release sample to volume reactive effects (not really necessary as float FFT calculation takes only 2ms)
+ // 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;
micDataReal = maxSample;
- // run FFT
+ // run FFT (takes 3-5ms on ESP32)
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.dcRemoval(); // remove DC offset
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
@@ -240,7 +242,7 @@ void FFTcode(void * parameter)
fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max.
} // for()
-
+ // mapping of FFT result bins to frequency channels
if (sampleAvg > 1) { // noise gate open
#if 0
/* This FFT post processing is a DIY endeavour. What we really need is someone with sound engineering expertise to do a great job here AND most importantly, that the animations look GREAT as a result.
@@ -289,14 +291,14 @@ void FFTcode(void * parameter)
fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping
// don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise)
#endif
- } else { // noise gate closed
+ } else { // noise gate closed - just decay old values
for (int i=0; i < 16; i++) {
- //fftCalc[i] *= 0.82f; // decay to zero
fftCalc[i] *= 0.85f; // decay to zero
if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f;
}
}
+ // post-processing of frequency channels (pink noise adjustment, AGC, smooting, scaling)
for (int i=0; i < 16; i++) {
if (sampleAvg > 1) { // noise gate open
@@ -304,7 +306,7 @@ void FFTcode(void * parameter)
fftCalc[i] *= fftResultPink[i];
if (FFTScalingMode > 0) fftCalc[i] *= FFT_DOWNSCALE; // adjustment related to FFT windowing function
// Manual linear adjustment of gain using sampleGain adjustment for different input types.
- fftCalc[i] *= soundAgc ? multAgc : ((float)sampleGain/40.0f * (float)inputLevel/128.0f + 1.0f/16.0f); //with inputLevel adjustment
+ fftCalc[i] *= soundAgc ? multAgc : ((float)sampleGain/40.0f * (float)inputLevel/128.0f + 1.0f/16.0f); //apply gain, with inputLevel adjustment
if(fftCalc[i] < 0) fftCalc[i] = 0;
}