openMSX
WavWriter.cc
Go to the documentation of this file.
1#include "WavWriter.hh"
2
3#include "Mixer.hh"
4#include "MSXException.hh"
5
6#include "Math.hh"
7#include "endian.hh"
8#include "enumerate.hh"
9#include "narrow.hh"
10#include "ranges.hh"
11#include "small_buffer.hh"
12
13#include <array>
14#include <vector>
15
16namespace openmsx {
17
19 unsigned channels, unsigned bits, unsigned frequency)
20 : file(filename, "wb")
21{
22 // write wav header
23 struct WavHeader {
24 std::array<char, 4> chunkID; // + 0 'RIFF'
25 Endian::L32 chunkSize; // + 4 total size
26 std::array<char, 4> format; // + 8 'WAVE'
27 std::array<char, 4> subChunk1ID; // +12 'fmt '
28 Endian::L32 subChunk1Size; // +16 = 16 (fixed)
29 Endian::L16 audioFormat; // +20 = 1 (fixed)
30 Endian::L16 numChannels; // +22
31 Endian::L32 sampleRate; // +24
32 Endian::L32 byteRate; // +28
33 Endian::L16 blockAlign; // +32
34 Endian::L16 bitsPerSample; // +34
35 std::array<char, 4> subChunk2ID; // +36 'data'
36 Endian::L32 subChunk2Size; // +40
37 } header;
38
39 ranges::copy(std::string_view("RIFF"), header.chunkID);
40 header.chunkSize = 0; // actual value filled in later
41 ranges::copy(std::string_view("WAVE"), header.format);
42 ranges::copy(std::string_view("fmt "), header.subChunk1ID);
43 header.subChunk1Size = 16;
44 header.audioFormat = 1;
45 header.numChannels = narrow<uint16_t>(channels);
46 header.sampleRate = frequency;
47 header.byteRate = (channels * frequency * bits) / 8;
48 header.blockAlign = narrow<uint16_t>((channels * bits) / 8);
49 header.bitsPerSample = narrow<uint16_t>(bits);
50 ranges::copy(std::string_view("data"), header.subChunk2ID);
51 header.subChunk2Size = 0; // actual value filled in later
52
53 file.write(std::span{&header, 1});
54}
55
57{
58 try {
59 // data chunk must have an even number of bytes
60 if (bytes & 1) {
61 std::array<uint8_t, 1> pad = {0};
62 file.write(pad);
63 }
64
65 flush(); // write header
66 } catch (MSXException&) {
67 // ignore, can't throw from destructor
68 }
69}
70
72{
73 Endian::L32 totalSize((bytes + 44 - 8 + 1) & ~1); // round up to even number
74 Endian::L32 wavSize(bytes);
75
76 file.seek(4);
77 file.write(std::span{&totalSize, 1});
78 file.seek(40);
79 file.write(std::span{&wavSize, 1});
80 file.seek(file.getSize()); // SEEK_END
81 file.flush();
82}
83
84void Wav8Writer::write(std::span<const uint8_t> buffer)
85{
86 file.write(buffer);
87 bytes += narrow<uint32_t>(buffer.size_bytes());
88}
89
90void Wav16Writer::write(std::span<const int16_t> buffer)
91{
92 if constexpr (Endian::BIG) {
94 file.write(std::span{buf});
95 } else {
96 file.write(buffer);
97 }
98 bytes += narrow<uint32_t>(buffer.size_bytes());
99}
100
101static int16_t float2int16(float f)
102{
103 return Math::clipToInt16(lrintf(32768.0f * f));
104}
105
106void Wav16Writer::write(std::span<const float> buffer, float amp)
107{
108 std::vector<Endian::L16> buf_(buffer.size());
109 std::span buf{buf_};
110 ranges::transform(buffer, buf.data(), [=](float f) { return float2int16(f * amp); });
111 file.write(buf);
112 bytes += narrow<uint32_t>(buf.size_bytes());
113}
114
115void Wav16Writer::write(std::span<const StereoFloat> buffer, float ampLeft, float ampRight)
116{
117 std::vector<Endian::L16> buf(buffer.size() * 2);
118 for (auto [i, s] : enumerate(buffer)) { // TODO use zip() in the future
119 buf[2 * i + 0] = float2int16(s.left * ampLeft);
120 buf[2 * i + 1] = float2int16(s.right * ampRight);
121 }
122 std::span s{buf};
123 file.write(s);
124 bytes += narrow<uint32_t>(s.size_bytes());
125}
126
127void Wav16Writer::writeSilence(uint32_t samples)
128{
129 small_buffer<int16_t, 4096> buf_(samples, 0);
130 std::span buf{buf_};
131 file.write(buf);
132 bytes += narrow<uint32_t>(buf.size_bytes());
133}
134
135} // namespace openmsx
void seek(size_t pos)
Move read/write pointer to the specified position.
Definition File.cc:117
void write(std::span< const uint8_t > buffer)
Write to file.
Definition File.cc:97
size_t getSize()
Returns the size of this file.
Definition File.cc:112
void flush()
Force a write of all buffered data to disk.
Definition File.cc:132
This class represents a filename.
Definition Filename.hh:20
void write(std::span< const int16_t > buffer)
Definition WavWriter.cc:90
void writeSilence(uint32_t samples)
Definition WavWriter.cc:127
void write(std::span< const uint8_t > buffer)
Definition WavWriter.cc:84
void flush()
Flush data to file and update header.
Definition WavWriter.cc:71
WavWriter(const Filename &filename, unsigned channels, unsigned bits, unsigned frequency)
Definition WavWriter.cc:18
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
constexpr bool BIG
Definition endian.hh:16
int16_t clipToInt16(T x)
Clip x to range [-32768,32767].
Definition Math.hh:47
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
This file implemented 3 utility functions:
Definition Autofire.cc:11
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition ranges.hh:279
constexpr auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:252