Skip to content

Commit 171a789

Browse files
authored
Merge pull request #56 from arduino-libraries/wav_reader
Add WAV file reader.
2 parents 7c8d81c + 21c3525 commit 171a789

File tree

4 files changed

+227
-118
lines changed

4 files changed

+227
-118
lines changed

examples/Beginner/Audio_Playback/Audio_Playback.ino

+43-118
Original file line numberDiff line numberDiff line change
@@ -6,154 +6,79 @@
66
*/
77

88
#include <Arduino_AdvancedAnalog.h>
9-
109
#include <Arduino_USBHostMbed5.h>
11-
1210
#include <DigitalOut.h>
1311
#include <FATFileSystem.h>
1412

15-
AdvancedDAC dac1(A12);
16-
1713
USBHostMSD msd;
1814
mbed::FATFileSystem usb("USB_DRIVE");
1915

20-
FILE * file = nullptr;
21-
int sample_size = 0;
22-
int samples_count = 0;
23-
16+
WavReader wavreader;
17+
AdvancedDAC dac1(A12);
18+
#define N_SAMPLES (512)
2419

25-
void setup()
26-
{
20+
void setup() {
2721
Serial.begin(115200);
28-
while (!Serial);
22+
while (!Serial) {
23+
}
2924

30-
/* Enable power for HOST USB connector. */
25+
// Enable power for HOST USB connector.
3126
pinMode(PA_15, OUTPUT);
3227
digitalWrite(PA_15, HIGH);
3328

34-
Serial.println("Please connect a USB stick to the GIGA's USB port ...");
35-
while (!msd.connect()) delay(100);
29+
Serial.println("Please connect a USB stick to the USB host port ...");
30+
while (!msd.connect()) {
31+
delay(100);
32+
}
3633

3734
Serial.println("Mounting USB device ...");
3835
int const rc_mount = usb.mount(&msd);
39-
if (rc_mount)
40-
{
36+
if (rc_mount) {
4137
Serial.print("Error mounting USB device ");
4238
Serial.println(rc_mount);
43-
return;
39+
while (1);
4440
}
4541

4642
Serial.println("Opening audio file ...");
47-
48-
/* 16-bit PCM Mono 16kHz realigned noise reduction */
49-
file = fopen("/USB_DRIVE/AUDIO_SAMPLE.wav", "rb");
50-
if (file == nullptr)
51-
{
52-
Serial.print("Error opening audio file: ");
53-
Serial.println(strerror(errno));
54-
return;
43+
if (!wavreader.begin("/USB_DRIVE/AUDIO_SAMPLE.wav", N_SAMPLES, 1, false)) {
44+
Serial.print("Error opening audio file: ");
45+
while (1);
5546
}
5647

57-
Serial.println("Reading audio header ...");
58-
struct wav_header_t
59-
{
60-
char chunkID[4]; //"RIFF" = 0x46464952
61-
unsigned long chunkSize; //28 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes] + sum(sizeof(chunk.id) + sizeof(chunk.size) + chunk.size)
62-
char format[4]; //"WAVE" = 0x45564157
63-
char subchunk1ID[4]; //"fmt " = 0x20746D66
64-
unsigned long subchunk1Size; //16 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes]
65-
unsigned short audioFormat;
66-
unsigned short numChannels;
67-
unsigned long sampleRate;
68-
unsigned long byteRate;
69-
unsigned short blockAlign;
70-
unsigned short bitsPerSample;
71-
};
72-
73-
wav_header_t header;
74-
fread(&header, sizeof(header), 1, file);
75-
76-
Serial.println("WAV File Header read:");
7748
char msg[64] = {0};
78-
snprintf(msg, sizeof(msg), "File Type: %s", header.chunkID);
79-
Serial.println(msg);
80-
snprintf(msg, sizeof(msg), "File Size: %ld", header.chunkSize);
81-
Serial.println(msg);
82-
snprintf(msg, sizeof(msg), "WAV Marker: %s", header.format);
83-
Serial.println(msg);
84-
snprintf(msg, sizeof(msg), "Format Name: %s", header.subchunk1ID);
85-
Serial.println(msg);
86-
snprintf(msg, sizeof(msg), "Format Length: %ld", header.subchunk1Size);
87-
Serial.println(msg);
88-
snprintf(msg, sizeof(msg), "Format Type: %hd", header.audioFormat);
89-
Serial.println(msg);
90-
snprintf(msg, sizeof(msg), "Number of Channels: %hd", header.numChannels);
91-
Serial.println(msg);
92-
snprintf(msg, sizeof(msg), "Sample Rate: %ld", header.sampleRate);
93-
Serial.println(msg);
94-
snprintf(msg, sizeof(msg), "Sample Rate * Bits/Sample * Channels / 8: %ld", header.byteRate);
95-
Serial.println(msg);
96-
snprintf(msg, sizeof(msg), "Bits per Sample * Channels / 8: %hd", header.blockAlign);
97-
Serial.println(msg);
98-
snprintf(msg, sizeof(msg), "Bits per Sample: %hd", header.bitsPerSample);
99-
Serial.println(msg);
100-
101-
/* Find the data section of the WAV file. */
102-
struct chunk_t
103-
{
104-
char ID[4];
105-
unsigned long size;
106-
};
107-
108-
chunk_t chunk;
109-
snprintf(msg, sizeof(msg), "id\t" "size");
110-
Serial.println(msg);
111-
/* Find data chunk. */
112-
while (true)
113-
{
114-
fread(&chunk, sizeof(chunk), 1, file);
115-
snprintf(msg, sizeof(msg), "%c%c%c%c\t" "%li", chunk.ID[0], chunk.ID[1], chunk.ID[2], chunk.ID[3], chunk.size);
116-
Serial.println(msg);
117-
if (*(unsigned int *) &chunk.ID == 0x61746164)
118-
break;
119-
/* Skip chunk data bytes. */
120-
fseek(file, chunk.size, SEEK_CUR);
121-
}
122-
123-
/* Determine number of samples. */
124-
sample_size = header.bitsPerSample / 8;
125-
samples_count = chunk.size * 8 / header.bitsPerSample;
126-
snprintf(msg, sizeof(msg), "Sample size = %i", sample_size); Serial.println(msg);
127-
snprintf(msg, sizeof(msg), "Samples count = %i", samples_count); Serial.println(msg);
49+
snprintf(msg, sizeof(msg), "Number of Channels: %hd", wavreader.channels()); Serial.println(msg);
50+
snprintf(msg, sizeof(msg), "Sample Rate: %ld", wavreader.sample_rate()); Serial.println(msg);
51+
snprintf(msg, sizeof(msg), "Bits per Sample: %hd", wavreader.resolution()); Serial.println(msg);
52+
snprintf(msg, sizeof(msg), "Number of Samples = %i", wavreader.sample_count()); Serial.println(msg);
12853

129-
/* Configure the advanced DAC. */
130-
if (!dac1.begin(AN_RESOLUTION_12, header.sampleRate, 256, 16))
131-
{
54+
// Configure and start the DAC.
55+
if (!dac1.begin(AN_RESOLUTION_12, wavreader.sample_rate(), N_SAMPLES, 32)) {
13256
Serial.println("Failed to start DAC1 !");
133-
return;
57+
while (1);
13458
}
13559
}
13660

137-
void loop()
138-
{
139-
if (dac1.available() && !feof(file))
140-
{
141-
/* Read data from file. */
142-
uint16_t sample_data[256] = {0};
143-
fread(sample_data, sample_size, 256, file);
144-
145-
/* Get a free buffer for writing. */
146-
SampleBuffer buf = dac1.dequeue();
147-
148-
/* Write data to buffer. */
149-
for (size_t i = 0; i < buf.size(); i++)
150-
{
151-
/* Scale down to 12 bit. */
152-
uint16_t const dac_val = ((static_cast<unsigned int>(sample_data[i])+32768)>>4) & 0x0fff;
153-
buf[i] = dac_val;
61+
void loop() {
62+
if (dac1.available() && wavreader.available()) {
63+
// Get a free buffer for writing.
64+
SampleBuffer dacbuf = dac1.dequeue();
65+
66+
// Read a samples buffer from the wav file.
67+
SampleBuffer pcmbuf = wavreader.read();
68+
69+
// Process and write samples to the DAC buffer.
70+
for (size_t i=0; i<N_SAMPLES; i++) {
71+
// Map the samples to positive range and down scale to 12-bit.
72+
if (wavreader.channels() == 1) {
73+
dacbuf[i] = ((unsigned int) ((int16_t) pcmbuf[i] + 32768)) >> 4;
74+
} else {
75+
// If the file has two channels set the average.
76+
dacbuf[i] = ((unsigned int) ((((int16_t) pcmbuf[(i * 2)] + (int16_t) pcmbuf[(i * 2) + 1]) / 2) + 32768)) >> 4;
77+
}
15478
}
15579

156-
/* Write the buffer to DAC. */
157-
dac1.write(buf);
80+
// Write the buffer to DAC.
81+
dac1.write(dacbuf);
82+
pcmbuf.release();
15883
}
15984
}

src/Arduino_AdvancedAnalog.h

+1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@
2626

2727
#include "AdvancedADC.h"
2828
#include "AdvancedDAC.h"
29+
#include "WavReader.h"
2930

3031
#endif /* ADVANCEDANALOGREDUX_ARDUINO_ADVANCEDANALOG_H */

src/WavReader.cpp

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
This file is part of the Arduino_AdvancedAnalog library.
3+
Copyright (c) 2024 Arduino SA. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
#include "Arduino.h"
21+
#include "WavReader.h"
22+
23+
WavReader::~WavReader() {
24+
stop();
25+
}
26+
27+
int WavReader::begin(const char *path, size_t n_samples, size_t n_buffers, bool loop) {
28+
this->loop = loop;
29+
30+
if ((file = fopen(path, "rb")) == nullptr) {
31+
return 0;
32+
}
33+
34+
// Read file header
35+
fread(&header, sizeof(header), 1, file);
36+
37+
// Add more sanity checks if needed.
38+
if (memcmp(header.chunk_id, "RIFF", 4) != 0 ||
39+
memcmp(header.format, "WAVEfmt", 7) != 0 ||
40+
((sizeof(Sample) * 8) < header.bits_per_sample) ||
41+
(n_samples * header.num_channels) > sample_count()) {
42+
stop();
43+
return 0;
44+
}
45+
46+
// Allocate the DMA buffer pool.
47+
pool = new DMABufferPool<Sample>(n_samples, header.num_channels, n_buffers);
48+
if (pool == nullptr) {
49+
stop();
50+
return 0;
51+
}
52+
return 1;
53+
}
54+
55+
void WavReader::stop() {
56+
if (file) {
57+
fclose(file);
58+
}
59+
if (pool) {
60+
delete pool;
61+
}
62+
pool = nullptr;
63+
file = nullptr;
64+
}
65+
66+
bool WavReader::available() {
67+
if (file != nullptr && pool != nullptr) {
68+
return pool->writable();
69+
}
70+
return false;
71+
}
72+
73+
DMABuffer<Sample> &WavReader::read() {
74+
while (!available()) {
75+
__WFI();
76+
}
77+
78+
DMABuffer<Sample> *buf = pool->allocate();
79+
size_t offset = 0;
80+
Sample *rawbuf = buf->data();
81+
size_t n_samples = buf->size();
82+
83+
while (offset < n_samples) {
84+
offset += fread(&rawbuf[offset], sizeof(Sample), n_samples - offset, file);
85+
if (offset < n_samples) {
86+
if (loop) {
87+
rewind();
88+
} else {
89+
for (size_t i=offset; i<n_samples; i++) {
90+
rawbuf[i] = 0;
91+
}
92+
fclose(file);
93+
file = nullptr;
94+
break;
95+
}
96+
}
97+
}
98+
return *buf;
99+
}
100+
101+
int WavReader::rewind() {
102+
if (file == nullptr) {
103+
return 0;
104+
}
105+
if (fseek(file, sizeof(WavHeader), SEEK_SET) == -1) {
106+
return 0;
107+
}
108+
return 1;
109+
}

src/WavReader.h

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
This file is part of the Arduino_AdvancedAnalog library.
3+
Copyright (c) 2024 Arduino SA. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
#include "AdvancedAnalog.h"
21+
22+
#ifndef ARDUINO_WAV_READER_H_
23+
#define ARDUINO_WAV_READER_H_
24+
25+
class WavReader {
26+
typedef struct {
27+
char chunk_id[4];
28+
unsigned int chunk_size;
29+
char format[4];
30+
char subchunk1_id[4];
31+
unsigned int subchunk1_size;
32+
unsigned short audio_format;
33+
unsigned short num_channels;
34+
unsigned int sample_rate;
35+
unsigned int byte_rate;
36+
unsigned short block_align;
37+
unsigned short bits_per_sample;
38+
char subchunk2_id[4];
39+
unsigned int subchunk2_size;
40+
} WavHeader;
41+
42+
private:
43+
FILE *file;
44+
bool loop;
45+
WavHeader header;
46+
DMABufferPool<Sample> *pool;
47+
48+
public:
49+
WavReader(): file(nullptr), loop(false), pool(nullptr) {
50+
}
51+
~WavReader();
52+
size_t channels() {
53+
return header.num_channels;
54+
}
55+
56+
size_t resolution() {
57+
return header.bits_per_sample;
58+
}
59+
60+
size_t sample_rate() {
61+
return header.sample_rate;
62+
}
63+
64+
size_t sample_count() {
65+
return (header.subchunk2_size * 8) / header.bits_per_sample;
66+
}
67+
68+
int begin(const char *path, size_t n_samples, size_t n_buffers, bool loop=false);
69+
void stop();
70+
bool available();
71+
SampleBuffer read();
72+
int rewind();
73+
};
74+
#endif /* ARDUINO_WAV_READER_H_ */

0 commit comments

Comments
 (0)