So far I have always used the Arduino loop to do the audio processing with the hint, that there must not be any longer delay added to the processing.

If you have some blocking functions in the loop, the solution is to run the audio in a separate task. I have added some nice concurrency C++ classes to my framework which help that we can keep the sketch quite short.

Arduino Example Sketch

I am using a simple internet streaming sketch as example which is using some long delays in the loop(). The copy of the mp3 data from the url to the decoder is executed in a separate task:

#include "AudioTools.h"
#include "AudioCodecs/CodecMP3Helix.h"
#include "AudioLibs/AudioBoardStream.h"

URLStream url("ssid","password");  // or replace with ICYStream to get metadata
AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream
EncodedAudioStream dec(&i2s, new MP3DecoderHelix()); // Decoding stream
StreamCopy copier(dec, url); // copy url to decoder
Task task("mp3-copy", 10000, 1, 0);

void setup(){
  Serial.begin(115200);
  AudioLogger::instance().begin(Serial, AudioLogger::Info);  

  // setup i2s
  auto config = i2s.defaultConfig(TX_MODE);
  i2s.begin(config);

  // setup I2S based on sampling rate provided by decoder
  dec.begin();

  // mp3 radio
  url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3");

  // start copy task
  task.begin([](){copier.copy();});
}

void loop(){
  delay(1000);
  Serial.println("ping...");
}

The core of the example is the Task class which requires a name, the stack size, the priority and the core.

Usually we would just call copier.copy() in the loop. But in the example we want to do this in a separate task.

In order to start the task we just call begin() by passing a global method or a lambda expression like in the example above.

Here is the output from the Serial Monitor:

22:19:33.534 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.566 -> ping...
22:19:33.598 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.694 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.727 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.790 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.854 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.918 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:33.982 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.078 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.111 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.175 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.239 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.304 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.368 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.433 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.497 -> [I] StreamCopy.h : 149 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
22:19:34.561 -> [I] StreamCopy.h : 149 - ping...
22:19:34.561 -> StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops

Further information can be found in the Wiki of the AudioTools project.


2 Comments

Carlo47 · 20. November 2024 at 10:02

Hi Phil
I tried your example with some minor changes in platformIO with
board_build.partitions = huge_app.csv set:

URLStream url(“Dodeka2G4″,”myPSK”);
//AudioBoardStream i2s(AudioKitEs8388V1);
I2SConfig config;
I2SStream i2s;
VolumeStream volume(i2s);
EncodedAudioStream dec(&volume, new MP3DecoderHelix());
StreamCopy copier(dec, url);
Task task(“mp3-copy”, 10000, 1, 0);

and in setup():
auto config = i2s.defaultConfig(TX_MODE);
config.pin_ws = 25;
config.pin_bck = 26;
config.pin_data = 27;
i2s.begin(config);

When I run the task, everything is fine, but I keep getting this error message
in the monitor window:
[WiFiClient.ccp: 539] connected() : Unexpected : RES : 0, ERR : 0

But when copier.copy() runs in the main loop(), the error does not appear.
I examined the code with Inspect in platformIO. This revealed many high
level errors:
“Buffer is accessed out of bounds: log_buffer_helix” src\MP3DecoderHelix.h:127:7

I think these are two different, independent problems. The access to the
log_buffer is probably a programming error, but I have no explanation
for the WiFi problem.

    pschatzmann · 20. November 2024 at 11:13

    I don’t see any problem here: These are just information messages!

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *