Merge branch 'audio-reactive' into mdev

This commit is contained in:
Ewowi
2022-09-02 20:55:47 +02:00
2 changed files with 84 additions and 20 deletions

View File

@@ -24,7 +24,7 @@
// #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter)
// #define FFT_SAMPLING_LOG // FFT result debugging
// #define SR_DEBUG // generic SR DEBUG messages
// #define NO_MIC_LOGGER // exclude MIC_LOGGER from SR_DEBUG
#ifdef SR_DEBUG
#define DEBUGSR_PRINT(x) Serial.print(x)
@@ -103,6 +103,7 @@ static void autoResetPeak(void); // peak auto-reset function
////////////////////
// Begin FFT Code //
////////////////////
#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
@@ -135,7 +136,6 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
#endif
// 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.
static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f};
static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON)
#ifdef SR_DEBUG
@@ -172,7 +172,7 @@ static float fftAddAvg(int from, int to) {
return result / float(to - from + 1);
}
// FFT main code
// FFT main task
void FFTcode(void * parameter)
{
DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID());
@@ -417,6 +417,10 @@ static void autoResetPeak(void) {
}
////////////////////
// usermod class //
////////////////////
//class name. Use something descriptive and leave the ": public Usermod" part :)
class AudioReactive : public Usermod {
@@ -533,6 +537,10 @@ class AudioReactive : public Usermod {
static const char UDP_SYNC_HEADER_v1[];
// private methods
////////////////////
// Debug support //
////////////////////
void logAudio()
{
#ifdef MIC_LOGGER
@@ -604,6 +612,10 @@ class AudioReactive : public Usermod {
} // logAudio()
//////////////////////
// Audio Processing //
//////////////////////
/*
* A "PI controller" multiplier to automatically adjust sound sensitivity.
*
@@ -698,7 +710,7 @@ class AudioReactive : public Usermod {
last_soundAgc = soundAgc;
} // agcAvg()
// post-processing and filtering of MIC sample (micDataReal) from FFTcode()
void getSample()
{
float sampleAdj; // Gain adjusted sample value
@@ -793,6 +805,26 @@ class AudioReactive : public Usermod {
}
//////////////////////
// UDP Sound Sync //
//////////////////////
// try to establish UDP sound sync connection
void connectUDPSoundSync(void) {
// This function tries to establish a UDP sync connection if needed
// necessary as we also want to transmit in "AP Mode", but the standard "connected()" callback only reacts on STA connection
static unsigned long last_connection_attempt = 0;
if ((audioSyncPort <= 0) || ((audioSyncEnabled & 0x03) == 0)) return; // Sound Sync not enabled
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
last_connection_attempt = millis();
connected(); // try to start UDP
}
void transmitAudioData()
{
if (!udpSyncConnected) return;
@@ -820,12 +852,10 @@ class AudioReactive : public Usermod {
return;
} // transmitAudioData()
static bool isValidUdpSyncVersion(const char *header) {
return strncmp_P(header, PSTR(UDP_SYNC_HEADER), 6) == 0;
}
bool receiveAudioData() // check & process new data. return TRUE in case that new audio data was received.
{
if (!udpSyncConnected) return false;
@@ -875,6 +905,10 @@ class AudioReactive : public Usermod {
}
//////////////////////
// usermod functions//
//////////////////////
public:
//Functions called by WLED or other usermods
@@ -967,6 +1001,7 @@ class AudioReactive : public Usermod {
disableSoundProcessing = true;
}
if (enabled) connectUDPSoundSync();
initDone = true;
}
@@ -977,6 +1012,11 @@ class AudioReactive : public Usermod {
*/
void connected()
{
if (udpSyncConnected) { // clean-up: if open, close old UDP sync connection
udpSyncConnected = false;
fftUdp.stop();
}
if (audioSyncPort > 0 && (audioSyncEnabled & 0x03)) {
#ifndef ESP8266
udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort);
@@ -1079,6 +1119,8 @@ class AudioReactive : public Usermod {
autoResetPeak(); // auto-reset sample peak after strip minShowDelay
if (!udpSyncConnected) udpSamplePeak = false; // reset UDP samplePeak while UDP is unconnected
connectUDPSoundSync(); // ensure we have a connection - if needed
// UDP Microphone Sync - receive mode
if ((audioSyncEnabled & 0x02) && udpSyncConnected) {
// Only run the audio listener code if we're in Receive mode
@@ -1196,6 +1238,10 @@ class AudioReactive : public Usermod {
}
////////////////////////////
// Settings and Info Page //
////////////////////////////
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
@@ -1317,6 +1363,10 @@ class AudioReactive : public Usermod {
infoArr.add(fftTime-sampleTime);
infoArr.add("ms");
#endif
// add a small horizontal line, for better readability
infoArr = user.createNestedArray(F("<hr style=\"height:1px;border-width:0;color:gray;background-color:gray\" />"));
infoArr.add(F(" <hr style=\"height:1px;border-width:0;color:gray;background-color:gray\" /> "));
}
}

View File

@@ -6250,7 +6250,7 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew
return FRAMETIME;
} // mode_gravcentric()
static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin
static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!;!;ix=128,mp12=3,ssim=0,1d,vo"; // Corner, Beatsin
///////////////////////
@@ -6386,7 +6386,7 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
return FRAMETIME;
} // mode_midnoise()
static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Maximum length;,!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin
static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Maximum length;,!;!;ix=128,mp12=1,ssim=0,1d,vo"; // Bar, Beatsin
//////////////////////
@@ -6511,7 +6511,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
}
float volumeSmth = *(float*) um_data->u_data[0];
SEGMENT.fadeToBlackBy(64);
SEGMENT.fadeToBlackBy(32);
plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually.
plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline.
@@ -6663,7 +6663,8 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
SEGENV.aux0 = 0;
}
SEGMENT.fade_out(SEGMENT.speed);
int fadeoutDelay = (256 - SEGMENT.speed) / 32;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed);
SEGENV.step += FRAMETIME;
if (SEGENV.step > SPEED_FORMULA_L) {
@@ -6731,7 +6732,9 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
float my_magnitude = *(float*) um_data->u_data[5] / 4.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
SEGMENT.fade_out(SEGMENT.speed);
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 32;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed);
int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN.
if (locn < 1) locn = 0; // avoid underflow
@@ -6746,7 +6749,7 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
return FRAMETIME;
} // mode_freqmap()
static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;,!;!;mp12=2,ssim=0,1d,fr"; // Circle, Beatsin
static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;,!;!;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin
///////////////////////
@@ -6801,7 +6804,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
return FRAMETIME;
} // mode_freqmatrix()
static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin
static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;mp12=3,ssim=0,1d,fr"; // Corner, Beatsin
//////////////////////
@@ -6822,7 +6825,10 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
SEGMENT.fade_out(fadeRate);
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate);
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
uint16_t locn = random16(0,SEGLEN);
@@ -6954,7 +6960,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
return FRAMETIME;
} // mode_gravfreq()
static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;,!;!;ix=128,mp12=2,ssim=0,1d,fr"; // Circle, Beatsin
static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;,!;!;ix=128,mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin
//////////////////////
@@ -6968,7 +6974,10 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
SEGMENT.fade_out(224); // Just in case something doesn't get faded.
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
//SEGMENT.fade_out(224); // Just in case something doesn't get faded.
int fadeoutDelay = (256 - SEGMENT.speed) / 96;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
for (int i=0; i<numBins; i++) { // How many active bins are we using.
@@ -6994,13 +7003,16 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
float FFT_MajorPeak = *(float*) um_data->u_data[4];
float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
SEGMENT.fadeToBlackBy(64); // Just in case something doesn't get faded.
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
float frTemp = FFT_MajorPeak;
uint8_t octCount = 0; // Octave counter.
uint8_t volTemp = 0;
if (my_magnitude > 32) volTemp = 255; // We need to squelch out the background noise.
volTemp = 32.0f + my_magnitude * 1.5f; // brightness = volume (overflows are handled in next lines)
if (my_magnitude < 48) volTemp = 0; // We need to squelch out the background noise.
if (my_magnitude > 144) volTemp = 255; // everything above this is full brightness
while ( frTemp > 249 ) {
octCount++; // This should go up to 5.
@@ -7016,7 +7028,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
return FRAMETIME;
} // mode_rocktaves()
static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;,!;!;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin
static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;,!;!;mp12=1,ssim=0,1d,fr"; // Bar, Beatsin
///////////////////////
@@ -7100,7 +7112,9 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
rippleTime = true;
}
SEGMENT.fadeToBlackBy(SEGMENT.speed);
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed);
for (int x=0; x < cols; x++) {
uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1);