wav_writer.cpp (2923B)
1 // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "wav_writer.h" 5 #include "common/file_system.h" 6 #include "common/log.h" 7 Log_SetChannel(WAVWriter); 8 9 namespace { 10 #pragma pack(push, 1) 11 struct WAV_HEADER 12 { 13 u32 chunk_id; // RIFF 14 u32 chunk_size; 15 u32 format; // WAVE 16 17 struct FormatChunk 18 { 19 u32 chunk_id; // "fmt " 20 u32 chunk_size; 21 u16 audio_format; // pcm = 1 22 u16 num_channels; 23 u32 sample_rate; 24 u32 byte_rate; 25 u16 block_align; 26 u16 bits_per_sample; 27 } fmt_chunk; 28 29 struct DataChunkHeader 30 { 31 u32 chunk_id; // "data " 32 u32 chunk_size; 33 } data_chunk_header; 34 }; 35 #pragma pack(pop) 36 } // namespace 37 38 WAVWriter::WAVWriter() = default; 39 40 WAVWriter::~WAVWriter() 41 { 42 if (IsOpen()) 43 Close(); 44 } 45 46 bool WAVWriter::Open(const char* filename, u32 sample_rate, u32 num_channels) 47 { 48 if (IsOpen()) 49 Close(); 50 51 m_file = FileSystem::OpenCFile(filename, "wb"); 52 if (!m_file) 53 return false; 54 55 m_sample_rate = sample_rate; 56 m_num_channels = num_channels; 57 58 if (!WriteHeader()) 59 { 60 ERROR_LOG("Failed to write header to file"); 61 m_sample_rate = 0; 62 m_num_channels = 0; 63 std::fclose(m_file); 64 m_file = nullptr; 65 return false; 66 } 67 68 return true; 69 } 70 71 void WAVWriter::Close() 72 { 73 if (!IsOpen()) 74 return; 75 76 if (std::fseek(m_file, 0, SEEK_SET) != 0 || !WriteHeader()) 77 ERROR_LOG("Failed to re-write header on file, file may be unplayable"); 78 79 std::fclose(m_file); 80 m_file = nullptr; 81 m_sample_rate = 0; 82 m_num_channels = 0; 83 m_num_frames = 0; 84 } 85 86 void WAVWriter::WriteFrames(const s16* samples, u32 num_frames) 87 { 88 const u32 num_frames_written = 89 static_cast<u32>(std::fwrite(samples, sizeof(s16) * m_num_channels, num_frames, m_file)); 90 if (num_frames_written != num_frames) 91 ERROR_LOG("Only wrote {} of {} frames to output file", num_frames_written, num_frames); 92 93 m_num_frames += num_frames_written; 94 } 95 96 bool WAVWriter::WriteHeader() 97 { 98 const u32 data_size = sizeof(SampleType) * m_num_channels * m_num_frames; 99 100 WAV_HEADER header = {}; 101 header.chunk_id = 0x46464952; // 0x52494646 102 header.chunk_size = sizeof(WAV_HEADER) - 8 + data_size; 103 header.format = 0x45564157; // 0x57415645 104 header.fmt_chunk.chunk_id = 0x20746d66; // 0x666d7420 105 header.fmt_chunk.chunk_size = sizeof(header.fmt_chunk) - 8; 106 header.fmt_chunk.audio_format = 1; 107 header.fmt_chunk.num_channels = static_cast<u16>(m_num_channels); 108 header.fmt_chunk.sample_rate = m_sample_rate; 109 header.fmt_chunk.byte_rate = m_sample_rate * m_num_channels * sizeof(SampleType); 110 header.fmt_chunk.block_align = static_cast<u16>(m_num_channels * sizeof(SampleType)); 111 header.fmt_chunk.bits_per_sample = 16; 112 header.data_chunk_header.chunk_id = 0x61746164; // 0x64617461 113 header.data_chunk_header.chunk_size = data_size; 114 115 return (std::fwrite(&header, sizeof(header), 1, m_file) == 1); 116 }