Problem description:
I have noticed that if some of the components do exceed the max allowed execution time in the audio ISR (load goes over 100%) the overall latency is increased and stays increased. That is - it is enough to do it once and the latency stays at new level until the next reset.
To test it i created a custom component that injects a single shot delay into the ISR, the update function works like this:
- receive a new block
- if the delay is not 0, wait x ms.
- transmit the block
- set the delay to 0, making it one shot delay
Scope shots show the latency right after reset, with value ~6.8ms, correct for the 128 words block size.
After adding a one shot delay in the ISR the latency is increased depending on the injected delay value, yet it never goes back to the initial value
Test project:
Input signal is ~30Hz square wave.
I know this is not a normal use case and such situations should be avoided. In some cases there are large memory buffers placed in PSRAM requiring clearing - this takes too much time for one ISR. I was able to fix it by splitting the operation into chunks. I do have one component that is reporting the max CPU load of 45%, yet any attempt to enable it permanently increases the latency. Stays the same even after disabling the component,where it just re-transmits the input blocks directly to output.
I have noticed that if some of the components do exceed the max allowed execution time in the audio ISR (load goes over 100%) the overall latency is increased and stays increased. That is - it is enough to do it once and the latency stays at new level until the next reset.
To test it i created a custom component that injects a single shot delay into the ISR, the update function works like this:
- receive a new block
- if the delay is not 0, wait x ms.
- transmit the block
- set the delay to 0, making it one shot delay
Scope shots show the latency right after reset, with value ~6.8ms, correct for the 128 words block size.
After adding a one shot delay in the ISR the latency is increased depending on the injected delay value, yet it never goes back to the initial value
Test project:
C++:
#include <Arduino.h>
#include <Audio.h>
#include <AudioStream.h>
class AudioEffectLatencyTest : public AudioStream
{
public:
AudioEffectLatencyTest(void) : AudioStream(1, inputQueueArray){ };
virtual void update(void)
{
audio_block_t *block;
block = receiveWritable();
if (!block) return;
delay(delay_ms);
transmit(block);
release(block);
delay_ms = 0;
}
void delay_set(uint32_t value)
{
__disable_irq();
delay_ms = value;
__enable_irq();
}
uint32_t delay_get() {return delay_ms;}
private:
uint32_t delay_ms = 0;
audio_block_t *inputQueueArray[1];
};
// GUItool: begin automatically generated code
AudioInputI2S i2s_1; //xy=598,332
AudioEffectLatencyTest blockDelay; // added custom component introducing a one shot delay into the audio ISR
AudioOutputI2S i2s_2; //xy=1230,344
AudioConnection patchCord1(i2s_1, 0, blockDelay, 0);
AudioConnection patchCord2(i2s_1, 1, i2s_2, 1);
AudioConnection patchCord3(blockDelay, 0, i2s_2, 0);
AudioControlSGTL5000 codec; //xy=647,561
// GUItool: end automatically generated code
bool codecOK = false;
void delayISR_set(uint32_t d);
void delay_NoInt_set(uint32_t d);
void setup()
{
Serial.begin(115200);
AudioMemory(20);
codecOK = codec.enable();
codec.inputSelect(AUDIO_INPUT_LINEIN);
codec.volume(0.8f);
codec.lineInLevel(10, 10);
codec.adcHighPassFilterDisable();
delay(3000);
Serial.println("T.41 Latency test - one shot delay injected into the audio ISR");
Serial.printf("Delay = %d ms\r\n", blockDelay.delay_get());
Serial.println("Press 1 to set the delay inside the ISR to 0ms");
Serial.println("Press 2 to set the delay inside the ISR to 5ms");
Serial.println("Press 3 to set the delay inside the ISR to 10ms");
Serial.println("Press 4 to set the delay inside the ISR to 20ms");
Serial.println("Press 5 to set the delay within AudioNoInterrupts to 0ms");
Serial.println("Press 6 to set the delay within AudioNoInterrupts to 5ms");
Serial.println("Press 7 to set the delay within AudioNoInterrupts to 10ms");
Serial.println("Press 8 to set the delay within AudioNoInterrupts to 20ms");
Serial.println("Press R to Reset the MCU");
}
void loop()
{
while (Serial.available())
{
uint8_t data = Serial.read();
switch(data)
{
case '1':
delayISR_set(0);
break;
case '2':
delayISR_set(5);
break;
case '3':
delayISR_set(10);
break;
case '4':
delayISR_set(20);
break;
case '5':
delay_NoInt_set(0);
break;
case '6':
delay_NoInt_set(5);
break;
case '7':
delay_NoInt_set(10);
break;
case '8':
delay_NoInt_set(20);
break;
case 'r':
case 'R':
SCB_AIRCR = 0x05FA0004; // MCU reset
break;
default:
break;
}
}
}
void delayISR_set(uint32_t d)
{
Serial.printf("Previous delay = %ld\r\n", blockDelay.delay_get());
Serial.printf("Delay set to %ld ms\r\n", d);
blockDelay.delay_set(d);
Serial.printf("AudioMem usage max = %d\r\n", AudioMemoryUsageMax());
AudioMemoryUsageMaxReset();
}
void delay_NoInt_set(uint32_t d)
{
Serial.printf("NoInterrupts Delay set to %ld ms\r\n", d);
AudioNoInterrupts();
delay(d);
AudioInterrupts();
}
Input signal is ~30Hz square wave.
I know this is not a normal use case and such situations should be avoided. In some cases there are large memory buffers placed in PSRAM requiring clearing - this takes too much time for one ISR. I was able to fix it by splitting the operation into chunks. I do have one component that is reporting the max CPU load of 45%, yet any attempt to enable it permanently increases the latency. Stays the same even after disabling the component,where it just re-transmits the input blocks directly to output.