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