1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "sound/vorbis.h"
30 #include "common/debug.h"
31 #include "common/stream.h"
32 #include "common/util.h"
34 #include "sound/audiostream.h"
35 #include "sound/audiocd.h"
38 #ifdef __GP32__ // GP32 uses custom libtremor
39 #include <ivorbisfile.h>
41 #include <tremor/ivorbisfile.h>
44 #include <vorbis/vorbisfile.h>
50 // These are wrapper functions to allow using a SeekableReadStream object to
51 // provide data to the OggVorbis_File object.
53 static size_t read_stream_wrap(void *ptr
, size_t size
, size_t nmemb
, void *datasource
) {
54 Common::SeekableReadStream
*stream
= (Common::SeekableReadStream
*)datasource
;
56 uint32 result
= stream
->read(ptr
, size
* nmemb
);
61 static int seek_stream_wrap(void *datasource
, ogg_int64_t offset
, int whence
) {
62 Common::SeekableReadStream
*stream
= (Common::SeekableReadStream
*)datasource
;
63 stream
->seek((int32
)offset
, whence
);
67 static int close_stream_wrap(void *datasource
) {
68 // Do nothing -- we leave it up to the VorbisInputStream to free memory as appropriate.
72 static long tell_stream_wrap(void *datasource
) {
73 Common::SeekableReadStream
*stream
= (Common::SeekableReadStream
*)datasource
;
77 static ov_callbacks g_stream_wrap
= {
78 read_stream_wrap
, seek_stream_wrap
, close_stream_wrap
, tell_stream_wrap
84 #pragma mark --- Ogg Vorbis stream ---
88 class VorbisInputStream
: public AudioStream
{
90 Common::SeekableReadStream
*_inStream
;
91 bool _disposeAfterUse
;
96 const uint _totalNumLoops
;
99 ogg_int64_t _startTime
;
100 ogg_int64_t _endTime
;
106 OggVorbis_File _ovFile
;
109 const int16
*_bufferEnd
;
113 // startTime / duration are in milliseconds
114 VorbisInputStream(Common::SeekableReadStream
*inStream
, bool dispose
, uint startTime
= 0, uint endTime
= 0, uint numLoops
= 1);
115 ~VorbisInputStream();
117 int readBuffer(int16
*buffer
, const int numSamples
);
119 bool endOfData() const { return _pos
>= _bufferEnd
; }
120 bool isStereo() const { return _isStereo
; }
121 int getRate() const { return _rate
; }
123 int32
getTotalPlayTime() const {
125 return AudioStream::kUnknownPlayTime
;
128 return (_endTime
- _startTime
) * _totalNumLoops
;
130 return (int32
)((_endTime
- _startTime
) * 1000.0) * _totalNumLoops
;
138 VorbisInputStream::VorbisInputStream(Common::SeekableReadStream
*inStream
, bool dispose
, uint startTime
, uint endTime
, uint numLoops
) :
140 _disposeAfterUse(dispose
),
142 _totalNumLoops(numLoops
),
143 _bufferEnd(_buffer
+ ARRAYSIZE(_buffer
)) {
145 bool err
= (ov_open_callbacks(inStream
, &_ovFile
, NULL
, 0, g_stream_wrap
) < 0);
146 // FIXME: proper error handling!
150 /* TODO: Symbian may have to use scumm_fixdfdi here? To quote:
151 "SumthinWicked says: fixing "relocation truncated to fit: ARM_26 __fixdfdi" during linking on GCC, see portdefs.h"
154 // In Tremor, the ov_time_seek() and ov_time_seek_page() calls take seeking
155 // positions in milliseconds as 64 bit integers, rather than in seconds as
156 // doubles as in Vorbisfile.
157 ogg_int64_t totalTime
;
158 _startTime
= startTime
;
162 _startTime
= startTime
/ 1000.0;
163 _endTime
= endTime
/ 1000.0;
166 // If endTime was 0, or is past the end of the file, set it to the maximal time possible
167 totalTime
= ov_time_total(&_ovFile
, -1);
168 if (_endTime
== 0 || _endTime
> totalTime
)
169 _endTime
= totalTime
;
171 // If the specified time range is empty, abort early.
172 if (_startTime
>= _endTime
) {
177 // Seek to the start position
178 ov_time_seek(&_ovFile
, _startTime
);
180 // Read in initial data
183 // Setup some header information
184 _isStereo
= ov_info(&_ovFile
, -1)->channels
>= 2;
185 _rate
= ov_info(&_ovFile
, -1)->rate
;
188 VorbisInputStream::~VorbisInputStream() {
190 if (_disposeAfterUse
)
194 int VorbisInputStream::readBuffer(int16
*buffer
, const int numSamples
) {
196 while (samples
< numSamples
&& _pos
< _bufferEnd
) {
197 const int len
= MIN(numSamples
- samples
, (int)(_bufferEnd
- _pos
));
198 memcpy(buffer
, _pos
, len
* 2);
202 if (_pos
>= _bufferEnd
) {
204 // If we are still out of data, and also past the end of specified
205 // time range, check whether looping is enabled...
206 if (_pos
>= _bufferEnd
&& ov_time_tell(&_ovFile
) >= _endTime
) {
207 if (_numLoops
!= 1) {
208 // If looping is on and there are loops left, rewind to the start
211 ov_time_seek(&_ovFile
, _startTime
);
220 void VorbisInputStream::refill() {
222 uint len_left
= sizeof(_buffer
);
223 char *read_pos
= (char *)_buffer
;
225 while (len_left
> 0) {
226 if (ov_time_tell(&_ovFile
) >= _endTime
) {
227 // If looping is on and there are loops left, rewind to the start
229 break; // Last loop, abort
232 ov_time_seek(&_ovFile
, _startTime
);
237 // Tremor ov_read() always returns data as signed 16 bit interleaved PCM
238 // in host byte order. As such, it does not take arguments to request
239 // specific signedness, byte order or bit depth as in Vorbisfile.
240 result
= ov_read(&_ovFile
, read_pos
, len_left
,
243 #ifdef SCUMM_BIG_ENDIAN
244 result
= ov_read(&_ovFile
, read_pos
, len_left
,
250 result
= ov_read(&_ovFile
, read_pos
, len_left
,
257 if (result
== OV_HOLE
) {
258 // Possibly recoverable, just warn about it
259 warning("Corrupted data in Vorbis file");
260 } else if (result
<= 0) {
262 debug(1, "Decode error %ld in Vorbis file", result
);
263 // Don't delete it yet, that causes problems in
264 // the CD player emulation code.
265 memset(read_pos
, 0, len_left
);
274 _bufferEnd
= (int16
*)read_pos
;
279 #pragma mark --- Ogg Vorbis factory functions ---
283 AudioStream
*makeVorbisStream(
284 Common::SeekableReadStream
*stream
,
285 bool disposeAfterUse
,
290 uint32 endTime
= duration
? (startTime
+ duration
) : 0;
292 return new VorbisInputStream(stream
, disposeAfterUse
, startTime
, endTime
, numLoops
);
296 } // End of namespace Audio
298 #endif // #ifdef USE_VORBIS