OXYGENHUB
◆ main openmsx / src / sound / WavWriter.cc
3.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "WavWriter.hh"
2
3
#include "Mixer.hh"
4
5
#include "MSXException.hh"
6
7
#include "Math.hh"
8
#include "endian.hh"
9
#include "enumerate.hh"
10
#include "narrow.hh"
11
#include "ranges.hh"
12
#include "small_buffer.hh"
13
14
#include <array>
15
#include <vector>
16
17
namespace openmsx {
18
19
WavWriter::WavWriter(const std::string& filename,
20
                     unsigned channels, unsigned bits, unsigned frequency)
21
	: file(filename, "wb")
22
{
23
	// write wav header
24
	struct WavHeader {
25
		std::array<char, 4> chunkID;       // + 0 'RIFF'
26
		Endian::L32         chunkSize;     // + 4 total size
27
		std::array<char, 4> format;        // + 8 'WAVE'
28
		std::array<char, 4> subChunk1ID;   // +12 'fmt '
29
		Endian::L32         subChunk1Size; // +16 = 16 (fixed)
30
		Endian::L16         audioFormat;   // +20 =  1 (fixed)
31
		Endian::L16         numChannels;   // +22
32
		Endian::L32         sampleRate;    // +24
33
		Endian::L32         byteRate;      // +28
34
		Endian::L16         blockAlign;    // +32
35
		Endian::L16         bitsPerSample; // +34
36
		std::array<char, 4> subChunk2ID;   // +36 'data'
37
		Endian::L32         subChunk2Size; // +40
38
	} header;
39
40
	copy_to_range(std::string_view("RIFF"), header.chunkID);
41
	header.chunkSize     = 0; // actual value filled in later
42
	copy_to_range(std::string_view("WAVE"), header.format);
43
	copy_to_range(std::string_view("fmt "), header.subChunk1ID);
44
	header.subChunk1Size = 16;
45
	header.audioFormat   = 1;
46
	header.numChannels   = narrow<uint16_t>(channels);
47
	header.sampleRate    = frequency;
48
	header.byteRate      = (channels * frequency * bits) / 8;
49
	header.blockAlign    = narrow<uint16_t>((channels * bits) / 8);
50
	header.bitsPerSample = narrow<uint16_t>(bits);
51
	copy_to_range(std::string_view("data"), header.subChunk2ID);
52
	header.subChunk2Size = 0; // actual value filled in later
53
54
	file.write(std::span{&header, 1});
55
}
56
57
WavWriter::~WavWriter()
58
{
59
	try {
60
		// data chunk must have an even number of bytes
61
		if (bytes & 1) {
62
			std::array<uint8_t, 1> pad = {0};
63
			file.write(pad);
64
		}
65
66
		flush(); // write header
67
	} catch (MSXException&) {
68
		// ignore, can't throw from destructor
69
	}
70
}
71
72
void WavWriter::flush()
73
{
74
	Endian::L32 totalSize((bytes + 44 - 8 + 1) & ~1); // round up to even number
75
	Endian::L32 wavSize(bytes);
76
77
	file.seek(4);
78
	file.write(std::span{&totalSize, 1});
79
	file.seek(40);
80
	file.write(std::span{&wavSize, 1});
81
	file.seek(file.getSize()); // SEEK_END
82
	file.flush();
83
}
84
85
void Wav8Writer::write(std::span<const uint8_t> buffer)
86
{
87
	file.write(buffer);
88
	bytes += narrow<uint32_t>(buffer.size_bytes());
89
}
90
91
void Wav16Writer::write(std::span<const int16_t> buffer)
92
{
93
	if constexpr (Endian::BIG) {
94
		small_buffer<Endian::L16, 4096> buf(buffer);
95
		file.write(std::span{buf});
96
	} else {
97
		file.write(buffer);
98
	}
99
	bytes += narrow<uint32_t>(buffer.size_bytes());
100
}
101
102
static int16_t float2int16(float f)
103
{
104
	return Math::clipToInt16(lrintf(32768.0f * f));
105
}
106
107
void Wav16Writer::write(std::span<const float> buffer, float amp)
108
{
109
	std::vector<Endian::L16> buf_(buffer.size());
110
	std::span buf{buf_};
111
	std::ranges::transform(buffer, buf.data(), [=](float f) { return float2int16(f * amp); });
112
	file.write(buf);
113
	bytes += narrow<uint32_t>(buf.size_bytes());
114
}
115
116
void Wav16Writer::write(std::span<const StereoFloat> buffer, float ampLeft, float ampRight)
117
{
118
	std::vector<Endian::L16> buf(buffer.size() * 2);
119
	for (auto [i, s] : enumerate(buffer)) { // TODO use zip() in the future
120
		buf[2 * i + 0] = float2int16(s.left  * ampLeft);
121
		buf[2 * i + 1] = float2int16(s.right * ampRight);
122
	}
123
	std::span s{buf};
124
	file.write(s);
125
	bytes += narrow<uint32_t>(s.size_bytes());
126
}
127
128
void Wav16Writer::writeSilence(uint32_t samples)
129
{
130
	small_buffer<int16_t, 4096> buf_(samples, 0);
131
	std::span buf{buf_};
132
	file.write(buf);
133
	bytes += narrow<uint32_t>(buf.size_bytes());
134
}
135
136
} // namespace openmsx