Skip to content

Commit fe89abc

Browse files
Wav Writer Example (#672)
* adds an example of writing a wav file * fixed warning for signed/unsigned type mismatch in channel loop
1 parent d4adab6 commit fe89abc

4 files changed

Lines changed: 125 additions & 17 deletions

File tree

examples/WavWriter/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set(FIRMWARE_NAME WavWriter)
2+
set(FIRMWARE_SOURCES WavWriter.cpp)
3+
include(DaisyProject)

examples/WavWriter/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Project Name
2+
TARGET = WavWriter
3+
4+
# Sources
5+
CPP_SOURCES = WavWriter.cpp
6+
7+
USE_FATFS=1
8+
9+
# Library Locations
10+
LIBDAISY_DIR = ../..
11+
12+
# Core location, and generic Makefile.
13+
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
14+
include $(SYSTEM_FILES_DIR)/Makefile

examples/WavWriter/WavWriter.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/** Generation of a simple Audio signal */
2+
#include "daisy_seed.h"
3+
#include <cmath>
4+
5+
/** This prevents us from having to type "daisy::" in front of a lot of things. */
6+
using namespace daisy;
7+
8+
static constexpr float kTargetSr = 48000.f;
9+
static constexpr size_t kTransferSize = 16384;
10+
11+
/** Global Hardware access */
12+
DaisySeed hw;
13+
SdmmcHandler sdmmc;
14+
FatFSInterface fsi;
15+
WavWriter<kTransferSize> wav_writer;
16+
17+
/** Basic Fixed-frequency oscillator */
18+
struct SimpleOsc
19+
{
20+
static constexpr float kTargetFreq = 220.f;
21+
static constexpr float kSignalIncrement
22+
= (M_TWOPI * kTargetFreq) * (1.f / kTargetSr);
23+
float phs_;
24+
SimpleOsc() : phs_(0.f) {}
25+
26+
inline float RenderSample()
27+
{
28+
float signal = sin(phs_) * 0.5f;
29+
phs_ += kSignalIncrement;
30+
if(phs_ > M_TWOPI)
31+
phs_ -= M_TWOPI;
32+
return signal;
33+
}
34+
};
35+
36+
int main(void)
37+
{
38+
/** Initialize our hardware */
39+
hw.Init();
40+
41+
/** Set up SD Card */
42+
SdmmcHandler::Config sd_cfg;
43+
sd_cfg.Defaults();
44+
sd_cfg.width = SdmmcHandler::BusWidth::BITS_1;
45+
sdmmc.Init(sd_cfg);
46+
FatFSInterface::Config fsi_cfg;
47+
fsi_cfg.media = FatFSInterface::Config::MEDIA_SD;
48+
fsi.Init(fsi_cfg);
49+
if(f_mount(&fsi.GetSDFileSystem(), "/", 0) != FR_OK)
50+
{
51+
while(1)
52+
{
53+
hw.SetLed((System::GetNow() & 127) > 63);
54+
}
55+
}
56+
hw.SetLed(true);
57+
58+
/** Set up WAV File */
59+
WavWriter<kTransferSize>::Config cfg;
60+
cfg.bitspersample = 16;
61+
cfg.channels = 2;
62+
cfg.samplerate = kTargetSr;
63+
wav_writer.Init(cfg);
64+
65+
/** Prepare to record a 1s 220Hz Audio File */
66+
SimpleOsc oscillator;
67+
size_t duration_sec = 1;
68+
size_t duration_in_samps = duration_sec * kTargetSr;
69+
70+
wav_writer.OpenFile("ExampleWavFile.wav");
71+
for(size_t i = 0; i < duration_in_samps; i++)
72+
{
73+
// If recording Realtime Audio:
74+
// The rendering/sampling should occur in the realtime audio interrupt
75+
float sample = oscillator.RenderSample();
76+
float samps_to_write[2] = {sample, sample};
77+
wav_writer.Sample(samps_to_write);
78+
79+
// The actual DiskIO should happen outside of the realtime audio interrupt
80+
// For offline-rendering, it is okay to do this check on every sample.
81+
wav_writer.Write();
82+
}
83+
// Flush and Close
84+
wav_writer.SaveFile();
85+
86+
while(1)
87+
{
88+
// Blink Afterwards to show success
89+
hw.SetLed((System::GetNow() & 511) > 255);
90+
}
91+
}

src/util/WavWriter.h

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
namespace daisy
66
{
77
/** Audio Recording Module
8-
**
9-
** Record audio into a working buffer that is gradually written to a WAV file on an SD Card.
108
**
11-
** Recordings are made with floating point input, and will be converted to the
12-
** specified bits per sample internally
9+
** Record audio into a working buffer that is gradually written to a WAV file on an SD Card.
10+
**
11+
** Recordings are made with floating point input, and will be converted to the
12+
** specified bits per sample internally
1313
**
1414
** For now only 16-bit and 32-bit (signed int) formats are supported
1515
** f32 and s24 formats will be added next
@@ -18,7 +18,7 @@ namespace daisy
1818
** effect on the performance of the streaming behavior of the WavWriter.
1919
** Memory use can be calculated as: (2 * transfer_size) bytes
2020
** Performance optimal with sizes: 16384, 32768
21-
**
21+
**
2222
** To use:
2323
** 1. Create a WavWriter<size> object (e.g. WavWriter<32768> writer)
2424
** 2. Configure the settings as desired by creating a WavWriter<32768>::Config struct and setting the settings.
@@ -27,7 +27,7 @@ namespace daisy
2727
** 5. Write to it within your audio callback using: writer.Sample(value)
2828
** 6. Fill the Wav File on the SD Card with data from your main loop by running: writer.Write()
2929
** 7. When finished with the recording finalize, and close the file with: writer.SaveFile();
30-
**
30+
**
3131
** */
3232
template <size_t transfer_size>
3333
class WavWriter
@@ -52,7 +52,7 @@ class WavWriter
5252
int32_t bitspersample;
5353
};
5454

55-
/** State of the internal Writing mechanism.
55+
/** State of the internal Writing mechanism.
5656
** When the buffer is a certain amount full one section will write its contents
5757
** while the other is still being written to. This is performed circularly
5858
** so that audio will be uninterrupted during writing. */
@@ -87,12 +87,12 @@ class WavWriter
8787
}
8888

8989
/** Records the current sample into the working buffer,
90-
** queues writes to media when necessary.
91-
**
90+
** queues writes to media when necessary.
91+
**
9292
** \param in should be a pointer to an array of samples */
9393
void Sample(const float *in)
9494
{
95-
for(size_t i = 0; i < cfg_.channels; i++)
95+
for(int i = 0; i < cfg_.channels; i++)
9696
{
9797
switch(cfg_.bitspersample)
9898
{
@@ -145,28 +145,28 @@ class WavWriter
145145
recording_ = false;
146146

147147
// Flush remaining data in the transfer buffer
148-
if (wptr_ > 0) // Check if there is unwritten data in the buffer
148+
if(wptr_ > 0) // Check if there is unwritten data in the buffer
149149
{
150150
uint32_t remaining_size = wptr_ * (cfg_.bitspersample / 8);
151151
// Ensure remaining_size does not exceed the buffer size
152-
if (remaining_size > sizeof(transfer_buff))
152+
if(remaining_size > sizeof(transfer_buff))
153153
{
154154
remaining_size = sizeof(transfer_buff);
155155
}
156156
f_write(&fp_, transfer_buff, remaining_size, &bw);
157157
}
158-
158+
159159
wavheader_.FileSize = CalcFileSize();
160160
f_lseek(&fp_, 0);
161161
f_write(&fp_, &wavheader_, sizeof(wavheader_), &bw);
162162
f_close(&fp_);
163163

164164
// Clear the transfer buffer and reset the buffer state
165165
memset(transfer_buff, 0, sizeof(transfer_buff));
166-
bstate_ = BufferState::IDLE;
167-
wptr_ = 0; // Reset the write pointer
168-
num_samps_ = 0; // Reset the number of samples
169-
recording_ = false; // Ensure recording is inactive
166+
bstate_ = BufferState::IDLE;
167+
wptr_ = 0; // Reset the write pointer
168+
num_samps_ = 0; // Reset the number of samples
169+
recording_ = false; // Ensure recording is inactive
170170
}
171171

172172
/** Opens a file for writing. Writes the initial WAV Header, and gets ready for stream-based recording. */

0 commit comments

Comments
 (0)