Var fixes.

This commit is contained in:
Blaž Kristan 2022-06-09 14:44:48 +02:00
parent 184ff7a3b3
commit a6746f77f0
2 changed files with 130 additions and 129 deletions

View File

@ -2,7 +2,6 @@
#include "wled.h" #include "wled.h"
#include <driver/i2s.h> #include <driver/i2s.h>
#include "audio_source.h"
#ifndef ESP32 #ifndef ESP32
#error This audio reactive usermod does not support the ESP8266. #error This audio reactive usermod does not support the ESP8266.
@ -28,6 +27,8 @@
#define DEBUGSR_PRINTF(x...) #define DEBUGSR_PRINTF(x...)
#endif #endif
#include "audio_source.h"
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; constexpr i2s_port_t I2S_PORT = I2S_NUM_0;
constexpr int BLOCK_SIZE = 128; constexpr int BLOCK_SIZE = 128;
constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz
@ -38,6 +39,10 @@ constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz
//#define MAJORPEAK_SUPPRESS_NOISE // define to activate a dirty hack that ignores the lowest + hightest FFT bins //#define MAJORPEAK_SUPPRESS_NOISE // define to activate a dirty hack that ignores the lowest + hightest FFT bins
byte audioSyncEnabled = 0;
uint16_t audioSyncPort = 11988;
uint8_t inputLevel; // UI slider value
// //
// AGC presets // AGC presets
// Note: in C++, "const" implies "static" - no need to explicitly declare everything as "static const" // Note: in C++, "const" implies "static" - no need to explicitly declare everything as "static const"
@ -92,41 +97,46 @@ const uint16_t samples = 512; // This value MUST ALWAYS be a p
static AudioSource *audioSource; static AudioSource *audioSource;
byte soundSquelch = 10; // default squelch value for volume reactive routines static byte soundSquelch = 10; // default squelch value for volume reactive routines
byte sampleGain = 1; // default sample gain static byte sampleGain = 1; // default sample gain
uint16_t micData; // Analog input for FFT static uint16_t micData; // Analog input for FFT
uint16_t micDataSm; // Smoothed mic data, as it's a bit twitchy static uint16_t micDataSm; // Smoothed mic data, as it's a bit twitchy
static float micDataReal = 0.0f; // future support - this one has the full 24bit MicIn data - lowest 8bit after decimal point
static byte soundAgc = 0; // default Automagic gain control
static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our multiplier
static uint16_t noiseFloor = 100; // default squelch value for FFT reactive routines
double FFT_MajorPeak = 0; static double FFT_MajorPeak = 0;
double FFT_Magnitude = 0; static double FFT_Magnitude = 0;
//uint16_t mAvg = 0; //static uint16_t mAvg = 0;
// These are the input and output vectors. Input vectors receive computed results from FFT. // These are the input and output vectors. Input vectors receive computed results from FFT.
static double vReal[samplesFFT]; static double vReal[samplesFFT];
static double vImag[samplesFFT]; static double vImag[samplesFFT];
float fftBin[samplesFFT]; static float fftBin[samplesFFT];
// Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256.
// Oh, and bins 0,1,2 are no good, so we'll zero them out. // Oh, and bins 0,1,2 are no good, so we'll zero them out.
float fftCalc[16]; static float fftCalc[16];
uint8_t fftResult[16]; // Our calculated result table, which we feed to the animations. static uint8_t fftResult[16]; // Our calculated result table, which we feed to the animations.
//float fftResultMax[16]; // A table used for testing to determine how our post-processing is working. #ifdef SR_DEBUG
float fftAvg[16]; static float fftResultMax[16]; // A table used for testing to determine how our post-processing is working.
#endif
static float fftAvg[16];
// Table of linearNoise results to be multiplied by soundSquelch in order to reduce squelch across fftResult bins. // Table of linearNoise results to be multiplied by soundSquelch in order to reduce squelch across fftResult bins.
uint16_t linearNoise[16] = { 34, 28, 26, 25, 20, 12, 9, 6, 4, 4, 3, 2, 2, 2, 2, 2 }; static uint16_t linearNoise[16] = { 34, 28, 26, 25, 20, 12, 9, 6, 4, 4, 3, 2, 2, 2, 2, 2 };
// 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.
float fftResultPink[16] = { 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[16] = { 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 };
// Create FFT object // Create FFT object
arduinoFFT FFT = arduinoFFT(vReal, vImag, samples, SAMPLE_RATE); static arduinoFFT FFT = arduinoFFT(vReal, vImag, samples, SAMPLE_RATE);
float fftAdd(int from, int to) { float fftAdd(int from, int to) {
int i = from; float result = 0.0f;
float result = 0; for (int i = from; i <= to; i++) {
while (i <= to) { result += fftBin[i];
result += fftBin[i++];
} }
return result; return result;
} }
@ -146,6 +156,7 @@ void FFTcode(void * parameter) {
// Only run the FFT computing code if we're not in Receive mode // Only run the FFT computing code if we're not in Receive mode
if (audioSyncEnabled & (1 << 1)) if (audioSyncEnabled & (1 << 1))
continue; continue;
audioSource->getSamples(vReal, samplesFFT); audioSource->getSamples(vReal, samplesFFT);
// old code - Last sample in vReal is our current mic sample // old code - Last sample in vReal is our current mic sample
@ -249,10 +260,8 @@ void FFTcode(void * parameter) {
#endif #endif
for (int i = 0; i < samplesFFT; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3. for (int i = 0; i < samplesFFT; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3.
double t = 0.0; float t = fabs(vReal[i]); // just to be sure - values in fft bins should be positive any way
t = fabs(vReal[i]); // just to be sure - values in fft bins should be positive any way fftBin[i] = t / 16.0; // Reduce magnitude. Want end result to be linear and ~4096 max.
t = t / 16.0; // Reduce magnitude. Want end result to be linear and ~4096 max.
fftBin[i] = t;
} // for() } // for()
@ -286,12 +295,12 @@ void FFTcode(void * parameter) {
// Noise supression of fftCalc bins using soundSquelch adjustment for different input types. // Noise supression of fftCalc bins using soundSquelch adjustment for different input types.
for (int i=0; i < 16; i++) { for (int i=0; i < 16; i++) {
fftCalc[i] = fftCalc[i]-(float)soundSquelch*(float)linearNoise[i]/4.0 <= 0? 0 : fftCalc[i]; fftCalc[i] -= (float)soundSquelch*(float)linearNoise[i]/4.0 <= 0? 0 : fftCalc[i];
} }
// Adjustment for frequency curves. // Adjustment for frequency curves.
for (int i=0; i < 16; i++) { for (int i=0; i < 16; i++) {
fftCalc[i] = fftCalc[i] * fftResultPink[i]; fftCalc[i] *= fftResultPink[i];
} }
// Manual linear adjustment of gain using sampleGain adjustment for different input types. // Manual linear adjustment of gain using sampleGain adjustment for different input types.
@ -325,8 +334,93 @@ void FFTcode(void * parameter) {
} // FFTcode() } // FFTcode()
void logAudio() { //class name. Use something descriptive and leave the ": public Usermod" part :)
#ifdef MIC_LOGGER class AudioReactive : public Usermod {
private:
#ifndef AUDIOPIN
int8_t audioPin = 36;
#else
int8_t audioPin = AUDIOPIN;
#endif
#ifndef DMENABLED // aka DOUT
uint8_t dmType = 0;
#else
uint8_t dmType = DMENABLED;
#endif
#ifndef I2S_SDPIN // aka DOUT
int8_t i2ssdPin = 32;
#else
int8_t i2ssdPin = I2S_SDPIN;
#endif
#ifndef I2S_WSPIN // aka LRCL
int8_t i2swsPin = 15;
#else
int8_t i2swsPin = I2S_WSPIN;
#endif
#ifndef I2S_CKPIN // aka BCLK
int8_t i2sckPin = 14;
#else
int8_t i2sckPin = I2S_CKPIN;
#endif
WiFiUDP fftUdp;
struct audioSyncPacket {
char header[6] = UDP_SYNC_HEADER;
uint8_t myVals[32]; // 32 Bytes
int sampleAgc; // 04 Bytes
int sample; // 04 Bytes
float sampleAvg; // 04 Bytes
bool samplePeak; // 01 Bytes
uint8_t fftResult[16]; // 16 Bytes
double FFT_Magnitude; // 08 Bytes
double FFT_MajorPeak; // 08 Bytes
};
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
#define UDP_SYNC_HEADER "00001"
uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger
uint8_t binNum; // Used to select the bin for FFT based beat detection.
uint8_t targetAgc = 60; // This is our setPoint at 20% of max for the adjusted output
uint8_t myVals[32]; // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag
bool udpSamplePeak = 0; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData
int delayMs = 10; // I don't want to sample too often and overload WLED
int micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed
int sample; // Current sample. Must only be updated ONCE!!!
float sampleMax = 0.f; // Max sample over a few seconds. Needed for AGC controler.
float sampleReal = 0.f; // "sample" as float, to provide bits that are lost otherwise. Needed for AGC.
float tmpSample; // An interim sample variable used for calculatioins.
float sampleAdj; // Gain adjusted sample value
float sampleAgc = 0.f; // Our AGC sample
int rawSampleAgc = 0; // Our AGC sample - raw
long timeOfPeak = 0;
long lastTime = 0;
float micLev = 0.f; // Used to convert returned value to have '0' as minimum. A leveller
float sampleAvg = 0.f; // Smoothed Average
float beat = 0.f; // beat Detection
float expAdjF; // Used for exponential filter.
float weighting = 0.2; // Exponential filter weighting. Will be adjustable in a future release.
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
double mapf(double x, double in_min, double in_max, double out_min, double out_max);
bool isValidUdpSyncVersion(char header[6]) {
if (strncmp(header, UDP_SYNC_HEADER, 6) == 0) {
return true;
} else {
return false;
}
}
void logAudio() {
#ifdef MIC_LOGGER
//Serial.print("micData:"); Serial.print(micData); Serial.print("\t"); //Serial.print("micData:"); Serial.print(micData); Serial.print("\t");
//Serial.print("micDataSm:"); Serial.print(micDataSm); Serial.print("\t"); //Serial.print("micDataSm:"); Serial.print(micDataSm); Serial.print("\t");
//Serial.print("micIn:"); Serial.print(micIn); Serial.print("\t"); //Serial.print("micIn:"); Serial.print(micIn); Serial.print("\t");
@ -338,9 +432,9 @@ void logAudio() {
Serial.print("multAgc:"); Serial.print(multAgc, 4); Serial.print("\t"); Serial.print("multAgc:"); Serial.print(multAgc, 4); Serial.print("\t");
Serial.print("sampleAgc:"); Serial.print(sampleAgc); Serial.print("\t"); Serial.print("sampleAgc:"); Serial.print(sampleAgc); Serial.print("\t");
Serial.println(" "); Serial.println(" ");
#endif #endif
#ifdef MIC_SAMPLING_LOG #ifdef MIC_SAMPLING_LOG
//------------ Oscilloscope output --------------------------- //------------ Oscilloscope output ---------------------------
Serial.print(targetAgc); Serial.print(" "); Serial.print(targetAgc); Serial.print(" ");
Serial.print(multAgc); Serial.print(" "); Serial.print(multAgc); Serial.print(" ");
@ -354,9 +448,9 @@ void logAudio() {
Serial.print(100); Serial.print(" "); Serial.print(100); Serial.print(" ");
Serial.print(0); Serial.print(" "); Serial.print(0); Serial.print(" ");
Serial.println(" "); Serial.println(" ");
#endif #endif
#ifdef FFT_SAMPLING_LOG #ifdef FFT_SAMPLING_LOG
#if 0 #if 0
for(int i=0; i<16; i++) { for(int i=0; i<16; i++) {
Serial.print(fftResult[i]); Serial.print(fftResult[i]);
@ -403,101 +497,8 @@ void logAudio() {
else else
Serial.printf("max:%04d ", 256); Serial.printf("max:%04d ", 256);
Serial.println(); Serial.println();
#endif // FFT_SAMPLING_LOG #endif // FFT_SAMPLING_LOG
} // logAudio() } // logAudio()
//class name. Use something descriptive and leave the ": public Usermod" part :)
class AudioReactive : public Usermod {
private:
#ifndef AUDIOPIN
int8_t audioPin = 36;
#else
int8_t audioPin = AUDIOPIN;
#endif
#ifndef DMENABLED // aka DOUT
uint8_t dmType = 0;
#else
uint8_t dmType = DMENABLED;
#endif
#ifndef I2S_SDPIN // aka DOUT
int8_t i2ssdPin = 32;
#else
int8_t i2ssdPin = I2S_SDPIN;
#endif
#ifndef I2S_WSPIN // aka LRCL
int8_t i2swsPin = 15;
#else
int8_t i2swsPin = I2S_WSPIN;
#endif
#ifndef I2S_CKPIN // aka BCLK
int8_t i2sckPin = 14;
#else
int8_t i2sckPin = I2S_CKPIN;
#endif
//byte soundAgc = 0; // default Automagic gain control
//uint16_t noiseFloor = 100; // default squelch value for FFT reactive routines
WiFiUDP fftUdp;
byte audioSyncEnabled = 0;
uint16_t audioSyncPort = 11988;
struct audioSyncPacket {
char header[6] = UDP_SYNC_HEADER;
uint8_t myVals[32]; // 32 Bytes
int sampleAgc; // 04 Bytes
int sample; // 04 Bytes
float sampleAvg; // 04 Bytes
bool samplePeak; // 01 Bytes
uint8_t fftResult[16]; // 16 Bytes
double FFT_Magnitude; // 08 Bytes
double FFT_MajorPeak; // 08 Bytes
};
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
#define UDP_SYNC_HEADER "00001"
uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger
uint8_t binNum; // Used to select the bin for FFT based beat detection.
uint8_t targetAgc = 60; // This is our setPoint at 20% of max for the adjusted output
uint8_t myVals[32]; // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag
bool udpSamplePeak = 0; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData
int delayMs = 10; // I don't want to sample too often and overload WLED
int micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed
int sample; // Current sample. Must only be updated ONCE!!!
float sampleMax = 0.f; // Max sample over a few seconds. Needed for AGC controler.
float sampleReal = 0.f; // "sample" as float, to provide bits that are lost otherwise. Needed for AGC.
float tmpSample; // An interim sample variable used for calculatioins.
float sampleAdj; // Gain adjusted sample value
float sampleAgc = 0.f; // Our AGC sample
int rawSampleAgc = 0; // Our AGC sample - raw
long timeOfPeak = 0;
long lastTime = 0;
float micDataReal = 0.0; // future support - this one has the full 24bit MicIn data - lowest 8bit after decimal point
float micLev = 0.f; // Used to convert returned value to have '0' as minimum. A leveller
float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our multiplier
float sampleAvg = 0.f; // Smoothed Average
float beat = 0.f; // beat Detection
float expAdjF; // Used for exponential filter.
float weighting = 0.2; // Exponential filter weighting. Will be adjustable in a future release.
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
double mapf(double x, double in_min, double in_max, double out_min, double out_max);
bool isValidUdpSyncVersion(char header[6]) {
if (strncmp(header, UDP_SYNC_HEADER, 6) == 0) {
return true;
} else {
return false;
}
}
/* /*
* A "PI control" multiplier to automatically adjust sound sensitivity. * A "PI control" multiplier to automatically adjust sound sensitivity.

View File

@ -135,13 +135,13 @@ public:
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr); esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to install i2s driver: %d\n", err); DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
return; return;
} }
err = i2s_set_pin(I2S_NUM_0, &_pinConfig); err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to set i2s pin config: %d\n", err); DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
return; return;
} }
@ -152,7 +152,7 @@ public:
_initialized = false; _initialized = false;
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0); esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to uninstall i2s driver: %d\n", err); DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
return; return;
} }
pinManager.deallocatePin(i2swsPin, PinOwner::DigitalMic); pinManager.deallocatePin(i2swsPin, PinOwner::DigitalMic);
@ -174,13 +174,13 @@ public:
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY); err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
if ((err != ESP_OK)){ if ((err != ESP_OK)){
Serial.printf("Failed to get samples: %d\n", err); DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
return; return;
} }
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s // For correct operation, we need to read exactly sizeof(samples) bytes from i2s
if(bytes_read != sizeof(newSamples)) { if(bytes_read != sizeof(newSamples)) {
Serial.printf("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read); DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
return; return;
} }
@ -349,7 +349,7 @@ public:
// Determine Analog channel. Only Channels on ADC1 are supported // Determine Analog channel. Only Channels on ADC1 are supported
int8_t channel = digitalPinToAnalogChannel(audioPin); int8_t channel = digitalPinToAnalogChannel(audioPin);
if (channel > 9) { if (channel > 9) {
Serial.printf("Incompatible GPIO used for audio in: %d\n", audioPin); DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", audioPin);
return; return;
} else { } else {
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel)); adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
@ -358,14 +358,14 @@ public:
// Install Driver // Install Driver
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr); esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to install i2s driver: %d\n", err); DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
return; return;
} }
// Enable I2S mode of ADC // Enable I2S mode of ADC
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel)); err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to set i2s adc mode: %d\n", err); DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
return; return;
} }
@ -374,7 +374,7 @@ public:
// fingers crossed // fingers crossed
err = i2s_adc_enable(I2S_NUM_0); err = i2s_adc_enable(I2S_NUM_0);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to enable i2s adc: %d\n", err); DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
//return; //return;
} }
#endif #endif
@ -393,7 +393,7 @@ public:
esp_err_t err = i2s_adc_enable(I2S_NUM_0); esp_err_t err = i2s_adc_enable(I2S_NUM_0);
//esp_err_t err = i2s_start(I2S_NUM_0); //esp_err_t err = i2s_start(I2S_NUM_0);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to enable i2s adc: %d\n", err); DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
return; return;
} }
#endif #endif
@ -404,7 +404,7 @@ public:
err = i2s_adc_disable(I2S_NUM_0); err = i2s_adc_disable(I2S_NUM_0);
//err = i2s_stop(I2S_NUM_0); //err = i2s_stop(I2S_NUM_0);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to disable i2s adc: %d\n", err); DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
return; return;
} }
#endif #endif
@ -420,13 +420,13 @@ public:
// fingers crossed // fingers crossed
err = i2s_adc_disable(I2S_NUM_0); err = i2s_adc_disable(I2S_NUM_0);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to disable i2s adc: %d\n", err); DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
//return; //return;
} }
#endif #endif
err = i2s_driver_uninstall(I2S_NUM_0); err = i2s_driver_uninstall(I2S_NUM_0);
if (err != ESP_OK) { if (err != ESP_OK) {
Serial.printf("Failed to uninstall i2s driver: %d\n", err); DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
return; return;
} }
} }