Panda3D
wavAudioCursor.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file wavAudioCursor.cxx
10  * @author rdb
11  * @date 2013-08-23
12  */
13 
14 #include "wavAudioCursor.h"
15 #include "config_movies.h"
16 #include "virtualFileSystem.h"
17 #include "wavAudio.h"
18 
19 // Tables for decompressing mu-law and A-law wav files.
20 static int16_t mulaw_table[256] = {
21  -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
22  -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
23  -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
24  -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
25  -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
26  -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
27  -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
28  -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
29  -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
30  -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
31  -876, -844, -812, -780, -748, -716, -684, -652,
32  -620, -588, -556, -524, -492, -460, -428, -396,
33  -372, -356, -340, -324, -308, -292, -276, -260,
34  -244, -228, -212, -196, -180, -164, -148, -132,
35  -120, -112, -104, -96, -88, -80, -72, -64,
36  -56, -48, -40, -32, -24, -16, -8, -1,
37  32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
38  23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
39  15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
40  11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
41  7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
42  5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
43  3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
44  2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
45  1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
46  1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
47  876, 844, 812, 780, 748, 716, 684, 652,
48  620, 588, 556, 524, 492, 460, 428, 396,
49  372, 356, 340, 324, 308, 292, 276, 260,
50  244, 228, 212, 196, 180, 164, 148, 132,
51  120, 112, 104, 96, 88, 80, 72, 64,
52  56, 48, 40, 32, 24, 16, 8, 0
53 };
54 
55 static int16_t alaw_table[256] = {
56  -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
57  -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
58  -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
59  -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
60  -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
61  -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
62  -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
63  -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
64  -344, -328, -376, -360, -280, -264, -312, -296,
65  -472, -456, -504, -488, -408, -392, -440, -424,
66  -88, -72, -120, -104, -24, -8, -56, -40,
67  -216, -200, -248, -232, -152, -136, -184, -168,
68  -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
69  -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
70  -688, -656, -752, -720, -560, -528, -624, -592,
71  -944, -912, -1008, -976, -816, -784, -880, -848,
72  5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
73  7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
74  2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
75  3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
76  22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
77  30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
78  11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
79  15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
80  344, 328, 376, 360, 280, 264, 312, 296,
81  472, 456, 504, 488, 408, 392, 440, 424,
82  88, 72, 120, 104, 24, 8, 56, 40,
83  216, 200, 248, 232, 152, 136, 184, 168,
84  1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
85  1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
86  688, 656, 752, 720, 560, 528, 624, 592,
87  944, 912, 1008, 976, 816, 784, 880, 848
88 };
89 
90 TypeHandle WavAudioCursor::_type_handle;
91 
92 /**
93  * Reads the .wav header from the indicated stream. This leaves the read
94  * pointer positioned at the start of the data.
95  */
97 WavAudioCursor(WavAudio *src, std::istream *stream) :
98  MovieAudioCursor(src),
99  _is_valid(false),
100  _stream(stream),
101  _reader(stream, false),
102  _format(F_pcm),
103  _data_pos(0),
104  _data_size(0)
105 {
106  nassertv(stream != nullptr);
107 
108  // Beginning of "RIFF" chunk.
109  unsigned char magic[4];
110  if (_reader.extract_bytes(magic, 4) != 4 || memcmp(magic, "RIFF", 4) != 0) {
111  movies_cat.error()
112  << ".wav file is not a valid RIFF file.\n";
113  return;
114  }
115 
116  unsigned int chunk_size = _reader.get_uint32();
117 
118  if (_reader.extract_bytes(magic, 4) != 4 || memcmp(magic, "WAVE", 4) != 0) {
119  movies_cat.error()
120  << ".wav file is a RIFF file but does not start with a WAVE chunk.\n";
121  return;
122  }
123 
124  // Find a "fmt " subchunk followed by a "data" subchunk.
125  bool have_fmt = false, have_data = false;
126  unsigned int bytes_read = 4;
127 
128  while ((!have_fmt || !have_data) && _stream->good() && (bytes_read + 8) < chunk_size) {
129 
130  _reader.extract_bytes(magic, 4);
131  unsigned int subchunk_size = _reader.get_uint32();
132 
133  if (memcmp(magic, "fmt ", 4) == 0) {
134  // The format chunk specifies information about the storage.
135  nassertv(subchunk_size >= 16);
136  have_fmt = true;
137 
138  _format = (Format) _reader.get_uint16();
139 
140  _audio_channels = _reader.get_uint16();
141  _audio_rate = _reader.get_uint32();
142 
143  _byte_rate = (double) _reader.get_uint32();
144  _block_align = _reader.get_uint16();
145 
146  // We can round up to next multiple of 8.
147  uint16_t bps = _reader.get_uint16();
148  bps = (bps + 7) & 0xfff8;
149 
150  // How many bytes in this chunk we've read so far.
151  unsigned int read_bytes = 16;
152 
153  // See if there is an extra header to read.
154  if (_format == F_extensible) {
155  unsigned short ext_size = _reader.get_uint16();
156  read_bytes += 2;
157 
158  if (ext_size >= 8) {
159  /*n_valid_bits =*/ _reader.get_uint16();
160  /*speaker_mask =*/ _reader.get_uint32();
161  _format = (Format) _reader.get_uint16();
162 
163  read_bytes += 8;
164  }
165  }
166 
167  switch (_format) {
168  case F_pcm:
169  if (bps != 8 && bps != 16 && bps != 24 && bps != 32 && bps != 64) {
170  movies_cat.error()
171  << "Unsupported number of bits per sample for PCM storage: " << bps << "\n";
172  return;
173  }
174  break;
175 
176  case F_float:
177  if (bps != 32 && bps != 64) {
178  movies_cat.error()
179  << "Unsupported number of bits per sample for float storage: " << bps << "\n";
180  return;
181  }
182  break;
183 
184  case F_alaw:
185  case F_mulaw:
186  if (bps != 8) {
187  movies_cat.error()
188  << ".wav file with A-law or mu-law compression must specify 8 bits per sample, not " << bps << ".\n";
189  return;
190  }
191  break;
192 
193  default:
194  movies_cat.error()
195  << "Unsupported .wav format " << _format << ". Only PCM, float, A-law and mu-law encodings are supported.\n";
196  return;
197  }
198 
199  _bytes_per_sample = bps / 8;
200 
201  // Skip to the end of the chunk.
202  if (subchunk_size > read_bytes) {
203  _reader.skip_bytes(subchunk_size - read_bytes);
204  }
205 
206  } else if (memcmp(magic, "data", 4) == 0) {
207  // The data chunk contains the actual sammples.
208  if (!have_fmt) {
209  movies_cat.error()
210  << ".wav file specifies 'data' chunk before 'fmt ' chunk.\n";
211  break;
212  }
213 
214  // Excellent! We've reached the beginning of the data. Write down
215  // where we are and don't move until we want to start reading the data.
216  _data_start = stream->tellg();
217  _data_size = subchunk_size;
218  have_data = true;
219  break;
220 
221  } else {
222  // A chunk we do not recognize. Just skip it.
223  _reader.skip_bytes(subchunk_size);
224  }
225 
226  bytes_read += subchunk_size + 8;
227  }
228 
229  // If we bailed out prematurely, there must have been an error.
230  if (_stream->eof()) {
231  movies_cat.error()
232  << "Reached end of file while reading .wav file header.\n";
233 
234  } else if (!_stream->good()) {
235  movies_cat.error()
236  << "Stream error while reading .wav file header.\n";
237  return;
238  }
239 
240  if (!have_fmt) {
241  movies_cat.error()
242  << ".wav file did not specify a 'fmt ' chunk.\n";
243  return;
244  }
245 
246  if (!have_data) {
247  movies_cat.error()
248  << ".wav file did not specify a 'data' chunk.\n";
249  return;
250  }
251 
252  // We can always seek by skipping bytes, rereading if necessary.
253  _can_seek = true;
254 
255  // How to tell if a stream is seekable? We'll set it to true, and then
256  // change it to false as soon as we find out that we can't.
257  _can_seek_fast = true;
258 
259  if (_block_align != _audio_channels * _bytes_per_sample) {
260  movies_cat.warning()
261  << ".wav file specified an unexpected block alignment of " << _block_align
262  << ", expected " << (_audio_channels * _bytes_per_sample) << ".\n";
263  _block_align = _audio_channels * _bytes_per_sample;
264  }
265 
266  if (_byte_rate != _audio_rate * _block_align) {
267  movies_cat.warning()
268  << ".wav file specified an unexpected byte rate of " << _byte_rate
269  << ", expected " << (_audio_rate * _block_align) << ".\n";
270  _byte_rate = _audio_rate * _block_align;
271  }
272 
273  _length = _data_size / _byte_rate;
274  _is_valid = true;
275 }
276 
277 /**
278  * xxx
279  */
282  if (_stream != nullptr) {
284  vfs->close_read_file(_stream);
285  }
286 }
287 
288 /**
289  * Seeks to a target location. Afterward, the packet_time is guaranteed to be
290  * less than or equal to the specified time.
291  */
292 void WavAudioCursor::
293 seek(double t) {
294  t = std::max(t, 0.0);
295  std::streampos pos = _data_start + (std::streampos) std::min((size_t) (t * _byte_rate), _data_size);
296 
297  if (_can_seek_fast) {
298  _stream->seekg(pos);
299  if (_stream->tellg() != pos) {
300  // Clearly, we can't seek fast. Fall back to the case below.
301  _can_seek_fast = false;
302  }
303  }
304 
305  if (!_can_seek_fast) {
306  std::streampos current = _stream->tellg();
307 
308  if (pos > current) {
309  // It is ahead of our current position. Skip ahead.
310  _reader.skip_bytes(pos - current);
311 
312  } else if (pos < current) {
313  // We'll have to reopen the file. TODO
314  }
315  }
316 
317  _data_pos = _stream->tellg() - _data_start;
318  _last_seek = _data_pos / _byte_rate;
319  _samples_read = 0;
320 }
321 
322 /**
323  * Read audio samples from the stream. N is the number of samples you wish to
324  * read. Your buffer must be equal in size to N * channels. Multiple-channel
325  * audio will be interleaved.
326  */
327 void WavAudioCursor::
328 read_samples(int n, int16_t *data) {
329  int desired = n * _audio_channels;
330  int read_samples = std::min(desired, ((int) (_data_size - _data_pos)) / _bytes_per_sample);
331 
332  if (read_samples <= 0) {
333  return;
334  }
335 
336  switch (_format) {
337  case F_pcm:
338  // Linear PCM storage.
339  switch (_bytes_per_sample) {
340  case 1:
341  // Upsample. Also, 8-bits samples are stored unsigned.
342  for (int i = 0; i < read_samples; ++i) {
343  data[i] = (_reader.get_uint8() - 128) * 258/*ish*/;
344  }
345  break;
346 
347  case 2:
348  // Our native format. Read directly from file.
349  read_samples = _reader.extract_bytes((unsigned char*) data, read_samples * 2) / 2;
350  break;
351 
352  case 3: {
353  // The scale factor happens to be 256 for 24-bit samples. That means we
354  // can just read the most significant bytes.
355  for (int i = 0; i < read_samples; ++i) {
356  _reader.skip_bytes(1);
357  data[i] = _reader.get_int16();
358  }
359  break;
360 
361  } case 4: {
362  // Downsample.
363  const int32_t scale_factor = 0x7fffffff / 0x7fff;
364 
365  for (int i = 0; i < read_samples; ++i) {
366  data[i] = _reader.get_int32() / scale_factor;
367  }
368  break;
369 
370  } case 8: {
371  // Downsample.
372  const int64_t scale_factor = 0x7fffffffffffffffLL / 0x7fffLL;
373 
374  for (int i = 0; i < read_samples; ++i) {
375  data[i] = _reader.get_int64() / scale_factor;
376  }
377  break;
378 
379  } default:
380  // Huh?
381  read_samples = 0;
382  }
383  break;
384 
385  case F_float:
386  // IEEE float storage.
387  switch (_bytes_per_sample) {
388  case 4:
389  for (int i = 0; i < read_samples; ++i) {
390  data[i] = (int16_t) (_reader.get_float32() * 0x7fff);
391  }
392  break;
393 
394  case 8:
395  for (int i = 0; i < read_samples; ++i) {
396  data[i] = (int16_t) (_reader.get_float64() * 0x7fff);
397  }
398  break;
399 
400  default:
401  read_samples = 0;
402  }
403  break;
404 
405  case F_alaw:
406  for (int i = 0; i < read_samples; ++i) {
407  data[i] = alaw_table[_reader.get_uint8()];
408  }
409  break;
410 
411  case F_mulaw:
412  for (int i = 0; i < read_samples; ++i) {
413  data[i] = mulaw_table[_reader.get_uint8()];
414  }
415  break;
416 
417  default:
418  read_samples = 0;
419  }
420 
421  // Fill the rest of the buffer with silence.
422  if (read_samples < desired) {
423  memset(data + read_samples, 0, (desired - read_samples) * 2);
424  }
425 
426  _data_pos = _stream->tellg() - _data_start;
427  _samples_read += read_samples / _audio_channels;
428 }
WavAudioCursor(WavAudio *src, std::istream *stream)
Reads the .wav header from the indicated stream.
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
Definition: streamReader.I:139
int64_t get_int64()
Extracts a signed 64-bit integer.
Definition: streamReader.I:127
int32_t get_int32()
Extracts a signed 32-bit integer.
Definition: streamReader.I:115
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
virtual void read_samples(int n, int16_t *data)
Read audio samples from the stream.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: streamReader.I:151
virtual void seek(double offset)
Seeks to a target location.
A MovieAudio is actually any source that provides a sequence of audio samples.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
Definition: streamReader.I:194
float get_float32()
Extracts a 32-bit single-precision floating-point number.
Definition: streamReader.I:177
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual ~WavAudioCursor()
xxx
size_t extract_bytes(unsigned char *into, size_t size)
Extracts the indicated number of bytes in the stream into the given character buffer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A native PCM .wav loader.
Definition: wavAudio.h:26
int16_t get_int16()
Extracts a signed 16-bit integer.
Definition: streamReader.I:103
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
Definition: streamReader.I:95
void skip_bytes(size_t size)
Skips over the indicated number of bytes in the stream.