From a2b660ea7805c770a21d1c7bd3babd82ee387b6f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 22 Feb 2024 16:01:27 +0100 Subject: [PATCH 1/2] Add basic WAV file reader for use in examples. Signed-off-by: iabdalkader --- src/Arduino_AdvancedAnalog.h | 1 + src/WavReader.cpp | 109 +++++++++++++++++++++++++++++++++++ src/WavReader.h | 74 ++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 src/WavReader.cpp create mode 100644 src/WavReader.h diff --git a/src/Arduino_AdvancedAnalog.h b/src/Arduino_AdvancedAnalog.h index f654650..e4bc8c4 100644 --- a/src/Arduino_AdvancedAnalog.h +++ b/src/Arduino_AdvancedAnalog.h @@ -26,5 +26,6 @@ #include "AdvancedADC.h" #include "AdvancedDAC.h" +#include "WavReader.h" #endif /* ADVANCEDANALOGREDUX_ARDUINO_ADVANCEDANALOG_H */ diff --git a/src/WavReader.cpp b/src/WavReader.cpp new file mode 100644 index 0000000..f5753ec --- /dev/null +++ b/src/WavReader.cpp @@ -0,0 +1,109 @@ +/* + This file is part of the Arduino_AdvancedAnalog library. + Copyright (c) 2024 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Arduino.h" +#include "WavReader.h" + +WavReader::~WavReader() { + stop(); +} + +int WavReader::begin(const char *path, size_t n_samples, size_t n_buffers, bool loop) { + this->loop = loop; + + if ((file = fopen(path, "rb")) == nullptr) { + return 0; + } + + // Read file header + fread(&header, sizeof(header), 1, file); + + // Add more sanity checks if needed. + if (memcmp(header.chunk_id, "RIFF", 4) != 0 || + memcmp(header.format, "WAVEfmt", 7) != 0 || + ((sizeof(Sample) * 8) < header.bits_per_sample) || + (n_samples * header.num_channels) > sample_count()) { + stop(); + return 0; + } + + // Allocate the DMA buffer pool. + pool = new DMABufferPool(n_samples, header.num_channels, n_buffers); + if (pool == nullptr) { + stop(); + return 0; + } + return 1; +} + +void WavReader::stop() { + if (file) { + fclose(file); + } + if (pool) { + delete pool; + } + pool = nullptr; + file = nullptr; +} + +bool WavReader::available() { + if (file != nullptr && pool != nullptr) { + return pool->writable(); + } + return false; +} + +DMABuffer &WavReader::read() { + while (!available()) { + __WFI(); + } + + DMABuffer *buf = pool->allocate(); + size_t offset = 0; + Sample *rawbuf = buf->data(); + size_t n_samples = buf->size(); + + while (offset < n_samples) { + offset += fread(&rawbuf[offset], sizeof(Sample), n_samples - offset, file); + if (offset < n_samples) { + if (loop) { + rewind(); + } else { + for (size_t i=offset; i *pool; + + public: + WavReader(): file(nullptr), loop(false), pool(nullptr) { + } + ~WavReader(); + size_t channels() { + return header.num_channels; + } + + size_t resolution() { + return header.bits_per_sample; + } + + size_t sample_rate() { + return header.sample_rate; + } + + size_t sample_count() { + return (header.subchunk2_size * 8) / header.bits_per_sample; + } + + int begin(const char *path, size_t n_samples, size_t n_buffers, bool loop=false); + void stop(); + bool available(); + SampleBuffer read(); + int rewind(); +}; +#endif /* ARDUINO_WAV_READER_H_ */ From 21c3525092d9e54dc5cb138306a5fde7c7bc300c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 22 Feb 2024 16:41:47 +0100 Subject: [PATCH 2/2] examples: Update Audio_Playback example. - Switch to WavReader. - Fix the handling of multi-channels. - Simplify the example code. Signed-off-by: iabdalkader --- .../Audio_Playback/Audio_Playback.ino | 161 +++++------------- 1 file changed, 43 insertions(+), 118 deletions(-) diff --git a/examples/Beginner/Audio_Playback/Audio_Playback.ino b/examples/Beginner/Audio_Playback/Audio_Playback.ino index 72fcc75..856a8e5 100644 --- a/examples/Beginner/Audio_Playback/Audio_Playback.ino +++ b/examples/Beginner/Audio_Playback/Audio_Playback.ino @@ -6,154 +6,79 @@ */ #include - #include - #include #include -AdvancedDAC dac1(A12); - USBHostMSD msd; mbed::FATFileSystem usb("USB_DRIVE"); -FILE * file = nullptr; -int sample_size = 0; -int samples_count = 0; - +WavReader wavreader; +AdvancedDAC dac1(A12); +#define N_SAMPLES (512) -void setup() -{ +void setup() { Serial.begin(115200); - while (!Serial); + while (!Serial) { + } - /* Enable power for HOST USB connector. */ + // Enable power for HOST USB connector. pinMode(PA_15, OUTPUT); digitalWrite(PA_15, HIGH); - Serial.println("Please connect a USB stick to the GIGA's USB port ..."); - while (!msd.connect()) delay(100); + Serial.println("Please connect a USB stick to the USB host port ..."); + while (!msd.connect()) { + delay(100); + } Serial.println("Mounting USB device ..."); int const rc_mount = usb.mount(&msd); - if (rc_mount) - { + if (rc_mount) { Serial.print("Error mounting USB device "); Serial.println(rc_mount); - return; + while (1); } Serial.println("Opening audio file ..."); - - /* 16-bit PCM Mono 16kHz realigned noise reduction */ - file = fopen("/USB_DRIVE/AUDIO_SAMPLE.wav", "rb"); - if (file == nullptr) - { - Serial.print("Error opening audio file: "); - Serial.println(strerror(errno)); - return; + if (!wavreader.begin("/USB_DRIVE/AUDIO_SAMPLE.wav", N_SAMPLES, 1, false)) { + Serial.print("Error opening audio file: "); + while (1); } - Serial.println("Reading audio header ..."); - 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); - - Serial.println("WAV File Header read:"); char msg[64] = {0}; - snprintf(msg, sizeof(msg), "File Type: %s", header.chunkID); - Serial.println(msg); - snprintf(msg, sizeof(msg), "File Size: %ld", header.chunkSize); - Serial.println(msg); - snprintf(msg, sizeof(msg), "WAV Marker: %s", header.format); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Format Name: %s", header.subchunk1ID); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Format Length: %ld", header.subchunk1Size); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Format Type: %hd", header.audioFormat); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Number of Channels: %hd", header.numChannels); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Sample Rate: %ld", header.sampleRate); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Sample Rate * Bits/Sample * Channels / 8: %ld", header.byteRate); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Bits per Sample * Channels / 8: %hd", header.blockAlign); - Serial.println(msg); - snprintf(msg, sizeof(msg), "Bits per Sample: %hd", header.bitsPerSample); - Serial.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"); - Serial.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); - Serial.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); Serial.println(msg); - snprintf(msg, sizeof(msg), "Samples count = %i", samples_count); Serial.println(msg); + snprintf(msg, sizeof(msg), "Number of Channels: %hd", wavreader.channels()); Serial.println(msg); + snprintf(msg, sizeof(msg), "Sample Rate: %ld", wavreader.sample_rate()); Serial.println(msg); + snprintf(msg, sizeof(msg), "Bits per Sample: %hd", wavreader.resolution()); Serial.println(msg); + snprintf(msg, sizeof(msg), "Number of Samples = %i", wavreader.sample_count()); Serial.println(msg); - /* Configure the advanced DAC. */ - if (!dac1.begin(AN_RESOLUTION_12, header.sampleRate, 256, 16)) - { + // Configure and start the DAC. + if (!dac1.begin(AN_RESOLUTION_12, wavreader.sample_rate(), N_SAMPLES, 32)) { Serial.println("Failed to start DAC1 !"); - return; + while (1); } } -void loop() -{ - if (dac1.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 = dac1.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(sample_data[i])+32768)>>4) & 0x0fff; - buf[i] = dac_val; +void loop() { + if (dac1.available() && wavreader.available()) { + // Get a free buffer for writing. + SampleBuffer dacbuf = dac1.dequeue(); + + // Read a samples buffer from the wav file. + SampleBuffer pcmbuf = wavreader.read(); + + // Process and write samples to the DAC buffer. + for (size_t i=0; i> 4; + } else { + // If the file has two channels set the average. + dacbuf[i] = ((unsigned int) ((((int16_t) pcmbuf[(i * 2)] + (int16_t) pcmbuf[(i * 2) + 1]) / 2) + 32768)) >> 4; + } } - /* Write the buffer to DAC. */ - dac1.write(buf); + // Write the buffer to DAC. + dac1.write(dacbuf); + pcmbuf.release(); } }