File WavWriter.h¶
File List > external-docs > libDaisy > src > util > WavWriter.h
Go to the documentation of this file
Source Code¶
#pragma once
#pragma once
#include "fatfs.h"
namespace daisy
{
template <size_t transfer_size>
class WavWriter
{
public:
WavWriter() {}
~WavWriter() {}
enum class Result
{
OK,
ERROR,
};
struct Config
{
float samplerate;
int32_t channels;
int32_t bitspersample;
};
enum class BufferState
{
IDLE,
FLUSH0,
FLUSH1,
};
void Init(const Config &cfg)
{
cfg_ = cfg;
num_samps_ = 0;
// Prep the wav header according to config.
// Certain things (i.e. Size, etc. will have to wait until the finalization of the file, or be updated while streaming).
wavheader_.ChunkId = kWavFileChunkId;
wavheader_.FileFormat = kWavFileWaveId;
wavheader_.SubChunk1ID = kWavFileSubChunk1Id;
wavheader_.SubChunk1Size = 16; // for PCM
wavheader_.AudioFormat = WAVE_FORMAT_PCM;
wavheader_.NbrChannels = cfg.channels;
wavheader_.SampleRate = static_cast<int>(cfg.samplerate);
wavheader_.ByteRate = CalcByteRate();
wavheader_.BlockAlign = cfg_.channels * cfg_.bitspersample / 8;
wavheader_.BitPerSample = cfg_.bitspersample;
wavheader_.SubChunk2ID = kWavFileSubChunk2Id;
wavheader_.FileSize = CalcFileSize();
// This is calculated as part of the subchunk size
}
void Sample(const float *in)
{
for(int i = 0; i < cfg_.channels; i++)
{
switch(cfg_.bitspersample)
{
case 16:
{
int16_t *tp;
tp = (int16_t *)transfer_buff;
tp[wptr_ + i] = f2s16(in[i]);
}
break;
case 32: transfer_buff[wptr_ + i] = f2s32(in[i]); break;
default: break;
}
}
num_samps_++;
wptr_ += cfg_.channels;
size_t cap_point
= cfg_.bitspersample == 16 ? kTransferSamps * 2 : kTransferSamps;
if(wptr_ == cap_point)
{
bstate_ = BufferState::FLUSH0;
}
if(wptr_ >= cap_point * 2)
{
wptr_ = 0;
bstate_ = BufferState::FLUSH1;
}
}
void Write()
{
if(bstate_ != BufferState::IDLE && IsRecording())
{
uint32_t offset;
unsigned int bw = 0;
//offset = bstate_ == BufferState::FLUSH0 ? 0 : transfer_size;
offset = bstate_ == BufferState::FLUSH0 ? 0 : kTransferSamps;
bstate_ = BufferState::IDLE;
f_write(&fp_, &transfer_buff[offset], transfer_size, &bw);
}
}
void SaveFile()
{
unsigned int bw = 0;
recording_ = false;
// Flush remaining data in the transfer 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))
{
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);
f_close(&fp_);
// 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
}
void OpenFile(const char *name)
{
if(f_open(&fp_, name, FA_WRITE | FA_CREATE_ALWAYS) == FR_OK)
{
unsigned int bw = 0;
if(f_write(&fp_, &wavheader_, sizeof(wavheader_), &bw) == FR_OK)
{
recording_ = true;
num_samps_ = 0;
}
}
}
inline bool IsRecording() const { return recording_; }
inline uint32_t GetLengthSamps() { return num_samps_; }
inline float GetLengthSeconds()
{
return (float)num_samps_ / (float)cfg_.samplerate;
}
private:
inline uint32_t CalcFileSize()
{
wavheader_.SubCHunk2Size
= num_samps_ * cfg_.channels * cfg_.bitspersample / 8;
return 36 + wavheader_.SubCHunk2Size;
}
inline uint32_t CalcByteRate()
{
return cfg_.samplerate * cfg_.channels * cfg_.bitspersample / 8;
}
static constexpr int kTransferSamps = transfer_size / sizeof(int32_t);
WAV_FormatTypeDef wavheader_;
uint32_t num_samps_, wptr_;
Config cfg_;
int32_t transfer_buff[kTransferSamps * 2];
BufferState bstate_;
bool recording_;
FIL fp_;
};
} // namespace daisy