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