From f16ab376989031806ba5823c4b34a16742c1a628 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 22 Feb 2007 14:30:12 +0000 Subject: [PATCH] Added looping support and an enhanced factory function to the Vorbis code svn-id: r25789 --- sound/mp3.cpp | 9 ++++--- sound/vorbis.cpp | 66 +++++++++++++++++++++++++++++++++++------------- sound/vorbis.h | 24 +++++++++++++++++- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/sound/mp3.cpp b/sound/mp3.cpp index 4071d1e5db4..3233aa504fe 100644 --- a/sound/mp3.cpp +++ b/sound/mp3.cpp @@ -208,10 +208,11 @@ void MP3InputStream::decodeMP3Data() { break; } - if (_eos) { - // If looping is enabled, rewind to the start - if (_numLoops == 0 || --_numLoops > 0) - rewind(); + if (_eos && _numLoops != 1) { + // If looping is on and there are loops left, rewind to the start + if (_numLoops != 0) + _numLoops--; + rewind(); } } while (_stream.error == MAD_ERROR_BUFLEN); diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp index 485176a44e3..71255c78297 100644 --- a/sound/vorbis.cpp +++ b/sound/vorbis.cpp @@ -86,17 +86,12 @@ static ov_callbacks g_stream_wrap = { class VorbisInputStream : public AudioStream { protected: - OggVorbis_File _ovFile; - Common::SeekableReadStream *_inStream; bool _disposeAfterUse; - int16 _buffer[4096]; - const int16 *_bufferEnd; - const int16 *_pos; - bool _isStereo; int _rate; + uint _numLoops; #ifdef USE_TREMOR ogg_int64_t _startTime; @@ -106,25 +101,31 @@ protected: double _endTime; #endif - void refill(); - inline bool eosIntern() const; + OggVorbis_File _ovFile; + + int16 _buffer[4096]; + const int16 *_bufferEnd; + const int16 *_pos; + public: // startTime / duration are in milliseconds - VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime = 0, uint endTime = 0); + VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime = 0, uint endTime = 0, uint numLoops = 1); ~VorbisInputStream(); int readBuffer(int16 *buffer, const int numSamples); - bool endOfData() const { return eosIntern(); } + bool endOfData() const { return _pos >= _bufferEnd; } bool isStereo() const { return _isStereo; } - int getRate() const { return _rate; } +protected: + void refill(); }; -VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime, uint endTime) : +VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime, uint endTime, uint numLoops) : _inStream(inStream), _disposeAfterUse(dispose), + _numLoops(numLoops), _bufferEnd(_buffer + ARRAYSIZE(_buffer)) { bool err = (ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap) < 0); @@ -176,13 +177,9 @@ VorbisInputStream::~VorbisInputStream() { delete _inStream; } -inline bool VorbisInputStream::eosIntern() const { - return _pos >= _bufferEnd; -} - int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { int samples = 0; - while (samples < numSamples && !eosIntern()) { + while (samples < numSamples && _pos < _bufferEnd) { const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos)); memcpy(buffer, _pos, len * 2); buffer += len; @@ -190,6 +187,17 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { samples += len; if (_pos >= _bufferEnd) { refill(); + // If we are still out of data, and also past the end of specified + // time range, check whether looping is enabled... + if (_pos >= _bufferEnd && ov_time_tell(&_ovFile) >= _endTime) { + if (_numLoops != 1) { + // If looping is on and there are loops left, rewind to the start + if (_numLoops != 0) + _numLoops--; + ov_time_seek(&_ovFile, _startTime); + refill(); + } + } } } return samples; @@ -200,7 +208,16 @@ void VorbisInputStream::refill() { uint len_left = sizeof(_buffer); char *read_pos = (char *)_buffer; - while (len_left > 0 && ov_time_tell(&_ovFile) < _endTime) { + while (len_left > 0) { + if (ov_time_tell(&_ovFile) >= _endTime) { + // If looping is on and there are loops left, rewind to the start + if (_numLoops == 1) + break; // Last loop, abort + if (_numLoops != 0) + _numLoops--; + ov_time_seek(&_ovFile, _startTime); + } + long result; #ifdef USE_TREMOR // Tremor ov_read() always returns data as signed 16 bit interleaved PCM @@ -260,6 +277,19 @@ AudioStream *makeVorbisStream(File *file, uint32 size) { return new VorbisInputStream(stream, true); } +AudioStream *makeVorbisStream( + Common::SeekableReadStream *stream, + bool disposeAfterUse, + uint32 startTime, + uint32 duration, + uint numLoops) { + + uint32 endTime = duration ? (startTime + duration) : 0; + + return new VorbisInputStream(stream, disposeAfterUse, startTime, endTime, numLoops); +} + + #pragma mark - #pragma mark --- Ogg Vorbis Audio CD emulation --- diff --git a/sound/vorbis.h b/sound/vorbis.h index 205f578e34e..3587fff7833 100644 --- a/sound/vorbis.h +++ b/sound/vorbis.h @@ -30,6 +30,7 @@ namespace Common { class File; + class SeekableReadStream; } namespace Audio { @@ -40,13 +41,34 @@ class DigitalTrackInfo; DigitalTrackInfo *getVorbisTrack(int track); /** - * Create a new AudioStream from the Vorbis data in the given + * Create a new AudioStream from the Ogg Vorbis data in the given * file. If you only want to play part of that file, then seek * to the start position in file before passing it to this * factory function, and specify the appropriate size. */ AudioStream *makeVorbisStream(Common::File *file, uint32 size); + +/** + * Create a new AudioStream from the Ogg Vorbis data in the given stream. + * Allows for looping (which is why we require a SeekableReadStream), + * and specifying only a portion of the data to be played, based + * on time offsets. + * + * @param stream the SeekableReadStream from which to read the MP3 data + * @param disposeAfterUse whether to delete the stream after use + * @param startTime the (optional) time offset in milliseconds from which to start playback + * @param duration the (optional) time in milliseconds specifying how long to play + * @param numLoops how often the data shall be looped (0 = infinite) + * @return a new AudioStream, or NULL, if an error occured + */ +AudioStream *makeVorbisStream( + Common::SeekableReadStream *stream, + bool disposeAfterUse, + uint32 startTime = 0, + uint32 duration = 0, + uint numLoops = 1); + } // End of namespace Audio #endif // #ifdef USE_VORBIS