[Scummvm-cvs-logs] SF.net SVN: scummvm:[43356] scummvm/branches/branch-1-0-0

agent-q at users.sourceforge.net agent-q at users.sourceforge.net
Thu Aug 13 23:45:00 CEST 2009


Revision: 43356
          http://scummvm.svn.sourceforge.net/scummvm/?rev=43356&view=rev
Author:   agent-q
Date:     2009-08-13 21:44:59 +0000 (Thu, 13 Aug 2009)

Log Message:
-----------
Allow VOCs to stream from disk.  Disabled by default, use symbol STREAM_AUDIO_FROM_DISK to enable.  See patch #2834001.

Modified Paths:
--------------
    scummvm/branches/branch-1-0-0/engines/agos/sound.cpp
    scummvm/branches/branch-1-0-0/engines/kyra/sound.cpp
    scummvm/branches/branch-1-0-0/sound/audiostream.cpp
    scummvm/branches/branch-1-0-0/sound/audiostream.h
    scummvm/branches/branch-1-0-0/sound/voc.cpp
    scummvm/branches/branch-1-0-0/sound/voc.h

Modified: scummvm/branches/branch-1-0-0/engines/agos/sound.cpp
===================================================================
--- scummvm/branches/branch-1-0-0/engines/agos/sound.cpp	2009-08-13 18:02:44 UTC (rev 43355)
+++ scummvm/branches/branch-1-0-0/engines/agos/sound.cpp	2009-08-13 21:44:59 UTC (rev 43356)
@@ -133,8 +133,10 @@
 };
 
 class VocSound : public BaseSound {
+	byte _flags;
 public:
-	VocSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigEndian = false) : BaseSound(mixer, file, base, bigEndian) {}
+	VocSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigEndian = false) : BaseSound(mixer, file, base, bigEndian), _flags(0) {}
+	Audio::AudioStream *makeAudioStream(uint sound);
 	void playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, byte flags, int vol = 0);
 };
 
@@ -255,14 +257,15 @@
 	_mixer->playInputStream(type, handle, new LoopingAudioStream(this, sound, loopSound, (flags & Audio::Mixer::FLAG_LOOP) != 0), -1, vol);
 }
 
-void VocSound::playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, byte flags, int vol) {
-	if (_offsets == NULL)
-		return;
-
+Audio::AudioStream *VocSound::makeAudioStream(uint sound) {
 	_file->seek(_offsets[sound], SEEK_SET);
+	return Audio::makeVOCStream(*_file, _flags);
+}
 
-	Audio::AudioStream *stream = Audio::makeVOCStream(*_file, flags);
-	_mixer->playInputStream(type, handle, stream);
+void VocSound::playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, byte flags, int vol) {
+	convertVolume(vol);
+	_flags = flags;
+	_mixer->playInputStream(type, handle, new LoopingAudioStream(this, sound, loopSound, (flags & Audio::Mixer::FLAG_LOOP) != 0), -1, vol);
 }
 
 void RawSound::playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, byte flags, int vol) {

Modified: scummvm/branches/branch-1-0-0/engines/kyra/sound.cpp
===================================================================
--- scummvm/branches/branch-1-0-0/engines/kyra/sound.cpp	2009-08-13 18:02:44 UTC (rev 43355)
+++ scummvm/branches/branch-1-0-0/engines/kyra/sound.cpp	2009-08-13 21:44:59 UTC (rev 43356)
@@ -235,10 +235,15 @@
 // 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) {
+
+#ifdef STREAM_AUDIO_FROM_DISK
+	Audio::AudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED, 0, 0, disposeAfterUse);
+#else
 	Audio::AudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED);
 
 	if (disposeAfterUse)
 		delete stream;
+#endif
 
 	return as;
 }

Modified: scummvm/branches/branch-1-0-0/sound/audiostream.cpp
===================================================================
--- scummvm/branches/branch-1-0-0/sound/audiostream.cpp	2009-08-13 18:02:44 UTC (rev 43355)
+++ scummvm/branches/branch-1-0-0/sound/audiostream.cpp	2009-08-13 21:44:59 UTC (rev 43356)
@@ -173,7 +173,163 @@
 }
 
 
+
 #pragma mark -
+#pragma mark --- LinearDiskStream ---
+#pragma mark -
+
+
+
+/**
+ *  LinearDiskStream.  This can stream linear (PCM) audio from disk.  The
+ *  function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the
+ *  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 {
+
+// Allow backends to override buffer size
+#ifdef CUSTOM_AUDIO_BUFFER_SIZE
+	static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE;
+#else
+	static const int32 BUFFER_SIZE = 16384;
+#endif
+
+protected:
+	byte* _buffer;			///< Streaming buffer
+	const byte *_ptr;		///< Pointer to current position in stream buffer
+	const int _rate;		///< Sample rate of stream
+
+	int32 _playtime;		///< Calculated total play time
+	Common::SeekableReadStream *_stream;	///< Stream to read data from
+	int32 _filePos;			///< Current position in stream
+	int32 _diskLeft;		///< Samples left in stream in current block not yet read to buffer
+	int32 _bufferLeft;		///< Samples left in buffer in current block
+	bool _disposeAfterUse;		///< If true, delete stream object when LinearDiskStream is destructed
+
+	LinearDiskStreamAudioBlock *_audioBlock;	///< Audio block list
+	int _audioBlockCount;		///< Number of blocks in _audioBlock
+	int _currentBlock;		///< Current audio block number
+
+	int _beginLoop;			///< Loop parameter, currently not implemented
+	int _endLoop;			///< Loop parameter, currently not implemented
+
+
+public:
+	LinearDiskStream(int rate, uint beginLoop, uint endLoop, bool disposeStream, Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, uint numBlocks)
+		: _rate(rate), _stream(stream), _beginLoop(beginLoop), _endLoop(endLoop), _disposeAfterUse(disposeStream),
+		  _audioBlockCount(numBlocks) {
+
+		// Allocate streaming buffer
+		if (is16Bit) {
+			_buffer = (byte *)malloc(BUFFER_SIZE * sizeof(int16));
+		} else {
+			_buffer = (byte *)malloc(BUFFER_SIZE * sizeof(byte));
+		}
+
+		_ptr = _buffer;
+		_bufferLeft = 0;
+
+		// Copy audio block data to our buffer
+		// TODO: Replace this with a Common::Array or Common::List to
+		// make it a little friendlier.
+		_audioBlock = new LinearDiskStreamAudioBlock[numBlocks];
+		memcpy(_audioBlock, block, numBlocks * sizeof(LinearDiskStreamAudioBlock));
+
+		// Set current buffer state, playing first block
+		_currentBlock = 0;
+		_filePos = _audioBlock[_currentBlock].pos;
+		_diskLeft = _audioBlock[_currentBlock].len;
+
+		// Add up length of all blocks in order to caluclate total play time
+		int len = 0;
+		for (int r = 0; r < _audioBlockCount; r++) {
+			len += _audioBlock[r].len;
+		}
+		_playtime = calculatePlayTime(rate, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1));
+	}
+
+
+	virtual ~LinearDiskStream() {
+		if (_disposeAfterUse) {
+			delete _stream;
+		}
+
+		delete[] _audioBlock;
+		free(_buffer);
+	}
+	int readBuffer(int16 *buffer, const int numSamples);
+
+	bool isStereo() const			{ return stereo; }
+	bool endOfData() const			{ return (_currentBlock == _audioBlockCount - 1) && (_diskLeft == 0) && (_bufferLeft == 0); }
+
+	int getRate() const			{ return _rate; }
+	int32 getTotalPlayTime() const	{ return _playtime; }
+};
+
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+int LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
+	int oldPos = _stream->pos();
+	bool restoreFilePosition = false;
+
+	int samples = numSamples;
+
+	while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || (_currentBlock != _audioBlockCount - 1))  ) {
+
+		// Output samples in the buffer to the output		
+		int len = MIN(samples, _bufferLeft);
+		samples -= len;
+		_bufferLeft -= len;
+
+		while (len > 0) {
+			*buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
+			_ptr += (is16Bit ? 2 : 1);
+			len--;
+		}
+
+		// Have we now finished this block?  If so, read the next block
+		if ((_bufferLeft == 0) && (_diskLeft == 0) && (_currentBlock != _audioBlockCount - 1)) {
+			// Next block
+			_currentBlock++;
+
+			_filePos = _audioBlock[_currentBlock].pos;
+			_diskLeft = _audioBlock[_currentBlock].len;
+		}
+			
+
+		// Now read more data from disk if there is more to be read
+		if ((_bufferLeft == 0) && (_diskLeft > 0)) {
+			int32 readAmount = MIN(_diskLeft, BUFFER_SIZE);
+
+			_stream->seek(_filePos, SEEK_SET);
+			_stream->read(_buffer, readAmount * (is16Bit? 2: 1));
+
+			// Amount of data in buffer is now the amount read in, and
+			// the amount left to read on disk is decreased by the same amount
+			_bufferLeft = readAmount;
+			_diskLeft -= readAmount;
+			_ptr = (byte *)_buffer;
+			_filePos += readAmount * (is16Bit? 2: 1);
+
+			// Set this flag now we've used the file, it restores it's
+			// original position.
+			restoreFilePosition = true;
+		}
+	}
+
+	// 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.	
+	if (restoreFilePosition) {
+		_stream->seek(oldPos, SEEK_SET);
+	}
+
+	return numSamples-samples;
+}
+
+
+
+#pragma mark -
 #pragma mark --- Input stream factory ---
 #pragma mark -
 
@@ -202,6 +358,7 @@
 	const bool isLE       = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0;
 	const bool autoFree   = (flags & Audio::Mixer::FLAG_AUTOFREE) != 0;
 
+
 	uint loopOffset = 0, loopLen = 0;
 	if (flags & Audio::Mixer::FLAG_LOOP) {
 		if (loopEnd == 0)
@@ -236,6 +393,44 @@
 }
 
 
+
+
+
+#define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \
+		if (is16Bit) { \
+			if (isLE) \
+				return new LinearDiskStream<STEREO, true, UNSIGNED, true>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks); \
+			else  \
+				return new LinearDiskStream<STEREO, true, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks); \
+		} else \
+			return new LinearDiskStream<STEREO, false, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks)
+
+
+AudioStream *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;
+	const bool isLE       = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0;
+
+
+	if (isStereo) {
+		if (isUnsigned) {
+			MAKE_LINEAR_DISK(true, true);
+		} else {
+			MAKE_LINEAR_DISK(true, false);
+		}
+	} else {
+		if (isUnsigned) {
+			MAKE_LINEAR_DISK(false, true);
+		} else {
+			MAKE_LINEAR_DISK(false, false);
+		}
+	}
+}
+
+
+
+
 #pragma mark -
 #pragma mark --- Appendable audio stream ---
 #pragma mark -
@@ -306,7 +501,6 @@
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
 int AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
 	Common::StackLock lock(_mutex);
-
 	int samples = numSamples;
 	while (samples > 0 && !eosIntern()) {
 		Buffer buf = *_bufferQueue.begin();

Modified: scummvm/branches/branch-1-0-0/sound/audiostream.h
===================================================================
--- scummvm/branches/branch-1-0-0/sound/audiostream.h	2009-08-13 18:02:44 UTC (rev 43355)
+++ scummvm/branches/branch-1-0-0/sound/audiostream.h	2009-08-13 21:44:59 UTC (rev 43356)
@@ -28,8 +28,8 @@
 
 #include "common/util.h"
 #include "common/scummsys.h"
+#include "common/stream.h"
 
-
 namespace Audio {
 
 /**
@@ -109,6 +109,7 @@
 	virtual int32 getTotalPlayTime() const { return kUnknownPlayTime; }
 };
 
+
 /**
  * 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 specified
@@ -118,6 +119,23 @@
  */
 AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd);
 
+
+/** Struct used to define the audio data to be played by a LinearDiskStream */
+
+struct LinearDiskStreamAudioBlock {
+	int32 pos;		///< Position in stream of the block
+	int32 len;		///< Length of the block (in samples)
+};
+
+
+/** Factory function for a Linear Disk Stream.  This can stream linear (PCM) audio from disk.  The
+ *  function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the
+ *  start position and length of each block of uncompressed audio in the stream.
+ */
+
+AudioStream *makeLinearDiskStream(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int 
+		numBlocks, int rate, byte flags, bool disposeStream, uint loopStart, uint loopEnd);
+
 /**
  * An audio stream to which additional data can be appended on-the-fly.
  * Used by SMUSH, iMuseDigital, the Kyrandia 3 VQA player, etc.

Modified: scummvm/branches/branch-1-0-0/sound/voc.cpp
===================================================================
--- scummvm/branches/branch-1-0-0/sound/voc.cpp	2009-08-13 18:02:44 UTC (rev 43355)
+++ scummvm/branches/branch-1-0-0/sound/voc.cpp	2009-08-13 21:44:59 UTC (rev 43356)
@@ -166,14 +166,155 @@
 	return loadVOCFromStream(stream, size, rate, loops, begin_loop, end_loop);
 }
 
-AudioStream *makeVOCStream(Common::ReadStream &stream, byte flags, uint loopStart, uint loopEnd) {
+
+#ifdef STREAM_AUDIO_FROM_DISK
+
+int parseVOCFormat(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int &rate, int &loops, int &begin_loop, int &end_loop) {
+	VocFileHeader fileHeader;
+	int currentBlock = 0;
+	int size = 0;
+
+	if (stream.read(&fileHeader, 8) != 8)
+		goto invalid;
+
+	if (!memcmp(&fileHeader, "VTLK", 4)) {
+		if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader))
+			goto invalid;
+	} else if (!memcmp(&fileHeader, "Creative", 8)) {
+		if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8)
+			goto invalid;
+	} else {
+	invalid:;
+		warning("loadVOCFromStream: Invalid header");
+		return 0;
+	}
+
+	if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0)
+		error("loadVOCFromStream: Invalid header");
+	if (fileHeader.desc[19] != 0x1A)
+		debug(3, "loadVOCFromStream: Partially invalid header");
+
+	int32 offset = FROM_LE_16(fileHeader.datablock_offset);
+	int16 version = FROM_LE_16(fileHeader.version);
+	int16 code = FROM_LE_16(fileHeader.id);
+	assert(offset == sizeof(VocFileHeader));
+	// 0x100 is an invalid VOC version used by German version of DOTT (Disk) and
+	// French version of Simon the Sorcerer 2 (CD)
+	assert(version == 0x010A || version == 0x0114 || version == 0x0100);
+	assert(code == ~version + 0x1234);
+
+	int len;
+	size = 0;
+	begin_loop = 0;
+	end_loop = 0;
+
+	while ((code = stream.readByte())) {
+		len = stream.readByte();
+		len |= stream.readByte() << 8;
+		len |= stream.readByte() << 16;
+
+		switch (code) {
+		case 1:
+		case 9: {
+			int packing;
+			if (code == 1) {
+				int time_constant = stream.readByte();
+				packing = stream.readByte();
+				len -= 2;
+				rate = getSampleRateFromVOCRate(time_constant);
+			} else {
+				rate = stream.readUint32LE();
+				int bits = stream.readByte();
+				int channels = stream.readByte();
+				if (bits != 8 || channels != 1) {
+					warning("Unsupported VOC file format (%d bits per sample, %d channels)", bits, channels);
+					break;
+				}
+				packing = stream.readUint16LE();
+				stream.readUint32LE();
+				len -= 12;
+			}
+			debug(9, "VOC Data Block: %d, %d, %d", rate, packing, len);
+			if (packing == 0) {
+				
+				// Found a data block - so add it to the block list
+				block[currentBlock].pos = stream.pos();
+				block[currentBlock].len = len;
+				currentBlock++;
+
+				stream.seek(len, SEEK_CUR);
+
+				size += len;
+				begin_loop = size;
+				end_loop = size;
+			} else {
+				warning("VOC file packing %d unsupported", packing);
+			}
+			} break;
+		case 3: // silence
+			// occur with a few Igor sounds, voc file starts with a silence block with a
+			// frequency different from the data block. Just ignore fow now (implementing
+			// it wouldn't make a big difference anyway...)
+			assert(len == 3);
+			stream.readUint16LE();
+			stream.readByte();
+			break;
+		case 6:	// begin of loop
+			assert(len == 2);
+			loops = stream.readUint16LE();
+			break;
+		case 7:	// end of loop
+			assert(len == 0);
+			break;
+		case 8: // "Extended"
+			// This occures in the LoL Intro demo. This block can usually be used to create stereo
+			// sound, but the LoL intro has only an empty block, thus this dummy implementation will
+			// work.
+			assert(len == 4);
+			stream.readUint16LE();
+			stream.readByte();
+			stream.readByte();
+			break;
+		default:
+			warning("Unhandled code in VOC file : %d", code);
+			return 0;
+		}
+	}
+	debug(4, "VOC Data Size : %d", size);
+	return currentBlock;
+}
+
+AudioStream *makeVOCDiskStream(Common::SeekableReadStream &stream, byte flags, bool takeOwnership) {
+	const int MAX_AUDIO_BLOCKS = 256;
+
+	LinearDiskStreamAudioBlock *block = new LinearDiskStreamAudioBlock[MAX_AUDIO_BLOCKS];
+	int rate, loops, begin_loop, end_loop;
+
+	int numBlocks = parseVOCFormat(stream, block, rate, loops, begin_loop, end_loop);
+
+	AudioStream* audioStream = makeLinearDiskStream(stream, block, numBlocks, rate, flags, takeOwnership, begin_loop, end_loop);
+
+	delete[] block;
+
+	return audioStream;
+}
+	
+#endif
+
+
+AudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags, uint loopStart, uint loopEnd, bool takeOwnershipOfStream) {
+#ifdef STREAM_AUDIO_FROM_DISK
+	return makeVOCDiskStream(stream, flags, takeOwnershipOfStream);
+#else
 	int size, rate;
 
 	byte *data = loadVOCFromStream(stream, size, rate);
+
 	if (!data)
 		return 0;
 
 	return makeLinearInputStream(data, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE, loopStart, loopEnd);
+#endif
 }
 
 

Modified: scummvm/branches/branch-1-0-0/sound/voc.h
===================================================================
--- scummvm/branches/branch-1-0-0/sound/voc.h	2009-08-13 18:02:44 UTC (rev 43355)
+++ scummvm/branches/branch-1-0-0/sound/voc.h	2009-08-13 21:44:59 UTC (rev 43356)
@@ -41,6 +41,7 @@
 #include "common/scummsys.h"
 
 namespace Common { class ReadStream; }
+namespace Common { class SeekableReadStream; }
 
 namespace Audio {
 
@@ -78,7 +79,7 @@
 extern int getSampleRateFromVOCRate(int vocSR);
 
 /**
- * Try to load a VOC from the given seekable stream. Returns a pointer to memory
+ * Try to load a VOC from the given stream. Returns a pointer to memory
  * containing the PCM sample data (allocated with malloc). It is the callers
  * responsibility to dellocate that data again later on! Currently this
  * function only supports uncompressed raw PCM data.
@@ -92,7 +93,7 @@
  *
  * This function uses loadVOCFromStream() internally.
  */
-AudioStream *makeVOCStream(Common::ReadStream &stream, byte flags = 0, uint loopStart = 0, uint loopEnd = 0);
+AudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags = 0, uint loopStart = 0, uint loopEnd = 0, bool takeOwnershipOfStream = false);
 
 } // End of namespace Audio
 


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