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