I’m a complete beginner with freeRTOS, so I decided to try and learn it on the ESP32S3 platform (ESP-IDF). My objective is to create a real-time audio synthesis task that generates a simple sine wave. The task functions as expected when running standalone. However, when I attempt to incorporate additional functionality, such as periodic statistics printing using a separate task, I observe strange popping artifacts in the audio output.
I have configured the priorities of the audio task (configMAX_PRIORITIES - 1) to ensure that the audio synthesis task takes precedence. However, I wonder if there might be some other issue related to task priorities or scheduling.
For the audio generation, I use a PWM Audio library (I can’t put a link here, but the documentation is easily available). The library uses interrupts and timers to simulate a DAC, with external filtering. The library also works well when run standalone, in an endless loop.
The audio task looks like this:
BUF_LEN 8 // Changing the buffer length does not change the popping intensity
#define SAMPLE_RATE (48000)
// Accumulated phase
static float p = 0.0f;
float f = 5000;
// Output buffer (2ch interleaved)
static uint16_t out_buf[DMA_BUF_LEN];
static void audio_task() {
pac.duty_resolution = LEDC_TIMER_10_BIT;
pac.gpio_num_left = CONFIG_LEFT_CHANNEL_GPIO;
pac.gpio_num_right = CONFIG_RIGHT_CHANNEL_GPIO;
pac.ledc_channel_left = LEDC_CHANNEL_0;
pac.ledc_channel_right = LEDC_CHANNEL_1;
pac.ledc_timer_sel = LEDC_TIMER_0;
pac.ringbuf_len = 1024;
pwm_audio_init(&pac);
/**< Initialize pwm audio */
pwm_audio_set_param(SAMPLE_RATE, 16, 1); /**< Set sample rate, bits and channel numbner */
pwm_audio_start();
pwm_audio_set_volume(0);
while (true) {
float inc = (TWOPI * f / SAMPLE_RATE);
float samp = 0.0f;
size_t bytes_written;
for (int i = 0; i < BUF_LEN; i++) {
samp = (sinf(p));
}
pwm_audio_write(&out_buf, BUF_LEN*2, &bytes_written, 10000);
}
}
Thank you in advance for any advice or recommendation.
Is the PWM Audio Library written to be RTOS aware? The task code you posted appears to run continuously without ever giving up the CPU. There are (apparently) no calls to synchronization or delay functions provided by FreeRTOS.
If you assign the above task to the highest priority (configMAX_PRIORITIES - 1) and you assign any other tasks to lower priorities, do your other tasks ever execute?
Yes, other tasks are executed correctly, briefly looking at the PWM Audio Library, it uses semaphores to control ringbuffer, so it should be RTOS aware. Thinking about it now, it’s maybe a bug in the library itself…
It seems you are using this library from Espressif. I reviewed it briefly. Audio samples are played under interrupt control, not DMA, so you’ll need to be sure all interrupt handlers in your application are short/fast. And you should also avoid masking interrupts for very long. You have an interrupt every 1/48kHz (= 21 microseconds) that requires real-time servicing. You might want to experiment with a 16kHz sample rate to see what happens.
I also noticed a small bug in the library. This line should initialize xHigherPriorityTaskWoken to pdFALSE. Otherwise, the library might be accidentally invoke the FreeRTOS scheduler every sample period (currently every 21 microseconds) for no reason. That could eat up a lot of your processing power and could also contribute to your audio glitches.
Good find, but unfortunately, it did not improve my situation. I guess I’ll just have to give up on this “hack” and use a proper I2S DAC, until the library matures. Thank you all for your time!
If you have the resources to do so, you may also want to run your application under supervision of tracealyzer. That will give you all the information you need to analyze (real) time issues.
@petrichorko@jefftenney Thanks for raising the issue. There seems to be a couple of libraries in esp-iot-solution that don’t initialize xHigherPriorityTaskWoken properly. I’ve already created a fix internally and they should be part of each the next release of the various components.