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