Intro
Previously I investigated how to use the GIGA Display's microphone, but now it is time to explore the audio out put, This blog demonstrates how the Arduino GIGA R1 can read a WAV audio file from a USB memory device and play it using its DAC output via its audio connector.
Audio files can be large so it is useful to be able to store them on external USB memory. It is also useful to have a display available to show the status of audio activity.
WAV Player Video
WAV Player Sketch
/*
* GIGA WAV Player
* GIGA R1 - Audio Playback of a wav file store on a USB memory device
* Simple wav format audio playback via 12-Bit DAC output by reading from a USB drive.
* This sketch assumes the USB memory device is named "USB"
* This sketch assumes the audio file is named "GIGAchorus.wav"
* by Doug Wong 2025
*/
#include <Arduino_AdvancedAnalog.h>
#include <DigitalOut.h>
#include <Arduino_USBHostMbed5.h>
#include <FATFileSystem.h>
#include "Arduino_GigaDisplay_GFX.h"
#define WHITE 0xffff
#define BLACK 0x0000
#define YELLOW 0xFFE0
#define CYAN 0x07FFF
#define PURPLE 0x8010
AdvancedDAC dac0(A12);
USBHostMSD msd;
mbed::FATFileSystem usb("USB");
GigaDisplay_GFX display;
FILE * file = nullptr;
int sample_size = 0;
int samples_count = 0;
void setup()
{
Serial.begin(115200);
while (!Serial);
display.begin();
display.setRotation(1); // Landscape mode
display.fillScreen(PURPLE);
display.setTextColor(YELLOW);
display.setTextSize(8);
display.setCursor(35, 200);
display.println("GIGA WAV Player");
delay(4000);
display.fillScreen(PURPLE);
display.setTextSize(4);
display.setCursor(10, 10);
display.println("Reading USB...");
/* Enable power for HOST USB connector. */
pinMode(PA_15, OUTPUT);
digitalWrite(PA_15, HIGH);
if (!msd.connect()) {
display.println("Insert USB memory device");
while (!msd.connect()) delay(100);
}
display.println("Mounting USB...");
int const rc_mount = usb.mount(&msd);
if (rc_mount)
{
display.println("Error mounting USB device ");
display.println(rc_mount);
return;
}
display.println("Opening audio file ...");
/* 16-bit PCM Mono 16kHz realigned noise reduction */
file = fopen("/USB/GigaChorus.wav", "rb");
if (file == nullptr)
{
display.print("Error opening audio file: ");
display.println(strerror(errno));
return;
}
display.println("Reading audio header ...");
delay(200);
display.fillScreen(PURPLE);
display.setTextSize(3);
display.setCursor(10, 1);
struct wav_header_t
{
char chunkID[4]; //"RIFF" = 0x46464952
unsigned long chunkSize; //28 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes] + sum(sizeof(chunk.id) + sizeof(chunk.size) + chunk.size)
char format[4]; //"WAVE" = 0x45564157
char subchunk1ID[4]; //"fmt " = 0x20746D66
unsigned long subchunk1Size; //16 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes]
unsigned short audioFormat;
unsigned short numChannels;
unsigned long sampleRate;
unsigned long byteRate;
unsigned short blockAlign;
unsigned short bitsPerSample;
};
wav_header_t header;
fread(&header, sizeof(header), 1, file);
display.println("WAV File Header read:");
char msg[64] = {0};
snprintf(msg, sizeof(msg), "File Type: %s", header.chunkID);
display.println(msg);
snprintf(msg, sizeof(msg), "File Size: %ld", header.chunkSize);
display.println(msg);
snprintf(msg, sizeof(msg), "WAV Marker: %s", header.format);
display.println(msg);
snprintf(msg, sizeof(msg), "Format Name: %s", header.subchunk1ID);
display.println(msg);
snprintf(msg, sizeof(msg), "Format Length: %ld", header.subchunk1Size);
display.println(msg);
snprintf(msg, sizeof(msg), "Format Type: %hd", header.audioFormat);
display.println(msg);
snprintf(msg, sizeof(msg), "Number of Channels: %hd", header.numChannels);
display.println(msg);
snprintf(msg, sizeof(msg), "Sample Rate: %ld", header.sampleRate);
display.println(msg);
snprintf(msg, sizeof(msg), "Sample Rate * Bits/Sample * Channels / 8: %ld", header.byteRate);
display.println(msg);
snprintf(msg, sizeof(msg), "Bits per Sample * Channels / 8: %hd", header.blockAlign);
display.println(msg);
snprintf(msg, sizeof(msg), "Bits per Sample: %hd", header.bitsPerSample);
display.println(msg);
/* Find the data section of the WAV file. */
struct chunk_t
{
char ID[4];
unsigned long size;
};
chunk_t chunk;
snprintf(msg, sizeof(msg), "id\t" "size");
display.println(msg);
/* Find data chunk. */
while (true)
{
fread(&chunk, sizeof(chunk), 1, file);
snprintf(msg, sizeof(msg), "%c%c%c%c\t" "%li", chunk.ID[0], chunk.ID[1], chunk.ID[2], chunk.ID[3], chunk.size);
display.println(msg);
if (*(unsigned int *) &chunk.ID == 0x61746164)
break;
/* Skip chunk data bytes. */
fseek(file, chunk.size, SEEK_CUR);
}
/* Determine number of samples. */
sample_size = header.bitsPerSample / 8;
samples_count = chunk.size * 8 / header.bitsPerSample;
snprintf(msg, sizeof(msg), "Sample size = %i", sample_size); display.println(msg);
snprintf(msg, sizeof(msg), "Samples count = %i", samples_count); display.println(msg);
/* Configure the advanced DAC. */
if (!dac0.begin(AN_RESOLUTION_12, header.sampleRate * 2, 256, 16))
{
display.println("Failed to start DAC1 !");
return;
}
delay(1500);
display.fillScreen(PURPLE);
display.setCursor(50, 50);
display.setTextSize(5);
display.println("Playing ......");
display.setCursor(50, 200);
display.setTextSize(7);
display.println("GIGAchorus.WAV");
}
void loop()
{
if (dac0.available() && !feof(file))
{
/* Read data from file. */
uint16_t sample_data[256] = {0};
fread(sample_data, sample_size, 256, file);
/* Get a free buffer for writing. */
SampleBuffer buf = dac0.dequeue();
/* Write data to buffer. */
for (size_t i = 0; i < buf.size(); i++)
{
/* Scale down to 12 bit. */
uint16_t const dac_val = ((static_cast<unsigned int>(sample_data[i])+32768)>>4) & 0x0fff;
buf[i] = dac_val;
}
/* Write the buffer to DAC. */
dac0.write(buf);
}
}
Discussion
It took me a while to get my little audio amplifier together and the cabling took even longer. There is potential to introduce audio noise if the power supplies are noisy or the cables aren't very good. I had a bit of an issue finding an audio cable connector that would fit beside my USB memory stick, which is old and big. When I finally got everything working I was surprised at how good it sounded.
The purple background on the display worked great to allow the display to be captured well on camera. The system was set up near my computer - not my workbench/studio area, so lighting was not ideal, but this blog is about audio, so I can live with non-ideal lighting.
It is taking a while to get through testing the myriad features of the GIGA kit, partly because I am trying to expand on the existing demo software and show how features combine to provide system level performance. This demo used the Audio output DAC and jack in combination with external USB memory capability and the GIGA Display to create an audio file player.
Links:
Touch Screen and USB memory demo
GIGA display of an Arducam video camera