Merge remote-tracking branch 'origin/dev' into mdev
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
#error This audio reactive usermod does not support the ESP8266.
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||
#include <esp_timer.h>
|
||||
#endif
|
||||
|
||||
@@ -41,13 +41,15 @@
|
||||
|
||||
constexpr i2s_port_t I2S_PORT = I2S_NUM_0;
|
||||
constexpr int BLOCK_SIZE = 128;
|
||||
constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms
|
||||
//constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms
|
||||
//constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz - previous default. Physical sample time -> 50ms
|
||||
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
|
||||
//constexpr SRate_t SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms
|
||||
//constexpr SRate_t SAMPLE_RATE = 10240; // Base sample rate in Hz - previous default. Physical sample time -> 50ms
|
||||
|
||||
#define FFT_MIN_CYCLE 18 // minimum time before FFT task is repeated. Use with 22Khz sampling
|
||||
//#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Use with 20Khz sampling
|
||||
//#define FFT_MIN_CYCLE 44 // minimum time before FFT task is repeated. Use with 10Khz sampling
|
||||
#define FFT_MIN_CYCLE 21 // minimum time before FFT task is repeated. Use with 22Khz sampling
|
||||
//#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
|
||||
|
||||
// globals
|
||||
static uint8_t inputLevel = 128; // UI slider value
|
||||
@@ -113,7 +115,7 @@ static void autoResetPeak(void); // peak auto-reset function
|
||||
#else
|
||||
// lib_deps += https://github.com/blazoncek/arduinoFFT.git
|
||||
#endif
|
||||
#include "arduinoFFT.h"
|
||||
#include <arduinoFFT.h>
|
||||
|
||||
// FFT Output variables shared with animations
|
||||
#define NUM_GEQ_CHANNELS 16 // number of frequency channels. Don't change !!
|
||||
@@ -145,7 +147,7 @@ static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calcula
|
||||
static float fftResultMax[NUM_GEQ_CHANNELS] = {0.0f}; // A table used for testing to determine how our post-processing is working.
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||
static uint64_t fftTime = 0;
|
||||
static uint64_t sampleTime = 0;
|
||||
#endif
|
||||
@@ -188,31 +190,33 @@ void FFTcode(void * parameter)
|
||||
// see https://www.freertos.org/vtaskdelayuntil.html
|
||||
const TickType_t xFrequency = FFT_MIN_CYCLE * portTICK_PERIOD_MS;
|
||||
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
for(;;) {
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy.
|
||||
// taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work.
|
||||
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||
// Only run the FFT computing code if we're not in Receive mode and not in realtime mode
|
||||
// Don't run FFT computing code if we're in Receive mode or in realtime mode
|
||||
if (disableSoundProcessing || (audioSyncEnabled & 0x02)) {
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||
uint64_t start = esp_timer_get_time();
|
||||
#endif
|
||||
|
||||
// get a fresh batch of samples from I2S
|
||||
if (audioSource) audioSource->getSamples(vReal, samplesFFT);
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||
if (start < esp_timer_get_time()) { // filter out overflows
|
||||
unsigned long sampleTimeInMillis = (esp_timer_get_time() - start +5ULL) / 10ULL; // "+5" to ensure proper rounding
|
||||
sampleTime = (sampleTimeInMillis*3 + sampleTime*7)/10; // smooth
|
||||
}
|
||||
#endif
|
||||
|
||||
xLastWakeTime = xTaskGetTickCount(); // update "last unblocked time" for vTaskDelay
|
||||
|
||||
// find highest sample in the batch
|
||||
float maxSample = 0.0f; // max sample from FFT batch
|
||||
for (int i=0; i < samplesFFT; i++) {
|
||||
@@ -226,30 +230,42 @@ void FFTcode(void * parameter)
|
||||
// early release allows the filters (getSample() and agcAvg()) to work with fresh values - we will have matching gain and noise gate values when we want to process the FFT results. micDataReal = maxSample;
|
||||
micDataReal = maxSample;
|
||||
|
||||
// run FFT (takes 3-5ms on ESP32)
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
FFT.dcRemoval(); // remove DC offset
|
||||
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
|
||||
//FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
|
||||
FFT.compute( FFTDirection::Forward ); // Compute FFT
|
||||
FFT.complexToMagnitude(); // Compute magnitudes
|
||||
#ifdef SR_DEBUG
|
||||
if (true) { // this allows measure FFT runtimes, as it disables the "only when needed" optimization
|
||||
#else
|
||||
FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples()
|
||||
if (sampleAvg > 1) { // noise gate open means that FFT results will be used. Don't run FFT if results are not needed.
|
||||
#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
|
||||
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection
|
||||
FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy
|
||||
FFT.Compute( FFT_FORWARD ); // Compute FFT
|
||||
FFT.ComplexToMagnitude(); // Compute magnitudes
|
||||
// run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2)
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
FFT.dcRemoval(); // remove DC offset
|
||||
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
|
||||
//FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
|
||||
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
|
||||
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection
|
||||
FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy
|
||||
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
|
||||
FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
|
||||
#else
|
||||
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
|
||||
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
|
||||
#endif
|
||||
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
||||
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
||||
|
||||
} else { // noise gate closed - only clear results as FFT was skipped. MIC samples are still valid when we do this.
|
||||
memset(vReal, 0, sizeof(vReal));
|
||||
FFT_MajorPeak = 1;
|
||||
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
|
||||
@@ -387,7 +403,7 @@ void FFTcode(void * parameter)
|
||||
fftResult[i] = constrain((int)currentResult, 0, 255);
|
||||
}
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||
if (start < esp_timer_get_time()) { // filter out overflows
|
||||
unsigned long fftTimeInMillis = ((esp_timer_get_time() - start) +5ULL) / 10ULL; // "+5" to ensure proper rounding
|
||||
fftTime = (fftTimeInMillis*3 + fftTime*7)/10; // smooth
|
||||
@@ -397,6 +413,8 @@ void FFTcode(void * parameter)
|
||||
autoResetPeak();
|
||||
detectSamplePeak();
|
||||
|
||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||
|
||||
} // for(;;)ever
|
||||
} // FFTcode() task end
|
||||
|
||||
@@ -455,7 +473,7 @@ class AudioReactive : public Usermod {
|
||||
int8_t i2swsPin = I2S_WSPIN;
|
||||
#endif
|
||||
#ifndef I2S_CKPIN // aka BCLK
|
||||
int8_t i2sckPin = 14;
|
||||
int8_t i2sckPin = 14; /*PDM: set to I2S_PIN_NO_CHANGE*/
|
||||
#else
|
||||
int8_t i2sckPin = I2S_CKPIN;
|
||||
#endif
|
||||
@@ -470,9 +488,9 @@ class AudioReactive : public Usermod {
|
||||
int8_t sclPin = ES7243_SCLPIN;
|
||||
#endif
|
||||
#ifndef MCLK_PIN
|
||||
int8_t mclkPin = -1;
|
||||
int8_t mclkPin = I2S_PIN_NO_CHANGE; /* ESP32: only -1, 0, 1, 3 allowed*/
|
||||
#else
|
||||
int8_t mclkPin = MLCK_PIN;
|
||||
int8_t mclkPin = MCLK_PIN;
|
||||
#endif
|
||||
|
||||
// new "V2" audiosync struct - 40 Bytes
|
||||
@@ -956,10 +974,18 @@ class AudioReactive : public Usermod {
|
||||
|
||||
// Reset I2S peripheral for good measure
|
||||
i2s_driver_uninstall(I2S_NUM_0);
|
||||
periph_module_reset(PERIPH_I2S0_MODULE);
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
periph_module_reset(PERIPH_I2S0_MODULE); // not possible on -C3
|
||||
#endif
|
||||
delay(100); // Give that poor microphone some time to setup.
|
||||
switch (dmType) {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// stub cases for not-yet-supported I2S modes on other ESP32 chips
|
||||
case 0: //ADC analog
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case 5: //PDM Microphone
|
||||
#endif
|
||||
#endif
|
||||
case 1:
|
||||
DEBUGSR_PRINT(F("AR: Generic I2S Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
@@ -984,12 +1010,16 @@ class AudioReactive : public Usermod {
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
break;
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case 5:
|
||||
DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
|
||||
break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// ADC over I2S is only possible on "classic" ESP32
|
||||
case 0:
|
||||
default:
|
||||
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||
@@ -997,6 +1027,7 @@ class AudioReactive : public Usermod {
|
||||
delay(100);
|
||||
if (audioSource) audioSource->initialize(audioPin);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
delay(250); // give microphone enough time to initialise
|
||||
|
||||
@@ -1371,13 +1402,15 @@ class AudioReactive : public Usermod {
|
||||
infoArr.add("off");
|
||||
if (audioSyncEnabled && !udpSyncConnected) infoArr.add(" <i>(unconnected)</i>");
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
|
||||
infoArr = user.createNestedArray(F("Sampling time"));
|
||||
infoArr.add(float(sampleTime)/100.0f);
|
||||
infoArr.add(" ms");
|
||||
infoArr = user.createNestedArray(F("FFT time"));
|
||||
infoArr.add(float(fftTime-sampleTime)/100.0f);
|
||||
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-sampleTime)/100.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1545,12 +1578,16 @@ class AudioReactive : public Usermod {
|
||||
void appendConfigData()
|
||||
{
|
||||
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
|
||||
oappend(SET_F("dd=addDropdown('AudioReactive','cfg:AGC');"));
|
||||
oappend(SET_F("addOption(dd,'Off',0);"));
|
||||
oappend(SET_F("addOption(dd,'Normal',1);"));
|
||||
@@ -1583,7 +1620,11 @@ class AudioReactive : public Usermod {
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'I2S SD');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'I2S WS');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',2,'I2S SCK');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'I2S Master CLK');"));
|
||||
#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 for MCLK</i>');"));
|
||||
#else
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'I2S Master CLK');"));
|
||||
#endif
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',4,'I2C SDA');"));
|
||||
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',5,'I2C SCL');"));
|
||||
}
|
||||
|
||||
@@ -4,15 +4,31 @@
|
||||
#include "wled.h"
|
||||
#include <driver/i2s.h>
|
||||
#include <driver/adc.h>
|
||||
#include <soc/i2s_reg.h> // needed for SPH0465 timing workaround (classic ESP32)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#include <driver/adc_deprecated.h>
|
||||
#include <driver/adc_types_deprecated.h>
|
||||
#endif
|
||||
// type of i2s_config_t.SampleRate was changed from "int" to "unsigned" in IDF 4.4.x
|
||||
#define SRate_t uint32_t
|
||||
#else
|
||||
#define SRate_t int
|
||||
#endif
|
||||
|
||||
//#include <driver/i2s_std.h>
|
||||
//#include <driver/i2s_pdm.h>
|
||||
//#include <driver/i2s_tdm.h>
|
||||
//#include <driver/gpio.h>
|
||||
|
||||
// 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)
|
||||
// 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
|
||||
#warning This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2.
|
||||
#endif
|
||||
|
||||
/* ToDo: remove. ES7243 is controlled via compiler defines
|
||||
Until this configuration is moved to the webinterface
|
||||
@@ -20,13 +36,6 @@
|
||||
|
||||
// if you have problems to get your microphone work on the left channel, uncomment the following line
|
||||
//#define I2S_USE_RIGHT_CHANNEL // (experimental) define this to use right channel (digital mics only)
|
||||
#ifdef I2S_USE_RIGHT_CHANNEL
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT
|
||||
#define I2S_MIC_CHANNEL_TEXT "right channel only."
|
||||
#else
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT
|
||||
#define I2S_MIC_CHANNEL_TEXT "left channel only."
|
||||
#endif
|
||||
|
||||
// Uncomment the line below to utilize ADC1 _exclusively_ for I2S sound input.
|
||||
// benefit: analog mic inputs will be sampled contiously -> better response times and less "glitches"
|
||||
@@ -36,18 +45,56 @@
|
||||
|
||||
// data type requested from the I2S driver - currently we always use 32bit
|
||||
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
|
||||
|
||||
#ifdef I2S_USE_16BIT_SAMPLES
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
|
||||
#define I2S_datatype int16_t
|
||||
#define I2S_unsigned_datatype uint16_t
|
||||
#define I2S_data_size I2S_BITS_PER_CHAN_16BIT
|
||||
#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
#else
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT
|
||||
//#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_24BIT
|
||||
#define I2S_datatype int32_t
|
||||
#define I2S_unsigned_datatype uint32_t
|
||||
#define I2S_data_size I2S_BITS_PER_CHAN_32BIT
|
||||
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
#endif
|
||||
|
||||
/* There are several (confusing) options in IDF 4.4.x:
|
||||
* I2S_CHANNEL_FMT_RIGHT_LEFT, I2S_CHANNEL_FMT_ALL_RIGHT and I2S_CHANNEL_FMT_ALL_LEFT stands for stereo mode, which means two channels will transport different data.
|
||||
* I2S_CHANNEL_FMT_ONLY_RIGHT and I2S_CHANNEL_FMT_ONLY_LEFT they are mono mode, both channels will only transport same data.
|
||||
* I2S_CHANNEL_FMT_MULTIPLE means TDM channels, up to 16 channel will available, and they are stereo as default.
|
||||
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
|
||||
*/
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 3))
|
||||
// espressif bug: only_left has no sound, left and right are swapped
|
||||
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
|
||||
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)
|
||||
// https://github.com/espressif/esp-idf/issues/6625 I2S: left/right channels are swapped for read (IDFGH-4826)
|
||||
#ifdef I2S_USE_RIGHT_CHANNEL
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT
|
||||
#define I2S_MIC_CHANNEL_TEXT "right channel only (work-around swapped channel bug in IDF 4.4)."
|
||||
#else
|
||||
//#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ALL_LEFT
|
||||
//#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_RIGHT_LEFT
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT
|
||||
#define I2S_MIC_CHANNEL_TEXT "left channel only (work-around swapped channel bug in IDF 4.4)."
|
||||
#endif
|
||||
|
||||
#else
|
||||
// not swapped
|
||||
#ifdef I2S_USE_RIGHT_CHANNEL
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT
|
||||
#define I2S_MIC_CHANNEL_TEXT "right channel only."
|
||||
#else
|
||||
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT
|
||||
#define I2S_MIC_CHANNEL_TEXT "left channel only."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* Interface class
|
||||
AudioSource serves as base class for all microphone types
|
||||
This enables accessing all microphones with one single interface
|
||||
@@ -91,13 +138,13 @@ class AudioSource {
|
||||
virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing
|
||||
|
||||
// Private constructor, to make sure it is not callable except from derived classes
|
||||
AudioSource(int sampleRate, int blockSize) :
|
||||
AudioSource(SRate_t sampleRate, int blockSize) :
|
||||
_sampleRate(sampleRate),
|
||||
_blockSize(blockSize),
|
||||
_initialized(false)
|
||||
{};
|
||||
|
||||
int _sampleRate; // Microphone sampling rate
|
||||
SRate_t _sampleRate; // Microphone sampling rate
|
||||
int _blockSize; // I2S block size
|
||||
bool _initialized; // Gets set to true if initialization is successful
|
||||
};
|
||||
@@ -107,7 +154,7 @@ class AudioSource {
|
||||
*/
|
||||
class I2SSource : public AudioSource {
|
||||
public:
|
||||
I2SSource(int sampleRate, int blockSize) :
|
||||
I2SSource(SRate_t sampleRate, int blockSize) :
|
||||
AudioSource(sampleRate, blockSize) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||
@@ -116,12 +163,19 @@ class I2SSource : public AudioSource {
|
||||
.channel_format = I2S_MIC_CHANNEL,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
|
||||
//.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = _blockSize,
|
||||
.use_apll = 0,
|
||||
.bits_per_chan = I2S_data_size,
|
||||
#else
|
||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
||||
#endif
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = _blockSize
|
||||
.dma_buf_len = _blockSize,
|
||||
.use_apll = false
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
@@ -129,18 +183,24 @@ class I2SSource : public AudioSource {
|
||||
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
|
||||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
|
||||
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pins: ws=%d, sd=%d\n", i2swsPin, i2ssdPin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// i2ssckPin needs special treatment, since it might be unused on PDM mics
|
||||
if (i2sckPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!pinManager.allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) return;
|
||||
if (!pinManager.allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) {
|
||||
DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pins: sck=%d\n", i2sckPin);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// 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
|
||||
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided
|
||||
_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
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reserve the master clock pin if provided
|
||||
@@ -151,6 +211,9 @@ class I2SSource : public AudioSource {
|
||||
}
|
||||
|
||||
_pinConfig = {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
.mck_io_num = mclkPin, // "classic" ESP32 supports setting MCK on GPIO0/GPIO1/GPIO3 only. i2s_set_pin() will fail if wrong mck_io_num is provided.
|
||||
#endif
|
||||
.bck_io_num = i2sckPin,
|
||||
.ws_io_num = i2swsPin,
|
||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||
@@ -166,9 +229,18 @@ class I2SSource : public AudioSource {
|
||||
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
|
||||
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
|
||||
return;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
err = i2s_set_clk(I2S_NUM_0, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed.
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to configure i2s clocks: %d\n", err);
|
||||
i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
@@ -222,7 +294,11 @@ class I2SSource : public AudioSource {
|
||||
|
||||
protected:
|
||||
void _routeMclk(int8_t mclkPin) {
|
||||
/* Enable the mclk routing depending on the selected mclk pin
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// MCLK routing by writing registers is not needed any more with IDF > 4.4.0
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// this way of MCLK routing only works on "classic" ESP32
|
||||
/* Enable the mclk routing depending on the selected mclk pin (ESP32: only 0,1,3)
|
||||
Only I2S_NUM_0 is supported
|
||||
*/
|
||||
if (mclkPin == GPIO_NUM_0) {
|
||||
@@ -235,6 +311,8 @@ class I2SSource : public AudioSource {
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
|
||||
WRITE_PERI_REG(PIN_CTRL, 0xFF00);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
i2s_config_t _config;
|
||||
@@ -275,7 +353,7 @@ class ES7243 : public I2SSource {
|
||||
}
|
||||
|
||||
public:
|
||||
ES7243(int sampleRate, int blockSize) :
|
||||
ES7243(SRate_t sampleRate, int blockSize) :
|
||||
I2SSource(sampleRate, blockSize) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||
};
|
||||
@@ -307,6 +385,9 @@ public:
|
||||
int8_t pin_ES7243_SCL;
|
||||
};
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// ADC over I2S is only availeable in "classic" ESP32
|
||||
|
||||
/* ADC over I2S Microphone
|
||||
This microphone is an ADC pin sampled via the I2S interval
|
||||
This allows to use the I2S API to obtain ADC samples with high sample rates
|
||||
@@ -314,7 +395,7 @@ public:
|
||||
*/
|
||||
class I2SAdcSource : public I2SSource {
|
||||
public:
|
||||
I2SAdcSource(int sampleRate, int blockSize) :
|
||||
I2SAdcSource(SRate_t sampleRate, int blockSize) :
|
||||
I2SSource(sampleRate, blockSize) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||
@@ -363,13 +444,17 @@ class I2SAdcSource : public I2SSource {
|
||||
return;
|
||||
}
|
||||
|
||||
adc1_config_width(ADC_WIDTH_BIT_12); // ensure that ADC runs with 12bit resolution
|
||||
|
||||
// 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);
|
||||
return;
|
||||
}
|
||||
// adc1_config_channel_atten(adc1_channel_t(channel), ADC_ATTEN_DB_11)); //see https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino
|
||||
|
||||
// see example in https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino
|
||||
adc1_config_channel_atten(adc1_channel_t(channel), ADC_ATTEN_DB_11); // configure ADC input amplification
|
||||
|
||||
#if defined(I2S_GRAB_ADC1_COMPLETELY)
|
||||
// according to docs from espressif, the ADC needs to be started explicitly
|
||||
@@ -489,20 +574,29 @@ class I2SAdcSource : public I2SSource {
|
||||
int8_t _audioPin;
|
||||
int8_t _myADCchannel = 0x0F; // current ADC channel for analog input. 0x0F means "undefined"
|
||||
};
|
||||
#endif
|
||||
|
||||
/* SPH0645 Microphone
|
||||
This is an I2S microphone with some timing quirks that need
|
||||
special consideration.
|
||||
*/
|
||||
|
||||
// https://github.com/espressif/esp-idf/issues/7192 SPH0645 i2s microphone issue when migrate from legacy esp-idf version (IDFGH-5453)
|
||||
// a user recommended this: Try to set .communication_format to I2S_COMM_FORMAT_STAND_I2S and call i2s_set_clk() after i2s_set_pin().
|
||||
class SPH0654 : public I2SSource {
|
||||
public:
|
||||
SPH0654(int sampleRate, int blockSize) :
|
||||
SPH0654(SRate_t sampleRate, int blockSize) :
|
||||
I2SSource(sampleRate, blockSize)
|
||||
{}
|
||||
|
||||
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// these registers are only existing in "classic" ESP32
|
||||
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
|
||||
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
|
||||
#else
|
||||
#warning FIX ME! Please.
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user