mirror of
https://github.com/wled/WLED.git
synced 2026-04-20 14:12:55 +00:00
Compare commits
7 Commits
ar-network
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09faf4c981 | ||
|
|
55e88685b6 | ||
|
|
bfb2b88559 | ||
|
|
9adbba6018 | ||
|
|
746f8c1147 | ||
|
|
5390cfcf43 | ||
|
|
779a82faaf |
@@ -71,7 +71,7 @@
|
||||
#define PLOT_PRINTF(x...)
|
||||
#endif
|
||||
|
||||
#define MAX_PALETTES 3
|
||||
#define MAX_PALETTES 5
|
||||
|
||||
static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks.
|
||||
static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1 - receive (config value)
|
||||
@@ -98,6 +98,9 @@ static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same t
|
||||
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
|
||||
static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects
|
||||
|
||||
static float paletteBandAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Slowly smoothed band averages used only by audio palettes 3 & 4 (EMA, alpha=0.05 → ~400ms time constant at 20ms cycle)
|
||||
static constexpr float PALETTE_SMOOTHING = 0.05f; // EMA smoothing factor for paletteBandAvg: 0.05 gives ~400ms time constant; increase for faster response, decrease for slower
|
||||
|
||||
// TODO: probably best not used by receive nodes
|
||||
//static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255
|
||||
|
||||
@@ -1683,6 +1686,7 @@ class AudioReactive : public Usermod {
|
||||
memset(fftCalc, 0, sizeof(fftCalc));
|
||||
memset(fftAvg, 0, sizeof(fftAvg));
|
||||
memset(fftResult, 0, sizeof(fftResult));
|
||||
memset(paletteBandAvg, 0, sizeof(paletteBandAvg));
|
||||
for(int i=(init?0:1); i<NUM_GEQ_CHANNELS; i+=2) fftResult[i] = 16; // make a tiny pattern
|
||||
inputLevel = 128; // reset level slider to default
|
||||
autoResetPeak();
|
||||
@@ -1723,6 +1727,7 @@ class AudioReactive : public Usermod {
|
||||
// reset sound data
|
||||
volumeRaw = 0; volumeSmth = 0;
|
||||
for(int i=(init?0:1); i<NUM_GEQ_CHANNELS; i+=2) fftResult[i] = 16; // make a tiny pattern
|
||||
memset(paletteBandAvg, 0, sizeof(paletteBandAvg));
|
||||
autoResetPeak();
|
||||
if (init) {
|
||||
if (udpSyncConnected) { // close UDP sync connection (if open)
|
||||
@@ -2223,6 +2228,69 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) {
|
||||
hsv = CHSV(fftResult[b], 255, map(fftResult[b], 0, 255, 30, 255)); // pick hue
|
||||
value = hsv; // convert to R,G,B
|
||||
break;
|
||||
// AI: below section was generated by an AI
|
||||
case 3: {
|
||||
// "Track Character" palette (palette index 3)
|
||||
// Uses the spectral centroid of paletteBandAvg[] to derive a single hue that
|
||||
// reflects the tonal balance of the music over the past ~400ms: // softhack007 this statement is wrong
|
||||
// low centroid (bass-heavy drop) → warm red/orange (hue ≈ 0)
|
||||
// mid centroid (vocals/melody) → green/cyan (hue ≈ 80-120)
|
||||
// high centroid (cymbals/bright synth) → blue/purple (hue ≈ 200)
|
||||
// x (0-255) spreads palette positions ±30 hue units around that base hue.
|
||||
static const float bandFreq[NUM_GEQ_CHANNELS] = { // approximate centre frequency (Hz) of each GEQ channel
|
||||
65, 107, 172, 258, 365, 495, 689, 969,
|
||||
1270, 1658, 2153, 2713, 3359, 4091, 5792, 8182
|
||||
};
|
||||
float wSum = 0, tEnergy = 0;
|
||||
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
||||
wSum += paletteBandAvg[i] * bandFreq[i]; // frequency-weighted energy
|
||||
tEnergy += paletteBandAvg[i]; // total energy
|
||||
}
|
||||
// centroid = energy-weighted average frequency; default to 500 Hz when signal is silent
|
||||
float centroid = (tEnergy > 1.0f) ? (wSum / tEnergy) : 500.0f;
|
||||
// Map centroid to hue on a log scale (human pitch perception is logarithmic).
|
||||
// log2(60 Hz) ≈ 5.9, log2(8000 Hz) ≈ 13.0 → hue range 0..200 (red → blue-purple)
|
||||
float logC = log2f(constrain(centroid, 60.0f, 8000.0f)); // softhack007 ToDO: use logf() instead of log2f()
|
||||
uint8_t baseHue = (uint8_t)mapf(logC, 5.9f, 13.0f, 0.0f, 200.0f); // mapf() cannot produce negative results due to previous constrain() --> safe to directly cast to uint8_t
|
||||
int8_t hueSpread = map(x, 0, 255, -30, 30); // spread palette positions ±30 hue units // softhack007 ToDO: use CHSV32 with 16bit HUE
|
||||
uint8_t saturation = (uint8_t)constrain((int)(tEnergy / 6.0f) + 180, 180, 255); // louder = more saturated // softhack007 WTF dude?
|
||||
hsv = CHSV(baseHue + hueSpread, saturation, (uint8_t)constrain(x, 30, 255));
|
||||
value = hsv;
|
||||
break;
|
||||
}
|
||||
// AI: end
|
||||
// AI: below section was generated by an AI
|
||||
case 4: {
|
||||
// "Spectral Balance" palette (palette index 4)
|
||||
// Divides the spectrum into three broad bands and uses their energy ratio to derive hue:
|
||||
// bass dominant (channels 0-3, ~43-301 Hz) → warm hue ≈ 20 (red/orange)
|
||||
// mid dominant (channels 4-9, ~301-1895 Hz) → green hue ≈ 110 (green/cyan)
|
||||
// high dominant (channels 10-15, ~1895-9259 Hz)→ cool hue ≈ 190 (blue/violet)
|
||||
// x (0-255) spreads palette positions ±25 hue units around that weighted hue,
|
||||
// giving a smooth colour band rather than a single flat colour.
|
||||
float bassEnergy = 0, midEnergy = 0, highEnergy = 0;
|
||||
for (int i = 0; i < 4; i++) bassEnergy += paletteBandAvg[i]; // sub-bass + bass
|
||||
for (int i = 4; i < 10; i++) midEnergy += paletteBandAvg[i]; // midrange
|
||||
for (int i = 10; i < 16; i++) highEnergy += paletteBandAvg[i]; // high-mid + high
|
||||
float total = bassEnergy + midEnergy + highEnergy;
|
||||
if (total < 1.0f) total = 1.0f; // avoid division by zero when silent
|
||||
float bassRatio = bassEnergy / total; // fraction of energy in bass band
|
||||
float midRatio = midEnergy / total;
|
||||
float highRatio = highEnergy / total;
|
||||
// Weighted hue: pure bass→20, pure mid→110, pure high→190
|
||||
int weightedHue = roundf(bassRatio * 20.0f + midRatio * 110.0f + highRatio * 190.0f);
|
||||
uint8_t hue = min(255, max(0, weightedHue)); // clamp to [0...255]
|
||||
// Saturation: dominated spectrum (one band clearly wins) → high sat; balanced → lower sat
|
||||
float maxRatio = max(bassRatio, max(midRatio, highRatio));
|
||||
uint8_t sat = (uint8_t)constrain((int)(maxRatio * 255.0f * 1.5f), 180, 255); // softhack007 OMG, WTF?
|
||||
int8_t hueOffset = map(x, 0, 255, -25, 25); // spread palette positions ±25 hue units // softhack007 ToDO: use CHSV32 with 16bit HUE
|
||||
// brightness: minimum 30, boosted by overall loudness and palette position
|
||||
uint8_t val = (uint8_t)constrain((int)(total / 8.0f) + (int)map(x, 0, 255, 30, 255), 30, 255);
|
||||
hsv = CHSV(hue + hueOffset, sat, val);
|
||||
value = hsv;
|
||||
break;
|
||||
}
|
||||
// AI: end
|
||||
default:
|
||||
if (x == 1) {
|
||||
value = CRGB(fftResult[10]/2, fftResult[4]/2, fftResult[0]/2);
|
||||
@@ -2240,6 +2308,17 @@ void AudioReactive::fillAudioPalettes() {
|
||||
if (!palettes) return;
|
||||
size_t lastCustPalette = customPalettes.size();
|
||||
if (int(lastCustPalette) >= palettes) lastCustPalette -= palettes;
|
||||
|
||||
// AI: below section was generated by an AI
|
||||
// Update slowly-smoothed band averages used by palettes 3 & 4.
|
||||
// Alpha=PALETTE_SMOOTHING gives ~400ms time constant at a 20ms update cycle,
|
||||
// so palette colours reflect the overall tonal character of the music rather than
|
||||
// reacting to individual beats (which would appear "twitchy").
|
||||
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
||||
paletteBandAvg[i] += PALETTE_SMOOTHING * ((float)fftResult[i] - paletteBandAvg[i]); // BUG: this IIR filter assumes 20ms activation rate (which is totally wrong)
|
||||
}
|
||||
// AI: end
|
||||
|
||||
for (int pal=0; pal<palettes; pal++) {
|
||||
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
||||
// 3 colors = 12, 4 colors = 16, etc.
|
||||
|
||||
Reference in New Issue
Block a user