AME
ame_Pcm.hpp
Go to the documentation of this file.
1
9#pragma once
10
11#include "ame_AudioBuffer.hpp"
12#include "ame_String.hpp"
13
14#include <cassert>
15#include <concepts>
16#include <cstddef>
17#include <type_traits>
18
20
21namespace
22{
23// clang-format off
27inline constexpr int16_t imaStepTable[89] = {
28 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
29 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
30 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
31 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
32 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
33 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
34 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
35 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
36 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
37};
38// clang-format on
39} // namespace
40
41namespace ame::wav
42{
43enum class ChunkId : uint_fast8_t
44{
45 Unknown = 0,
46 fmt,
47 PEAK,
48 fact,
49 data
50};
51
55template <class BytePointerType>
56struct Chunk
57{
58 ChunkId id;
59 uint32_t size;
60 BytePointerType data;
61};
62
63namespace fmt
64{
65 enum class wFormatTag : uint16_t
66 {
67 Unknown = 0,
68 PCM = 1, //For LinearPCM
69 MicrosoftAdpcm = 2,
70 IeeeFloat = 3, //For 32bitFloat
71 Alaw = 6,
72 Ulaw = 7,
73 ImaAdpcm = 0x11, // IMA-ADPCM
74 YamahaAdpcm = 0x16,
75 Gsm610 = 0x21,
76 G721Adpcm = 0x40,
77 Mpeg = 0x50,
78 Experimental = 0xFF
79 };
80} // namespace fmt
81
82namespace
83{
84 namespace wavChunkId
85 {
86 constexpr char fmt[] = "fmt ";
87 constexpr char PEAK[] = "PEAK";
88 constexpr char fact[] = "fact";
89 constexpr char data[] = "data";
90 } // namespace wavChunkId
91} // namespace
92
96template <typename BytePointerType>
98{
99private:
100 using baseType = typename std::remove_pointer<BytePointerType>::type;
101 using cvRemovedBaseType = typename std::remove_cv<baseType>::type;
102 static_assert (std::is_pointer<BytePointerType>::value, "BytePointerType must be a Pointer type.");
103 static_assert (std::is_same<cvRemovedBaseType,
104 unsigned char>::value,
105 "BytePointerType must be unsigned char* (or CV-qualified).");
106
107public:
108 constexpr WavReader (BytePointerType wavByteArray, size_t length)
109 : wav (wavByteArray),
110 length (length)
111 {
112 assert (length > 46); //46: RIFFヘッダーと最低限のチャンクを合わせた最小のサイズ
113 parseRiffHeader();
114
115 while (offset < length)
116 {
117 const auto chunk = parseChunk();
118 switch (chunk.id)
119 {
120 case ChunkId::fmt:
121 //assert (chunk.size == 16);
122 formatTag = static_cast<fmt::wFormatTag> ((chunk.data[1] << 8) | chunk.data[0]);
123 assert (formatTag == fmt::wFormatTag::PCM || formatTag == fmt::wFormatTag::IeeeFloat || formatTag == fmt::wFormatTag::ImaAdpcm);
124 numChannels = (chunk.data[3] << 8) | chunk.data[2];
125 sampleRate = (chunk.data[5] << 8) | chunk.data[4];
126 wBitsPerSample = (chunk.data[15] << 8) | chunk.data[14];
127 //assert (wBitsPerSample == 16 || wBitsPerSample == 24 || wBitsPerSample == 32);
128 break;
129 case ChunkId::PEAK:
130 //とりあえずPEAKチャンクは無視する
131 break;
132 case ChunkId::fact:
133 //とりえあずfactチャンクは無視する
134 break;
135 case ChunkId::data:
136 numSamplesPerChannel = chunk.size / numChannels / (wBitsPerSample / 8);
137 dataChunk = chunk;
138 break;
139 default:
140 //JUNK, LIST, IDv3チャンクなどは無視する
141 break;
142 }
143 }
144 }
145 ~WavReader() = default;
146
148 constexpr uint32_t getFileSize() const noexcept
149 {
150 return fileSize;
151 }
152
154 constexpr uint32_t getSampleRate() const noexcept
155 {
156 return sampleRate;
157 }
158
160 constexpr uint16_t getBitRate() const noexcept
161 {
162 return wBitsPerSample;
163 }
164
166 constexpr uint16_t getNumChannels() const noexcept
167 {
168 return numChannels;
169 }
170
172 constexpr uint32_t getNumSamples() const noexcept
173 {
174 return numSamplesPerChannel;
175 }
176
178 constexpr Chunk<BytePointerType> getDataChunk() const noexcept
179 {
180 return dataChunk;
181 }
182
183 constexpr BytePointerType getDataPointer() const noexcept
184 {
185 return dataChunk.data;
186 }
187
188 constexpr fmt::wFormatTag getFormatTag() const noexcept
189 {
190 return formatTag;
191 }
192
193private:
194 constexpr void parseRiffHeader()
195 {
196 assert (wav[0] == 'R' && wav[1] == 'I' && wav[2] == 'F' && wav[3] == 'F'); //RIFF ID must be RIFF
197 assert (wav[8] == 'W' && wav[9] == 'A' && wav[10] == 'V' && wav[11] == 'E'); //Format must be WAVE
198 fileSize = (wav[7] << 24) | (wav[6] << 16) | (wav[5] << 8) | wav[4];
199 }
200
201 constexpr Chunk<BytePointerType> parseChunk()
202 {
203 unsigned char cid[4] = {};
204 for (int i = 0; i < 4; ++i)
205 {
206 cid[i] = wav[offset + i];
207 }
208
209 ChunkId chunkId = ChunkId::Unknown;
210 if (stringComp (cid, wavChunkId::fmt, 4))
211 {
212 chunkId = ChunkId::fmt;
213 }
214 else if (stringComp (cid, wavChunkId::PEAK, 4))
215 {
216 chunkId = ChunkId::PEAK;
217 }
218 else if (stringComp (cid, wavChunkId::fact, 4))
219 {
220 chunkId = ChunkId::fact;
221 }
222 else if (stringComp (cid, wavChunkId::data, 4))
223 {
224 chunkId = ChunkId::data;
225 }
226
227 const uint32_t chunkSize = (wav[offset + 7] << 24) | (wav[offset + 6] << 16) | (wav[offset + 5] << 8) | wav[offset + 4]; //constexprのためにretinterpret_cast<uint32_t>を使わない
228 const size_t dataIndex = offset + 8;
229
231 offset += chunkSize + 8; //8: chunkIDとchunkSizeの8byte分
232 return Chunk<BytePointerType> { chunkId, chunkSize, &wav[dataIndex] };
233 }
234
235 const BytePointerType wav;
236 uint32_t fileSize = 0;
237 const size_t length = 0;
238 size_t offset = 12; //FMTチャンク冒頭
239
240 //fmtチャンク
241 fmt::wFormatTag formatTag = fmt::wFormatTag::Unknown;
242 uint16_t numChannels = 0;
243 uint32_t sampleRate = 0;
244 uint32_t numSamplesPerChannel = 0;
245 uint16_t wBitsPerSample = 0;
246 Chunk<BytePointerType> dataChunk {};
247};
248
252template <typename BytePointerType>
254{
255 class PCMDecoder
256 {
257 public:
258 PCMDecoder() = default;
259 ~PCMDecoder() = default;
260
261 private:
262 };
263
264public:
265 explicit constexpr WavPlayer (const WavReader<BytePointerType>& reader)
266 : formatTag (reader.getFormatTag()),
267 numChannels (reader.getNumChannels()),
268 numSamples (reader.getNumSamples()),
269 bitRate (reader.getBitRate()),
270 dataChunk (reader.getDataChunk())
271 {
272 }
273 ~WavPlayer() = default;
274
276 void play()
277 {
278 playing.store (true);
279 }
280
282 void pause()
283 {
284 playing.store (false);
285 }
286
289 {
290 readPosition.store (0);
291 }
292
297 template <typename FloatType, size_t N>
299 {
300 if (! playing.load())
301 {
302 return;
303 }
304
305 assert (block.getNumChannels() >= numChannels);
306 assert (0 <= readPosition.load() && readPosition <= numSamples);
307
309 switch (formatTag)
310 {
311 case fmt::wFormatTag::PCM:
312 {
313 auto offset = (bitRate / 8) * numChannels * readPosition.load();
314 const auto nSamp = block.getNumSamplesPerChannel();
315 for (uint_fast32_t samp = 0; samp < nSamp; ++samp)
316 {
317 if (readPosition + samp >= numSamples)
318 {
320 readPosition.store (0);
321 playing.store (false);
322 return;
323 }
324
325 for (uint_fast32_t ch = 0; ch < block.getNumChannels(); ++ch)
326 {
327 int32_t temp = 0;
328 for (uint32_t b = 32 - bitRate; b < 32; b += 8) //bitDepthが16bitや24bitのときは下部を0埋めする.
329 {
330 temp |= (dataChunk.data[offset] << b);
331 ++offset;
332 }
333 const FloatType s = static_cast<FloatType> (temp) / normalizeFactor;
334 block.addSample (ch, samp, s);
335 }
336 }
337 readPosition += nSamp;
338 break;
339 }
340 case fmt::wFormatTag::ImaAdpcm:
341 assert (false);
342 break;
343 case fmt::wFormatTag::IeeeFloat:
344 assert (false);
345 break;
346 default:
347 assert (false); //invalid wFormat
348 break;
349 }
350 }
351
352 bool isPlaying() const noexcept
353 {
354 return playing.load();
355 }
356
357 void setLoopPlay (bool loopPlay)
358 {
359 loop.store (loopPlay);
360 }
361
362 bool isLooping() const noexcept
363 {
364 return loop.load();
365 }
366
367private:
368 const fmt::wFormatTag formatTag;
369 const uint32_t numChannels;
370 const uint32_t numSamples;
371 const uint16_t bitRate;
372 std::atomic<uint32_t> readPosition { 0 };
373 std::atomic<bool> loop { false };
374 std::atomic<bool> playing { false };
375 const Chunk<BytePointerType> dataChunk;
376 static constexpr uint32_t normalizeFactor = 2147483648; //2^31
377};
378} // namespace ame::wav
Audio buffer.
String utilities.
A lightweight data structure that stores a pointer to an audio buffer.
Definition: ame_AudioBuffer.hpp:32
uint_fast32_t getNumChannels() const noexcept
Returns the number of channels.
Definition: ame_AudioBuffer.hpp:49
void addSample(const uint_fast32_t destChannel, const uint_fast32_t destSample, const ElementType valueToAdd)
Add a value to a sample in the buffer.
Definition: ame_AudioBuffer.hpp:77
uint_fast32_t getNumSamplesPerChannel() const noexcept
Returns the number of samples per channel.
Definition: ame_AudioBuffer.hpp:55
WAVE file player.
Definition: ame_Pcm.hpp:254
void skipToHome()
Skip to home.
Definition: ame_Pcm.hpp:288
void pause()
Pause.
Definition: ame_Pcm.hpp:282
void process(AudioBlockView< FloatType, N > &block)
Add to sound to AudioBlockView if playing.
Definition: ame_Pcm.hpp:298
void play()
Start (or re-start) playing.
Definition: ame_Pcm.hpp:276
WAVE file reader.
Definition: ame_Pcm.hpp:98
constexpr uint32_t getNumSamples() const noexcept
return number of samples per channel.
Definition: ame_Pcm.hpp:172
constexpr uint16_t getBitRate() const noexcept
return bit rate.
Definition: ame_Pcm.hpp:160
constexpr uint32_t getFileSize() const noexcept
return RIFF header file size.
Definition: ame_Pcm.hpp:148
constexpr uint16_t getNumChannels() const noexcept
return number of channels
Definition: ame_Pcm.hpp:166
constexpr uint32_t getSampleRate() const noexcept
return sample rate.
Definition: ame_Pcm.hpp:154
constexpr Chunk< BytePointerType > getDataChunk() const noexcept
return data chunk.
Definition: ame_Pcm.hpp:178
WaveChunk.
Definition: ame_Pcm.hpp:57