From f5c3bd887e2257fc7fdfb0d141ddbae592f1e83b Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 5 Jan 2010 02:27:24 +0000 Subject: - Add a new SeekableAudioStream interface. Soon to be used to replace audio stream specific looping code by generic code in Mixer... - Adapted some existing AudioStreams to implement that interface (not tested!) svn-id: r47013 --- sound/audiostream.cpp | 82 ++++++++++++++++--- sound/audiostream.h | 43 ++++++++-- sound/flac.cpp | 14 +++- sound/flac.h | 4 +- sound/mp3.cpp | 220 +++++++++++++++++++++++++++----------------------- sound/mp3.h | 4 +- sound/voc.cpp | 4 +- sound/voc.h | 4 +- sound/vorbis.cpp | 23 +++++- sound/vorbis.h | 4 +- 10 files changed, 270 insertions(+), 132 deletions(-) (limited to 'sound') diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index e1847a4aac..9453eb24da 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -55,7 +55,7 @@ struct StreamFileFormat { * Pointer to a function which tries to open a file of type StreamFormat. * Return NULL in case of an error (invalid/nonexisting file). */ - AudioStream* (*openStreamFile)(Common::SeekableReadStream *stream, bool disposeAfterUse, + SeekableAudioStream *(*openStreamFile)(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops); }; @@ -75,8 +75,8 @@ static const StreamFileFormat STREAM_FILEFORMATS[] = { { NULL, NULL, NULL } // Terminator }; -AudioStream* AudioStream::openStreamFile(const Common::String &basename, uint32 startTime, uint32 duration, uint numLoops) { - AudioStream* stream = NULL; +SeekableAudioStream *AudioStream::openStreamFile(const Common::String &basename, uint32 startTime, uint32 duration, uint numLoops) { + SeekableAudioStream *stream = NULL; Common::File *fileHandle = new Common::File(); for (int i = 0; i < ARRAYSIZE(STREAM_FILEFORMATS)-1 && stream == NULL; ++i) { @@ -109,6 +109,17 @@ inline int32 calculatePlayTime(int rate, int samples) { return seconds * 1000 + milliseconds; } +uint32 calculateSampleOffset(const Timestamp &where, int rate) { + const uint32 msecs = where.msecs(); + + const Timestamp msecStamp(msecs, rate); + const uint32 seconds = msecs / 1000; + const uint32 millis = msecs % 1000; + const uint32 samples = msecStamp.frameDiff(where) + (millis * rate) / 1000; + + return seconds * rate + samples; +} + /** * A simple raw audio stream, purely memory based. It operates on a single * block of data, which is passed to it upon creation. @@ -120,7 +131,7 @@ inline int32 calculatePlayTime(int rate, int samples) { * case. This results in a total of 12 versions of the code being generated. */ template -class LinearMemoryStream : public AudioStream { +class LinearMemoryStream : public SeekableAudioStream { protected: const byte *_ptr; const byte *_end; @@ -163,6 +174,8 @@ public: return _playtime * _numLoops; } + bool seek(const Timestamp &where); + void setNumLoops(uint numLoops) { _numLoops = numLoops; _numPlayedLoops = 0; @@ -201,7 +214,20 @@ int LinearMemoryStream::readBuffer(int16 *buf return numSamples-samples; } - +template +bool LinearMemoryStream::seek(const Timestamp &where) { + const uint8 *ptr = _origPtr + calculateSampleOffset(where, getRate()) * (is16Bit ? 2 : 1) * (stereo ? 2 : 1); + if (ptr > _end) { + _ptr = _end; + return false; + } else if (ptr == _end) { + _ptr = _end; + return true; + } else { + _ptr = ptr; + return true; + } +} #pragma mark - #pragma mark --- LinearDiskStream --- @@ -215,7 +241,7 @@ int LinearMemoryStream::readBuffer(int16 *buf * start position and length of each block of uncompressed audio in the stream. */ template -class LinearDiskStream : public AudioStream { +class LinearDiskStream : public SeekableAudioStream { // Allow backends to override buffer size #ifdef CUSTOM_AUDIO_BUFFER_SIZE @@ -308,6 +334,8 @@ public: return kUnknownPlayTime; return _playtime * _numLoops; } + + bool seek(const Timestamp &where); }; template @@ -318,8 +346,7 @@ int LinearDiskStream::readBuffer(int16 *buffe int samples = numSamples; while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || (_currentBlock != _audioBlockCount - 1)) ) { - - // Output samples in the buffer to the output + // Output samples in the buffer to the output int len = MIN(samples, _bufferLeft); samples -= len; _bufferLeft -= len; @@ -371,15 +398,46 @@ int LinearDiskStream::readBuffer(int16 *buffe // In case calling code relies on the position of this stream staying // constant, I restore the location if I've changed it. This is probably - // not necessary. + // not necessary. if (restoreFilePosition) { _stream->seek(oldPos, SEEK_SET); } - return numSamples-samples; + return numSamples - samples; } +template +bool LinearDiskStream::seek(const Timestamp &where) { + const uint32 seekSample = calculateSampleOffset(where, getRate()) * (stereo ? 2 : 1); + uint32 curSample = 0; + + // Search for the disk block in which the specific sample is placed + _currentBlock = 0; + while (_currentBlock < _audioBlockCount) { + uint32 nextBlockSample = curSample + _audioBlock[_currentBlock].len; + + if (nextBlockSample > seekSample) + break; + + curSample = nextBlockSample; + ++_currentBlock; + } + _filePos = 0; + _diskLeft = 0; + _bufferLeft = 0; + + if (_currentBlock == _audioBlockCount) { + return ((seekSample - curSample) == (uint32)_audioBlock[_currentBlock - 1].len); + } else { + const uint32 offset = seekSample - curSample; + + _filePos = _audioBlock[_currentBlock].pos + offset * (is16Bit? 2: 1); + _diskLeft = _audioBlock[_currentBlock].len - offset; + + return true; + } +} #pragma mark - #pragma mark --- Input stream factory --- @@ -403,7 +461,7 @@ int LinearDiskStream::readBuffer(int16 *buffe } else \ return new LinearMemoryStream(rate, ptr, len, loopOffset, loopLen, autoFree) -AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd) { +SeekableAudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd) { const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; @@ -458,7 +516,7 @@ AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte f return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop) -AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { +SeekableAudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; diff --git a/sound/audiostream.h b/sound/audiostream.h index ac4f783f28..15e7ea6d33 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -30,8 +30,12 @@ #include "common/scummsys.h" #include "common/stream.h" +#include "sound/timestamp.h" + namespace Audio { +class SeekableAudioStream; + /** * Generic audio input stream. Subclasses of this are used to feed arbitrary * sampled audio data into ScummVM's audio mixer. @@ -93,10 +97,10 @@ public: * @return an Audiostream ready to use in case of success; * NULL in case of an error (e.g. invalid/nonexisting file) */ - static AudioStream* openStreamFile(const Common::String &basename, - uint32 startTime = 0, - uint32 duration = 0, - uint numLoops = 1); + static SeekableAudioStream *openStreamFile(const Common::String &basename, + uint32 startTime = 0, + uint32 duration = 0, + uint numLoops = 1); /** * Sets number of times the stream is supposed to get looped @@ -129,6 +133,32 @@ public: virtual int32 getTotalPlayTime() const { return kUnknownPlayTime; } }; +/** + * A seekable audio stream. Subclasses of this class implement a + * working seeking. The seeking itself is not required to be + * working when the stream is being played by Mixer! + */ +class SeekableAudioStream : public AudioStream { +public: + /** + * Seeks to a given offset in the stream. + * + * @param where offset in milliseconds + * @return true on success, false on failure. + */ + bool seek(uint32 where) { + return seek(Timestamp(where, getRate())); + } + + /** + * Seeks to a given offset in the stream. + * + * @param where offset as timestamp + * @return true on success, false on failure. + */ + virtual bool seek(const Timestamp &where) = 0; +}; + /** * Factory function for a raw linear AudioStream, which will simply treat all @@ -138,7 +168,7 @@ public: * signed native endian). Optionally supports (infinite) looping of a portion * of the data. */ -AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, +SeekableAudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd); @@ -157,8 +187,7 @@ struct LinearDiskStreamAudioBlock { * LinearDiskStreamAudioBlock which defines the start position and length of * each block of uncompressed audio in the stream. */ - -AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, +SeekableAudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool disposeStream, uint loopStart, uint loopEnd); /** diff --git a/sound/flac.cpp b/sound/flac.cpp index 8699e8cfce..ddfd9bf329 100644 --- a/sound/flac.cpp +++ b/sound/flac.cpp @@ -82,7 +82,7 @@ namespace Audio { static const uint MAX_OUTPUT_CHANNELS = 2; -class FlacInputStream : public AudioStream { +class FlacInputStream : public SeekableAudioStream { protected: Common::SeekableReadStream *_inStream; bool _disposeAfterUse; @@ -149,6 +149,8 @@ public: return _totalPlayTime * _numLoops; } + bool seek(const Timestamp &where); + bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ; } void setNumLoops(uint numLoops = 1) { @@ -334,6 +336,14 @@ bool FlacInputStream::seekAbsolute(FLAC__uint64 sample) { return result; } +bool FlacInputStream::seek(const Timestamp &where) { + _sampleCache.bufFill = 0; + _sampleCache.bufReadPos = NULL; + // Compute the start/end sample (we use floating point arithmetics here to + // avoid overflows). + return seekAbsolute((FLAC__uint64)(where.msecs() * (_streaminfo.sample_rate / 1000.0))); +} + int FlacInputStream::readBuffer(int16 *buffer, const int numSamples) { const uint numChannels = getChannels(); @@ -770,7 +780,7 @@ void FlacInputStream::callWrapError(const ::FLAC__SeekableStreamDecoder *decoder #pragma mark - -AudioStream *makeFlacStream( +SeekableAudioStream *makeFlacStream( Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, diff --git a/sound/flac.h b/sound/flac.h index 541b322d7a..1eab47d015 100644 --- a/sound/flac.h +++ b/sound/flac.h @@ -51,7 +51,7 @@ namespace Common { namespace Audio { -class AudioStream; +class SeekableAudioStream; /** * Create a new AudioStream from the FLAC data in the given stream. @@ -66,7 +66,7 @@ class AudioStream; * @param numLoops how often the data shall be looped (0 = infinite) * @return a new AudioStream, or NULL, if an error occured */ -AudioStream *makeFlacStream( +SeekableAudioStream *makeFlacStream( Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime = 0, diff --git a/sound/mp3.cpp b/sound/mp3.cpp index fede2a32cb..72d4652789 100644 --- a/sound/mp3.cpp +++ b/sound/mp3.cpp @@ -45,7 +45,7 @@ namespace Audio { #pragma mark - -class MP3InputStream : public AudioStream { +class MP3InputStream : public SeekableAudioStream { protected: enum State { MP3_STATE_INIT, // Need to init the decoder @@ -63,7 +63,8 @@ protected: const mad_timer_t _endTime; mad_timer_t _totalTime; - int32 _totalPlayTime; + int32 _totalPlayTime; // Length of one loop iteration + uint32 _length; // Total length of the MP3 stream uint _numLoops; ///< Number of loops to play uint _numPlayedLoops; ///< Number of loops which have been played @@ -98,6 +99,8 @@ public: return _totalPlayTime * _numLoops; } + bool seek(const Timestamp &where); + void setNumLoops(uint numLoops) { _numLoops = numLoops; _numPlayedLoops = 0; @@ -107,6 +110,10 @@ public: protected: void decodeMP3Data(); void readMP3Data(); + + void initStream(); + void readHeader(); + void deinitStream(); }; MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, bool dispose, mad_timer_t start, mad_timer_t end, uint numLoops) : @@ -127,6 +134,19 @@ MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, bool dispos // may read a few bytes beyond the end of the input buffer). memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD); + // Calculate the length of the stream + initStream(); + + while (_state != MP3_STATE_EOS) + readHeader(); + + _length = mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS); + + deinitStream(); + + // Reinit stream + _state = MP3_STATE_INIT; + // Calculate play time mad_timer_t length; @@ -137,54 +157,9 @@ MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, bool dispos if (mad_timer_sign(end) != 0) { mad_timer_add(&length, end); } else { - mad_stream_init(&_stream); - mad_frame_init(&_frame); - - // Reset the stream data - _inStream->seek(0, SEEK_SET); - - // Update state - _state = MP3_STATE_READY; - - // Read the first few sample bytes - readMP3Data(); - - do { - // If necessary, load more data into the stream decoder - if (_stream.error == MAD_ERROR_BUFLEN) - readMP3Data(); - - while (_state == MP3_STATE_READY) { - _stream.error = MAD_ERROR_NONE; - - // Decode the next header. Note: mad_frame_decode would do this for us, too. - // However, for seeking we don't want to decode the full frame (else it would - // be far too slow). - if (mad_header_decode(&_frame.header, &_stream) == -1) { - if (_stream.error == MAD_ERROR_BUFLEN) { - break; // Read more data - } else if (MAD_RECOVERABLE(_stream.error)) { - debug(6, "MP3InputStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - continue; - } else { - warning("MP3InputStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - break; - } - } - - // Sum up the total playback time so far - mad_timer_add(&length, _frame.header.duration); - } - } while (_state != MP3_STATE_EOS); - - mad_synth_finish(&_synth); - mad_frame_finish(&_frame); - - // Reinit stream - _state = MP3_STATE_INIT; - - // Reset the stream data - _inStream->seek(0, SEEK_SET); + mad_timer_set(&_totalTime, _length / 1000, _length % 1000, 1000); + mad_timer_add(&length, _totalTime); + _totalTime = mad_timer_zero; } _totalPlayTime = mad_timer_count(length, MAD_UNITS_MILLISECONDS); @@ -198,37 +173,16 @@ MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, bool dispos } MP3InputStream::~MP3InputStream() { - if (_state != MP3_STATE_INIT) { - // Deinit MAD - mad_synth_finish(&_synth); - mad_frame_finish(&_frame); - mad_stream_finish(&_stream); - } + deinitStream(); if (_disposeAfterUse) delete _inStream; } void MP3InputStream::decodeMP3Data() { - do { - if (_state == MP3_STATE_INIT) { - // Init MAD - mad_stream_init(&_stream); - mad_frame_init(&_frame); - mad_synth_init(&_synth); - - // Reset the stream data - _inStream->seek(0, SEEK_SET); - _totalTime = mad_timer_zero; - _posInFrame = 0; - - // Update state - _state = MP3_STATE_READY; - - // Read the first few sample bytes - readMP3Data(); - } + if (_state == MP3_STATE_INIT) + initStream(); if (_state == MP3_STATE_EOS) return; @@ -238,25 +192,7 @@ void MP3InputStream::decodeMP3Data() { readMP3Data(); while (_state == MP3_STATE_READY) { - _stream.error = MAD_ERROR_NONE; - - // Decode the next header. Note: mad_frame_decode would do this for us, too. - // However, for seeking we don't want to decode the full frame (else it would - // be far too slow). Hence we perform this explicitly in a separate step. - if (mad_header_decode(&_frame.header, &_stream) == -1) { - if (_stream.error == MAD_ERROR_BUFLEN) { - break; // Read more data - } else if (MAD_RECOVERABLE(_stream.error)) { - debug(6, "MP3InputStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - continue; - } else { - warning("MP3InputStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - break; - } - } - - // Sum up the total playback time so far - mad_timer_add(&_totalTime, _frame.header.duration); + readHeader(); // If we have not yet reached the start point, skip to the next frame if (mad_timer_compare(_totalTime, _startTime) < 0) @@ -294,10 +230,7 @@ void MP3InputStream::decodeMP3Data() { ++_numPlayedLoops; // If looping is on and there are loops left, rewind to the start if (!_numLoops || _numPlayedLoops < _numLoops) { - // Deinit MAD - mad_synth_finish(&_synth); - mad_frame_finish(&_frame); - mad_stream_finish(&_stream); + deinitStream(); // Reset the decoder state to indicate we should start over _state = MP3_STATE_INIT; @@ -340,6 +273,96 @@ void MP3InputStream::readMP3Data() { mad_stream_buffer(&_stream, _buf, size + remaining); } +bool MP3InputStream::seek(const Timestamp &where) { + const uint32 time = where.msecs(); + + if (time == _length) { + _state = MP3_STATE_EOS; + return true; + } else if (time > _length) { + return false; + } + + mad_timer_t destination; + mad_timer_set(&destination, time / 1000, time % 1000, 1000); + + if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) + initStream(); + + while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS) + readHeader(); + + return (_state != MP3_STATE_EOS); +} + +void MP3InputStream::initStream() { + if (_state != MP3_STATE_INIT) + deinitStream(); + + // Init MAD + mad_stream_init(&_stream); + mad_frame_init(&_frame); + mad_synth_init(&_synth); + + // Reset the stream data + _inStream->seek(0, SEEK_SET); + _totalTime = mad_timer_zero; + _posInFrame = 0; + + // Update state + _state = MP3_STATE_READY; + + // Read the first few sample bytes + readMP3Data(); +} + +void MP3InputStream::readHeader() { + if (_state != MP3_STATE_READY) + return; + + // If necessary, load more data into the stream decoder + if (_stream.error == MAD_ERROR_BUFLEN) + readMP3Data(); + + while (_state != MP3_STATE_EOS) { + _stream.error = MAD_ERROR_NONE; + + // Decode the next header. Note: mad_frame_decode would do this for us, too. + // However, for seeking we don't want to decode the full frame (else it would + // be far too slow). Hence we perform this explicitly in a separate step. + if (mad_header_decode(&_frame.header, &_stream) == -1) { + if (_stream.error == MAD_ERROR_BUFLEN) { + readMP3Data(); // Read more data + continue; + } else if (MAD_RECOVERABLE(_stream.error)) { + debug(6, "MP3InputStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); + continue; + } else { + warning("MP3InputStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); + break; + } + } + + // Sum up the total playback time so far + mad_timer_add(&_totalTime, _frame.header.duration); + break; + } + + if (_stream.error != MAD_ERROR_NONE) + _state = MP3_STATE_EOS; +} + +void MP3InputStream::deinitStream() { + if (_state == MP3_STATE_INIT) + return; + + // Deinit MAD + mad_synth_finish(&_synth); + mad_frame_finish(&_frame); + mad_stream_finish(&_stream); + + _state = MP3_STATE_EOS; +} static inline int scale_sample(mad_fixed_t sample) { // round @@ -382,8 +405,7 @@ int MP3InputStream::readBuffer(int16 *buffer, const int numSamples) { #pragma mark --- MP3 factory functions --- #pragma mark - - -AudioStream *makeMP3Stream( +SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, diff --git a/sound/mp3.h b/sound/mp3.h index 2ea07ab0a8..544f4861ba 100644 --- a/sound/mp3.h +++ b/sound/mp3.h @@ -51,7 +51,7 @@ namespace Common { namespace Audio { -class AudioStream; +class SeekableAudioStream; /** * Create a new AudioStream from the MP3 data in the given stream. @@ -66,7 +66,7 @@ class AudioStream; * @param numLoops how often the data shall be looped (0 = infinite) * @return a new AudioStream, or NULL, if an error occured */ -AudioStream *makeMP3Stream( +SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime = 0, diff --git a/sound/voc.cpp b/sound/voc.cpp index 5db32693eb..05a3618d1b 100644 --- a/sound/voc.cpp +++ b/sound/voc.cpp @@ -292,7 +292,7 @@ int parseVOCFormat(Common::SeekableReadStream& stream, LinearDiskStreamAudioBloc return currentBlock; } -AudioStream *makeVOCDiskStream(Common::SeekableReadStream &stream, byte flags, bool takeOwnership) { +SeekableAudioStream *makeVOCDiskStream(Common::SeekableReadStream &stream, byte flags, bool takeOwnership) { const int MAX_AUDIO_BLOCKS = 256; LinearDiskStreamAudioBlock *block = new LinearDiskStreamAudioBlock[MAX_AUDIO_BLOCKS]; @@ -315,7 +315,7 @@ AudioStream *makeVOCDiskStream(Common::SeekableReadStream &stream, byte flags, b #endif -AudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags, uint loopStart, uint loopEnd, bool takeOwnershipOfStream) { +SeekableAudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags, uint loopStart, uint loopEnd, bool takeOwnershipOfStream) { #ifdef STREAM_AUDIO_FROM_DISK return makeVOCDiskStream(stream, flags, takeOwnershipOfStream); #else diff --git a/sound/voc.h b/sound/voc.h index eae09f2a4c..44bef758f3 100644 --- a/sound/voc.h +++ b/sound/voc.h @@ -45,7 +45,7 @@ namespace Common { class SeekableReadStream; } namespace Audio { -class AudioStream; +class SeekableAudioStream; #include "common/pack-start.h" // START STRUCT PACKING @@ -93,7 +93,7 @@ extern byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate) * * This function uses loadVOCFromStream() internally. */ -AudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags = 0, uint loopStart = 0, uint loopEnd = 0, bool takeOwnershipOfStream = false); +SeekableAudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags = 0, uint loopStart = 0, uint loopEnd = 0, bool takeOwnershipOfStream = false); } // End of namespace Audio diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp index bb73a26d06..eced67726a 100644 --- a/sound/vorbis.cpp +++ b/sound/vorbis.cpp @@ -85,7 +85,7 @@ static ov_callbacks g_stream_wrap = { #pragma mark - -class VorbisInputStream : public AudioStream { +class VorbisInputStream : public SeekableAudioStream { protected: Common::SeekableReadStream *_inStream; bool _disposeAfterUse; @@ -138,6 +138,8 @@ public: #endif } + bool seek(const Timestamp &where); + protected: bool refill(); }; @@ -240,6 +242,23 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { return samples; } +bool VorbisInputStream::seek(const Timestamp &where) { +#ifdef USE_TREMOR + ogg_int64_t pos = where.msecs(); +#else + double pos = where.msecs() / 1000.0; +#endif + + int res = ov_time_seek(&_ovFile, pos); + if (res < 0) { + warning("Error seeking in Vorbis stream (%d)", res); + _pos = _bufferEnd; + return false; + } + + return refill(); +} + bool VorbisInputStream::refill() { // Read the samples int res; @@ -314,7 +333,7 @@ bool VorbisInputStream::refill() { #pragma mark - -AudioStream *makeVorbisStream( +SeekableAudioStream *makeVorbisStream( Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, diff --git a/sound/vorbis.h b/sound/vorbis.h index 0db4308b0d..369cd14d01 100644 --- a/sound/vorbis.h +++ b/sound/vorbis.h @@ -51,7 +51,7 @@ namespace Common { namespace Audio { -class AudioStream; +class SeekableAudioStream; /** * Create a new AudioStream from the Ogg Vorbis data in the given stream. @@ -66,7 +66,7 @@ class AudioStream; * @param numLoops how often the data shall be looped (0 = infinite) * @return a new AudioStream, or NULL, if an error occured */ -AudioStream *makeVorbisStream( +SeekableAudioStream *makeVorbisStream( Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime = 0, -- cgit v1.2.3