Panda3D
vorbisAudioCursor.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 vorbisAudioCursor.cxx
10  * @author rdb
11  * @date 2013-08-23
12  */
13 
14 #include "vorbisAudioCursor.h"
15 #include "virtualFileSystem.h"
16 
17 #ifdef HAVE_VORBIS
18 
19 using std::istream;
20 
21 TypeHandle VorbisAudioCursor::_type_handle;
22 
23 /**
24  * Reads the .wav header from the indicated stream. This leaves the read
25  * pointer positioned at the start of the data.
26  */
27 VorbisAudioCursor::
28 VorbisAudioCursor(VorbisAudio *src, istream *stream) :
29  MovieAudioCursor(src),
30  _is_valid(false),
31  _bitstream(0)
32 {
33  nassertv(stream != nullptr);
34  nassertv(stream->good());
35 
36  // Set up the callbacks to read via the VFS.
37  ov_callbacks callbacks;
38  callbacks.read_func = &cb_read_func;
39  callbacks.close_func = &cb_close_func;
40  callbacks.tell_func = &cb_tell_func;
41 
42  if (vorbis_enable_seek) {
43  callbacks.seek_func = &cb_seek_func;
44  } else {
45  callbacks.seek_func = nullptr;
46  }
47 
48  if (ov_open_callbacks((void*) stream, &_ov, nullptr, 0, callbacks) != 0) {
49  movies_cat.error()
50  << "Failed to read Ogg Vorbis file.\n";
51  return;
52  }
53 
54  double time_total = ov_time_total(&_ov, -1);
55  if (time_total != OV_EINVAL) {
56  _length = time_total;
57  }
58 
59  vorbis_info *vi = ov_info(&_ov, -1);
60  _audio_channels = vi->channels;
61  _audio_rate = vi->rate;
62 
63  _can_seek = vorbis_enable_seek && (ov_seekable(&_ov) != 0);
64  _can_seek_fast = _can_seek;
65 
66  _is_valid = true;
67 }
68 
69 /**
70  * xxx
71  */
72 VorbisAudioCursor::
73 ~VorbisAudioCursor() {
74  ov_clear(&_ov);
75 }
76 
77 /**
78  * Seeks to a target location. Afterward, the packet_time is guaranteed to be
79  * less than or equal to the specified time.
80  */
81 void VorbisAudioCursor::
82 seek(double t) {
83  if (!vorbis_enable_seek) {
84  return;
85  }
86 
87  t = std::max(t, 0.0);
88 
89  // Use ov_time_seek_lap if cross-lapping is enabled.
90  if (vorbis_seek_lap) {
91  if (ov_time_seek_lap(&_ov, t) != 0) {
92  movies_cat.error()
93  << "Seek failed. Ogg Vorbis stream may not be seekable.\n";
94  return;
95  }
96  } else {
97  if (ov_time_seek(&_ov, t) != 0) {
98  movies_cat.error()
99  << "Seek failed. Ogg Vorbis stream may not be seekable.\n";
100  return;
101  }
102  }
103 
104  _last_seek = ov_time_tell(&_ov);
105  _samples_read = 0;
106 }
107 
108 /**
109  * Read audio samples from the stream. N is the number of samples you wish to
110  * read. Your buffer must be equal in size to N * channels. Multiple-channel
111  * audio will be interleaved.
112  */
113 void VorbisAudioCursor::
114 read_samples(int n, int16_t *data) {
115  int desired = n * _audio_channels;
116 
117  char *buffer = (char*) data;
118  int length = desired * 2;
119 
120  // Call ov_read repeatedly until the buffer is full.
121  while (length > 0) {
122  int bitstream;
123 
124  // ov_read can give it to us in the exact format we need. Nifty!
125  long read_bytes = ov_read(&_ov, buffer, length, 0, 2, 1, &bitstream);
126  if (read_bytes > 0) {
127  buffer += read_bytes;
128  length -= read_bytes;
129  } else {
130  break;
131  }
132 
133  if (_bitstream != bitstream) {
134  // It is technically possible for it to change parameters from one
135  // bitstream to the next. However, we don't offer this flexibility.
136  vorbis_info *vi = ov_info(&_ov, -1);
137  if (vi->channels != _audio_channels || vi->rate != _audio_rate) {
138  movies_cat.error()
139  << "Ogg Vorbis file has non-matching bitstreams!\n";
140 
141  // We'll change it anyway. Not sure what happens next.
142  _audio_channels = vi->channels;
143  _audio_rate = vi->rate;
144  break;
145  }
146 
147  _bitstream = bitstream;
148  }
149  }
150 
151  // Fill the rest of the buffer with silence.
152  if (length > 0) {
153  memset(buffer, 0, length);
154  n -= length / 2 / _audio_channels;
155  }
156 
157  _samples_read += n;
158 }
159 
160 /**
161  * Callback passed to libvorbisfile to implement file I/O via the
162  * VirtualFileSystem.
163  */
164 size_t VorbisAudioCursor::
165 cb_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) {
166  istream *stream = (istream*) datasource;
167  nassertr(stream != nullptr, -1);
168 
169  stream->read((char *)ptr, size * nmemb);
170 
171  if (stream->eof()) {
172  // Gracefully handle EOF.
173  stream->clear();
174  }
175 
176  return stream->gcount();
177 }
178 
179 /**
180  * Callback passed to libvorbisfile to implement file I/O via the
181  * VirtualFileSystem.
182  */
183 int VorbisAudioCursor::
184 cb_seek_func(void *datasource, ogg_int64_t offset, int whence) {
185  if (!vorbis_enable_seek) {
186  return -1;
187  }
188 
189  istream *stream = (istream*) datasource;
190  nassertr(stream != nullptr, -1);
191 
192  switch (whence) {
193  case SEEK_SET:
194  stream->seekg(offset, std::ios::beg);
195  break;
196 
197  case SEEK_CUR:
198  stream->seekg(offset, std::ios::cur);
199  break;
200 
201  case SEEK_END:
202  stream->seekg(offset, std::ios::end);
203  break;
204 
205  default:
206  movies_cat.error()
207  << "Illegal parameter to seek in VorbisAudioCursor::cb_seek_func\n";
208  return -1;
209  }
210 
211  if (stream->fail()) {
212  // This is a fatal error and usually leads to a libvorbis crash.
213  movies_cat.error()
214  << "Failure to seek to byte " << offset;
215 
216  switch (whence) {
217  case SEEK_CUR:
218  movies_cat.error(false)
219  << " from current location!\n";
220  break;
221 
222  case SEEK_END:
223  movies_cat.error(false)
224  << " from end of file!\n";
225  break;
226 
227  default:
228  movies_cat.error(false) << "!\n";
229  }
230 
231  return -1;
232  }
233 
234  return 0;
235 }
236 
237 /**
238  * Callback passed to libvorbisfile to implement file I/O via the
239  * VirtualFileSystem.
240  */
241 int VorbisAudioCursor::
242 cb_close_func(void *datasource) {
243  istream *stream = (istream*) datasource;
244  nassertr(stream != nullptr, -1);
245 
247  vfs->close_read_file(stream);
248 
249  // Return value isn't checked, but let's be predictable
250  return 0;
251 }
252 
253 /**
254  * Callback passed to libvorbisfile to implement file I/O via the
255  * VirtualFileSystem.
256  */
257 long VorbisAudioCursor::
258 cb_tell_func(void *datasource) {
259  istream *stream = (istream*) datasource;
260  nassertr(stream != nullptr, -1);
261 
262  return stream->tellg();
263 }
264 
265 #endif // HAVE_VORBIS
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.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
A MovieAudio is actually any source that provides a sequence of audio samples.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81