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