diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 8dddc530..151bb21f 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -57,6 +57,91 @@ private: File file; }; +class WAVFileWriter : public Writer { +public: + WAVFileWriter( + const std::string& filename, + size_t sampling_rate + ) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }, + header { sampling_rate } + { + update_header(); + } + + ~WAVFileWriter() { + update_header(); + } + + bool write(const void* const buffer, const size_t bytes) override { + const auto success = file.write(buffer, bytes) ; + if( success ) { + bytes_written += bytes; + } + return success; + } + +private: + struct fmt_pcm_t { + constexpr fmt_pcm_t( + const uint32_t sampling_rate + ) : nSamplesPerSec { sampling_rate }, + nAvgBytesPerSec { nSamplesPerSec * nBlockAlign } + { + } + + private: + const uint8_t ckID[4] { 'f', 'm', 't', ' ' }; + const uint32_t cksize { 16 }; + const uint16_t wFormatTag { 0x0001 }; + const uint16_t nChannels { 1 }; + const uint32_t nSamplesPerSec; + const uint32_t nAvgBytesPerSec; + const uint16_t nBlockAlign { 2 }; + const uint16_t wBitsPerSample { 16 }; + }; + + struct data_t { + void set_size(const uint32_t value) { + cksize = value; + } + + private: + const uint8_t ckID[4] { 'd', 'a', 't', 'a' }; + uint32_t cksize { 0 }; + }; + + struct header_t { + constexpr header_t( + const uint32_t sampling_rate + ) : fmt { sampling_rate } + { + } + + void set_data_size(const uint32_t value) { + data.set_size(value); + cksize = sizeof(header_t) + value - 8; + } + + private: + const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; + uint32_t cksize { 0 }; + const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; + fmt_pcm_t fmt; + data_t data; + }; + + File file; + header_t header; + uint64_t bytes_written { 0 }; + + void update_header() { + header.set_data_size(bytes_written); + const auto old_position = file.seek(0); + file.write(&header, sizeof(header)); + file.seek(old_position); + } +}; + class StreamOutput { public: StreamOutput(