[Scummvm-cvs-logs] SF.net SVN: scummvm:[47013] scummvm/trunk

lordhoto at users.sourceforge.net lordhoto at users.sourceforge.net
Tue Jan 5 03:27:24 CET 2010


Revision: 47013
          http://scummvm.svn.sourceforge.net/scummvm/?rev=47013&view=rev
Author:   lordhoto
Date:     2010-01-05 02:27:24 +0000 (Tue, 05 Jan 2010)

Log Message:
-----------
- 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!)

Modified Paths:
--------------
    scummvm/trunk/engines/kyra/sound.cpp
    scummvm/trunk/engines/kyra/sound.h
    scummvm/trunk/engines/kyra/sound_digital.cpp
    scummvm/trunk/engines/touche/resource.cpp
    scummvm/trunk/engines/tucker/resource.cpp
    scummvm/trunk/sound/audiostream.cpp
    scummvm/trunk/sound/audiostream.h
    scummvm/trunk/sound/flac.cpp
    scummvm/trunk/sound/flac.h
    scummvm/trunk/sound/mp3.cpp
    scummvm/trunk/sound/mp3.h
    scummvm/trunk/sound/voc.cpp
    scummvm/trunk/sound/voc.h
    scummvm/trunk/sound/vorbis.cpp
    scummvm/trunk/sound/vorbis.h

Modified: scummvm/trunk/engines/kyra/sound.cpp
===================================================================
--- scummvm/trunk/engines/kyra/sound.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/engines/kyra/sound.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -241,12 +241,12 @@
 
 // A simple wrapper to create VOC streams the way like creating MP3, OGG/Vorbis and FLAC streams.
 // Possible TODO: Think of making this complete and moving it to sound/voc.cpp ?
-Audio::AudioStream *makeVOCStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops) {
+Audio::SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops) {
 
 #ifdef STREAM_AUDIO_FROM_DISK
-	Audio::AudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED, 0, 0, disposeAfterUse);
+	Audio::SeekableAudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED, 0, 0, disposeAfterUse);
 #else
-	Audio::AudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED);
+	Audio::SeekableAudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED);
 
 	if (disposeAfterUse)
 		delete stream;

Modified: scummvm/trunk/engines/kyra/sound.h
===================================================================
--- scummvm/trunk/engines/kyra/sound.h	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/engines/kyra/sound.h	2010-01-05 02:27:24 UTC (rev 47013)
@@ -35,6 +35,7 @@
 
 namespace Audio {
 class AudioStream;
+class SeekableAudioStream;
 } // End of namespace Audio
 
 namespace Kyra {
@@ -240,7 +241,7 @@
 
 	struct SpeechCodecs {
 		const char *fileext;
-		Audio::AudioStream *(*streamFunc)(
+		Audio::SeekableAudioStream *(*streamFunc)(
 			Common::SeekableReadStream *stream,
 			bool disposeAfterUse,
 			uint32 startTime,
@@ -358,7 +359,7 @@
 
 	struct AudioCodecs {
 		const char *fileext;
-		Audio::AudioStream *(*streamFunc)(
+		Audio::SeekableAudioStream *(*streamFunc)(
 			Common::SeekableReadStream *stream,
 			bool disposeAfterUse,
 			uint32 startTime,

Modified: scummvm/trunk/engines/kyra/sound_digital.cpp
===================================================================
--- scummvm/trunk/engines/kyra/sound_digital.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/engines/kyra/sound_digital.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -107,7 +107,7 @@
 
 // TODO: cleanup of whole AUDStream
 
-class AUDStream : public Audio::AudioStream {
+class AUDStream : public Audio::SeekableAudioStream {
 public:
 	AUDStream(Common::SeekableReadStream *stream, bool loop = false);
 	~AUDStream();
@@ -118,6 +118,10 @@
 	bool endOfData() const { return _endOfData; }
 
 	int getRate() const { return _rate; }
+
+	// GROSS HACK, if anyone sees this, be aware that you should
+	// never copy this! This is just a temporary hack...
+	bool seek(const Audio::Timestamp &) { return false; }
 private:
 	Common::SeekableReadStream *_stream;
 	bool _loop;
@@ -475,7 +479,7 @@
 
 namespace {
 
-Audio::AudioStream *makeAUDStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops) {
+Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops) {
 	return new AUDStream(stream, numLoops == 0 ? true : false);
 }
 

Modified: scummvm/trunk/engines/touche/resource.cpp
===================================================================
--- scummvm/trunk/engines/touche/resource.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/engines/touche/resource.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -30,6 +30,7 @@
 #include "sound/mp3.h"
 #include "sound/voc.h"
 #include "sound/vorbis.h"
+#include "sound/audiostream.h"
 
 #include "touche/midi.h"
 #include "touche/touche.h"
@@ -44,7 +45,7 @@
 
 struct CompressedSpeechFile {
 	const char *filename;
-	Audio::AudioStream *(*makeStream)(
+	Audio::SeekableAudioStream *(*makeStream)(
 			Common::SeekableReadStream *stream,
 			bool disposeAfterUse,
 			uint32 startTime,

Modified: scummvm/trunk/engines/tucker/resource.cpp
===================================================================
--- scummvm/trunk/engines/tucker/resource.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/engines/tucker/resource.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -43,7 +43,7 @@
 
 struct CompressedSoundFile {
 	const char *filename;
-	Audio::AudioStream *(*makeStream)(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops);
+	Audio::SeekableAudioStream *(*makeStream)(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops);
 };
 
 static const CompressedSoundFile compressedSoundFilesTable[] = {

Modified: scummvm/trunk/sound/audiostream.cpp
===================================================================
--- scummvm/trunk/sound/audiostream.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/audiostream.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -55,7 +55,7 @@
 	 * 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 @@
 	{ 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 @@
 	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 @@
  * 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 @@
 		return _playtime * _numLoops;
 	}
 
+	bool seek(const Timestamp &where);
+
 	void setNumLoops(uint numLoops) {
 		_numLoops = numLoops;
 		_numPlayedLoops = 0;
@@ -201,8 +214,21 @@
 	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 ---
 #pragma mark -
@@ -215,7 +241,7 @@
  *  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 @@
 			return kUnknownPlayTime;
 		return _playtime * _numLoops;
 	}
+
+	bool seek(const Timestamp &where);
 };
 
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
@@ -318,8 +346,7 @@
 	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,16 +398,47 @@
 
 	// 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 ---
 #pragma mark -
@@ -403,7 +461,7 @@
 		} 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 @@
 			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;

Modified: scummvm/trunk/sound/audiostream.h
===================================================================
--- scummvm/trunk/sound/audiostream.h	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/audiostream.h	2010-01-05 02:27:24 UTC (rev 47013)
@@ -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 @@
 	 * @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,7 +133,33 @@
 	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
  * data in the buffer described by ptr and len as raw sample data in the
@@ -138,7 +168,7 @@
  * 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 @@
  * 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);
 
 /**

Modified: scummvm/trunk/sound/flac.cpp
===================================================================
--- scummvm/trunk/sound/flac.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/flac.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -82,7 +82,7 @@
 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 @@
 		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 @@
 	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 @@
 #pragma mark -
 
 
-AudioStream *makeFlacStream(
+SeekableAudioStream *makeFlacStream(
 	Common::SeekableReadStream *stream,
 	bool disposeAfterUse,
 	uint32 startTime,

Modified: scummvm/trunk/sound/flac.h
===================================================================
--- scummvm/trunk/sound/flac.h	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/flac.h	2010-01-05 02:27:24 UTC (rev 47013)
@@ -51,7 +51,7 @@
 
 namespace Audio {
 
-class AudioStream;
+class SeekableAudioStream;
 
 /**
  * Create a new AudioStream from the FLAC data in the given stream.
@@ -66,7 +66,7 @@
  * @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,

Modified: scummvm/trunk/sound/mp3.cpp
===================================================================
--- scummvm/trunk/sound/mp3.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/mp3.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -45,7 +45,7 @@
 #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 @@
 	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 @@
 		return _totalPlayTime * _numLoops;
 	}
 
+	bool seek(const Timestamp &where);
+
 	void setNumLoops(uint numLoops) {
 		_numLoops = numLoops;
 		_numPlayedLoops = 0;
@@ -107,6 +110,10 @@
 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 @@
 	// 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 @@
 	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,38 +173,17 @@
 }
 
 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);
+		if (_state == MP3_STATE_INIT)
+			initStream();
 
-			// 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_EOS)
 			return;
 
@@ -238,26 +192,8 @@
 			readMP3Data();
 
 		while (_state == MP3_STATE_READY) {
-			_stream.error = MAD_ERROR_NONE;
+			readHeader();
 
-			// 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);
-
 			// If we have not yet reached the start point, skip to the next frame
 			if (mad_timer_compare(_totalTime, _startTime) < 0)
 				continue;
@@ -294,10 +230,7 @@
 			++_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,7 +273,97 @@
 	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
 	sample += (1L << (MAD_F_FRACBITS - 16));
@@ -382,8 +405,7 @@
 #pragma mark --- MP3 factory functions ---
 #pragma mark -
 
-
-AudioStream *makeMP3Stream(
+SeekableAudioStream *makeMP3Stream(
 	Common::SeekableReadStream *stream,
 	bool disposeAfterUse,
 	uint32 startTime,

Modified: scummvm/trunk/sound/mp3.h
===================================================================
--- scummvm/trunk/sound/mp3.h	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/mp3.h	2010-01-05 02:27:24 UTC (rev 47013)
@@ -51,7 +51,7 @@
 
 namespace Audio {
 
-class AudioStream;
+class SeekableAudioStream;
 
 /**
  * Create a new AudioStream from the MP3 data in the given stream.
@@ -66,7 +66,7 @@
  * @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,

Modified: scummvm/trunk/sound/voc.cpp
===================================================================
--- scummvm/trunk/sound/voc.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/voc.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -292,7 +292,7 @@
 	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 @@
 #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

Modified: scummvm/trunk/sound/voc.h
===================================================================
--- scummvm/trunk/sound/voc.h	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/voc.h	2010-01-05 02:27:24 UTC (rev 47013)
@@ -45,7 +45,7 @@
 
 namespace Audio {
 
-class AudioStream;
+class SeekableAudioStream;
 
 
 #include "common/pack-start.h"	// START STRUCT PACKING
@@ -93,7 +93,7 @@
  *
  * 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
 

Modified: scummvm/trunk/sound/vorbis.cpp
===================================================================
--- scummvm/trunk/sound/vorbis.cpp	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/vorbis.cpp	2010-01-05 02:27:24 UTC (rev 47013)
@@ -85,7 +85,7 @@
 #pragma mark -
 
 
-class VorbisInputStream : public AudioStream {
+class VorbisInputStream : public SeekableAudioStream {
 protected:
 	Common::SeekableReadStream *_inStream;
 	bool _disposeAfterUse;
@@ -138,6 +138,8 @@
 #endif
 	}
 
+	bool seek(const Timestamp &where);
+
 protected:
 	bool refill();
 };
@@ -240,6 +242,23 @@
 	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 @@
 #pragma mark -
 
 
-AudioStream *makeVorbisStream(
+SeekableAudioStream *makeVorbisStream(
 	Common::SeekableReadStream *stream,
 	bool disposeAfterUse,
 	uint32 startTime,

Modified: scummvm/trunk/sound/vorbis.h
===================================================================
--- scummvm/trunk/sound/vorbis.h	2010-01-05 01:52:56 UTC (rev 47012)
+++ scummvm/trunk/sound/vorbis.h	2010-01-05 02:27:24 UTC (rev 47013)
@@ -51,7 +51,7 @@
 
 namespace Audio {
 
-class AudioStream;
+class SeekableAudioStream;
 
 /**
  * Create a new AudioStream from the Ogg Vorbis data in the given stream.
@@ -66,7 +66,7 @@
  * @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,


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list