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