aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorJohannes Schickel2010-01-05 02:27:24 +0000
committerJohannes Schickel2010-01-05 02:27:24 +0000
commitf5c3bd887e2257fc7fdfb0d141ddbae592f1e83b (patch)
treeebbd8705d76d5d23a2b217f7bd31012afe271d2c /sound
parent00e48f687ff0b0302383abfb9971f5420dc3c265 (diff)
downloadscummvm-rg350-f5c3bd887e2257fc7fdfb0d141ddbae592f1e83b.tar.gz
scummvm-rg350-f5c3bd887e2257fc7fdfb0d141ddbae592f1e83b.tar.bz2
scummvm-rg350-f5c3bd887e2257fc7fdfb0d141ddbae592f1e83b.zip
- 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
Diffstat (limited to 'sound')
-rw-r--r--sound/audiostream.cpp82
-rw-r--r--sound/audiostream.h43
-rw-r--r--sound/flac.cpp14
-rw-r--r--sound/flac.h4
-rw-r--r--sound/mp3.cpp220
-rw-r--r--sound/mp3.h4
-rw-r--r--sound/voc.cpp4
-rw-r--r--sound/voc.h4
-rw-r--r--sound/vorbis.cpp23
-rw-r--r--sound/vorbis.h4
10 files changed, 270 insertions, 132 deletions
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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
-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<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buf
return numSamples-samples;
}
-
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+bool LinearMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buf
* start position and length of each block of uncompressed audio in the stream.
*/
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
-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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
@@ -318,8 +346,7 @@ int LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::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<int>(samples, _bufferLeft);
samples -= len;
_bufferLeft -= len;
@@ -371,15 +398,46 @@ int LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::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 stereo, bool is16Bit, bool isUnsigned, bool isLE>
+bool LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::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<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffe
} else \
return new LinearMemoryStream<STEREO, false, UNSIGNED, false>(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<STEREO, false, UNSIGNED, false>(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,