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