In one of my last Posts I have described an approach how we can implement a simple audio output using PWM using the Raspberry Pico.

The advantage of this approach is, that we can output audio to almost any GPIO pin, and that it can be used on any other processor which supports PWM.

I have extended my arduino-audio-tools project to provide this functionality with a Stream based and a callback based API.

The following processors are currently supported:

  • Raspberry Pico
  • ESP32
  • MBED based Arduino Boards (e.g. Nano 33 BLE)
  • Arduino UNO R4

It should not be too difficult to extend the functionality to support other processors as well: All that is needed is to implement a timer which is executed in the speed of the audio sampling rate and some PWM logic which sets the PWM frequency above the audible range. Here is the ESP32 specific implementation logic as an example. I also tried to add an experimental implementation for the Arduino Nano, but unfortunately there is just not enough memory and speed available.

Finally I close with an example Arduino Sketch which plays an wav file provided as a simple array using the stream API:

#include "AudioTools.h"
#include "CodecWAV.h"
#include "knghtsng.h"

using namespace audio_tools;  

// MemoryStream -> AudioOutputStream -> WAVDecoder -> PWMAudioStream
MemoryStream wav(knghtsng_wav, knghtsng_wav_len);
PWMAudioStream pwm;          // PWM output 
WAVDecoder decoder(pwm);        // decode wav to pcm and send it to printer
AudioOutputStream out(decoder); // output to decoder
StreamCopy copier(out, wav);    // copy in to out

void setup(){
  Serial.begin(115200);

  // setup pwm output
  PWMConfig config = pwm.defaultConfig();
  config.channels = 1;
  config.sample_rate = 11025;
  pwm.begin(config);
}

void loop(){
  if (wav) {
    copier.copy();
  } else {
    // after we are done we just print some info form the wav file
    auto info = decoder.audioInfo();
    LOGI("The audio rate from the wav file is %d", info.sample_rate);
    LOGI("The channels from the wav file is %d", info.channels);
    stop();
  }
}

The full source can be found on Github!


0 Comments

Leave a Reply

Avatar placeholder

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