Audio Glitching while Playing WAV from SD card using the Audio Shield on a TEENSY 4.0

Juliangresh

New member
Hi,

I am running into some issues while using the Audio Shield on the Teensy 4.0.

I am trying to make a simple Audio Player to be controlled over UART by an ESP32.

Here is my code:

Screenshot 2024-08-22 at 20.16.35.png
Screenshot 2024-08-22 at 20.16.53.png

Screenshot 2024-08-22 at 20.17.15.png

Screenshot 2024-08-22 at 20.17.37.png

Screenshot 2024-08-22 at 20.17.55.png


All seems to work fine but every couple of minutes, when playing a file, it sounds like some data gets dropped and there is a loud audio glitch for about a second (paired with the device LED getting much brighter) before returning to playing the audio.

Initially thought that this might relate to the UART communication either from the ESP32, or from the laptop I am using to monitor it, though this keeps happening even after both are disconnected and the audio file just plays uninterrupted.

Note: The Audio file is 8 min long.

I am new to working with both the Teensy board and C++, since I usually work with the ESP32 an C, But I did follow the examples on the Github from which I got most of my code.

I did find a similar thread (describing what I think is the exact same issue) posted over 2 years ago but I didn't really understand if it was resolved.

Does anything seem obviously wrong? What should I try?

Thank you!!
 
You won't get anyone helping you with that code unless you post it as code. An image can't be compiled/loaded onto a Teensy! Use the code tags, the </> button at the start of the posting icon menu.
 
Oh, Thank you for the heads up.

Here it is:

C++:
// Libraries

#include <Arduino.h>
#include <Audio.h>
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <SerialFlash.h>


// Constants
  #define SDCARD_CS_PIN    10
  #define SDCARD_MOSI_PIN  7   // Teensy 4 ignores this, uses pin 11
  #define SDCARD_SCK_PIN   14  // Teensy 4 ignores this, uses pin 13


// Global Variables
  float currentVolume = 0.5; // Default volume (50%)


// Teensy Audio Library setup
AudioPlaySdWav           playWav1;
AudioOutputI2S           audioOutput;
AudioConnection          patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection          patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000     sgtl5000_1;


// Function Prototypes
void listSDContents(const char *dirname, uint8_t levels = 0);


// Setup Code
void setup() {
  // Initialize Serial for USB communication (debugging)
  Serial.begin(9600);
 
  // Initialize serial communication for UART (Teensy listens on Serial1)
  // Serial1.begin(115200, SERIAL_8E2);  // Set the same baud rate as the ESP32
  Serial1.begin(115200);                  // Simpler Serial com

  for (int i = 0; i < 30; i++) Serial.println(); // Print 30 new lines

  // Initialize audio system
  Serial.println("Initialising audio System");

  AudioMemory(8);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);  // Set initial volume to 50%

  Serial.println("Audio system initialised");

  // Initialize SD card
  Serial.println("Initialising SD card");

  // Setup SPI for the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);

  if (!SD.begin(SDCARD_CS_PIN)) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }                               // !!! should retry intead
  }

  Serial.println("SD card initialised");

  // List all files on the SD card
  listSDContents("/");

  Serial.println("Teensy Audio System Ready");       
  // Serial1.println("Teensy Audio System Ready");          // !!! Might be good to communicate this to the ESP32
}


// Main Loop
void loop() {
  // Check if data is available on Serial1 (UART)
  if (Serial1.available() > 0) {
    Serial.println("Serial Data Received");

    // Read the incoming command
    String command = Serial1.readStringUntil('\n');
    command.trim();                                         // Remove any trailing whitespace

    Serial.print("Command: ");
    Serial.println(command);

    // Parse the command
    parseCommand(command);
    // Serial.print("Sending acknowlegment");
    // Serial1.println("ACK");
  }
}


// Functions

// UART Functions

// Function to process the incoming UART command
void parseCommand(String command) {
  Serial.println("Parsing received command");

  // Parse volume commands (e.g., "VOLUME_50")
  if (command.startsWith("VOLUME_")) {
    Serial.println("Volume command");

    int volume = command.substring(7).toInt();  // Extract the volume level (0-100)
    setVolume(volume);
    Serial.println("Volume has been set");
  }

  // Parse play commands (e.g., "PLAY_filename.wav")
  else if (command.startsWith("PLAY_")) {
    Serial.println("Play audio file command");

    String filename = command.substring(5);  // Extract the filename
    playAudioFile(filename.c_str());
  }

  // Parse pause command
  else if (command == "TOGGLE_PAUSE") {
    Serial.println("Pause audio command");

    playWav1.togglePlayPause();  // Stop the current playback
  }

  // Parse stop command
  else if (command == "STOP") {
    Serial.println("Stop audio command");
    
    playWav1.stop();  // Stop the current playback
  }
}


// Audio Functions

// Function to set the volume
void setVolume(int volume) {
  // Ensure volume is within the valid range of 0 to 100
  if (volume < 0) volume = 0;
  if (volume > 100) volume = 100;

  // Volume is expected as a percentage (0-100), convert it to 0.0 - 1.0 range
  currentVolume = volume / 100.0;
  sgtl5000_1.volume(currentVolume);

  Serial.print("Volume set to ");
  Serial.print(volume);
}

// Function to play an audio file
void playAudioFile(const char *filename) {
  // Stop any current playback
  if (playWav1.isPlaying()) {
      playWav1.stop();
  }

  // Play the new file
  playWav1.play(filename);
  delay(10);  // Allow time for the file to start playing

  if (!playWav1.isPlaying()) {
    Serial.println("Failed to start playback.");
  }

  else if (playWav1.isPlaying()){
    Serial.print("Playing file: ");
    Serial.println(filename);
  }
}


// Misc Functions

// List contents of SD card
void listSDContents(const char *dirname, uint8_t levels) {
  File root = SD.open(dirname);

  if (!root) {
    Serial.println("Failed to open directory.");
    return;
  }
 
  if (!root.isDirectory()) {
    Serial.println("Not a directory.");
    return;
  }

  File file = root.openNextFile();
 
  while (file) {
    if (file.isDirectory()) {
      Serial.print("DIR : ");
      Serial.println(file.name());

      // Recursively list contents of the directory if needed
      if (levels) {
        listSDContents(file.name(), levels - 1);
      }
    }
    
    else {
      Serial.print("FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }

    file = root.openNextFile();
  }
}
 
It could be that your SDcard has long housekeeping events - try using a lot more AudioMemory, say 200 blocks, so such drop outs are buffered. Such housekeeping delays depend a lot on the card you are using - which card are you using? Have you tried others?
 
It'd be great if the advice @MarkT gives works ... but I fear it won't. AudioPlaySdWav reads the absolute bare minimum (512 bytes) of audio data, and if it's a stereo file then it does that on every audio update(), i.e. every 2.9ms. It then puts the audio in AudioMemory and sends it out. No queueing or buffering. Any housekeeping events taking close to or more than 2.9ms will cause failure.

In addition, because the SD card is in use playing back audio, doing reads within interrupt code, you MUST NOT use it while playing. So using listSDContents() to pick your next file while playing would be a big no-no. You don't appear to be doing that ... yet.

You could try my new and improved play/record objects described and supported on this thread - you should find links etc. on there.
 
It could be that your SDcard has long housekeeping events - try using a lot more AudioMemory, say 200 blocks, so such drop outs are buffered. Such housekeeping delays depend a lot on the card you are using - which card are you using? Have you tried others?
Thank you!
I will definitely try.
I am using the following card:

IMG_6712.jpeg
 
It'd be great if the advice @MarkT gives works ... but I fear it won't. AudioPlaySdWav reads the absolute bare minimum (512 bytes) of audio data, and if it's a stereo file then it does that on every audio update(), i.e. every 2.9ms. It then puts the audio in AudioMemory and sends it out. No queueing or buffering. Any housekeeping events taking close to or more than 2.9ms will cause failure.

In addition, because the SD card is in use playing back audio, doing reads within interrupt code, you MUST NOT use it while playing. So using listSDContents() to pick your next file while playing would be a big no-no. You don't appear to be doing that ... yet.

You could try my new and improved play/record objects described and supported on this thread - you should find links etc. on there.
Thank you for the reply!
Noted.
I know that other audio reading functions (like the ones provided by feather for their audio boards) are blocking, I would assume, for this exact reason.
I will only use the SD car list function as a debugging function to make sure that the card is correctly mounted and that all the files are as I expect them to be.
I will look into the thread you link and see if these two suggestions remove the glitches.
 
Back
Top