AR: gracefull suspend when under external control

- same fix as in SR-WLED upstream
- if strip.isupdating() is true for more than 12ms, run audio filter loop regardlessly. The userloop is very fast, so I'm expect no bad side-effects from this.
This commit is contained in:
Frank 2022-07-10 22:47:42 +02:00
parent 67bcf42125
commit ff5d899a92

View File

@ -2,6 +2,7 @@
#include "wled.h"
#include <driver/i2s.h>
#include <driver/adc.h>
#ifndef ESP32
#error This audio reactive usermod does not support the ESP8266.
@ -71,6 +72,7 @@ const float agcSampleSmooth[AGC_NUM_PRESETS] = { 1/12.f, 1/6.f, 1/16.f}; //
// AGC presets end
static AudioSource *audioSource = nullptr;
static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks.
//static uint16_t micData; // Analog input for FFT
static uint16_t micDataSm; // Smoothed mic data, as it's a bit twitchy
@ -152,8 +154,11 @@ void FFTcode(void * parameter)
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.
// Only run the FFT computing code if we're not in Receive mode
if (audioSyncEnabled & 0x02) continue;
// Only run the FFT computing code if we're not in Receive mode and not in realtime mode
if (disableSoundProcessing || (audioSyncEnabled & 0x02)) {
//delay(7); // release CPU - delay is implemeted using vTaskDelay(). cannot use yield() because we are out of arduino loop context
continue;
}
#ifdef WLED_DEBUG
// unsigned long start = millis();
@ -435,7 +440,7 @@ class AudioReactive : public Usermod {
float sampleAgc = 0.0f; // Our AGC sample
int16_t rawSampleAgc = 0; // Our AGC sample - raw
uint32_t timeOfPeak = 0;
uint32_t lastTime = 0;
unsigned long lastTime = 0; // last time of running UDP Microphone Sync
float micLev = 0.0f; // Used to convert returned value to have '0' as minimum. A leveller
float expAdjF = 0.0f; // Used for exponential filter.
@ -813,6 +818,7 @@ class AudioReactive : public Usermod {
*/
void setup()
{
disableSoundProcessing = true; // just to be sure
if (!initDone) {
// usermod exchangeable data
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
@ -908,6 +914,7 @@ class AudioReactive : public Usermod {
if (!audioSource) enabled = false; // audio failed to initialise
if (enabled) onUpdateBegin(false); // create FFT task
if (enabled) disableSoundProcessing = false;
initDone = true;
}
@ -941,13 +948,66 @@ class AudioReactive : public Usermod {
*/
void loop()
{
if (!enabled || strip.isUpdating()) return;
static unsigned long lastUMRun = millis();
if (!(audioSyncEnabled & 0x02)) { // Only run the sampling code IF we're not in Receive mode
if (!enabled) {
disableSoundProcessing = true; // keep processing suspended (FFT task)
lastUMRun = millis(); // update time keeping
return;
}
// We cannot wait indefinitely before processing audio data
//if (!enabled || strip.isUpdating()) return;
if (strip.isUpdating() && (millis() - lastUMRun < 12)) return; // be nice, but not too nice
// suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET)
if ( (realtimeOverride == REALTIME_OVERRIDE_NONE) // please odd other orrides here if needed
&&( (realtimeMode == REALTIME_MODE_GENERIC)
||(realtimeMode == REALTIME_MODE_E131)
||(realtimeMode == REALTIME_MODE_UDP)
||(realtimeMode == REALTIME_MODE_ADALIGHT)
||(realtimeMode == REALTIME_MODE_ARTNET) ) ) // please add other modes here if needed
{
#ifdef WLED_DEBUG
if ((disableSoundProcessing == false) && (audioSyncEnabled == 0)) { // we just switched to "disabled"
DEBUG_PRINTLN("[AR userLoop] realtime mode active - audio processing suspended.");
DEBUG_PRINTF( " RealtimeMode = %d; RealtimeOverride = %d\n", int(realtimeMode), int(realtimeOverride));
}
#endif
disableSoundProcessing = true;
} else {
#ifdef WLED_DEBUG
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0)) { // we just switched to "disabled"
DEBUG_PRINTLN("[AR userLoop] realtime mode ended - audio processing resumed.");
DEBUG_PRINTF( " RealtimeMode = %d; RealtimeOverride = %d\n", int(realtimeMode), int(realtimeOverride));
}
#endif
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0)) lastUMRun = millis(); // just left "realtime mode" - update timekeeping
disableSoundProcessing = false;
}
if (audioSyncEnabled & 0x02) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode
if (audioSyncEnabled & 0x01) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode
if (!(audioSyncEnabled & 0x02) && !disableSoundProcessing) { // Only run the sampling code IF we're not in Receive mode or realtime mode
bool agcEffect = false;
if (soundAgc > AGC_NUM_PRESETS) soundAgc = 0; // make sure that AGC preset is valid (to avoid array bounds violation)
int userloopDelay = int(millis() - lastUMRun); // how long since last run? we might need to cat up to compensate lost times
int samplesSkipped = 0;
if (userloopDelay > 12) samplesSkipped = (userloopDelay + 12) / 25; // every 25ms we get a new batch of samples
if (samplesSkipped > 100) samplesSkipped = 100; // don't be silly
#ifdef WLED_DEBUG
// 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)) {
// Expect lagging in soundreactive effects if you see the next messages !!!
DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
if (samplesSkipped > 0) DEBUG_PRINTF("[AR userLoop] lost %d sample(s).\n", samplesSkipped);
}
#endif
lastUMRun = millis(); // update time keeping
getSample(); // Sample the microphone
agcAvg(); // Calculated the PI adjusted value as sampleAvg
@ -982,9 +1042,10 @@ class AudioReactive : public Usermod {
new_user_inputLevel = MIN(MAX(new_user_inputLevel, 0),255);
// update user interfaces - restrict frequency to avoid flooding UI's with small changes
if ( (((now_time - last_update_time > 3500) && (abs(new_user_inputLevel - inputLevel) > 2)) // small change - every 3.5 sec (max)
|| ((now_time - last_update_time > 2200) && (abs(new_user_inputLevel - inputLevel) > 15)) // medium change
|| ((now_time - last_update_time > 1200) && (abs(new_user_inputLevel - inputLevel) > 31))) ) // BIG change - every second
if (( ((now_time - last_update_time > 3500) && (abs(new_user_inputLevel - inputLevel) > 2)) // small change - every 3.5 sec (max)
||((now_time - last_update_time > 2200) && (abs(new_user_inputLevel - inputLevel) > 15)) // medium change
||((now_time - last_update_time > 1200) && (abs(new_user_inputLevel - inputLevel) > 31))) // BIG change - every second
&& !strip.isUpdating()) // don't interfere while strip is updating
{
inputLevel = new_user_inputLevel; // change of least 3 units -> update user variable
updateInterfaces(CALL_MODE_WS_SEND); // is this the correct way to notify UIs ? Yes says blazoncek
@ -1026,6 +1087,7 @@ class AudioReactive : public Usermod {
#ifdef WLED_DEBUG
fftTime = sampleTime = 0;
#endif
disableSoundProcessing = true;
if (init && FFT_Task) {
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
} else {
@ -1043,6 +1105,7 @@ class AudioReactive : public Usermod {
0 // Core where the task should run
);
}
if (enabled) disableSoundProcessing = false;
}