openMSX
WavWriter.cc
Go to the documentation of this file.
1 #include "WavWriter.hh"
2 #include "MSXException.hh"
3 #include "Math.hh"
4 #include "vla.hh"
5 #include "endian.hh"
6 #include <cstring>
7 #include <vector>
8 
9 namespace openmsx {
10 
12  unsigned channels, unsigned bits, unsigned frequency)
13  : file(filename, "wb")
14  , bytes(0)
15 {
16  // write wav header
17  struct WavHeader {
18  char chunkID[4]; // + 0 'RIFF'
19  Endian::L32 chunkSize; // + 4 total size
20  char format[4]; // + 8 'WAVE'
21  char subChunk1ID[4]; // +12 'fmt '
22  Endian::L32 subChunk1Size; // +16 = 16 (fixed)
23  Endian::L16 audioFormat; // +20 = 1 (fixed)
24  Endian::L16 numChannels; // +22
25  Endian::L32 sampleRate; // +24
26  Endian::L32 byteRate; // +28
27  Endian::L16 blockAlign; // +32
28  Endian::L16 bitsPerSample; // +34
29  char subChunk2ID[4]; // +36 'data'
30  Endian::L32 subChunk2Size; // +40
31  } header;
32 
33  memcpy(header.chunkID, "RIFF", sizeof(header.chunkID));
34  header.chunkSize = 0; // actual value filled in later
35  memcpy(header.format, "WAVE", sizeof(header.format));
36  memcpy(header.subChunk1ID, "fmt ", sizeof(header.subChunk1ID));
37  header.subChunk1Size = 16;
38  header.audioFormat = 1;
39  header.numChannels = channels;
40  header.sampleRate = frequency;
41  header.byteRate = (channels * frequency * bits) / 8;
42  header.blockAlign = (channels * bits) / 8;
43  header.bitsPerSample = bits;
44  memcpy(header.subChunk2ID, "data", sizeof(header.subChunk2ID));
45  header.subChunk2Size = 0; // actaul value filled in later
46 
47  file.write(&header, sizeof(header));
48 }
49 
51 {
52  try {
53  // data chunk must have an even number of bytes
54  if (bytes & 1) {
55  uint8_t pad = 0;
56  file.write(&pad, 1);
57  }
58 
59  flush(); // write header
60  } catch (MSXException&) {
61  // ignore, can't throw from destructor
62  }
63 }
64 
66 {
67  Endian::L32 totalSize = (bytes + 44 - 8 + 1) & ~1; // round up to even number
68  Endian::L32 wavSize = bytes;
69 
70  file.seek(4);
71  file.write(&totalSize, 4);
72  file.seek(40);
73  file.write(&wavSize, 4);
74  file.seek(file.getSize()); // SEEK_END
75  file.flush();
76 }
77 
78 void Wav8Writer::write(const uint8_t* buffer, unsigned samples)
79 {
80  file.write(buffer, samples);
81  bytes += samples;
82 }
83 
84 void Wav16Writer::write(const int16_t* buffer, unsigned samples)
85 {
86  unsigned size = sizeof(int16_t) * samples;
87  if (OPENMSX_BIGENDIAN) {
88  // Variable length arrays (VLA) are part of c99 but not of c++
89  // (not even c++11). Some compilers (like gcc) do support VLA
90  // in c++ mode, others (like VC++) don't. Still other compilers
91  // (like clang) only support VLA for POD types.
92  // To side-step this issue we simply use a std::vector, this
93  // code is anyway not performance critical.
94  //VLA(Endian::L16, buf, samples); // doesn't work in clang
95  std::vector<Endian::L16> buf(buffer, buffer + samples);
96  file.write(buf.data(), size);
97  } else {
98  file.write(buffer, size);
99  }
100  bytes += size;
101 }
102 
103 static int16_t float2int16(float f)
104 {
105  return Math::clipIntToShort(lrintf(32768.0f * f));
106 }
107 
108 void Wav16Writer::write(const float* buffer, unsigned stereo, unsigned samples,
109  float ampLeft, float ampRight)
110 {
111  assert(stereo == 1 || stereo == 2);
112  std::vector<Endian::L16> buf(samples * stereo);
113  if (stereo == 1) {
114  assert(ampLeft == ampRight);
115  for (unsigned i = 0; i < samples; ++i) {
116  buf[i] = float2int16(buffer[i] * ampLeft);
117  }
118  } else {
119  for (unsigned i = 0; i < samples; ++i) {
120  buf[2 * i + 0] = float2int16(buffer[2 * i + 0] * ampLeft);
121  buf[2 * i + 0] = float2int16(buffer[2 * i + 0] * ampRight);
122  }
123  }
124  unsigned size = sizeof(int16_t) * samples * stereo;
125  file.write(buf.data(), size);
126  bytes += size;
127 }
128 
129 void Wav16Writer::writeSilence(unsigned samples)
130 {
131  VLA(int16_t, buf, samples);
132  unsigned size = sizeof(int16_t) * samples;
133  memset(buf, 0, size);
134  file.write(buf, size);
135  bytes += size;
136 }
137 
138 } // namespace openmsx
void writeSilence(unsigned stereo, unsigned samples)
Definition: WavWriter.hh:66
size_t getSize()
Returns the size of this file.
Definition: File.cc:103
int16_t clipIntToShort(int x)
Clip x to range [-32768,32767].
Definition: Math.hh:110
void flush()
Flush data to file and update header.
Definition: WavWriter.cc:65
WavWriter(const Filename &filename, unsigned channels, unsigned bits, unsigned frequency)
Definition: WavWriter.cc:11
void flush()
Force a write of all buffered data to disk.
Definition: File.cc:123
This class represents a filename.
Definition: Filename.hh:17
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void write(const uint8_t *buffer, unsigned stereo, unsigned samples)
Definition: WavWriter.hh:43
void write(const void *buffer, size_t num)
Write to file.
Definition: File.cc:88
void write(const int16_t *buffer, unsigned stereo, unsigned samples)
Definition: WavWriter.hh:60
void seek(size_t pos)
Move read/write pointer to the specified position.
Definition: File.cc:108
void format(SectorAccessibleDisk &disk, bool dos1)
Format the given disk (= a single partition).
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10