In my last post I have complained that my sine wave test sketch was producing just noise. So I am trying now to investigate where the issue is coming from.

Using Basic APIs

So first, I try an Arduino Sketch which is just using the low level API of my libraries: This

  • configures the codec using the driver API,
  • sets up I2S using the STM32 I2S library and just
  • uses the sine generator from the AudioKit to fill the data in the callback.
#include "AudioTools.h"
#include "stm32-i2s.h"
#include "AudioBoard.h"

using namespace stm32_i2s;

SineWaveGenerator<int16_t> sineWave(32000);  
int sample_rate = 8000;
int channels = 2;

void getData(uint8_t *buffer, uint16_t byteCount, void*) {
    uint16_t samples = byteCount / sizeof(int16_t);
    int16_t *buffer_16 = (int16_t*) buffer;
    for (uint j = 0; j < samples; j+=2) {
        buffer_16[j] = sineWave.readSample();
        buffer_16[j+1] = buffer_16[j];
    }
}

void setup() {
  Serial.begin(115200);
  // setup sine wave
  sineWave.begin(channels, sample_rate, N_B1);

  // setup codec
  CodecConfig cfg;
  cfg.i2s.rate = RATE_8K;
  STM32F411Disco.begin(cfg);   

  // setup i2s
  I2SSettingsSTM32 i2s_settings;
  i2s_settings.sample_rate = I2S_AUDIOFREQ_8K;
  I2S.beginWriteDMA(i2s_settings, getData);
}

// Arduino loop - copy sound to out 
void loop() {
  delay(1000);
}

This is working much better. So the issue must be in my STM32 driver in the AudioTools and indeed, after adding some more error messages I was getting plenty of buffer under-runs.

Using the Regular API

In my original sketch I was expecting that the buffer is filled by the Arduino loop and consumed by the DMA. But it seems that this works only with very low sample rates.

As I kind of hack, I am calling the loop now in the DMA callback, if the buffer is empty and this seems to resolve this issue!

Using the Driver API

I thought that it might be also useful to have the functionality from my first sketch available via the driver API:

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

AudioInfo info(44100, 2, 16);
SineWaveGenerator<int16_t> sineWave(32000);                
GeneratedSoundStream<int16_t> sound(sineWave);  
AudioBoardStream out(STM32F411Disco);

// Arduino Setup
void setup(void) {  
  // Open Serial and set up logging
  Serial.begin(115200);
  while(!Serial);
  AudioLogger::instance().begin(Serial, AudioLogger::Info);

  // define the audio source stream
  out.driver()->setDMAInputStream(sound);

  // Setup sine wave
  sineWave.begin(info, N_B1);
  Serial.println("started...");

  // start I2S
  Serial.println("starting I2S...");
  auto config = out.defaultConfig(TX_MODE);
  config.buffer_size = 1024;
  config.buffer_count = 2;
  config.copyFrom(info); 
  if (!out.begin(config)){
    Serial.println("error!");
    stop();
  }

  // defines the output volume
  out.setVolume(0.3);

}

// Arduino loop - do nothing 
void loop() {}

By calling setDMAInputStream() we can make the input stream available to the DMA logic in the driver, so that the DMA can directly read from it.

Dependencies


2 Comments

Plouc68000 · 18. February 2024 at 15:55

has this a effect on software stability?

I am using your Audio-Tools with I2s output for a webradio code, because don’t need external mp3 decoder board VS1053 anymore, thanks 😉

but the software is a bit crashy….forcing me to use WDT TimeOut

    pschatzmann · 18. February 2024 at 20:03

    I just completed the development, so I don’t have any experience with this yet…

Leave a Reply

Avatar placeholder

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