openMSX
WavData.hh
Go to the documentation of this file.
1#ifndef WAVDATA_HH
2#define WAVDATA_HH
3
4#include "File.hh"
5#include "MemBuffer.hh"
6#include "MSXException.hh"
7
8#include "endian.hh"
9#include "one_of.hh"
10#include "xrange.hh"
11
12#include <array>
13#include <bit>
14#include <cstdint>
15#include <span>
16#include <string>
17
18namespace openmsx {
19
21{
22 struct NoFilter {
23 void setFreq(unsigned /*freq*/) const {}
24 uint16_t operator()(uint16_t x) const { return x; }
25 };
26
27public:
29 WavData() = default;
30
32 template<typename Filter = NoFilter>
33 explicit WavData(const std::string& filename, Filter filter = {})
34 : WavData(File(filename), filter) {}
35
36 template<typename Filter = NoFilter>
37 explicit WavData(File file, Filter filter = {});
38
39 [[nodiscard]] unsigned getFreq() const { return freq; }
40 [[nodiscard]] unsigned getSize() const { return length; }
41 [[nodiscard]] int16_t getSample(unsigned pos) const {
42 return (pos < length) ? buffer[pos] : int16_t(0);
43 }
44
45private:
46 template<typename T>
47 [[nodiscard]] static const T* read(std::span<const uint8_t> raw, size_t offset, size_t count = 1);
48
49private:
50 MemBuffer<int16_t> buffer;
51 unsigned freq = 0;
52 unsigned length = 0;
53};
54
56
57template<typename T>
58inline const T* WavData::read(std::span<const uint8_t> raw, size_t offset, size_t count)
59{
60 if ((offset + count * sizeof(T)) > raw.size()) {
61 throw MSXException("Read beyond end of wav file.");
62 }
63 return std::bit_cast<const T*>(raw.data() + offset);
64}
65
66template<typename Filter>
67inline WavData::WavData(File file, Filter filter)
68{
69 // Read and check header
70 auto raw = file.mmap();
71 struct WavHeader {
72 std::array<char, 4> riffID;
73 Endian::L32 riffSize;
74 std::array<char, 4> riffType;
75 std::array<char, 4> fmtID;
76 Endian::L32 fmtSize;
77 Endian::L16 wFormatTag;
78 Endian::L16 wChannels;
79 Endian::L32 dwSamplesPerSec;
80 Endian::L32 dwAvgBytesPerSec;
81 Endian::L16 wBlockAlign;
82 Endian::L16 wBitsPerSample;
83 };
84 const auto* header = read<WavHeader>(raw, 0);
85 if ((std::string_view{header->riffID.data(), 4} != "RIFF") ||
86 (std::string_view{header->riffType.data(), 4} != "WAVE") ||
87 (std::string_view{header->fmtID.data(), 4} != "fmt ")) {
88 throw MSXException("Invalid WAV file.");
89 }
90 unsigned bits = header->wBitsPerSample;
91 if ((header->wFormatTag != 1) || (bits != one_of(8u, 16u))) {
92 throw MSXException("WAV format unsupported, must be 8 or 16 bit PCM.");
93 }
94 freq = header->dwSamplesPerSec;
95 unsigned channels = header->wChannels;
96
97 // Skip any extra format bytes
98 size_t pos = 20 + header->fmtSize;
99
100 // Find 'data' chunk
101 struct DataHeader {
102 std::array<char, 4> dataID;
103 Endian::L32 chunkSize;
104 };
105 const DataHeader* dataHeader;
106 while (true) {
107 // Read chunk header
108 dataHeader = read<DataHeader>(raw, pos);
109 pos += sizeof(DataHeader);
110 if (std::string_view{dataHeader->dataID.data(), 4} == "data") break;
111 // Skip non-data chunk
112 pos += dataHeader->chunkSize;
113 }
114
115 // Read and convert sample data
116 length = dataHeader->chunkSize / ((bits / 8) * channels);
117 buffer.resize(length);
118 filter.setFreq(freq);
119 auto convertLoop = [&](const auto* in, auto convertFunc) {
120 for (auto i : xrange(length)) {
121 buffer[i] = filter(convertFunc(*in));
122 in += channels; // discard all but the first channel
123 }
124 };
125 if (bits == 8) {
126 convertLoop(read<uint8_t>(raw, pos, size_t(length) * channels),
127 [](uint8_t u8) { return int16_t((int16_t(u8) - 0x80) << 8); });
128 } else {
129 convertLoop(read<Endian::L16>(raw, pos, size_t(length) * channels),
130 [](Endian::L16 s16) { return int16_t(s16); });
131 }
132}
133
134} // namespace openmsx
135
136#endif
std::span< const uint8_t > mmap()
Map file in memory.
Definition File.cc:102
This class manages the lifetime of a block of memory.
Definition MemBuffer.hh:29
void resize(size_t size)
Grow or shrink the memory block.
Definition MemBuffer.hh:111
int16_t getSample(unsigned pos) const
Definition WavData.hh:41
WavData(const std::string &filename, Filter filter={})
Construct from .wav file and optional filter.
Definition WavData.hh:33
WavData()=default
Construct empty wav.
unsigned getSize() const
Definition WavData.hh:40
unsigned getFreq() const
Definition WavData.hh:39
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr auto xrange(T e)
Definition xrange.hh:132