Merge branch 'mdev' into ES8388
This commit is contained in:
@@ -54,6 +54,18 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(MIC_LOGGER) || defined(FFT_SAMPLING_LOG)
|
||||
#define PLOT_PRINT(x) DEBUGOUT.print(x)
|
||||
#define PLOT_PRINTLN(x) DEBUGOUT.println(x)
|
||||
#define PLOT_PRINTF(x...) DEBUGOUT.printf(x)
|
||||
#define PLOT_FLUSH() DEBUGOUT.flush()
|
||||
#else
|
||||
#define PLOT_PRINT(x)
|
||||
#define PLOT_PRINTLN(x)
|
||||
#define PLOT_PRINTF(x...)
|
||||
#define PLOT_FLUSH()
|
||||
#endif
|
||||
|
||||
// use audio source class (ESP32 specific)
|
||||
#include "audio_source.h"
|
||||
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !)
|
||||
@@ -131,12 +143,24 @@ static unsigned long timeOfPeak = 0; // time of last sample peak detection.
|
||||
static void detectSamplePeak(void); // peak detection function (needs scaled FFT reasults in vReal[])
|
||||
static void autoResetPeak(void); // peak auto-reset function
|
||||
|
||||
// shared vars for debugging
|
||||
#ifdef MIC_LOGGER
|
||||
static volatile float micReal_min = 0.0f; // MicIn data min from last batch of samples
|
||||
static volatile float micReal_avg = 0.0f; // MicIn data average (from last batch of samples)
|
||||
static volatile float micReal_max = 0.0f; // MicIn data max from last batch of samples
|
||||
#if 0
|
||||
static volatile float micReal_min2 = 0.0f; // MicIn data min after filtering
|
||||
static volatile float micReal_max2 = 0.0f; // MicIn data max after filtering
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////
|
||||
// Begin FFT Code //
|
||||
////////////////////
|
||||
|
||||
// some prototypes, to ensure consistent interfaces
|
||||
static float mapf(float x, float in_min, float in_max, float out_min, float out_max); // map function for float
|
||||
static float fftAddAvg(int from, int to); // average of several FFT result bins
|
||||
void FFTcode(void * parameter); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results
|
||||
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
|
||||
@@ -201,17 +225,20 @@ static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequen
|
||||
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) || defined(SR_STATS)
|
||||
static uint64_t fftTime = 0;
|
||||
static uint64_t sampleTime = 0;
|
||||
static float fftTaskCycle = 0; // avg cycle time for FFT task
|
||||
static float fftTime = 0; // avg time for single FFT
|
||||
static float sampleTime = 0; // avg (blocked) time for reading I2S samples
|
||||
#endif
|
||||
|
||||
// FFT Task variables (filtering and post-processing)
|
||||
static float lastFftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // backup of last FFT channels (before postprocessing)
|
||||
static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256.
|
||||
static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON)
|
||||
#ifdef SR_DEBUG
|
||||
static float fftResultMax[NUM_GEQ_CHANNELS] = {0.0f}; // A table used for testing to determine how our post-processing is working.
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// audio source parameters and constant
|
||||
constexpr SRate_t SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms
|
||||
//constexpr SRate_t SAMPLE_RATE = 16000; // 16kHz - use if FFTtask takes more than 20ms. Physical sample time -> 32ms
|
||||
@@ -221,6 +248,16 @@ constexpr SRate_t SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz
|
||||
//#define FFT_MIN_CYCLE 30 // Use with 16Khz sampling
|
||||
//#define FFT_MIN_CYCLE 23 // minimum time before FFT task is repeated. Use with 20Khz sampling
|
||||
//#define FFT_MIN_CYCLE 46 // minimum time before FFT task is repeated. Use with 10Khz sampling
|
||||
#else
|
||||
// slightly lower the sampling rate for -C3, to improve stability
|
||||
//constexpr SRate_t SAMPLE_RATE = 20480; // 20Khz; Physical sample time -> 25ms
|
||||
//#define FFT_MIN_CYCLE 23 // minimum time before FFT task is repeated.
|
||||
constexpr SRate_t SAMPLE_RATE = 18000; // 18Khz; Physical sample time -> 28ms
|
||||
#define FFT_MIN_CYCLE 25 // minimum time before FFT task is repeated.
|
||||
// try 16Khz in case your device still lags and responds too slowly.
|
||||
//constexpr SRate_t SAMPLE_RATE = 16000; // 16Khz -> Physical sample time -> 32ms
|
||||
//#define FFT_MIN_CYCLE 30 // minimum time before FFT task is repeated.
|
||||
#endif
|
||||
|
||||
// FFT Constants
|
||||
constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2
|
||||
@@ -228,7 +265,7 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul
|
||||
// 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 // log2(256)
|
||||
#define LOG_256 5.54517744f // log(256)
|
||||
|
||||
// These are the input and output vectors. Input vectors receive computed results from FFT.
|
||||
static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins
|
||||
@@ -240,13 +277,18 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
|
||||
// Create FFT object
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
|
||||
#define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups
|
||||
#define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt
|
||||
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION)
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// these options actually cause slow-down on -S2 (-S2 doesn't have floating point hardware)
|
||||
//#define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups - WLEDMM not faster on ESP32
|
||||
//#define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - WLEDMM slower on ESP32
|
||||
#endif
|
||||
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION)
|
||||
#else
|
||||
// around 50% slower on -S2
|
||||
// lib_deps += https://github.com/blazoncek/arduinoFFT.git
|
||||
#endif
|
||||
#include <arduinoFFT.h>
|
||||
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors);
|
||||
#else
|
||||
@@ -279,15 +321,22 @@ static float fftAddAvg(int from, int to) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
constexpr bool skipSecondFFT = true;
|
||||
#else
|
||||
constexpr bool skipSecondFFT = false;
|
||||
#endif
|
||||
//
|
||||
// FFT main task
|
||||
//
|
||||
void FFTcode(void * parameter)
|
||||
{
|
||||
DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID());
|
||||
// DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID()); // causes trouble on -S2
|
||||
|
||||
// see https://www.freertos.org/vtaskdelayuntil.html
|
||||
const TickType_t xFrequency = FFT_MIN_CYCLE * portTICK_PERIOD_MS;
|
||||
const TickType_t xFrequencyDouble = FFT_MIN_CYCLE * portTICK_PERIOD_MS * 2;
|
||||
static bool isFirstRun = false;
|
||||
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
for(;;) {
|
||||
@@ -296,6 +345,7 @@ void FFTcode(void * parameter)
|
||||
|
||||
// Don't run FFT computing code if we're in Receive mode or in realtime mode
|
||||
if (disableSoundProcessing || (audioSyncEnabled & 0x02)) {
|
||||
isFirstRun = false;
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||
continue;
|
||||
}
|
||||
@@ -303,6 +353,15 @@ void FFTcode(void * parameter)
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS)
|
||||
uint64_t start = esp_timer_get_time();
|
||||
bool haveDoneFFT = false; // indicates if second measurement (FFT time) is valid
|
||||
|
||||
static uint64_t lastCycleStart = 0;
|
||||
static uint64_t lastLastTime = 0;
|
||||
if ((lastCycleStart > 0) && (lastCycleStart < start)) { // filter out overflows
|
||||
uint64_t taskTimeInMillis = ((start - lastCycleStart) +5ULL) / 10ULL; // "+5" to ensure proper rounding
|
||||
fftTaskCycle = (((taskTimeInMillis + lastLastTime)/2) *4 + fftTaskCycle*6)/10.0; // smart smooth
|
||||
lastLastTime = taskTimeInMillis;
|
||||
}
|
||||
lastCycleStart = start;
|
||||
#endif
|
||||
|
||||
// get a fresh batch of samples from I2S
|
||||
@@ -311,12 +370,28 @@ void FFTcode(void * parameter)
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS)
|
||||
if (start < esp_timer_get_time()) { // filter out overflows
|
||||
uint64_t sampleTimeInMillis = (esp_timer_get_time() - start +5ULL) / 10ULL; // "+5" to ensure proper rounding
|
||||
sampleTime = (sampleTimeInMillis*3 + sampleTime*7)/10; // smooth
|
||||
sampleTime = (sampleTimeInMillis*3 + sampleTime*7)/10.0; // smooth
|
||||
}
|
||||
start = esp_timer_get_time(); // start measuring FFT time
|
||||
#endif
|
||||
|
||||
xLastWakeTime = xTaskGetTickCount(); // update "last unblocked time" for vTaskDelay
|
||||
isFirstRun = !isFirstRun; // toggle throtte
|
||||
|
||||
#ifdef MIC_LOGGER
|
||||
float datMin = 0.0f;
|
||||
float datMax = 0.0f;
|
||||
double datAvg = 0.0f;
|
||||
for (int i=0; i < samplesFFT; i++) {
|
||||
if (i==0) {
|
||||
datMin = datMax = vReal[i];
|
||||
} else {
|
||||
if (datMin > vReal[i]) datMin = vReal[i];
|
||||
if (datMax < vReal[i]) datMax = vReal[i];
|
||||
}
|
||||
datAvg += vReal[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
// band pass filter - can reduce noise floor by a factor of 50
|
||||
// downside: frequencies below 100Hz will be ignored
|
||||
@@ -334,42 +409,62 @@ void FFTcode(void * parameter)
|
||||
// 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;
|
||||
|
||||
#ifdef MIC_LOGGER
|
||||
micReal_min = datMin;
|
||||
micReal_max = datMax;
|
||||
micReal_avg = datAvg / samplesFFT;
|
||||
#if 0
|
||||
// compute mix/max again after filering - usefull for filter debugging
|
||||
for (int i=0; i < samplesFFT; i++) {
|
||||
if (i==0) {
|
||||
datMin = datMax = vReal[i];
|
||||
} else {
|
||||
if (datMin > vReal[i]) datMin = vReal[i];
|
||||
if (datMax < vReal[i]) datMax = vReal[i];
|
||||
}
|
||||
}
|
||||
micReal_min2 = datMin;
|
||||
micReal_max2 = datMax;
|
||||
#endif
|
||||
#endif
|
||||
// run FFT (takes 3-5ms on ESP32)
|
||||
//if (fabsf(sampleAvg) > 0.25f) { // noise gate open
|
||||
if (fabsf(volumeSmth) > 0.25f) { // noise gate open
|
||||
if ((skipSecondFFT == false) || (isFirstRun == true)) {
|
||||
// run FFT (takes 2-3ms on ESP32, ~12ms on ESP32-S2, ~30ms on -C3)
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
FFT.dcRemoval(); // remove DC offset
|
||||
#if !defined(FFT_PREFER_EXACT_PEAKS)
|
||||
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
|
||||
#else
|
||||
FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
|
||||
#endif
|
||||
FFT.compute( FFTDirection::Forward ); // Compute FFT
|
||||
FFT.complexToMagnitude(); // Compute magnitudes
|
||||
#else
|
||||
FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples()
|
||||
|
||||
// run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2)
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
FFT.dcRemoval(); // remove DC offset
|
||||
#if !defined(FFT_PREFER_EXACT_PEAKS)
|
||||
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
|
||||
#else
|
||||
FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
|
||||
#endif
|
||||
FFT.compute( FFTDirection::Forward ); // Compute FFT
|
||||
FFT.complexToMagnitude(); // Compute magnitudes
|
||||
#else
|
||||
FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples()
|
||||
//FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window
|
||||
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection
|
||||
#if !defined(FFT_PREFER_EXACT_PEAKS)
|
||||
FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy
|
||||
#else
|
||||
FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection
|
||||
#endif
|
||||
FFT.Compute( FFT_FORWARD ); // Compute FFT
|
||||
FFT.ComplexToMagnitude(); // Compute magnitudes
|
||||
#endif
|
||||
|
||||
//FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window
|
||||
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection
|
||||
#if !defined(FFT_PREFER_EXACT_PEAKS)
|
||||
FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy
|
||||
#else
|
||||
FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection
|
||||
#endif
|
||||
FFT.Compute( FFT_FORWARD ); // Compute FFT
|
||||
FFT.ComplexToMagnitude(); // Compute magnitudes
|
||||
#endif
|
||||
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
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
|
||||
#endif
|
||||
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
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
|
||||
#endif
|
||||
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
||||
|
||||
} else { // skip second run --> clear fft results, keep peaks
|
||||
memset(vReal, 0, sizeof(vReal));
|
||||
}
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS)
|
||||
haveDoneFFT = true;
|
||||
#endif
|
||||
@@ -380,14 +475,16 @@ void FFTcode(void * parameter)
|
||||
FFT_Magnitude = 0.001;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samplesFFT; i++) {
|
||||
float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way
|
||||
vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max.
|
||||
} // for()
|
||||
if ((skipSecondFFT == false) || (isFirstRun == true)) {
|
||||
|
||||
// mapping of FFT result bins to frequency channels
|
||||
//if (fabsf(sampleAvg) > 0.25f) { // noise gate open
|
||||
if (fabsf(volumeSmth) > 0.25f) { // noise gate open
|
||||
for (int i = 0; i < samplesFFT; i++) {
|
||||
float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way
|
||||
vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max.
|
||||
} // for()
|
||||
|
||||
// mapping of FFT result bins to frequency channels
|
||||
//if (fabsf(sampleAvg) > 0.25f) { // noise gate open
|
||||
if (fabsf(volumeSmth) > 0.25f) { // 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.
|
||||
*
|
||||
@@ -445,24 +542,34 @@ void FFTcode(void * parameter)
|
||||
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
|
||||
#endif
|
||||
} else { // noise gate closed - just decay old values
|
||||
for (int i=0; i < NUM_GEQ_CHANNELS; i++) {
|
||||
fftCalc[i] *= 0.85f; // decay to zero
|
||||
if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f;
|
||||
} else { // noise gate closed - just decay old values
|
||||
isFirstRun = false;
|
||||
for (int i=0; i < NUM_GEQ_CHANNELS; i++) {
|
||||
fftCalc[i] *= 0.85f; // decay to zero
|
||||
if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(lastFftCalc, fftCalc, sizeof(lastFftCalc)); // make a backup of last "good" channels
|
||||
|
||||
} else { // if second run skipped
|
||||
memcpy(fftCalc, lastFftCalc, sizeof(fftCalc)); // restore last "good" channels
|
||||
}
|
||||
|
||||
// post-processing of frequency channels (pink noise adjustment, AGC, smooting, scaling)
|
||||
if (pinkIndex > MAX_PINK) pinkIndex = MAX_PINK;
|
||||
//postProcessFFTResults((fabsf(sampleAvg) > 0.25f)? true : false , NUM_GEQ_CHANNELS);
|
||||
postProcessFFTResults((fabsf(volumeSmth)>0.25f)? true : false , NUM_GEQ_CHANNELS);
|
||||
postProcessFFTResults((fabsf(volumeSmth)>0.25f)? true : false , NUM_GEQ_CHANNELS); // this function modifies fftCalc, fftAvg and fftResult
|
||||
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS)
|
||||
static uint64_t lastLastFFT = 0;
|
||||
if (haveDoneFFT && (start < esp_timer_get_time())) { // filter out overflows
|
||||
uint64_t fftTimeInMillis = ((esp_timer_get_time() - start) +5ULL) / 10ULL; // "+5" to ensure proper rounding
|
||||
fftTime = (fftTimeInMillis*3 + fftTime*7)/10; // smooth
|
||||
fftTime = (((fftTimeInMillis + lastLastFFT)/2) *3 + fftTime*7)/10.0; // smart smooth
|
||||
lastLastFFT = fftTimeInMillis;
|
||||
}
|
||||
#endif
|
||||
|
||||
// run peak detection
|
||||
autoResetPeak();
|
||||
detectSamplePeak();
|
||||
@@ -470,8 +577,13 @@ void FFTcode(void * parameter)
|
||||
#if !defined(I2S_GRAB_ADC1_COMPLETELY)
|
||||
if ((audioSource == nullptr) || (audioSource->getType() != AudioSource::Type_I2SAdc)) // the "delay trick" does not help for analog ADC
|
||||
#endif
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||
|
||||
{
|
||||
if ((skipSecondFFT == false) || (fabsf(volumeSmth) < 0.25f)) {
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||
} else if (isFirstRun == true) {
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequencyDouble); // release CPU after performing FFT in "skip second run" mode
|
||||
}
|
||||
}
|
||||
} // for(;;)ever
|
||||
} // FFTcode() task end
|
||||
|
||||
@@ -488,10 +600,10 @@ static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // p
|
||||
constexpr float alpha = 0.0225f; // 80hz
|
||||
//constexpr float alpha = 0.01693f;// 60hz
|
||||
// high frequency cutoff parameter
|
||||
//constexpr float beta1 = 0.75; // 11Khz
|
||||
//constexpr float beta1 = 0.82; // 15Khz
|
||||
//constexpr float beta1 = 0.8285; // 18Khz
|
||||
constexpr float beta1 = 0.85; // 20Khz
|
||||
//constexpr float beta1 = 0.75f; // 11Khz
|
||||
//constexpr float beta1 = 0.82f; // 15Khz
|
||||
//constexpr float beta1 = 0.8285f; // 18Khz
|
||||
constexpr float beta1 = 0.85f; // 20Khz
|
||||
|
||||
constexpr float beta2 = (1.0f - beta1) / 2.0;
|
||||
static float last_vals[2] = { 0.0f }; // FIR high freq cutoff filter
|
||||
@@ -598,13 +710,14 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
|
||||
// peak detection is called from FFT task when vReal[] contains valid FFT results
|
||||
static void detectSamplePeak(void) {
|
||||
bool havePeak = false;
|
||||
|
||||
#if 0
|
||||
// Poor man's beat detection by seeing if sample > Average + some value.
|
||||
// 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 ((sampleAvg > 1) && (maxVol > 0) && (binNum > 1) && (vReal[binNum] > maxVol) && ((millis() - timeOfPeak) > 100)) {
|
||||
havePeak = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// alternate detection, based on FFT_MajorPeak and FFT_Magnitude. Not much better...
|
||||
@@ -620,7 +733,6 @@ static void detectSamplePeak(void) {
|
||||
timeOfPeak = millis();
|
||||
udpSamplePeak = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void autoResetPeak(void) {
|
||||
@@ -708,7 +820,11 @@ class AudioReactive : public Usermod {
|
||||
};
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
#ifdef SR_ENABLE_DEFAULT
|
||||
bool enabled = true; // WLEDMM
|
||||
#else
|
||||
bool enabled = false;
|
||||
#endif
|
||||
bool initDone = false;
|
||||
|
||||
// variables for UDP sound sync
|
||||
@@ -724,7 +840,7 @@ class AudioReactive : public Usermod {
|
||||
// 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
|
||||
double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controler.
|
||||
double micLev = 0.0f; // Used to convert returned value to have '0' as minimum. A leveller
|
||||
double micLev = 0.0; // Used to convert returned value to have '0' as minimum. A leveller
|
||||
float expAdjF = 0.0f; // Used for exponential filter.
|
||||
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)
|
||||
@@ -760,29 +876,36 @@ class AudioReactive : public Usermod {
|
||||
if (disableSoundProcessing && (!udpSyncConnected || ((audioSyncEnabled & 0x02) == 0))) return; // no audio availeable
|
||||
#ifdef MIC_LOGGER
|
||||
// Debugging functions for audio input and sound processing. Comment out the values you want to see
|
||||
Serial.print("micReal:"); Serial.print(micDataReal); Serial.print("\t");
|
||||
Serial.print("volumeSmth:"); Serial.print(volumeSmth); Serial.print("\t");
|
||||
//Serial.print("volumeRaw:"); Serial.print(volumeRaw); Serial.print("\t");
|
||||
Serial.print("DC_Level:"); Serial.print(micLev); Serial.print("\t");
|
||||
//Serial.print("sampleAgc:"); Serial.print(sampleAgc); Serial.print("\t");
|
||||
//Serial.print("sampleAvg:"); Serial.print(sampleAvg); Serial.print("\t");
|
||||
//Serial.print("sampleReal:"); Serial.print(sampleReal); Serial.print("\t");
|
||||
//Serial.print("micIn:"); Serial.print(micIn); Serial.print("\t");
|
||||
//Serial.print("sample:"); Serial.print(sample); Serial.print("\t");
|
||||
//Serial.print("sampleMax:"); Serial.print(sampleMax); Serial.print("\t");
|
||||
//Serial.print("samplePeak:"); Serial.print((samplePeak!=0) ? 128:0); Serial.print("\t");
|
||||
//Serial.print("multAgc:"); Serial.print(multAgc, 4); Serial.print("\t");
|
||||
Serial.println();
|
||||
PLOT_PRINT("micMin:"); PLOT_PRINT(0.5f * micReal_min); PLOT_PRINT("\t"); // scaled down to 50%, for better readability
|
||||
PLOT_PRINT("micMax:"); PLOT_PRINT(0.5f * micReal_max); PLOT_PRINT("\t"); // scaled down to 50%
|
||||
//PLOT_PRINT("micAvg:"); PLOT_PRINT(0.5f * micReal_avg); PLOT_PRINT("\t"); // scaled down to 50%
|
||||
//PLOT_PRINT("micDC:"); PLOT_PRINT(0.5f * (micReal_min + micReal_max)/2.0f);PLOT_PRINT("\t"); // scaled down to 50%
|
||||
PLOT_PRINT("micReal:"); PLOT_PRINT(micDataReal + 256.0f); PLOT_PRINT("\t"); // +256 to move above other lines
|
||||
PLOT_PRINT("volumeSmth:"); PLOT_PRINT(volumeSmth + 256.0f); PLOT_PRINT("\t"); // +256 to move above other lines
|
||||
//PLOT_PRINT("volumeRaw:"); PLOT_PRINT(volumeRaw + 256.0f); PLOT_PRINT("\t"); // +256 to move above other lines
|
||||
PLOT_PRINT("DC_Level:"); PLOT_PRINT(micLev + 256.0f); PLOT_PRINT("\t"); // +256 to move above other lines
|
||||
// //PLOT_PRINT("filtmicMin:"); PLOT_PRINT(0.5f * micReal_min2); PLOT_PRINT("\t"); // scaled down to 50%
|
||||
// //PLOT_PRINT("filtmicMax:"); PLOT_PRINT(0.5f * micReal_max2); PLOT_PRINT("\t"); // scaled down to 50%
|
||||
//PLOT_PRINT("sampleAgc:"); PLOT_PRINT(sampleAgc); PLOT_PRINT("\t");
|
||||
//PLOT_PRINT("sampleAvg:"); PLOT_PRINT(sampleAvg); PLOT_PRINT("\t");
|
||||
//PLOT_PRINT("sampleReal:"); PLOT_PRINT(sampleReal); PLOT_PRINT("\t");
|
||||
//PLOT_PRINT("micIn:"); PLOT_PRINT(micIn); PLOT_PRINT("\t");
|
||||
//PLOT_PRINT("sample:"); PLOT_PRINT(sample); 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("multAgc:"); PLOT_PRINT(multAgc, 4); PLOT_PRINT("\t");
|
||||
PLOT_PRINTLN();
|
||||
PLOT_FLUSH();
|
||||
#endif
|
||||
|
||||
#ifdef FFT_SAMPLING_LOG
|
||||
#if 0
|
||||
for(int i=0; i<NUM_GEQ_CHANNELS; i++) {
|
||||
Serial.print(fftResult[i]);
|
||||
Serial.print("\t");
|
||||
PLOT_PRINT(fftResult[i]);
|
||||
PLOT_PRINT("\t");
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
PLOT_PRINTLN();
|
||||
#endif
|
||||
|
||||
// OPTIONS are in the following format: Description \n Option
|
||||
//
|
||||
@@ -808,20 +931,21 @@ class AudioReactive : public Usermod {
|
||||
if(fftResult[i] < minVal) minVal = fftResult[i];
|
||||
}
|
||||
for(int i = 0; i < NUM_GEQ_CHANNELS; i++) {
|
||||
Serial.print(i); Serial.print(":");
|
||||
Serial.printf("%04ld ", map(fftResult[i], 0, (scaleValuesFromCurrentMaxVal ? maxVal : defaultScalingFromHighValue), (mapValuesToPlotterSpace*i*scalingToHighValue)+0, (mapValuesToPlotterSpace*i*scalingToHighValue)+scalingToHighValue-1));
|
||||
PLOT_PRINT(i); PLOT_PRINT(":");
|
||||
PLOT_PRINTF("%04ld ", map(fftResult[i], 0, (scaleValuesFromCurrentMaxVal ? maxVal : defaultScalingFromHighValue), (mapValuesToPlotterSpace*i*scalingToHighValue)+0, (mapValuesToPlotterSpace*i*scalingToHighValue)+scalingToHighValue-1));
|
||||
}
|
||||
if(printMaxVal) {
|
||||
Serial.printf("maxVal:%04d ", maxVal + (mapValuesToPlotterSpace ? 16*256 : 0));
|
||||
PLOT_PRINTF("maxVal:%04d ", maxVal + (mapValuesToPlotterSpace ? 16*256 : 0));
|
||||
}
|
||||
if(printMinVal) {
|
||||
Serial.printf("%04d:minVal ", minVal); // printed with value first, then label, so negative values can be seen in Serial Monitor but don't throw off y axis in Serial Plotter
|
||||
PLOT_PRINTF("%04d:minVal ", minVal); // printed with value first, then label, so negative values can be seen in Serial Monitor but don't throw off y axis in Serial Plotter
|
||||
}
|
||||
if(mapValuesToPlotterSpace)
|
||||
Serial.printf("max:%04d ", (printMaxVal ? 17 : 16)*256); // print line above the maximum value we expect to see on the plotter to avoid autoscaling y axis
|
||||
else
|
||||
Serial.printf("max:%04d ", 256);
|
||||
Serial.println();
|
||||
PLOT_PRINTF("max:%04d ", (printMaxVal ? 17 : 16)*256); // print line above the maximum value we expect to see on the plotter to avoid autoscaling y axis
|
||||
else {
|
||||
PLOT_PRINTF("max:%04d ", 256);
|
||||
}
|
||||
PLOT_PRINTLN();
|
||||
#endif // FFT_SAMPLING_LOG
|
||||
} // logAudio()
|
||||
|
||||
@@ -955,12 +1079,7 @@ class AudioReactive : public Usermod {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//micLev = ((micLev * 8191.0f) + micDataReal) / 8192.0f; // takes a few seconds to "catch up" with the Mic Input
|
||||
//if (useBandPassFilter)
|
||||
// micLev += (micDataReal-micLev) / 8192.0f; // we expect some more fluctuations with mics that need pre-filtering
|
||||
//else
|
||||
micLev += (micDataReal-micLev) / 12288.0f;
|
||||
|
||||
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
|
||||
@@ -1053,8 +1172,16 @@ class AudioReactive : public Usermod {
|
||||
static unsigned long last_connection_attempt = 0;
|
||||
|
||||
if ((audioSyncPort <= 0) || ((audioSyncEnabled & 0x03) == 0)) return; // Sound Sync not enabled
|
||||
if (!(apActive || WLED_CONNECTED || interfacesInited)) {
|
||||
if (udpSyncConnected) {
|
||||
udpSyncConnected = false;
|
||||
fftUdp.stop();
|
||||
receivedFormat = 0;
|
||||
DEBUGSR_PRINTLN(F("AR connectUDPSoundSync(): connection lost, UDP closed."));
|
||||
}
|
||||
return; // neither AP nor other connections availeable
|
||||
}
|
||||
if (udpSyncConnected) return; // already connected
|
||||
if (!(apActive || interfacesInited)) return; // neither AP nor other connections availeable
|
||||
if (millis() - last_connection_attempt < 15000) return; // only try once in 15 seconds
|
||||
|
||||
// if we arrive here, we need a UDP connection but don't have one
|
||||
@@ -1283,7 +1410,7 @@ class AudioReactive : public Usermod {
|
||||
case 0:
|
||||
default:
|
||||
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||
//useBandPassFilter = true;
|
||||
useBandPassFilter = true;
|
||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(audioPin);
|
||||
@@ -1301,11 +1428,17 @@ class AudioReactive : public Usermod {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));
|
||||
#else
|
||||
ERRORSR_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));
|
||||
USER_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));
|
||||
#endif
|
||||
disableSoundProcessing = true;
|
||||
} else {
|
||||
USER_PRINTLN(F("AR: sound input driver initialized successfully."));
|
||||
}
|
||||
|
||||
// try to start UDP
|
||||
last_UDPTime = 0;
|
||||
receivedFormat = 0;
|
||||
delay(100);
|
||||
if (enabled) connectUDPSoundSync();
|
||||
initDone = true;
|
||||
}
|
||||
@@ -1320,6 +1453,8 @@ class AudioReactive : public Usermod {
|
||||
if (udpSyncConnected) { // clean-up: if open, close old UDP sync connection
|
||||
udpSyncConnected = false;
|
||||
fftUdp.stop();
|
||||
receivedFormat = 0;
|
||||
DEBUGSR_PRINTLN(F("AR connected(): old UDP connection closed."));
|
||||
}
|
||||
|
||||
if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) {
|
||||
@@ -1328,6 +1463,13 @@ class AudioReactive : public Usermod {
|
||||
#else
|
||||
udpSyncConnected = fftUdp.beginMulticast(WiFi.localIP(), IPAddress(239, 0, 0, 1), audioSyncPort);
|
||||
#endif
|
||||
receivedFormat = 0;
|
||||
if (udpSyncConnected) last_UDPTime = millis();
|
||||
if (apActive && !(WLED_CONNECTED)) {
|
||||
DEBUGSR_PRINTLN(udpSyncConnected ? F("AR connected(): UDP: connected using AP.") : F("AR connected(): UDP is disconnected (AP)."));
|
||||
} else {
|
||||
DEBUGSR_PRINTLN(udpSyncConnected ? F("AR connected(): UDP: connected to WIFI.") : F("AR connected(): UDP is disconnected (Wifi)."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1393,10 +1535,10 @@ class AudioReactive : public Usermod {
|
||||
int userloopDelay = int(t_now - lastUMRun);
|
||||
if (lastUMRun == 0) userloopDelay=0; // startup - don't have valid data from last run.
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS)
|
||||
// complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second.
|
||||
if ((userloopDelay > 23) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
|
||||
DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
|
||||
if ((userloopDelay > /*23*/ 30) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
|
||||
USER_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1439,6 +1581,22 @@ class AudioReactive : public Usermod {
|
||||
if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample
|
||||
else volumeSmth = syncVolumeSmth; // restore originally received sample for next run of dynamics limiter
|
||||
limitSampleDynamics(); // run dynamics limiter on received volumeSmth, to hide jumps and hickups
|
||||
} else {
|
||||
receivedFormat = 0;
|
||||
}
|
||||
|
||||
if ( (audioSyncEnabled & 0x02) // receive mode
|
||||
&& udpSyncConnected // connected
|
||||
&& (receivedFormat > 0) // we actually received something in the past
|
||||
&& ((millis() - last_UDPTime) > 25000)) { // close connection after 25sec idle
|
||||
udpSyncConnected = false;
|
||||
receivedFormat = 0;
|
||||
fftUdp.stop();
|
||||
volumeSmth =0.0f;
|
||||
volumeRaw =0;
|
||||
my_magnitude = 0.1; FFT_Magnitude = 0.01; FFT_MajorPeak = 2;
|
||||
multAgc = 1;
|
||||
DEBUGSR_PRINTLN(F("AR loop(): UDP closed due to inactivity."));
|
||||
}
|
||||
|
||||
#if defined(MIC_LOGGER) || defined(MIC_SAMPLING_LOG) || defined(FFT_SAMPLING_LOG)
|
||||
@@ -1500,10 +1658,13 @@ class AudioReactive : public Usermod {
|
||||
autoResetPeak();
|
||||
|
||||
if (init && FFT_Task) {
|
||||
delay(25); // WLEDMM: givesome time for I2S driver to finish sampling
|
||||
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
|
||||
if (udpSyncConnected) { // close UDP sync connection (if open)
|
||||
udpSyncConnected = false;
|
||||
fftUdp.stop();
|
||||
DEBUGSR_PRINTLN(F("AR onUpdateBegin(true): UDP connection closed."));
|
||||
receivedFormat = 0;
|
||||
}
|
||||
} else {
|
||||
// update has failed or create task requested
|
||||
@@ -1675,12 +1836,16 @@ class AudioReactive : public Usermod {
|
||||
}
|
||||
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS)
|
||||
infoArr = user.createNestedArray(F("I2S cycle time"));
|
||||
infoArr.add(roundf(fftTaskCycle)/100.0f);
|
||||
infoArr.add(" ms");
|
||||
|
||||
infoArr = user.createNestedArray(F("Sampling time"));
|
||||
infoArr.add(float(sampleTime)/100.0f);
|
||||
infoArr.add(roundf(sampleTime)/100.0f);
|
||||
infoArr.add(" ms");
|
||||
|
||||
infoArr = user.createNestedArray(F("FFT time"));
|
||||
infoArr.add(float(fftTime)/100.0f);
|
||||
infoArr.add(roundf(fftTime)/100.0f);
|
||||
if ((fftTime/100) >= FFT_MIN_CYCLE) // FFT time over budget -> I2S buffer will overflow
|
||||
infoArr.add("<b style=\"color:red;\">! ms</b>");
|
||||
else if ((fftTime/80 + sampleTime/80) >= FFT_MIN_CYCLE) // FFT time >75% of budget -> risk of instability
|
||||
@@ -1688,8 +1853,9 @@ class AudioReactive : public Usermod {
|
||||
else
|
||||
infoArr.add(" ms");
|
||||
|
||||
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 I2S cycle time: %5.2f ms\n", roundf(fftTaskCycle)/100.0f);
|
||||
DEBUGSR_PRINTF("AR Sampling time : %5.2f ms\n", roundf(sampleTime)/100.0f);
|
||||
DEBUGSR_PRINTF("AR FFT time : %5.2f ms\n", roundf(fftTime)/100.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1774,7 +1940,7 @@ class AudioReactive : public Usermod {
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
||||
amic["pin"] = audioPin;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic));
|
||||
dmic[F("type")] = dmType;
|
||||
@@ -1798,7 +1964,7 @@ class AudioReactive : public Usermod {
|
||||
|
||||
JsonObject freqScale = top.createNestedObject("frequency");
|
||||
freqScale[F("scale")] = FFTScalingMode;
|
||||
freqScale[F("profile")] = pinkIndex;
|
||||
freqScale[F("profile")] = pinkIndex; //WLEDMM
|
||||
|
||||
JsonObject sync = top.createNestedObject("sync");
|
||||
sync[F("port")] = audioSyncPort;
|
||||
@@ -1858,7 +2024,7 @@ class AudioReactive : public Usermod {
|
||||
configComplete &= getJsonValue(top["dynamics"][F("fall")], decayTime);
|
||||
|
||||
configComplete &= getJsonValue(top["frequency"][F("scale")], FFTScalingMode);
|
||||
configComplete &= getJsonValue(top["frequency"][F("profile")], pinkIndex);
|
||||
configComplete &= getJsonValue(top["frequency"][F("profile")], pinkIndex); //WLEDMM
|
||||
|
||||
configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort);
|
||||
configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled);
|
||||
@@ -1869,18 +2035,58 @@ class AudioReactive : public Usermod {
|
||||
|
||||
void appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addInfo('AudioReactive:help',0,'<button onclick=\"location.href="https://mm.kno.wled.ge/soundreactive/Sound-Settings"\" type=\"button\">?</button>');"));
|
||||
|
||||
//WLEDMM: add defaults
|
||||
#ifdef AUDIOPIN
|
||||
oappend(SET_F("xOpt('AudioReactive:analogmic:pin',1,' ⎌',")); oappendi(AUDIOPIN); oappend(");");
|
||||
#endif
|
||||
oappend(SET_F("aOpt('AudioReactive:analogmic:pin',1);")); //only analog options
|
||||
|
||||
oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');"));
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
oappend(SET_F("addOption(dd,'Generic Analog',0);"));
|
||||
#endif
|
||||
oappend(SET_F("addOption(dd,'Generic I2S',1);"));
|
||||
oappend(SET_F("addOption(dd,'ES7243',2);"));
|
||||
oappend(SET_F("addOption(dd,'SPH0654',3);"));
|
||||
oappend(SET_F("addOption(dd,'Generic I2S with Mclk',4);"));
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
oappend(SET_F("addOption(dd,'Generic I2S PDM',5);"));
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#if SR_DMTYPE==0
|
||||
oappend(SET_F("addOption(dd,'Generic Analog (⎌)',0);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'Generic Analog',0);"));
|
||||
#endif
|
||||
#endif
|
||||
#if SR_DMTYPE==1
|
||||
oappend(SET_F("addOption(dd,'Generic I2S (⎌)',1);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'Generic I2S',1);"));
|
||||
#endif
|
||||
#if SR_DMTYPE==2
|
||||
oappend(SET_F("addOption(dd,'ES7243 (⎌)',2);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'ES7243',2);"));
|
||||
#endif
|
||||
#if SR_DMTYPE==3
|
||||
oappend(SET_F("addOption(dd,'SPH0654 (⎌)',3);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'SPH0654',3);"));
|
||||
#endif
|
||||
#if SR_DMTYPE==4
|
||||
oappend(SET_F("addOption(dd,'Generic I2S with Mclk (⎌)',4);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'Generic I2S with Mclk',4);"));
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#if SR_DMTYPE==5
|
||||
oappend(SET_F("addOption(dd,'Generic I2S PDM (⎌)',5);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'Generic I2S PDM',5);"));
|
||||
#endif
|
||||
#endif
|
||||
oappend(SET_F("addOption(dd,'ES8388',6);"));
|
||||
|
||||
#ifdef SR_SQUELCH
|
||||
oappend(SET_F("addInfo('AudioReactive:config:squelch',1,'<i>⎌ ")); oappendi(SR_SQUELCH); oappend("</i>');"); // 0 is field type, 1 is actual field
|
||||
#endif
|
||||
#ifdef SR_GAIN
|
||||
oappend(SET_F("addInfo('AudioReactive:config:gain',1,'<i>⎌ ")); oappendi(SR_GAIN); oappend("</i>');"); // 0 is field type, 1 is actual field
|
||||
#endif
|
||||
|
||||
oappend(SET_F("dd=addDropdown('AudioReactive','config:AGC');"));
|
||||
oappend(SET_F("addOption(dd,'Off',0);"));
|
||||
oappend(SET_F("addOption(dd,'Normal',1);"));
|
||||
@@ -1900,34 +2106,109 @@ class AudioReactive : public Usermod {
|
||||
oappend(SET_F("addOption(dd,'Square Root (Energy)',3);"));
|
||||
oappend(SET_F("addOption(dd,'Logarithmic (Loudness)',1);"));
|
||||
|
||||
//WLEDMM add defaults
|
||||
oappend(SET_F("dd=addDropdown('AudioReactive','frequency:profile');"));
|
||||
oappend(SET_F("addOption(dd,'Generic Microphone',0);"));
|
||||
oappend(SET_F("addOption(dd,'Generic Line-In',1);"));
|
||||
oappend(SET_F("addOption(dd,'ICS-43434',5);"));
|
||||
oappend(SET_F("addOption(dd,'ICS-43434 - big speakers',6);"));
|
||||
oappend(SET_F("addOption(dd,'SPM1423',7);"));
|
||||
oappend(SET_F("addOption(dd,'IMNP441',2);"));
|
||||
oappend(SET_F("addOption(dd,'IMNP441 - big speakers',3);"));
|
||||
oappend(SET_F("addOption(dd,'IMNP441 - small speakers',4);"));
|
||||
oappend(SET_F("addOption(dd,'flat - no adjustments',10);"));
|
||||
oappend(SET_F("addOption(dd,'userdefined #1',8);"));
|
||||
oappend(SET_F("addOption(dd,'userdefined #2',9);"));
|
||||
#if SR_FREQ_PROF==0
|
||||
oappend(SET_F("addOption(dd,'Generic Microphone (⎌)',0);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'Generic Microphone',0);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==1
|
||||
oappend(SET_F("addOption(dd,'Generic Line-In (⎌)',1);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'Generic Line-In',1);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==5
|
||||
oappend(SET_F("addOption(dd,'ICS-43434 (⎌)',5);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'ICS-43434',5);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==6
|
||||
oappend(SET_F("addOption(dd,'ICS-43434 - big speakers (⎌)',6);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'ICS-43434 - big speakers',6);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==7
|
||||
oappend(SET_F("addOption(dd,'SPM1423 (⎌)',7);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'SPM1423',7);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==2
|
||||
oappend(SET_F("addOption(dd,'IMNP441 (⎌)',2);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'IMNP441',2);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==3
|
||||
oappend(SET_F("addOption(dd,'IMNP441 - big speakers (⎌)',3);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'IMNP441 - big speakers',3);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==4
|
||||
oappend(SET_F("addOption(dd,'IMNP441 - small speakers (⎌)',4);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'IMNP441 - small speakers',4);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==10
|
||||
oappend(SET_F("addOption(dd,'flat - no adjustments (⎌)',10);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'flat - no adjustments',10);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==8
|
||||
oappend(SET_F("addOption(dd,'userdefined #1 (⎌)',8);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'userdefined #1',8);"));
|
||||
#endif
|
||||
#if SR_FREQ_PROF==9
|
||||
oappend(SET_F("addOption(dd,'userdefined #2 (⎌)',9);"));
|
||||
#else
|
||||
oappend(SET_F("addOption(dd,'userdefined #2',9);"));
|
||||
#endif
|
||||
|
||||
oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');"));
|
||||
oappend(SET_F("addOption(dd,'Off',0);"));
|
||||
oappend(SET_F("addOption(dd,'Send',1);"));
|
||||
oappend(SET_F("addOption(dd,'Receive',2);"));
|
||||
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,'I2S Serial Data', '<i><span class=\"h\">sd/data/dout</span></i>');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'I2S L/R Clock','<i><span class=\"h\">ws/clk/lrck</span></i>');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',2,'I2S Serial Clock','<i>sck/bclk</i>');"));
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'I2S Master CLK','<i>only use -1, 0, 1 or 3</i>');"));
|
||||
#else
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'I2S Master CLK','');"));
|
||||
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'<i>sd/data/dout</i>','I2S SD');"));
|
||||
#ifdef I2S_SDPIN
|
||||
oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',0,' ⎌',")); oappendi(I2S_SDPIN); oappend(");");
|
||||
#endif
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',4,'I2C SDA',' ');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',5,'I2C SCL',' ');"));
|
||||
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'<i>ws/clk/lrck</i>','I2S WS');"));
|
||||
oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',1);")); // disable read only pins
|
||||
#ifdef I2S_WSPIN
|
||||
oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',1,' ⎌',")); oappendi(I2S_WSPIN); oappend(");");
|
||||
#endif
|
||||
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',2,'<i>sck/bclk</i>','I2S SCK');"));
|
||||
oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',2);")); // disable read only pins
|
||||
#ifdef I2S_CKPIN
|
||||
oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',2,' ⎌',")); oappendi(I2S_CKPIN); oappend(");");
|
||||
#endif
|
||||
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'<i>master clock</i>','I2S MCLK');"));
|
||||
oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',3);")); // disable read only pins
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
oappend(SET_F("dOpt('AudioReactive:digitalmic:pin[]',3,2,2);")); //only use -1, 0, 1 or 3
|
||||
oappend(SET_F("dOpt('AudioReactive:digitalmic:pin[]',3,4,39);")); //only use -1, 0, 1 or 3
|
||||
#endif
|
||||
#ifdef MCLK_PIN
|
||||
oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',3,' ⎌',")); oappendi(MCLK_PIN); oappend(");");
|
||||
#endif
|
||||
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',4,'','I2C SDA');"));
|
||||
oappend(SET_F("rOpt('AudioReactive:digitalmic:pin[]',4,'use global (")); oappendi(i2c_sda); oappend(")',-1);");
|
||||
#ifdef ES7243_SDAPIN
|
||||
oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',4,' ⎌',")); oappendi(ES7243_SDAPIN); oappend(");");
|
||||
#endif
|
||||
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',5,'','I2C SCL');"));
|
||||
oappend(SET_F("rOpt('AudioReactive:digitalmic:pin[]',5,'use global (")); oappendi(i2c_scl); oappend(")',-1);");
|
||||
#ifdef ES7243_SCLPIN
|
||||
oappend(SET_F("xOpt('AudioReactive:digitalmic:pin[]',5,' ⎌',")); oappendi(ES7243_SCLPIN); oappend(");");
|
||||
#endif
|
||||
oappend(SET_F("dRO('AudioReactive:digitalmic:pin[]',5);")); // disable read only pins
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,14 +24,14 @@
|
||||
|
||||
// see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents
|
||||
// and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
|
||||
// there are two things in these MCUs that could lead to problems with audio processing:
|
||||
// * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x)
|
||||
// * single core, so FFT task might slow down other things like LED updates
|
||||
#if !defined(SOC_I2S_NUM) || (SOC_I2S_NUM < 1)
|
||||
#error This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2.
|
||||
#error This audio reactive usermod does not support ESP32-C2 or ESP32-C3.
|
||||
#else
|
||||
#warning This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2.
|
||||
#warning This audio reactive usermod does not support ESP32-C2 and ESP32-C3.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -219,10 +219,15 @@ class I2SSource : public AudioSource {
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// example from espressif: https://github.com/espressif/esp-idf/blob/release/v4.4/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c
|
||||
// This is an I2S PDM microphone, these microphones only use a clock and
|
||||
// data line, to make it simpler to debug, use the WS pin as CLK and SD
|
||||
// pin as DATA
|
||||
// data line, to make it simpler to debug, use the WS pin as CLK and SD pin as DATA
|
||||
// example from espressif: https://github.com/espressif/esp-idf/blob/release/v4.4/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c
|
||||
|
||||
// note to self: PDM has known bugs on S3, and does not work on C3
|
||||
// * S3: PDM sample rate only at 50% of expected rate: https://github.com/espressif/esp-idf/issues/9893
|
||||
// * S3: I2S PDM has very low amplitude: https://github.com/espressif/esp-idf/issues/8660
|
||||
// * C3: does not support PDM to PCM input. SoC would allow PDM RX, but there is no hardware to directly convert to PCM so it will not work. https://github.com/espressif/esp-idf/issues/8796
|
||||
|
||||
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided. PDM is not supported on ESP32-S2. PDM RX not supported on ESP32-C3
|
||||
_config.channel_format =I2S_PDM_MIC_CHANNEL; // seems that PDM mono mode always uses left channel.
|
||||
_config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality
|
||||
@@ -425,7 +430,7 @@ class ES7243 : public I2SSource {
|
||||
Wire.write((uint8_t)val);
|
||||
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
|
||||
if (i2cErr != 0) {
|
||||
DEBUGSR_PRINTF("AR: ES7243 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", ES7243_ADDR, i2cErr, reg, val);
|
||||
DEBUGSR_PRINTF("AR: ES7243 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES7243_ADDR, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +610,7 @@ class I2SAdcSource : public I2SSource {
|
||||
// Determine Analog channel. Only Channels on ADC1 are supported
|
||||
int8_t channel = digitalPinToAnalogChannel(_audioPin);
|
||||
if (channel > 9) {
|
||||
ERRORSR_PRINTF("Incompatible GPIO used for analog audio input: %d\n", _audioPin);
|
||||
USER_PRINTF("AR: Incompatible GPIO used for analog audio input: %d\n", _audioPin);
|
||||
return;
|
||||
} else {
|
||||
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
|
||||
@@ -624,7 +629,7 @@ class I2SAdcSource : public I2SSource {
|
||||
// Enable I2S mode of ADC
|
||||
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
|
||||
USER_PRINTF("AR: Failed to set i2s adc mode: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,73 @@
|
||||
# Audioreactive usermod
|
||||
|
||||
This usermod allows controlling LEDs using audio input. Audio input can be either microphone or analog-in (AUX) using appropriate adapter.
|
||||
Enabless controlling LEDs via audio input. Audio source can be a microphone or analog-in (AUX) using an appropriate adapter.
|
||||
Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...).
|
||||
|
||||
The usermod does audio processing and provides data structure that specially written effect can use.
|
||||
Does audio processing and provides data structure that specially written effects can use.
|
||||
|
||||
The usermod **does not** provide effects or draws anything to LED strip/matrix.
|
||||
**does not** provide effects or draw anything to an LED strip/matrix.
|
||||
|
||||
## Additional Documentation
|
||||
This usermod is an evolution of [SR-WLED](https://github.com/atuline/WLED), and a lot of documentation and information can be found in the [SR-WLED wiki](https://github.com/atuline/WLED/wiki):
|
||||
* [getting started with audio](https://github.com/atuline/WLED/wiki/First-Time-Setup#sound)
|
||||
* [Sound settings](https://github.com/atuline/WLED/wiki/Sound-Settings) - similar to options on the usemod settings page in WLED.
|
||||
* [Digital Audio](https://github.com/atuline/WLED/wiki/Digital-Microphone-Hookup)
|
||||
* [Analog Audio](https://github.com/atuline/WLED/wiki/Analog-Audio-Input-Options)
|
||||
* [UDP Sound sync](https://github.com/atuline/WLED/wiki/UDP-Sound-Sync)
|
||||
|
||||
|
||||
## Supported MCUs
|
||||
This audioreactive usermod works best on "classic ESP32" (dual core), and on ESP32-S3 which also has dual core and hardware floating point support.
|
||||
|
||||
It will compile succesfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3.
|
||||
|
||||
Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP32-S3.
|
||||
|
||||
Currently ESP8266 is not supported, due to low speed and small RAM of this chip.
|
||||
There are however plans to create a lightweight audioreactive for the 8266, with reduced features.
|
||||
## Installation
|
||||
|
||||
Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment as well as `arduinoFFT` to your `lib_deps`.
|
||||
### using customised _arduinoFFT_ library for use with this usermod
|
||||
Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment `build_flags`, as well as `https://github.com/blazoncek/arduinoFFT.git` to your `lib_deps`.
|
||||
If you are not using PlatformIO (which you should) try adding `#define USERMOD_AUDIOREACTIVE` to *my_config.h* and make sure you have _arduinoFFT_ library downloaded and installed.
|
||||
|
||||
Customised _arduinoFFT_ library for use with this usermod can be found at https://github.com/blazoncek/arduinoFFT.git
|
||||
|
||||
### using latest (develop) _arduinoFFT_ library
|
||||
Alternatively, you can use the latest arduinoFFT development version.
|
||||
ArduinoFFT `develop` library is slightly more accurate, and slighly faster than our customised library, however also needs additional 2kB RAM.
|
||||
|
||||
* `build_flags` = `-D USERMOD_AUDIOREACTIVE` `-D UM_AUDIOREACTIVE_USE_NEW_FFT`
|
||||
* `lib_deps`= `https://github.com/kosme/arduinoFFT#develop @ 1.9.2`
|
||||
|
||||
## Configuration
|
||||
|
||||
All parameters are runtime configurable though some may require hard boot after change (I2S microphone or selected GPIOs).
|
||||
All parameters are runtime configurable. Some may require a hard reset after changing them (I2S microphone or selected GPIOs).
|
||||
|
||||
If you want to define default GPIOs during compile time use the following (default values in parentheses):
|
||||
If you want to define default GPIOs during compile time, use the following (default values in parentheses):
|
||||
|
||||
- `SR_DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S, 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S
|
||||
- `AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
|
||||
- `I2S_SDPIN=x` : GPIO for SD pin on digital mcrophone (32)
|
||||
- `I2S_WSPIN=x` : GPIO for WS pin on digital mcrophone (15)
|
||||
- `I2S_CKPIN=x` : GPIO for SCK pin on digital mcrophone (14)
|
||||
- `ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
|
||||
- `ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
|
||||
- `MCLK_PIN=x` : GPIO for master clock pin on digital mcrophone (-1)
|
||||
- `-D SR_DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S (default), 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S
|
||||
- `-D AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
|
||||
- `-D I2S_SDPIN=x` : GPIO for SD pin on digital microphone (32)
|
||||
- `-D I2S_WSPIN=x` : GPIO for WS pin on digital microphone (15)
|
||||
- `-D I2S_CKPIN=x` : GPIO for SCK pin on digital microphone (14)
|
||||
- `-D MCLK_PIN=x` : GPIO for master clock pin on digital Line-In boards (-1)
|
||||
- `-D ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
|
||||
- `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
|
||||
|
||||
**NOTE** Due to the fact that usermod uses I2S peripherial for analog audio sampling, use of analog *buttons* (i.e. potentiometers) is disabled while running this usermod with analog microphone.
|
||||
**NOTE** I2S is used for analog audio sampling. Hence, the analog *buttons* (i.e. potentiometers) are disabled when running this usermod with an analog microphone.
|
||||
|
||||
### Advanced Compile-Time Options
|
||||
You can use the following additional flags in your `build_flags`
|
||||
* `-D SR_SQUELCH=x` : Default "squelch" setting (10)
|
||||
* `-D SR_GAIN=x` : Default "gain" setting (60)
|
||||
* `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this).
|
||||
* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM ressources (not recommended unless you absolutely need this).
|
||||
* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call.
|
||||
* `-D MIC_LOGGER` : (debugging) Logs samples from the microphone to serial USB. Use with serial plotter (Arduino IDE)
|
||||
* `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB.
|
||||
|
||||
## Release notes
|
||||
|
||||
2022-06 Ported from [soundreactive](https://github.com/atuline/WLED) by @blazoncek (AKA Blaz Kristan)
|
||||
* 2022-06 Ported from [soundreactive WLED](https://github.com/atuline/WLED) - by @blazoncek (AKA Blaz Kristan) and the [SR-WLED team](https://github.com/atuline/WLED/wiki#sound-reactive-wled-fork-team).
|
||||
* 2022-11 Updated to align with "[MoonModules/WLED](https://amg.wled.me)" audioreactive usermod - by @softhack007 (AKA Frank Möhle).
|
||||
|
||||
Reference in New Issue
Block a user