duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }