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