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