Panda3D
opusAudioCursor.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 opusAudioCursor.cxx
10  * @author rdb
11  * @date 2017-05-24
12  */
13 
14 #include "opusAudioCursor.h"
15 
16 #include "config_movies.h"
17 
18 #include "opusAudio.h"
19 #include "virtualFileSystem.h"
20 
21 #ifdef HAVE_OPUS
22 
23 #include <opus/opusfile.h>
24 
25 using std::istream;
26 
27 /**
28  * Callbacks passed to libopusfile to implement file I/O via the
29  * VirtualFileSystem.
30  */
31 int cb_read(void *stream, unsigned char *ptr, int nbytes) {
32  istream *in = (istream *)stream;
33  nassertr(in != nullptr, -1);
34 
35  in->read((char *)ptr, nbytes);
36 
37  if (in->eof()) {
38  // Gracefully handle EOF.
39  in->clear();
40  }
41 
42  return in->gcount();
43 }
44 
45 int cb_seek(void *stream, opus_int64 offset, int whence) {
46  if (!opus_enable_seek) {
47  return -1;
48  }
49 
50  istream *in = (istream *)stream;
51  nassertr(in != nullptr, -1);
52 
53  switch (whence) {
54  case SEEK_SET:
55  in->seekg(offset, std::ios::beg);
56  break;
57 
58  case SEEK_CUR:
59  in->seekg(offset, std::ios::cur);
60  break;
61 
62  case SEEK_END:
63  in->seekg(offset, std::ios::end);
64  break;
65 
66  default:
67  movies_cat.error()
68  << "Illegal parameter to seek in cb_seek\n";
69  return -1;
70  }
71 
72  if (in->fail()) {
73  movies_cat.error()
74  << "Failure to seek to byte " << offset;
75 
76  switch (whence) {
77  case SEEK_CUR:
78  movies_cat.error(false)
79  << " from current location!\n";
80  break;
81 
82  case SEEK_END:
83  movies_cat.error(false)
84  << " from end of file!\n";
85  break;
86 
87  default:
88  movies_cat.error(false) << "!\n";
89  }
90 
91  return -1;
92  }
93 
94  return 0;
95 }
96 
97 opus_int64 cb_tell(void *stream) {
98  istream *in = (istream *)stream;
99  nassertr(in != nullptr, -1);
100 
101  return in->tellg();
102 }
103 
104 int cb_close(void *stream) {
105  istream *in = (istream *)stream;
106  nassertr(in != nullptr, EOF);
107 
109  vfs->close_read_file(in);
110  return 0;
111 }
112 
113 static const OpusFileCallbacks callbacks = {cb_read, cb_seek, cb_tell, cb_close};
114 
115 TypeHandle OpusAudioCursor::_type_handle;
116 
117 /**
118  * Reads the .wav header from the indicated stream. This leaves the read
119  * pointer positioned at the start of the data.
120  */
121 OpusAudioCursor::
122 OpusAudioCursor(OpusAudio *src, istream *stream) :
123  MovieAudioCursor(src),
124  _is_valid(false),
125  _link(0)
126 {
127  nassertv(stream != nullptr);
128  nassertv(stream->good());
129 
130  int error = 0;
131  _op = op_open_callbacks((void *)stream, &callbacks, nullptr, 0, &error);
132  if (_op == nullptr) {
133  movies_cat.error()
134  << "Failed to read Opus file (error code " << error << ").\n";
135  return;
136  }
137 
138  ogg_int64_t samples = op_pcm_total(_op, -1);
139  if (samples != OP_EINVAL) {
140  // Opus timestamps are fixed at 48 kHz.
141  _length = (double)samples / 48000.0;
142  }
143 
144  _audio_channels = op_channel_count(_op, -1);
145  _audio_rate = 48000;
146 
147  _can_seek = opus_enable_seek && op_seekable(_op);
148  _can_seek_fast = _can_seek;
149 
150  _is_valid = true;
151 }
152 
153 /**
154  * xxx
155  */
156 OpusAudioCursor::
157 ~OpusAudioCursor() {
158  if (_op != nullptr) {
159  op_free(_op);
160  _op = nullptr;
161  }
162 }
163 
164 /**
165  * Seeks to a target location. Afterward, the packet_time is guaranteed to be
166  * less than or equal to the specified time.
167  */
168 void OpusAudioCursor::
169 seek(double t) {
170  if (!opus_enable_seek) {
171  return;
172  }
173 
174  t = std::max(t, 0.0);
175 
176  // Use op_time_seek_lap if cross-lapping is enabled.
177  int error = op_pcm_seek(_op, (ogg_int64_t)(t * 48000.0));
178  if (error != 0) {
179  movies_cat.error()
180  << "Seek failed (error " << error << "). Opus stream may not be seekable.\n";
181  return;
182  }
183 
184  _last_seek = op_pcm_tell(_op) / 48000.0;
185  _samples_read = 0;
186 }
187 
188 /**
189  * Read audio samples from the stream. N is the number of samples you wish to
190  * read. Your buffer must be equal in size to N * channels. Multiple-channel
191  * audio will be interleaved.
192  */
193 void OpusAudioCursor::
194 read_samples(int n, int16_t *data) {
195  int16_t *end = data + (n * _audio_channels);
196 
197  while (data < end) {
198  // op_read gives it to us in the exact format we need. Nifty!
199  int link;
200  int read_samples = op_read(_op, data, end - data, &link);
201  if (read_samples > 0) {
202  data += read_samples * _audio_channels;
203  _samples_read += read_samples;
204  } else {
205  break;
206  }
207 
208  if (_link != link) {
209  // It is technically possible for it to change parameters from one link
210  // to the next. However, we don't offer this flexibility.
211  int channels = op_channel_count(_op, link);
212  if (channels != _audio_channels) {
213  movies_cat.error()
214  << "Opus file has inconsistent channel count!\n";
215 
216  // We'll change it anyway. Not sure what happens next.
217  _audio_channels = channels;
218  }
219 
220  _link = link;
221  }
222  }
223 
224  // Fill the rest of the buffer with silence.
225  if (data < end) {
226  memset(data, 0, (unsigned char *)end - (unsigned char *)data);
227  }
228 }
229 
230 #endif // HAVE_OPUS
A hierarchy of directories and files that appears to be one continuous file system,...
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().
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