From e8639c52ec7b41b047795d7b17b11a361b73dc9e Mon Sep 17 00:00:00 2001 From: Stephen Hensley Date: Mon, 16 Jun 2025 10:11:30 -0700 Subject: [PATCH 1/2] adds an example of writing a wav file --- examples/WavWriter/CMakeLists.txt | 3 + examples/WavWriter/Makefile | 14 +++++ examples/WavWriter/WavWriter.cpp | 91 +++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 examples/WavWriter/CMakeLists.txt create mode 100644 examples/WavWriter/Makefile create mode 100644 examples/WavWriter/WavWriter.cpp diff --git a/examples/WavWriter/CMakeLists.txt b/examples/WavWriter/CMakeLists.txt new file mode 100644 index 000000000..900689426 --- /dev/null +++ b/examples/WavWriter/CMakeLists.txt @@ -0,0 +1,3 @@ +set(FIRMWARE_NAME WavWriter) +set(FIRMWARE_SOURCES WavWriter.cpp) +include(DaisyProject) diff --git a/examples/WavWriter/Makefile b/examples/WavWriter/Makefile new file mode 100644 index 000000000..27abd3f5f --- /dev/null +++ b/examples/WavWriter/Makefile @@ -0,0 +1,14 @@ +# Project Name +TARGET = WavWriter + +# Sources +CPP_SOURCES = WavWriter.cpp + +USE_FATFS=1 + +# Library Locations +LIBDAISY_DIR = ../.. + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/WavWriter/WavWriter.cpp b/examples/WavWriter/WavWriter.cpp new file mode 100644 index 000000000..1dc25158f --- /dev/null +++ b/examples/WavWriter/WavWriter.cpp @@ -0,0 +1,91 @@ +/** Generation of a simple Audio signal */ +#include "daisy_seed.h" +#include + +/** This prevents us from having to type "daisy::" in front of a lot of things. */ +using namespace daisy; + +static constexpr float kTargetSr = 48000.f; +static constexpr size_t kTransferSize = 16384; + +/** Global Hardware access */ +DaisySeed hw; +SdmmcHandler sdmmc; +FatFSInterface fsi; +WavWriter wav_writer; + +/** Basic Fixed-frequency oscillator */ +struct SimpleOsc +{ + static constexpr float kTargetFreq = 220.f; + static constexpr float kSignalIncrement + = (M_TWOPI * kTargetFreq) * (1.f / kTargetSr); + float phs_; + SimpleOsc() : phs_(0.f) {} + + inline float RenderSample() + { + float signal = sin(phs_) * 0.5f; + phs_ += kSignalIncrement; + if(phs_ > M_TWOPI) + phs_ -= M_TWOPI; + return signal; + } +}; + +int main(void) +{ + /** Initialize our hardware */ + hw.Init(); + + /** Set up SD Card */ + SdmmcHandler::Config sd_cfg; + sd_cfg.Defaults(); + sd_cfg.width = SdmmcHandler::BusWidth::BITS_1; + sdmmc.Init(sd_cfg); + FatFSInterface::Config fsi_cfg; + fsi_cfg.media = FatFSInterface::Config::MEDIA_SD; + fsi.Init(fsi_cfg); + if(f_mount(&fsi.GetSDFileSystem(), "/", 0) != FR_OK) + { + while(1) + { + hw.SetLed((System::GetNow() & 127) > 63); + } + } + hw.SetLed(true); + + /** Set up WAV File */ + WavWriter::Config cfg; + cfg.bitspersample = 16; + cfg.channels = 2; + cfg.samplerate = kTargetSr; + wav_writer.Init(cfg); + + /** Prepare to record a 1s 220Hz Audio File */ + SimpleOsc oscillator; + size_t duration_sec = 1; + size_t duration_in_samps = duration_sec * kTargetSr; + + wav_writer.OpenFile("ExampleWavFile.wav"); + for(size_t i = 0; i < duration_in_samps; i++) + { + // If recording Realtime Audio: + // The rendering/sampling should occur in the realtime audio interrupt + float sample = oscillator.RenderSample(); + float samps_to_write[2] = {sample, sample}; + wav_writer.Sample(samps_to_write); + + // The actual DiskIO should happen outside of the realtime audio interrupt + // For offline-rendering, it is okay to do this check on every sample. + wav_writer.Write(); + } + // Flush and Close + wav_writer.SaveFile(); + + while(1) + { + // Blink Afterwards to show success + hw.SetLed((System::GetNow() & 511) > 255); + } +} \ No newline at end of file From bca155cde234e6f466338d534c74020fa7546cfa Mon Sep 17 00:00:00 2001 From: Stephen Hensley Date: Mon, 16 Jun 2025 10:12:09 -0700 Subject: [PATCH 2/2] fixed warning for signed/unsigned type mismatch in channel loop --- src/util/WavWriter.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/util/WavWriter.h b/src/util/WavWriter.h index d4a072999..50ed7ebf1 100644 --- a/src/util/WavWriter.h +++ b/src/util/WavWriter.h @@ -5,11 +5,11 @@ namespace daisy { /** Audio Recording Module - ** - ** Record audio into a working buffer that is gradually written to a WAV file on an SD Card. ** - ** Recordings are made with floating point input, and will be converted to the - ** specified bits per sample internally + ** Record audio into a working buffer that is gradually written to a WAV file on an SD Card. + ** + ** Recordings are made with floating point input, and will be converted to the + ** specified bits per sample internally ** ** For now only 16-bit and 32-bit (signed int) formats are supported ** f32 and s24 formats will be added next @@ -18,7 +18,7 @@ namespace daisy ** effect on the performance of the streaming behavior of the WavWriter. ** Memory use can be calculated as: (2 * transfer_size) bytes ** Performance optimal with sizes: 16384, 32768 - ** + ** ** To use: ** 1. Create a WavWriter object (e.g. WavWriter<32768> writer) ** 2. Configure the settings as desired by creating a WavWriter<32768>::Config struct and setting the settings. @@ -27,7 +27,7 @@ namespace daisy ** 5. Write to it within your audio callback using: writer.Sample(value) ** 6. Fill the Wav File on the SD Card with data from your main loop by running: writer.Write() ** 7. When finished with the recording finalize, and close the file with: writer.SaveFile(); - ** + ** ** */ template class WavWriter @@ -52,7 +52,7 @@ class WavWriter int32_t bitspersample; }; - /** State of the internal Writing mechanism. + /** State of the internal Writing mechanism. ** When the buffer is a certain amount full one section will write its contents ** while the other is still being written to. This is performed circularly ** so that audio will be uninterrupted during writing. */ @@ -87,12 +87,12 @@ class WavWriter } /** Records the current sample into the working buffer, - ** queues writes to media when necessary. - ** + ** queues writes to media when necessary. + ** ** \param in should be a pointer to an array of samples */ void Sample(const float *in) { - for(size_t i = 0; i < cfg_.channels; i++) + for(int i = 0; i < cfg_.channels; i++) { switch(cfg_.bitspersample) { @@ -145,17 +145,17 @@ class WavWriter recording_ = false; // Flush remaining data in the transfer buffer - if (wptr_ > 0) // Check if there is unwritten data in the buffer + if(wptr_ > 0) // Check if there is unwritten data in the buffer { uint32_t remaining_size = wptr_ * (cfg_.bitspersample / 8); // Ensure remaining_size does not exceed the buffer size - if (remaining_size > sizeof(transfer_buff)) + if(remaining_size > sizeof(transfer_buff)) { remaining_size = sizeof(transfer_buff); } f_write(&fp_, transfer_buff, remaining_size, &bw); } - + wavheader_.FileSize = CalcFileSize(); f_lseek(&fp_, 0); f_write(&fp_, &wavheader_, sizeof(wavheader_), &bw); @@ -163,10 +163,10 @@ class WavWriter // Clear the transfer buffer and reset the buffer state memset(transfer_buff, 0, sizeof(transfer_buff)); - bstate_ = BufferState::IDLE; - wptr_ = 0; // Reset the write pointer - num_samps_ = 0; // Reset the number of samples - recording_ = false; // Ensure recording is inactive + bstate_ = BufferState::IDLE; + wptr_ = 0; // Reset the write pointer + num_samps_ = 0; // Reset the number of samples + recording_ = false; // Ensure recording is inactive } /** Opens a file for writing. Writes the initial WAV Header, and gets ready for stream-based recording. */