estimated audio sound pressure
This commit is contained in:
@@ -232,6 +232,7 @@ static const float fftResultPink[MAX_PINK+1][NUM_GEQ_CHANNELS] = {
|
||||
// globals and FFT Output variables shared with animations
|
||||
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
|
||||
static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency
|
||||
static float FFT_MajPeakSmth = 1.0f; // FFT: (peak) frequency, smooth
|
||||
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 float fftTaskCycle = 0; // avg cycle time for FFT task
|
||||
@@ -498,6 +499,7 @@ void FFTcode(void * parameter)
|
||||
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_MajPeakSmth = FFT_MajPeakSmth + 0.42 * (FFT_MajorPeak - FFT_MajPeakSmth); // I like this "swooping peak" look
|
||||
|
||||
} else { // skip second run --> clear fft results, keep peaks
|
||||
memset(vReal, 0, sizeof(vReal));
|
||||
@@ -918,6 +920,8 @@ class AudioReactive : public Usermod {
|
||||
// variables used in effects
|
||||
int16_t volumeRaw = 0; // either sampleRaw or rawSampleAgc depending on soundAgc
|
||||
float my_magnitude =0.0f; // FFT_Magnitude, scaled by multAgc
|
||||
float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255
|
||||
float soundPressure = 0; // Sound Pressure estimation, based on microphone raw readings. 0 ->5db, 255 ->105db
|
||||
|
||||
// used to feed "Info" Page
|
||||
unsigned long last_UDPTime = 0; // time of last valid UDP sound sync datapacket
|
||||
@@ -1228,6 +1232,53 @@ class AudioReactive : public Usermod {
|
||||
} // getSample()
|
||||
|
||||
|
||||
// current sensitivity, based on AGC gain (multAgc)
|
||||
float getSensitivity()
|
||||
{
|
||||
// start with AGC gain factor
|
||||
float tmpSound = multAgc;
|
||||
// experimental: this gives you a calculated "real gain"
|
||||
// if ((sampleAvg> 1.0) && (sampleReal > 0.05)) tmpSound = (float)sampleRaw / sampleReal; // calculate gain from sampleReal
|
||||
// else tmpSound = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // silence --> use values from user settings
|
||||
|
||||
if (soundAgc == 0)
|
||||
tmpSound = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // AGC off -> use non-AGC gain from presets
|
||||
else
|
||||
tmpSound /= (float)sampleGain/40.0f + 1.0f/16.0f; // AGC ON -> scale value so 1 = middle value
|
||||
|
||||
// scale to 0..255. Actually I'm not absolutely happy with this, but it works
|
||||
if (tmpSound > 1.0) tmpSound = sqrtf(tmpSound);
|
||||
if (tmpSound > 1.25) tmpSound = ((tmpSound-1.25f)/3.42f) +1.25f;
|
||||
// we have a value now that should be between 0 and 4 (representing gain 1/16 ... 16.0)
|
||||
return fminf(fmaxf(128.0*tmpSound -6.0f, 0), 255.0); // return scaled non-inverted value // "-6" to ignore values below 1/24
|
||||
}
|
||||
|
||||
// estimate sound pressure, based on some assumptions :
|
||||
// * sample max = 32676 -> Acoustic overload point --> 105db ==> 255
|
||||
// * sample < squelch -> just above hearing level --> 5db ==> 0
|
||||
// see https://en.wikipedia.org/wiki/Sound_pressure#Examples_of_sound_pressure
|
||||
// use with I2S digital microphones. Expect stupid values for analog in, and with Line-In !!
|
||||
float estimatePressure() {
|
||||
// some constants
|
||||
constexpr float logMinSample = 1.2237754316221f; // ln(3.4)
|
||||
constexpr float sampleMin = 3.4f;
|
||||
constexpr float logMaxSample = 10.1895683436f; // ln(32767 - 6144)
|
||||
constexpr float sampleMax = 32767.0f - 6144.0f;
|
||||
|
||||
// take the max sample from last I2S batch.
|
||||
float micSampleMax = fabsf(sampleReal); // from getSample() - nice results, however distorted by MicLev processing
|
||||
//float micSampleMax = fabsf(micDataReal); // from FFTCode() - better source, but I'll do more testing before activating this
|
||||
// make sure we are in expected ranges
|
||||
if(micSampleMax <= sampleMin) return 0.0f;
|
||||
if(micSampleMax >= sampleMax) return 255.0f;
|
||||
|
||||
// apply logarithmic scaling
|
||||
float scaledvalue = logf(micSampleMax);
|
||||
scaledvalue = (scaledvalue - logMinSample) / (logMaxSample - logMinSample); // 0...1
|
||||
return fminf(fmaxf(256.0*scaledvalue, 0), 255.0); // scaled value
|
||||
}
|
||||
|
||||
|
||||
/* Limits the dynamics of volumeSmth (= sampleAvg or sampleAgc).
|
||||
* does not affect FFTResult[] or volumeRaw ( = sample or rawSampleAgc)
|
||||
*/
|
||||
@@ -1422,12 +1473,12 @@ class AudioReactive : public Usermod {
|
||||
// usermod exchangeable data
|
||||
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
|
||||
um_data = new um_data_t;
|
||||
um_data->u_size = 8;
|
||||
um_data->u_size = 11;
|
||||
um_data->u_type = new um_types_t[um_data->u_size];
|
||||
um_data->u_data = new void*[um_data->u_size];
|
||||
um_data->u_data[0] = &volumeSmth; //*used (New)
|
||||
um_data->u_type[0] = UMT_FLOAT;
|
||||
um_data->u_data[1] = &volumeRaw; // used (New)
|
||||
um_data->u_data[1] = &volumeRaw; // used (New)
|
||||
um_data->u_type[1] = UMT_UINT16;
|
||||
um_data->u_data[2] = fftResult; //*used (Blurz, DJ Light, Noisemove, GEQ_base, 2D Funky Plank, Akemi)
|
||||
um_data->u_type[2] = UMT_BYTE_ARR;
|
||||
@@ -1435,12 +1486,18 @@ class AudioReactive : public Usermod {
|
||||
um_data->u_type[3] = UMT_BYTE;
|
||||
um_data->u_data[4] = &FFT_MajorPeak; //*used (Ripplepeak, Freqmap, Freqmatrix, Freqpixels, Freqwave, Gravfreq, Rocktaves, Waterfall)
|
||||
um_data->u_type[4] = UMT_FLOAT;
|
||||
um_data->u_data[5] = &my_magnitude; // used (New)
|
||||
um_data->u_data[5] = &my_magnitude; // used (New)
|
||||
um_data->u_type[5] = UMT_FLOAT;
|
||||
um_data->u_data[6] = &maxVol; // assigned in effect function from UI element!!! (Puddlepeak, Ripplepeak, Waterfall)
|
||||
um_data->u_type[6] = UMT_BYTE;
|
||||
um_data->u_data[7] = &binNum; // assigned in effect function from UI element!!! (Puddlepeak, Ripplepeak, Waterfall)
|
||||
um_data->u_type[7] = UMT_BYTE;
|
||||
um_data->u_data[8] = &FFT_MajPeakSmth; // new
|
||||
um_data->u_type[8] = UMT_FLOAT;
|
||||
um_data->u_data[9] = &soundPressure; // used (New)
|
||||
um_data->u_type[9] = UMT_FLOAT;
|
||||
um_data->u_data[10] = &agcSensitivity; // used (New)
|
||||
um_data->u_type[10] = UMT_FLOAT;
|
||||
}
|
||||
|
||||
// Reset I2S peripheral for good measure
|
||||
@@ -1666,6 +1723,16 @@ class AudioReactive : public Usermod {
|
||||
if (soundAgc) my_magnitude *= multAgc;
|
||||
if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
|
||||
|
||||
// get AGC sensitivity and sound pressure
|
||||
static unsigned long lastEstimate = 0;
|
||||
if (millis() - lastEstimate > 12) {
|
||||
lastEstimate = millis();
|
||||
agcSensitivity = getSensitivity();
|
||||
if (limiterOn)
|
||||
soundPressure = soundPressure + 0.38f * (estimatePressure() - soundPressure); // dynamics limiter on -> some smoothing
|
||||
else
|
||||
soundPressure = soundPressure + 0.95f * (estimatePressure() - soundPressure); // dynamics limiter on -> raw value
|
||||
}
|
||||
limitSampleDynamics();
|
||||
} // if (!disableSoundProcessing)
|
||||
|
||||
|
||||
@@ -5976,14 +5976,16 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;
|
||||
um_data_t *um_data;
|
||||
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
volumeSmth = *(float*) um_data->u_data[0];
|
||||
volumeRaw = *(float*) um_data->u_data[1];
|
||||
volumeRaw = *(int16_t*) um_data->u_data[1];
|
||||
fftResult = (uint8_t*) um_data->u_data[2];
|
||||
samplePeak = *(uint8_t*) um_data->u_data[3];
|
||||
FFT_MajorPeak = *(float*) um_data->u_data[4];
|
||||
my_magnitude = *(float*) um_data->u_data[5];
|
||||
maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element
|
||||
binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element
|
||||
fftBin = (float*) um_data->u_data[8];
|
||||
FFT_MajPeakSmth= *(float*) um_data->u_data[8]; // FFT Majorpeak smoothed
|
||||
soundPressure = *(float*) um_data->u_data[9]; // sound pressure ( = logarithmic scale microphone input). Range 0...255
|
||||
agcSensitivity = *(float*) um_data->u_data[10]; // current AGC gain, scaled to 0...255. use "255.0f - agcSensitivity" to get MIC input level
|
||||
} else {
|
||||
// add support for no audio data
|
||||
um_data = simulateSound(SEGMENT.soundSim);
|
||||
|
||||
@@ -423,7 +423,7 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
// NOTE!!!
|
||||
// This may change as AudioReactive usermod may change
|
||||
um_data = new um_data_t;
|
||||
um_data->u_size = 8;
|
||||
um_data->u_size = 11;
|
||||
um_data->u_type = new um_types_t[um_data->u_size];
|
||||
um_data->u_data = new void*[um_data->u_size];
|
||||
um_data->u_data[0] = &volumeSmth;
|
||||
@@ -434,6 +434,9 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
um_data->u_data[5] = &my_magnitude;
|
||||
um_data->u_data[6] = &maxVol;
|
||||
um_data->u_data[7] = &binNum;
|
||||
um_data->u_data[8] = &FFT_MajorPeak; // dummy (samplePeak smoothed)
|
||||
um_data->u_data[9] = &volumeSmth; // dummy (soundPressure)
|
||||
um_data->u_data[10] = &volumeSmth; // dummy (agcSensitivity)
|
||||
} else {
|
||||
// get arrays from um_data
|
||||
fftResult = (uint8_t*)um_data->u_data[2];
|
||||
|
||||
Reference in New Issue
Block a user