mirror of
https://github.com/wled/WLED.git
synced 2025-04-25 07:17:18 +00:00
Merge pull request #3962 from gaaat98/esp8266-audioreactive-sync
ESP8266 audioreactive UDP sound sync ported from MoonModules/WLED - receive only
This commit is contained in:
commit
ea608cecb0
@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "wled.h"
|
#include "wled.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
#include <driver/i2s.h>
|
#include <driver/i2s.h>
|
||||||
#include <driver/adc.h>
|
#include <driver/adc.h>
|
||||||
|
|
||||||
@ -8,11 +11,9 @@
|
|||||||
#error This audio reactive usermod is not compatible with DMX Out.
|
#error This audio reactive usermod is not compatible with DMX Out.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
|
||||||
#error This audio reactive usermod does not support the ESP8266.
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
#if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG))
|
||||||
#include <esp_timer.h>
|
#include <esp_timer.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -57,6 +58,50 @@
|
|||||||
|
|
||||||
#define MAX_PALETTES 3
|
#define MAX_PALETTES 3
|
||||||
|
|
||||||
|
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)
|
||||||
|
static bool udpSyncConnected = false; // UDP connection status -> true if connected to multicast group
|
||||||
|
|
||||||
|
#define NUM_GEQ_CHANNELS 16 // number of frequency channels. Don't change !!
|
||||||
|
|
||||||
|
// audioreactive variables
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point
|
||||||
|
static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our AGC multiplier
|
||||||
|
static float sampleAvg = 0.0f; // Smoothed Average sample - sampleAvg < 1 means "quiet" (simple noise gate)
|
||||||
|
static float sampleAgc = 0.0f; // Smoothed AGC sample
|
||||||
|
static uint8_t soundAgc = 0; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value)
|
||||||
|
#endif
|
||||||
|
//static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
|
||||||
|
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
|
||||||
|
static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency
|
||||||
|
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
|
||||||
|
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// user settable parameters for limitSoundDynamics()
|
||||||
|
#ifdef UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF
|
||||||
|
static bool limiterOn = false; // bool: enable / disable dynamics limiter
|
||||||
|
#else
|
||||||
|
static bool limiterOn = true;
|
||||||
|
#endif
|
||||||
|
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
|
||||||
|
|
||||||
|
// peak detection
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
static void detectSamplePeak(void); // peak detection function (needs scaled FFT results in vReal[]) - no used for 8266 receive-only mode
|
||||||
|
#endif
|
||||||
|
static void autoResetPeak(void); // peak auto-reset function
|
||||||
|
static uint8_t maxVol = 31; // (was 10) Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated)
|
||||||
|
static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated)
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
// use audio source class (ESP32 specific)
|
// use audio source class (ESP32 specific)
|
||||||
#include "audio_source.h"
|
#include "audio_source.h"
|
||||||
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !)
|
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !)
|
||||||
@ -74,18 +119,6 @@ static uint8_t inputLevel = 128; // UI slider value
|
|||||||
#else
|
#else
|
||||||
uint8_t sampleGain = SR_GAIN; // sample gain (config value)
|
uint8_t sampleGain = SR_GAIN; // sample gain (config value)
|
||||||
#endif
|
#endif
|
||||||
static uint8_t soundAgc = 1; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value)
|
|
||||||
static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1 - receive (config value)
|
|
||||||
static bool udpSyncConnected = false; // UDP connection status -> true if connected to multicast group
|
|
||||||
|
|
||||||
// user settable parameters for limitSoundDynamics()
|
|
||||||
#ifdef UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF
|
|
||||||
static bool limiterOn = false; // bool: enable / disable dynamics limiter
|
|
||||||
#else
|
|
||||||
static bool limiterOn = true;
|
|
||||||
#endif
|
|
||||||
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
|
// user settable options for FFTResult scaling
|
||||||
static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized square root
|
static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized square root
|
||||||
|
|
||||||
@ -109,25 +142,8 @@ const float agcSampleSmooth[AGC_NUM_PRESETS] = { 1/12.f, 1/6.f, 1/16.f}; //
|
|||||||
// AGC presets end
|
// AGC presets end
|
||||||
|
|
||||||
static AudioSource *audioSource = nullptr;
|
static AudioSource *audioSource = nullptr;
|
||||||
static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks.
|
|
||||||
static bool useBandPassFilter = false; // if true, enables a bandpass filter 80Hz-16Khz to remove noise. Applies before FFT.
|
static bool useBandPassFilter = false; // if true, enables a bandpass filter 80Hz-16Khz to remove noise. Applies before FFT.
|
||||||
|
|
||||||
// audioreactive variables shared with FFT task
|
|
||||||
static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point
|
|
||||||
static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our AGC multiplier
|
|
||||||
static float sampleAvg = 0.0f; // Smoothed Average sample - sampleAvg < 1 means "quiet" (simple noise gate)
|
|
||||||
static float sampleAgc = 0.0f; // Smoothed AGC sample
|
|
||||||
|
|
||||||
// peak detection
|
|
||||||
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
|
|
||||||
static uint8_t maxVol = 31; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated)
|
|
||||||
static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated)
|
|
||||||
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
|
|
||||||
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
|
|
||||||
static void detectSamplePeak(void); // peak detection function (needs scaled FFT results in vReal[])
|
|
||||||
static void autoResetPeak(void); // peak auto-reset function
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// Begin FFT Code //
|
// Begin FFT Code //
|
||||||
////////////////////
|
////////////////////
|
||||||
@ -139,17 +155,12 @@ void FFTcode(void * parameter); // audio processing task: read samples, run
|
|||||||
static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass)
|
static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass)
|
||||||
static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels); // post-processing and post-amp of GEQ channels
|
static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels); // post-processing and post-amp of GEQ channels
|
||||||
|
|
||||||
#define NUM_GEQ_CHANNELS 16 // number of frequency channels. Don't change !!
|
|
||||||
|
|
||||||
static TaskHandle_t FFT_Task = nullptr;
|
static TaskHandle_t FFT_Task = nullptr;
|
||||||
|
|
||||||
// Table of multiplication factors so that we can even out the frequency response.
|
// Table of multiplication factors so that we can even out the frequency response.
|
||||||
static float fftResultPink[NUM_GEQ_CHANNELS] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f };
|
static float fftResultPink[NUM_GEQ_CHANNELS] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f };
|
||||||
|
|
||||||
// globals and FFT Output variables shared with animations
|
// globals and FFT Output variables shared with animations
|
||||||
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
|
|
||||||
static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency
|
|
||||||
static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects
|
|
||||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||||
static uint64_t fftTime = 0;
|
static uint64_t fftTime = 0;
|
||||||
static uint64_t sampleTime = 0;
|
static uint64_t sampleTime = 0;
|
||||||
@ -522,6 +533,8 @@ static void detectSamplePeak(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void autoResetPeak(void) {
|
static void autoResetPeak(void) {
|
||||||
uint16_t MinShowDelay = MAX(50, strip.getMinShowDelay()); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC
|
uint16_t MinShowDelay = MAX(50, strip.getMinShowDelay()); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC
|
||||||
if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed.
|
if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed.
|
||||||
@ -539,6 +552,8 @@ static void autoResetPeak(void) {
|
|||||||
class AudioReactive : public Usermod {
|
class AudioReactive : public Usermod {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
#ifndef AUDIOPIN
|
#ifndef AUDIOPIN
|
||||||
int8_t audioPin = -1;
|
int8_t audioPin = -1;
|
||||||
#else
|
#else
|
||||||
@ -570,20 +585,23 @@ class AudioReactive : public Usermod {
|
|||||||
#else
|
#else
|
||||||
int8_t mclkPin = MCLK_PIN;
|
int8_t mclkPin = MCLK_PIN;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// new "V2" audiosync struct - 40 Bytes
|
// new "V2" audiosync struct - 44 Bytes
|
||||||
struct audioSyncPacket {
|
struct __attribute__ ((packed)) audioSyncPacket { // "packed" ensures that there are no additional gaps
|
||||||
char header[6]; // 06 Bytes
|
char header[6]; // 06 Bytes offset 0
|
||||||
float sampleRaw; // 04 Bytes - either "sampleRaw" or "rawSampleAgc" depending on soundAgc setting
|
uint8_t reserved1[2]; // 02 Bytes, offset 6 - gap required by the compiler - not used yet
|
||||||
float sampleSmth; // 04 Bytes - either "sampleAvg" or "sampleAgc" depending on soundAgc setting
|
float sampleRaw; // 04 Bytes offset 8 - either "sampleRaw" or "rawSampleAgc" depending on soundAgc setting
|
||||||
uint8_t samplePeak; // 01 Bytes - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude
|
float sampleSmth; // 04 Bytes offset 12 - either "sampleAvg" or "sampleAgc" depending on soundAgc setting
|
||||||
uint8_t reserved1; // 01 Bytes - for future extensions - not used yet
|
uint8_t samplePeak; // 01 Bytes offset 16 - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude
|
||||||
uint8_t fftResult[16]; // 16 Bytes
|
uint8_t reserved2; // 01 Bytes offset 17 - for future extensions - not used yet
|
||||||
float FFT_Magnitude; // 04 Bytes
|
uint8_t fftResult[16]; // 16 Bytes offset 18
|
||||||
float FFT_MajorPeak; // 04 Bytes
|
uint16_t reserved3; // 02 Bytes, offset 34 - gap required by the compiler - not used yet
|
||||||
|
float FFT_Magnitude; // 04 Bytes offset 36
|
||||||
|
float FFT_MajorPeak; // 04 Bytes offset 40
|
||||||
};
|
};
|
||||||
|
|
||||||
// old "V1" audiosync struct - 83 Bytes - for backwards compatibility
|
// old "V1" audiosync struct - 83 Bytes payload, 88 bytes total (with padding added by compiler) - for backwards compatibility
|
||||||
struct audioSyncPacket_v1 {
|
struct audioSyncPacket_v1 {
|
||||||
char header[6]; // 06 Bytes
|
char header[6]; // 06 Bytes
|
||||||
uint8_t myVals[32]; // 32 Bytes
|
uint8_t myVals[32]; // 32 Bytes
|
||||||
@ -596,6 +614,8 @@ class AudioReactive : public Usermod {
|
|||||||
double FFT_MajorPeak; // 08 Bytes
|
double FFT_MajorPeak; // 08 Bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define UDPSOUND_MAX_PACKET 88 // max packet size for audiosync
|
||||||
|
|
||||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||||
#ifdef UM_AUDIOREACTIVE_ENABLE
|
#ifdef UM_AUDIOREACTIVE_ENABLE
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
@ -613,10 +633,14 @@ class AudioReactive : public Usermod {
|
|||||||
const uint16_t delayMs = 10; // I don't want to sample too often and overload WLED
|
const uint16_t delayMs = 10; // I don't want to sample too often and overload WLED
|
||||||
uint16_t audioSyncPort= 11988;// default port for UDP sound sync
|
uint16_t audioSyncPort= 11988;// default port for UDP sound sync
|
||||||
|
|
||||||
|
bool updateIsRunning = false; // true during OTA.
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
// used for AGC
|
// used for AGC
|
||||||
int last_soundAgc = -1; // used to detect AGC mode change (for resetting AGC internal error buffers)
|
int last_soundAgc = -1; // used to detect AGC mode change (for resetting AGC internal error buffers)
|
||||||
double control_integrated = 0.0; // persistent across calls to agcAvg(); "integrator control" = accumulated error
|
double control_integrated = 0.0; // persistent across calls to agcAvg(); "integrator control" = accumulated error
|
||||||
|
|
||||||
|
|
||||||
// variables used by getSample() and agcAvg()
|
// variables used by getSample() and agcAvg()
|
||||||
int16_t micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed
|
int16_t micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed
|
||||||
double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controller.
|
double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controller.
|
||||||
@ -625,6 +649,7 @@ class AudioReactive : public Usermod {
|
|||||||
float sampleReal = 0.0f; // "sampleRaw" as float, to provide bits that are lost otherwise (before amplification by sampleGain or inputLevel). Needed for AGC.
|
float sampleReal = 0.0f; // "sampleRaw" as float, to provide bits that are lost otherwise (before amplification by sampleGain or inputLevel). Needed for AGC.
|
||||||
int16_t sampleRaw = 0; // Current sample. Must only be updated ONCE!!! (amplified mic value by sampleGain and inputLevel)
|
int16_t sampleRaw = 0; // Current sample. Must only be updated ONCE!!! (amplified mic value by sampleGain and inputLevel)
|
||||||
int16_t rawSampleAgc = 0; // not smoothed AGC sample
|
int16_t rawSampleAgc = 0; // not smoothed AGC sample
|
||||||
|
#endif
|
||||||
|
|
||||||
// variables used in effects
|
// variables used in effects
|
||||||
float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
|
float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
|
||||||
@ -645,7 +670,9 @@ class AudioReactive : public Usermod {
|
|||||||
static const char _dynamics[];
|
static const char _dynamics[];
|
||||||
static const char _frequency[];
|
static const char _frequency[];
|
||||||
static const char _inputLvl[];
|
static const char _inputLvl[];
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
static const char _analogmic[];
|
static const char _analogmic[];
|
||||||
|
#endif
|
||||||
static const char _digitalmic[];
|
static const char _digitalmic[];
|
||||||
static const char _addPalettes[];
|
static const char _addPalettes[];
|
||||||
static const char UDP_SYNC_HEADER[];
|
static const char UDP_SYNC_HEADER[];
|
||||||
@ -672,11 +699,13 @@ class AudioReactive : public Usermod {
|
|||||||
//PLOT_PRINT("sampleAgc:"); PLOT_PRINT(sampleAgc); PLOT_PRINT("\t");
|
//PLOT_PRINT("sampleAgc:"); PLOT_PRINT(sampleAgc); PLOT_PRINT("\t");
|
||||||
//PLOT_PRINT("sampleAvg:"); PLOT_PRINT(sampleAvg); PLOT_PRINT("\t");
|
//PLOT_PRINT("sampleAvg:"); PLOT_PRINT(sampleAvg); PLOT_PRINT("\t");
|
||||||
//PLOT_PRINT("sampleReal:"); PLOT_PRINT(sampleReal); PLOT_PRINT("\t");
|
//PLOT_PRINT("sampleReal:"); PLOT_PRINT(sampleReal); PLOT_PRINT("\t");
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
//PLOT_PRINT("micIn:"); PLOT_PRINT(micIn); PLOT_PRINT("\t");
|
//PLOT_PRINT("micIn:"); PLOT_PRINT(micIn); PLOT_PRINT("\t");
|
||||||
//PLOT_PRINT("sample:"); PLOT_PRINT(sample); PLOT_PRINT("\t");
|
//PLOT_PRINT("sample:"); PLOT_PRINT(sample); PLOT_PRINT("\t");
|
||||||
//PLOT_PRINT("sampleMax:"); PLOT_PRINT(sampleMax); PLOT_PRINT("\t");
|
//PLOT_PRINT("sampleMax:"); PLOT_PRINT(sampleMax); PLOT_PRINT("\t");
|
||||||
//PLOT_PRINT("samplePeak:"); PLOT_PRINT((samplePeak!=0) ? 128:0); PLOT_PRINT("\t");
|
//PLOT_PRINT("samplePeak:"); PLOT_PRINT((samplePeak!=0) ? 128:0); PLOT_PRINT("\t");
|
||||||
//PLOT_PRINT("multAgc:"); PLOT_PRINT(multAgc, 4); PLOT_PRINT("\t");
|
//PLOT_PRINT("multAgc:"); PLOT_PRINT(multAgc, 4); PLOT_PRINT("\t");
|
||||||
|
#endif
|
||||||
PLOT_PRINTLN();
|
PLOT_PRINTLN();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -732,6 +761,7 @@ class AudioReactive : public Usermod {
|
|||||||
} // logAudio()
|
} // logAudio()
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
//////////////////////
|
//////////////////////
|
||||||
// Audio Processing //
|
// Audio Processing //
|
||||||
//////////////////////
|
//////////////////////
|
||||||
@ -902,6 +932,7 @@ class AudioReactive : public Usermod {
|
|||||||
sampleAvg = fabsf(sampleAvg); // make sure we have a positive value
|
sampleAvg = fabsf(sampleAvg); // make sure we have a positive value
|
||||||
} // getSample()
|
} // getSample()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Limits the dynamics of volumeSmth (= sampleAvg or sampleAgc).
|
/* Limits the dynamics of volumeSmth (= sampleAvg or sampleAgc).
|
||||||
* does not affect FFTResult[] or volumeRaw ( = sample or rawSampleAgc)
|
* does not affect FFTResult[] or volumeRaw ( = sample or rawSampleAgc)
|
||||||
@ -948,12 +979,14 @@ class AudioReactive : public Usermod {
|
|||||||
if (udpSyncConnected) return; // already connected
|
if (udpSyncConnected) return; // already connected
|
||||||
if (!(apActive || interfacesInited)) return; // neither AP nor other connections availeable
|
if (!(apActive || interfacesInited)) return; // neither AP nor other connections availeable
|
||||||
if (millis() - last_connection_attempt < 15000) return; // only try once in 15 seconds
|
if (millis() - last_connection_attempt < 15000) return; // only try once in 15 seconds
|
||||||
|
if (updateIsRunning) return;
|
||||||
|
|
||||||
// if we arrive here, we need a UDP connection but don't have one
|
// if we arrive here, we need a UDP connection but don't have one
|
||||||
last_connection_attempt = millis();
|
last_connection_attempt = millis();
|
||||||
connected(); // try to start UDP
|
connected(); // try to start UDP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
void transmitAudioData()
|
void transmitAudioData()
|
||||||
{
|
{
|
||||||
if (!udpSyncConnected) return;
|
if (!udpSyncConnected) return;
|
||||||
@ -968,7 +1001,6 @@ class AudioReactive : public Usermod {
|
|||||||
transmitData.sampleSmth = (soundAgc) ? sampleAgc : sampleAvg;
|
transmitData.sampleSmth = (soundAgc) ? sampleAgc : sampleAvg;
|
||||||
transmitData.samplePeak = udpSamplePeak ? 1:0;
|
transmitData.samplePeak = udpSamplePeak ? 1:0;
|
||||||
udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it
|
udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it
|
||||||
transmitData.reserved1 = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
||||||
transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254);
|
transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254);
|
||||||
@ -984,37 +1016,44 @@ class AudioReactive : public Usermod {
|
|||||||
return;
|
return;
|
||||||
} // transmitAudioData()
|
} // transmitAudioData()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool isValidUdpSyncVersion(const char *header) {
|
static bool isValidUdpSyncVersion(const char *header) {
|
||||||
return strncmp_P(header, PSTR(UDP_SYNC_HEADER), 6) == 0;
|
return strncmp_P(header, UDP_SYNC_HEADER, 6) == 0;
|
||||||
}
|
}
|
||||||
static bool isValidUdpSyncVersion_v1(const char *header) {
|
static bool isValidUdpSyncVersion_v1(const char *header) {
|
||||||
return strncmp_P(header, PSTR(UDP_SYNC_HEADER_v1), 6) == 0;
|
return strncmp_P(header, UDP_SYNC_HEADER_v1, 6) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void decodeAudioData(int packetSize, uint8_t *fftBuff) {
|
void decodeAudioData(int packetSize, uint8_t *fftBuff) {
|
||||||
audioSyncPacket *receivedPacket = reinterpret_cast<audioSyncPacket*>(fftBuff);
|
audioSyncPacket receivedPacket;
|
||||||
|
memset(&receivedPacket, 0, sizeof(receivedPacket)); // start clean
|
||||||
|
memcpy(&receivedPacket, fftBuff, min((unsigned)packetSize, (unsigned)sizeof(receivedPacket))); // don't violate alignment - thanks @willmmiles#
|
||||||
|
|
||||||
// update samples for effects
|
// update samples for effects
|
||||||
volumeSmth = fmaxf(receivedPacket->sampleSmth, 0.0f);
|
volumeSmth = fmaxf(receivedPacket.sampleSmth, 0.0f);
|
||||||
volumeRaw = fmaxf(receivedPacket->sampleRaw, 0.0f);
|
volumeRaw = fmaxf(receivedPacket.sampleRaw, 0.0f);
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
// update internal samples
|
// update internal samples
|
||||||
sampleRaw = volumeRaw;
|
sampleRaw = volumeRaw;
|
||||||
sampleAvg = volumeSmth;
|
sampleAvg = volumeSmth;
|
||||||
rawSampleAgc = volumeRaw;
|
rawSampleAgc = volumeRaw;
|
||||||
sampleAgc = volumeSmth;
|
sampleAgc = volumeSmth;
|
||||||
multAgc = 1.0f;
|
multAgc = 1.0f;
|
||||||
|
#endif
|
||||||
// Only change samplePeak IF it's currently false.
|
// Only change samplePeak IF it's currently false.
|
||||||
// If it's true already, then the animation still needs to respond.
|
// If it's true already, then the animation still needs to respond.
|
||||||
autoResetPeak();
|
autoResetPeak();
|
||||||
if (!samplePeak) {
|
if (!samplePeak) {
|
||||||
samplePeak = receivedPacket->samplePeak >0 ? true:false;
|
samplePeak = receivedPacket.samplePeak >0 ? true:false;
|
||||||
if (samplePeak) timeOfPeak = millis();
|
if (samplePeak) timeOfPeak = millis();
|
||||||
//userVar1 = samplePeak;
|
//userVar1 = samplePeak;
|
||||||
}
|
}
|
||||||
//These values are only available on the ESP32
|
//These values are only computed by ESP32
|
||||||
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) fftResult[i] = receivedPacket->fftResult[i];
|
for (int i = 0; i < NUM_GEQ_CHANNELS; i++) fftResult[i] = receivedPacket.fftResult[i];
|
||||||
my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f);
|
my_magnitude = fmaxf(receivedPacket.FFT_Magnitude, 0.0f);
|
||||||
FFT_Magnitude = my_magnitude;
|
FFT_Magnitude = my_magnitude;
|
||||||
FFT_MajorPeak = constrain(receivedPacket->FFT_MajorPeak, 1.0f, 11025.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
|
||||||
}
|
}
|
||||||
|
|
||||||
void decodeAudioData_v1(int packetSize, uint8_t *fftBuff) {
|
void decodeAudioData_v1(int packetSize, uint8_t *fftBuff) {
|
||||||
@ -1022,12 +1061,14 @@ class AudioReactive : public Usermod {
|
|||||||
// update samples for effects
|
// update samples for effects
|
||||||
volumeSmth = fmaxf(receivedPacket->sampleAgc, 0.0f);
|
volumeSmth = fmaxf(receivedPacket->sampleAgc, 0.0f);
|
||||||
volumeRaw = volumeSmth; // V1 format does not have "raw" AGC sample
|
volumeRaw = volumeSmth; // V1 format does not have "raw" AGC sample
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
// update internal samples
|
// update internal samples
|
||||||
sampleRaw = fmaxf(receivedPacket->sampleRaw, 0.0f);
|
sampleRaw = fmaxf(receivedPacket->sampleRaw, 0.0f);
|
||||||
sampleAvg = fmaxf(receivedPacket->sampleAvg, 0.0f);;
|
sampleAvg = fmaxf(receivedPacket->sampleAvg, 0.0f);;
|
||||||
sampleAgc = volumeSmth;
|
sampleAgc = volumeSmth;
|
||||||
rawSampleAgc = volumeRaw;
|
rawSampleAgc = volumeRaw;
|
||||||
multAgc = 1.0f;
|
multAgc = 1.0f;
|
||||||
|
#endif
|
||||||
// Only change samplePeak IF it's currently false.
|
// Only change samplePeak IF it's currently false.
|
||||||
// If it's true already, then the animation still needs to respond.
|
// If it's true already, then the animation still needs to respond.
|
||||||
autoResetPeak();
|
autoResetPeak();
|
||||||
@ -1049,9 +1090,12 @@ class AudioReactive : public Usermod {
|
|||||||
bool haveFreshData = false;
|
bool haveFreshData = false;
|
||||||
|
|
||||||
size_t packetSize = fftUdp.parsePacket();
|
size_t packetSize = fftUdp.parsePacket();
|
||||||
if (packetSize > 5) {
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
if ((packetSize > 0) && ((packetSize < 5) || (packetSize > UDPSOUND_MAX_PACKET))) fftUdp.flush(); // discard invalid packets (too small or too big) - only works on esp32
|
||||||
|
#endif
|
||||||
|
if ((packetSize > 5) && (packetSize <= UDPSOUND_MAX_PACKET)) {
|
||||||
//DEBUGSR_PRINTLN("Received UDP Sync Packet");
|
//DEBUGSR_PRINTLN("Received UDP Sync Packet");
|
||||||
uint8_t fftBuff[packetSize];
|
uint8_t fftBuff[UDPSOUND_MAX_PACKET+1] = { 0 }; // fixed-size buffer for receiving (stack), to avoid heap fragmentation caused by variable sized arrays
|
||||||
fftUdp.read(fftBuff, packetSize);
|
fftUdp.read(fftBuff, packetSize);
|
||||||
|
|
||||||
// VERIFY THAT THIS IS A COMPATIBLE PACKET
|
// VERIFY THAT THIS IS A COMPATIBLE PACKET
|
||||||
@ -1113,6 +1157,9 @@ class AudioReactive : public Usermod {
|
|||||||
um_data->u_type[7] = UMT_BYTE;
|
um_data->u_type[7] = UMT_BYTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
// Reset I2S peripheral for good measure
|
// Reset I2S peripheral for good measure
|
||||||
i2s_driver_uninstall(I2S_NUM_0); // E (696) I2S: i2s_driver_uninstall(2006): I2S port 0 has not installed
|
i2s_driver_uninstall(I2S_NUM_0); // E (696) I2S: i2s_driver_uninstall(2006): I2S port 0 has not installed
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
@ -1190,10 +1237,12 @@ class AudioReactive : public Usermod {
|
|||||||
delay(250); // give microphone enough time to initialise
|
delay(250); // give microphone enough time to initialise
|
||||||
|
|
||||||
if (!audioSource) enabled = false; // audio failed to initialise
|
if (!audioSource) enabled = false; // audio failed to initialise
|
||||||
if (enabled) onUpdateBegin(false); // create FFT task
|
#endif
|
||||||
if (FFT_Task == nullptr) enabled = false; // FFT task creation failed
|
if (enabled) onUpdateBegin(false); // create FFT task, and initialize network
|
||||||
if (enabled) disableSoundProcessing = false; // all good - enable audio processing
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
if (FFT_Task == nullptr) enabled = false; // FFT task creation failed
|
||||||
if((!audioSource) || (!audioSource->isInitialized())) { // audio source failed to initialize. Still stay "enabled", as there might be input arriving via UDP Sound Sync
|
if((!audioSource) || (!audioSource->isInitialized())) { // audio source failed to initialize. Still stay "enabled", as there might be input arriving via UDP Sound Sync
|
||||||
#ifdef WLED_DEBUG
|
#ifdef WLED_DEBUG
|
||||||
DEBUG_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));
|
DEBUG_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));
|
||||||
@ -1202,7 +1251,8 @@ class AudioReactive : public Usermod {
|
|||||||
#endif
|
#endif
|
||||||
disableSoundProcessing = true;
|
disableSoundProcessing = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
if (enabled) disableSoundProcessing = false; // all good - enable audio processing
|
||||||
if (enabled) connectUDPSoundSync();
|
if (enabled) connectUDPSoundSync();
|
||||||
if (enabled && addPalettes) createAudioPalettes();
|
if (enabled && addPalettes) createAudioPalettes();
|
||||||
initDone = true;
|
initDone = true;
|
||||||
@ -1221,7 +1271,7 @@ class AudioReactive : public Usermod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) {
|
if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) {
|
||||||
#ifndef ESP8266
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort);
|
udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort);
|
||||||
#else
|
#else
|
||||||
udpSyncConnected = fftUdp.beginMulticast(WiFi.localIP(), IPAddress(239, 0, 0, 1), audioSyncPort);
|
udpSyncConnected = fftUdp.beginMulticast(WiFi.localIP(), IPAddress(239, 0, 0, 1), audioSyncPort);
|
||||||
@ -1260,7 +1310,7 @@ class AudioReactive : public Usermod {
|
|||||||
||(realtimeMode == REALTIME_MODE_ADALIGHT)
|
||(realtimeMode == REALTIME_MODE_ADALIGHT)
|
||||||
||(realtimeMode == REALTIME_MODE_ARTNET) ) ) // please add other modes here if needed
|
||(realtimeMode == REALTIME_MODE_ARTNET) ) ) // please add other modes here if needed
|
||||||
{
|
{
|
||||||
#ifdef WLED_DEBUG
|
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DEBUG)
|
||||||
if ((disableSoundProcessing == false) && (audioSyncEnabled == 0)) { // we just switched to "disabled"
|
if ((disableSoundProcessing == false) && (audioSyncEnabled == 0)) { // we just switched to "disabled"
|
||||||
DEBUG_PRINTLN(F("[AR userLoop] realtime mode active - audio processing suspended."));
|
DEBUG_PRINTLN(F("[AR userLoop] realtime mode active - audio processing suspended."));
|
||||||
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
|
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
|
||||||
@ -1268,7 +1318,7 @@ class AudioReactive : public Usermod {
|
|||||||
#endif
|
#endif
|
||||||
disableSoundProcessing = true;
|
disableSoundProcessing = true;
|
||||||
} else {
|
} else {
|
||||||
#ifdef WLED_DEBUG
|
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DEBUG)
|
||||||
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource->isInitialized()) { // we just switched to "enabled"
|
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource->isInitialized()) { // we just switched to "enabled"
|
||||||
DEBUG_PRINTLN(F("[AR userLoop] realtime mode ended - audio processing resumed."));
|
DEBUG_PRINTLN(F("[AR userLoop] realtime mode ended - audio processing resumed."));
|
||||||
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
|
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
|
||||||
@ -1280,6 +1330,7 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
if (audioSyncEnabled & 0x02) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode
|
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 (audioSyncEnabled & 0x01) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
if (!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source
|
if (!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source
|
||||||
|
|
||||||
|
|
||||||
@ -1319,6 +1370,7 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
limitSampleDynamics();
|
limitSampleDynamics();
|
||||||
} // if (!disableSoundProcessing)
|
} // if (!disableSoundProcessing)
|
||||||
|
#endif
|
||||||
|
|
||||||
autoResetPeak(); // auto-reset sample peak after strip minShowDelay
|
autoResetPeak(); // auto-reset sample peak after strip minShowDelay
|
||||||
if (!udpSyncConnected) udpSamplePeak = false; // reset UDP samplePeak while UDP is unconnected
|
if (!udpSyncConnected) udpSamplePeak = false; // reset UDP samplePeak while UDP is unconnected
|
||||||
@ -1352,6 +1404,7 @@ class AudioReactive : public Usermod {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Info Page: keep max sample from last 5 seconds
|
// Info Page: keep max sample from last 5 seconds
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
|
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
|
||||||
sampleMaxTimer = millis();
|
sampleMaxTimer = millis();
|
||||||
maxSample5sec = (0.15f * maxSample5sec) + 0.85f *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing
|
maxSample5sec = (0.15f * maxSample5sec) + 0.85f *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing
|
||||||
@ -1359,13 +1412,25 @@ class AudioReactive : public Usermod {
|
|||||||
} else {
|
} else {
|
||||||
if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume
|
if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume
|
||||||
}
|
}
|
||||||
|
#else // similar functionality for 8266 receive only - use VolumeSmth instead of raw sample data
|
||||||
|
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
|
||||||
|
sampleMaxTimer = millis();
|
||||||
|
maxSample5sec = (0.15 * maxSample5sec) + 0.85 * volumeSmth; // reset, and start with some smoothing
|
||||||
|
if (volumeSmth < 1.0f) maxSample5sec = 0; // noise gate
|
||||||
|
if (maxSample5sec < 0.0f) maxSample5sec = 0; // avoid negative values
|
||||||
|
} else {
|
||||||
|
if (volumeSmth >= 1.0f) maxSample5sec = fmaxf(maxSample5sec, volumeRaw); // follow maximum volume
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
//UDP Microphone Sync - transmit mode
|
//UDP Microphone Sync - transmit mode
|
||||||
if ((audioSyncEnabled & 0x01) && (millis() - lastTime > 20)) {
|
if ((audioSyncEnabled & 0x01) && (millis() - lastTime > 20)) {
|
||||||
// Only run the transmit code IF we're in Transmit mode
|
// Only run the transmit code IF we're in Transmit mode
|
||||||
transmitAudioData();
|
transmitAudioData();
|
||||||
lastTime = millis();
|
lastTime = millis();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
fillAudioPalettes();
|
fillAudioPalettes();
|
||||||
}
|
}
|
||||||
@ -1378,7 +1443,7 @@ class AudioReactive : public Usermod {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
void onUpdateBegin(bool init) override
|
void onUpdateBegin(bool init) override
|
||||||
{
|
{
|
||||||
#ifdef WLED_DEBUG
|
#ifdef WLED_DEBUG
|
||||||
@ -1427,9 +1492,32 @@ class AudioReactive : public Usermod {
|
|||||||
}
|
}
|
||||||
micDataReal = 0.0f; // just to be sure
|
micDataReal = 0.0f; // just to be sure
|
||||||
if (enabled) disableSoundProcessing = false;
|
if (enabled) disableSoundProcessing = false;
|
||||||
|
updateIsRunning = init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else // reduced function for 8266
|
||||||
|
void onUpdateBegin(bool init)
|
||||||
|
{
|
||||||
|
// gracefully suspend audio (if running)
|
||||||
|
disableSoundProcessing = true;
|
||||||
|
// 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
|
||||||
|
autoResetPeak();
|
||||||
|
if (init) {
|
||||||
|
if (udpSyncConnected) { // close UDP sync connection (if open)
|
||||||
|
udpSyncConnected = false;
|
||||||
|
fftUdp.stop();
|
||||||
|
DEBUGSR_PRINTLN(F("AR onUpdateBegin(true): UDP connection closed."));
|
||||||
|
receivedFormat = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enabled) disableSoundProcessing = init; // init = true means that OTA is just starting --> don't process audio
|
||||||
|
updateIsRunning = init;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
/**
|
/**
|
||||||
* handleButton() can be used to override default button behaviour. Returning true
|
* handleButton() can be used to override default button behaviour. Returning true
|
||||||
* will prevent button working in a default way.
|
* will prevent button working in a default way.
|
||||||
@ -1447,7 +1535,7 @@ class AudioReactive : public Usermod {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
// Settings and Info Page //
|
// Settings and Info Page //
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
@ -1459,7 +1547,9 @@ class AudioReactive : public Usermod {
|
|||||||
*/
|
*/
|
||||||
void addToJsonInfo(JsonObject& root) override
|
void addToJsonInfo(JsonObject& root) override
|
||||||
{
|
{
|
||||||
char myStringBuffer[16]; // buffer for snprintf()
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
char myStringBuffer[16]; // buffer for snprintf() - not used yet on 8266
|
||||||
|
#endif
|
||||||
JsonObject user = root["u"];
|
JsonObject user = root["u"];
|
||||||
if (user.isNull()) user = root.createNestedObject("u");
|
if (user.isNull()) user = root.createNestedObject("u");
|
||||||
|
|
||||||
@ -1477,6 +1567,7 @@ class AudioReactive : public Usermod {
|
|||||||
infoArr.add(uiDomString);
|
infoArr.add(uiDomString);
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
// Input Level Slider
|
// Input Level Slider
|
||||||
if (disableSoundProcessing == false) { // only show slider when audio processing is running
|
if (disableSoundProcessing == false) { // only show slider when audio processing is running
|
||||||
if (soundAgc > 0) {
|
if (soundAgc > 0) {
|
||||||
@ -1493,7 +1584,7 @@ class AudioReactive : public Usermod {
|
|||||||
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
|
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
|
||||||
infoArr.add(uiDomString);
|
infoArr.add(uiDomString);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
// The following can be used for troubleshooting user errors and is so not enclosed in #ifdef WLED_DEBUG
|
// The following can be used for troubleshooting user errors and is so not enclosed in #ifdef WLED_DEBUG
|
||||||
|
|
||||||
// current Audio input
|
// current Audio input
|
||||||
@ -1509,6 +1600,11 @@ class AudioReactive : public Usermod {
|
|||||||
} else {
|
} else {
|
||||||
infoArr.add(F(" - no connection"));
|
infoArr.add(F(" - no connection"));
|
||||||
}
|
}
|
||||||
|
#ifndef ARDUINO_ARCH_ESP32 // substitute for 8266
|
||||||
|
} else {
|
||||||
|
infoArr.add(F("sound sync Off"));
|
||||||
|
}
|
||||||
|
#else // ESP32 only
|
||||||
} else {
|
} else {
|
||||||
// Analog or I2S digital input
|
// Analog or I2S digital input
|
||||||
if (audioSource && (audioSource->isInitialized())) {
|
if (audioSource && (audioSource->isInitialized())) {
|
||||||
@ -1553,7 +1649,7 @@ class AudioReactive : public Usermod {
|
|||||||
infoArr.add(roundf(multAgc*100.0f) / 100.0f);
|
infoArr.add(roundf(multAgc*100.0f) / 100.0f);
|
||||||
infoArr.add("x");
|
infoArr.add("x");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
// UDP Sound Sync status
|
// UDP Sound Sync status
|
||||||
infoArr = user.createNestedArray(F("UDP Sound Sync"));
|
infoArr = user.createNestedArray(F("UDP Sound Sync"));
|
||||||
if (audioSyncEnabled) {
|
if (audioSyncEnabled) {
|
||||||
@ -1572,6 +1668,7 @@ class AudioReactive : public Usermod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
infoArr = user.createNestedArray(F("Sampling time"));
|
infoArr = user.createNestedArray(F("Sampling time"));
|
||||||
infoArr.add(float(sampleTime)/100.0f);
|
infoArr.add(float(sampleTime)/100.0f);
|
||||||
infoArr.add(" ms");
|
infoArr.add(" ms");
|
||||||
@ -1588,6 +1685,7 @@ class AudioReactive : public Usermod {
|
|||||||
DEBUGSR_PRINTF("AR Sampling time: %5.2f ms\n", float(sampleTime)/100.0f);
|
DEBUGSR_PRINTF("AR Sampling time: %5.2f ms\n", float(sampleTime)/100.0f);
|
||||||
DEBUGSR_PRINTF("AR FFT time : %5.2f ms\n", float(fftTime)/100.0f);
|
DEBUGSR_PRINTF("AR FFT time : %5.2f ms\n", float(fftTime)/100.0f);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1626,9 +1724,11 @@ class AudioReactive : public Usermod {
|
|||||||
if (!prevEnabled && enabled) createAudioPalettes();
|
if (!prevEnabled && enabled) createAudioPalettes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
if (usermod[FPSTR(_inputLvl)].is<int>()) {
|
if (usermod[FPSTR(_inputLvl)].is<int>()) {
|
||||||
inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as<int>()));
|
inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as<int>()));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as<bool>()) {
|
if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as<bool>()) {
|
||||||
// handle removal of custom palettes from JSON call so we don't break things
|
// handle removal of custom palettes from JSON call so we don't break things
|
||||||
@ -1684,6 +1784,7 @@ class AudioReactive : public Usermod {
|
|||||||
top[FPSTR(_enabled)] = enabled;
|
top[FPSTR(_enabled)] = enabled;
|
||||||
top[FPSTR(_addPalettes)] = addPalettes;
|
top[FPSTR(_addPalettes)] = addPalettes;
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
||||||
amic["pin"] = audioPin;
|
amic["pin"] = audioPin;
|
||||||
@ -1702,14 +1803,15 @@ class AudioReactive : public Usermod {
|
|||||||
cfg[F("gain")] = sampleGain;
|
cfg[F("gain")] = sampleGain;
|
||||||
cfg[F("AGC")] = soundAgc;
|
cfg[F("AGC")] = soundAgc;
|
||||||
|
|
||||||
|
JsonObject freqScale = top.createNestedObject(FPSTR(_frequency));
|
||||||
|
freqScale[F("scale")] = FFTScalingMode;
|
||||||
|
#endif
|
||||||
|
|
||||||
JsonObject dynLim = top.createNestedObject(FPSTR(_dynamics));
|
JsonObject dynLim = top.createNestedObject(FPSTR(_dynamics));
|
||||||
dynLim[F("limiter")] = limiterOn;
|
dynLim[F("limiter")] = limiterOn;
|
||||||
dynLim[F("rise")] = attackTime;
|
dynLim[F("rise")] = attackTime;
|
||||||
dynLim[F("fall")] = decayTime;
|
dynLim[F("fall")] = decayTime;
|
||||||
|
|
||||||
JsonObject freqScale = top.createNestedObject(FPSTR(_frequency));
|
|
||||||
freqScale[F("scale")] = FFTScalingMode;
|
|
||||||
|
|
||||||
JsonObject sync = top.createNestedObject("sync");
|
JsonObject sync = top.createNestedObject("sync");
|
||||||
sync["port"] = audioSyncPort;
|
sync["port"] = audioSyncPort;
|
||||||
sync["mode"] = audioSyncEnabled;
|
sync["mode"] = audioSyncEnabled;
|
||||||
@ -1741,6 +1843,7 @@ class AudioReactive : public Usermod {
|
|||||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
|
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||||
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
|
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
|
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
|
||||||
#else
|
#else
|
||||||
@ -1764,12 +1867,12 @@ class AudioReactive : public Usermod {
|
|||||||
configComplete &= getJsonValue(top[FPSTR(_config)][F("gain")], sampleGain);
|
configComplete &= getJsonValue(top[FPSTR(_config)][F("gain")], sampleGain);
|
||||||
configComplete &= getJsonValue(top[FPSTR(_config)][F("AGC")], soundAgc);
|
configComplete &= getJsonValue(top[FPSTR(_config)][F("AGC")], soundAgc);
|
||||||
|
|
||||||
|
configComplete &= getJsonValue(top[FPSTR(_frequency)][F("scale")], FFTScalingMode);
|
||||||
|
|
||||||
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("limiter")], limiterOn);
|
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("limiter")], limiterOn);
|
||||||
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("rise")], attackTime);
|
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("rise")], attackTime);
|
||||||
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("fall")], decayTime);
|
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("fall")], decayTime);
|
||||||
|
#endif
|
||||||
configComplete &= getJsonValue(top[FPSTR(_frequency)][F("scale")], FFTScalingMode);
|
|
||||||
|
|
||||||
configComplete &= getJsonValue(top["sync"]["port"], audioSyncPort);
|
configComplete &= getJsonValue(top["sync"]["port"], audioSyncPort);
|
||||||
configComplete &= getJsonValue(top["sync"]["mode"], audioSyncEnabled);
|
configComplete &= getJsonValue(top["sync"]["mode"], audioSyncEnabled);
|
||||||
|
|
||||||
@ -1784,6 +1887,7 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
void appendConfigData() override
|
void appendConfigData() override
|
||||||
{
|
{
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');"));
|
oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');"));
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
oappend(SET_F("addOption(dd,'Generic Analog',0);"));
|
oappend(SET_F("addOption(dd,'Generic Analog',0);"));
|
||||||
@ -1815,11 +1919,15 @@ class AudioReactive : public Usermod {
|
|||||||
oappend(SET_F("addOption(dd,'Linear (Amplitude)',2);"));
|
oappend(SET_F("addOption(dd,'Linear (Amplitude)',2);"));
|
||||||
oappend(SET_F("addOption(dd,'Square Root (Energy)',3);"));
|
oappend(SET_F("addOption(dd,'Square Root (Energy)',3);"));
|
||||||
oappend(SET_F("addOption(dd,'Logarithmic (Loudness)',1);"));
|
oappend(SET_F("addOption(dd,'Logarithmic (Loudness)',1);"));
|
||||||
|
#endif
|
||||||
|
|
||||||
oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');"));
|
oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');"));
|
||||||
oappend(SET_F("addOption(dd,'Off',0);"));
|
oappend(SET_F("addOption(dd,'Off',0);"));
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
oappend(SET_F("addOption(dd,'Send',1);"));
|
oappend(SET_F("addOption(dd,'Send',1);"));
|
||||||
|
#endif
|
||||||
oappend(SET_F("addOption(dd,'Receive',2);"));
|
oappend(SET_F("addOption(dd,'Receive',2);"));
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:type',1,'<i>requires reboot!</i>');")); // 0 is field type, 1 is actual field
|
oappend(SET_F("addInfo('AudioReactive:digitalmic:type',1,'<i>requires reboot!</i>');")); // 0 is field type, 1 is actual field
|
||||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'<i>sd/data/dout</i>','I2S SD');"));
|
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'<i>sd/data/dout</i>','I2S SD');"));
|
||||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'<i>ws/clk/lrck</i>','I2S WS');"));
|
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'<i>ws/clk/lrck</i>','I2S WS');"));
|
||||||
@ -1829,6 +1937,7 @@ class AudioReactive : public Usermod {
|
|||||||
#else
|
#else
|
||||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'<i>master clock</i>','I2S MCLK');"));
|
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'<i>master clock</i>','I2S MCLK');"));
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1907,8 +2016,8 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) {
|
|||||||
void AudioReactive::fillAudioPalettes() {
|
void AudioReactive::fillAudioPalettes() {
|
||||||
if (!palettes) return;
|
if (!palettes) return;
|
||||||
size_t lastCustPalette = strip.customPalettes.size();
|
size_t lastCustPalette = strip.customPalettes.size();
|
||||||
if (lastCustPalette >= palettes) lastCustPalette -= palettes;
|
if (int(lastCustPalette) >= palettes) lastCustPalette -= palettes;
|
||||||
for (size_t pal=0; pal<palettes; pal++) {
|
for (int pal=0; pal<palettes; pal++) {
|
||||||
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
||||||
// 3 colors = 12, 4 colors = 16, etc.
|
// 3 colors = 12, 4 colors = 16, etc.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user