[Scummvm-cvs-logs] scummvm master -> 09708f7224cc388ef45ec570a477a8ace0e61bec

lordhoto lordhoto at gmail.com
Sun Nov 6 13:37:46 CET 2011


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
9fa9f68789 AUDIO: Implement a basic VocStream class.
09708f7224 SCUMM: Stream sfx/voice sounds from mouster.sou.


Commit: 9fa9f68789ef51e078cb8631e06bead13cae13f2
    https://github.com/scummvm/scummvm/commit/9fa9f68789ef51e078cb8631e06bead13cae13f2
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2011-11-06T04:30:34-08:00

Commit Message:
AUDIO: Implement a basic VocStream class.

Now all VOCs are streamed rather than preloaded. This deprecates the
STREAM_AUDIO_FROM_DISK define, which was previously used to stream VOCs from
disk.

This might very well break some engines which relied on the stream not being
changed after makeVOCStream! I adapted all engines which had a check for
STREAM_AUDIO_FROM_DISK in their code.

It would be wise to check all other engines using VOC to see if this might
cause any problems for them.

Changed paths:
    audio/decoders/voc.cpp
    engines/kyra/sound.cpp



diff --git a/audio/decoders/voc.cpp b/audio/decoders/voc.cpp
index f06e7f9..69a9733 100644
--- a/audio/decoders/voc.cpp
+++ b/audio/decoders/voc.cpp
@@ -25,14 +25,478 @@
 #include "common/util.h"
 #include "common/stream.h"
 #include "common/textconsole.h"
+#include "common/list.h"
 
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
 #include "audio/decoders/voc.h"
 
-
 namespace Audio {
 
+namespace {
+
+bool checkVOCHeader(Common::ReadStream &stream) {
+	VocFileHeader fileHeader;
+
+	if (stream.read(&fileHeader, 8) != 8)
+		return false;
+
+	if (!memcmp(&fileHeader, "VTLK", 4)) {
+		if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader))
+			return false;
+	} else if (!memcmp(&fileHeader, "Creative", 8)) {
+		if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8)
+			return false;
+	} else {
+		return false;
+	}
+
+	if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0)
+		return false;
+	//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);
+
+	if (offset != sizeof(VocFileHeader))
+		return false;
+
+	// 0x100 is an invalid VOC version used by German version of DOTT (Disk) and
+	// French version of Simon the Sorcerer 2 (CD)
+	if (version != 0x010A && version != 0x0114 && version != 0x0100)
+		return false;
+
+	if (code != ~version + 0x1234)
+		return false;
+
+	return true;
+}
+
+class VocStream : public SeekableAudioStream {
+public:
+	VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse);
+	~VocStream();
+
+	virtual int readBuffer(int16 *buffer, const int numSamples);
+
+	virtual bool isStereo() const { return false; }
+
+	virtual int getRate() const { return _rate; }
+
+	virtual bool endOfData() const { return (_curBlock == _blocks.end()) && (_blockLeft == 0); }
+
+	virtual bool seek(const Timestamp &where);
+
+	virtual Timestamp getLength() const { return _length; }
+private:
+	void preProcess();
+
+	Common::SeekableReadStream *const _stream;
+	const DisposeAfterUse::Flag _disposeAfterUse;
+
+	const bool _isUnsigned;
+
+	int _rate;
+	Timestamp _length;
+
+	struct Block {
+		uint8 code;
+		uint32 length;
+
+		union {
+			struct {
+				uint32 offset;
+				int rate;
+				int samples;
+			} sampleBlock;
+
+			struct {
+				int count;
+			} loopBlock;
+		};
+	};
+
+	typedef Common::List<Block> BlockList;
+	BlockList _blocks;
+
+	BlockList::const_iterator _curBlock;
+	uint32 _blockLeft;
+
+	/**
+	 * Advance one block in the stream in case
+	 * the current one is empty.
+	 */
+	void updateBlockIfNeeded();
+
+	// Do some internal buffering for systems with really slow slow disk i/o
+	enum {
+		/**
+		 * How many samples we can buffer at once.
+		 *
+		 * TODO: Check whether this size suffices
+		 * for systems with slow disk I/O.
+		 */
+		kSampleBufferLength = 2048
+	};
+	byte _buffer[kSampleBufferLength];
+
+	/**
+	 * Fill the temporary sample buffer used in readBuffer.
+	 *
+	 * @param maxSamples Maximum samples to read.
+	 * @return actual count of samples read.
+	 */
+	int fillBuffer(int maxSamples);
+};
+
+VocStream::VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse)
+	: _stream(stream), _disposeAfterUse(disposeAfterUse), _isUnsigned(isUnsigned), _rate(0),
+	  _length(), _blocks(), _curBlock(_blocks.end()), _blockLeft(0), _buffer() {
+	preProcess();
+}
+
+VocStream::~VocStream() {
+	if (_disposeAfterUse == DisposeAfterUse::YES)
+		delete _stream;
+}
+
+int VocStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samplesLeft = numSamples;
+	while (samplesLeft > 0) {
+		// Try to read up to "samplesLeft" samples.
+		int len = fillBuffer(samplesLeft);
+
+		// In case we were not able to read any samples
+		// we will stop reading here.
+		if (!len)
+			break;
+
+		// Adjust the samples left to read.
+		samplesLeft -= len;
+
+		// Copy the data to the caller's buffer.
+		const byte *src = _buffer;
+		while (len-- > 0)
+			*buffer++ = (*src++ << 8) ^ (_isUnsigned ? 0x8000 : 0);
+	}
+
+	return numSamples - samplesLeft;
+}
+
+void VocStream::updateBlockIfNeeded() {
+	// Have we now finished this block? If so, read the next block
+	if (_blockLeft == 0 && _curBlock != _blocks.end()) {
+		// Find the next sample block
+		while (true) {
+			// Next block
+			++_curBlock;
+
+			// Check whether we reached the end of the stream
+			// yet.
+			if (_curBlock == _blocks.end())
+				return;
+
+			// Skip all none sample blocks for now
+			if (_curBlock->code != 1 && _curBlock->code != 9)
+				continue;
+
+			_stream->seek(_curBlock->sampleBlock.offset, SEEK_SET);
+
+			// In case of an error we will stop
+			// stream playback.
+			if (_stream->err()) {
+				_blockLeft = 0;
+				_curBlock = _blocks.end();
+			} else {
+				_blockLeft = _curBlock->sampleBlock.samples;
+			}
+
+			return;
+		}
+	}
+}
+
+int VocStream::fillBuffer(int maxSamples) {
+	int bufferedSamples = 0;
+	byte *dst = _buffer;
+
+	// We can only read up to "kSampleBufferLength" samples
+	// so we take this into consideration, when trying to
+	// read up to maxSamples.
+	maxSamples = MIN<int>(kSampleBufferLength, maxSamples);
+
+	// We will only read up to maxSamples
+	while (maxSamples > 0 && !endOfData()) {
+		// Calculate how many samples we can safely read
+		// from the current block.
+		const int len = MIN<int>(maxSamples, _blockLeft);
+
+		// Try to read all the sample data and update the
+		// destination pointer.
+		const int bytesRead = _stream->read(dst, len);
+		dst += bytesRead;
+
+		// Calculate how many samples we actually read.
+		const int samplesRead = bytesRead;
+
+		// Update all status variables
+		bufferedSamples += samplesRead;
+		maxSamples -= samplesRead;
+		_blockLeft -= samplesRead;
+
+		// In case of an error we will stop
+		// stream playback.
+		if (_stream->err()) {
+			_blockLeft = 0;
+			_curBlock = _blocks.end();
+			break;
+		}
+
+		// Advance to the next block in case the current
+		// one is already finished.
+		updateBlockIfNeeded();
+	}
+
+	return bufferedSamples;
+}
+
+bool VocStream::seek(const Timestamp &where) {
+	// Invalidate stream
+	_blockLeft = 0;
+	_curBlock = _blocks.end();
+
+	if (where > _length)
+		return false;
+
+	// Search for the block containing the requested sample
+	const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
+	uint32 curSample = 0;
+
+	for (_curBlock = _blocks.begin(); _curBlock != _blocks.end(); ++_curBlock) {
+		// Skip all none sample blocks for now
+		if (_curBlock->code != 1 && _curBlock->code != 9)
+			continue;
+
+		uint32 nextBlockSample = curSample + _curBlock->sampleBlock.samples;
+
+		if (nextBlockSample > seekSample)
+			break;
+
+		curSample = nextBlockSample;
+	}
+
+	if (_curBlock == _blocks.end()) {
+		return ((seekSample - curSample) == 0);
+	} else {
+		const uint32 offset = seekSample - curSample;
+
+		_stream->seek(_curBlock->sampleBlock.offset + offset, SEEK_SET);
+
+		// In case of an error we will stop
+		// stream playback.
+		if (_stream->err()) {
+			_blockLeft = 0;
+			_curBlock = _blocks.end();
+		} else {
+			_blockLeft = _curBlock->sampleBlock.samples - offset;
+		}
+
+		return true;
+	}
+}
+
+void VocStream::preProcess() {
+	Block block;
+
+	// Scan through the file and collect all blocks
+	while (true) {
+		block.code = _stream->readByte();
+		block.length = 0;
+
+		// If we hit EOS here we found the end of the VOC file.
+		// According to http://wiki.multimedia.cx/index.php?title=Creative_Voice
+		// there is no need for an "Terminator" block to be present.
+		// In case we hit a "Terminator" block we also break here.
+		if (_stream->eos() || block.code == 0)
+			break;
+
+		block.length = _stream->readByte();
+		block.length |= _stream->readByte() << 8;
+		block.length |= _stream->readByte() << 16;
+
+		// Premature end of stream => error!
+		if (_stream->eos() || _stream->err())
+			return;
+
+		uint32 skip = 0;
+
+		switch (block.code) {
+		// Sound data
+		case 1:
+		// Sound data (New format)
+		case 9:
+			if (block.code == 1) {
+				// Read header data
+				int freqDiv = _stream->readByte();
+				// Prevent division through 0
+				if (freqDiv == 256)
+					return;
+				block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv);
+
+				int codec = _stream->readByte();
+				// We only support 8bit PCM
+				if (codec != 0)
+					return;
+
+				block.sampleBlock.samples = skip = block.length - 2;
+				block.sampleBlock.offset = _stream->pos();
+
+				// Check the last block if there is any
+				if (_blocks.size() > 0) {
+					BlockList::iterator lastBlock = _blocks.end();
+					--lastBlock;
+					// When we have found a block 8 as predecessor
+					// we need to use its settings
+					if (lastBlock->code == 8) {
+						block.sampleBlock.rate = lastBlock->sampleBlock.rate;
+						// Remove the block since we don't need it anymore
+						_blocks.erase(lastBlock);
+					}
+				}
+
+				// Check whether we found a new highest rate
+				if (_rate < block.sampleBlock.rate)
+					_rate = block.sampleBlock.rate;
+			} else {
+				block.sampleBlock.rate = _stream->readUint32LE();
+				int bitsPerSample = _stream->readByte();
+				// We only support 8bit PCM
+				if (bitsPerSample != 8)
+					return;
+				int channels = _stream->readByte();
+				// We only support mono
+				if (channels != 1) {
+					warning("Unhandled channel count %d in VOC file", channels);
+					return;
+				}
+				int codec = _stream->readByte();
+				// We only support 8bit PCM
+				if (codec != 0) {
+					warning("Unhandled codec %d in VOC file", codec);
+					return;
+				}
+				/*uint32 reserved = */_stream->readUint32LE();
+				block.sampleBlock.offset = _stream->pos();
+				block.sampleBlock.samples = skip = block.length - 12;
+			}
+			break;
+
+		// Silence
+		case 3: {
+			if (block.length != 3)
+				return;
+
+			block.sampleBlock.offset = 0;
+
+			block.sampleBlock.samples = _stream->readUint16LE() + 1;
+			int freqDiv = _stream->readByte();
+			// Prevent division through 0
+			if (freqDiv == 256)
+				return;
+			block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv);
+			} break;
+
+		// Repeat start
+		case 6:
+			if (block.length != 2)
+				return;
+
+			block.loopBlock.count = _stream->readUint16LE() + 1;
+			break;
+
+		// Repeat end
+		case 7:
+			break;
+
+		// Extra info
+		case 8: {
+			if (block.length != 4)
+				return;
+
+			int freqDiv = _stream->readUint16LE();
+			// Prevent division through 0
+			if (freqDiv == 65536)
+				return;
+
+			int codec = _stream->readByte();
+			// We only support RAW 8bit PCM.
+			if (codec != 0) {
+				warning("Unhandled codec %d in VOC file", codec);
+				return;
+			}
+
+			int channels = _stream->readByte() + 1;
+			// We only support mono sound right now
+			if (channels != 1) {
+				warning("Unhandled channel count %d in VOC file", channels);
+				return;
+			}
+
+			block.sampleBlock.offset = 0;
+			block.sampleBlock.samples = 0;
+			block.sampleBlock.rate = 256000000L / (65536L - freqDiv);
+			} break;
+
+		default:
+			warning("Unhandled code %d in VOC file (len %d)", block.code, block.length);
+			return;
+		}
+
+		// Premature end of stream => error!
+		if (_stream->eos() || _stream->err())
+			return;
+
+		// Skip the rest of the block
+		if (skip)
+			_stream->skip(skip);
+
+		_blocks.push_back(block);
+	}
+
+	// Since we determined the sample rate we need for playback now, we will
+	// initialize the play length.
+	_length = Timestamp(0, _rate);
+
+	// Calculate the total play time and do some more sanity checks
+	for (BlockList::const_iterator i = _blocks.begin(), end = _blocks.end(); i != end; ++i) {
+		// Check whether we found a block 8 which survived, this is not
+		// allowed to happen!
+		if (i->code == 8) {
+			warning("VOC file contains unused block 8");
+			return;
+		}
+
+		// For now only use blocks with actual samples
+		if (i->code != 1 && i->code != 9)
+			continue;
+
+		// Check the sample rate
+		if (i->sampleBlock.rate != _rate) {
+			warning("VOC file contains chunks with different sample rates (%d != %d)", _rate, i->sampleBlock.rate);
+			return;
+		}
+
+		_length = _length.addFrames(i->sampleBlock.samples);
+	}
+
+	// Set the current block to the first block in the stream
+	rewind();
+}
+
+} // End of anonymous namespace
+
 int getSampleRateFromVOCRate(int vocSR) {
 	if (vocSR == 0xa5 || vocSR == 0xa6) {
 		return 11025;
@@ -169,161 +633,21 @@ byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate) {
 	return ret_sound;
 }
 
-#ifdef STREAM_AUDIO_FROM_DISK
-
-int parseVOCFormat(Common::SeekableReadStream& stream, RawStreamBlock* block, int &rate) {
-	VocFileHeader fileHeader;
-	int currentBlock = 0;
-	int size = 0;
-
-	debug(2, "parseVOCFormat");
-
-	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;
-
-	while ((code = stream.readByte())) {
-		len = stream.readByte();
-		len |= stream.readByte() << 8;
-		len |= stream.readByte() << 16;
-
-		debug(2, "Block code %d, len %d", code, len);
-
-		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;
-			} 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);
-			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 %d in VOC file (len %d)", code, len);
-			return 0;
-		}
-	}
-	debug(4, "VOC Data Size : %d", size);
-	return currentBlock;
-}
-
-SeekableAudioStream *makeVOCDiskStreamNoLoop(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) {
-	const int MAX_AUDIO_BLOCKS = 256;
-
-	RawStreamBlock *block = new RawStreamBlock[MAX_AUDIO_BLOCKS];
-	int rate;
-
-	int numBlocks = parseVOCFormat(*stream, block, rate);
-
-	SeekableAudioStream *audioStream = 0;
-
-	// Create an audiostream from the data. Note the numBlocks may be 0,
-	// e.g. when invalid data is encountered. See bug #2890038.
-	if (numBlocks)
-		audioStream = makeRawDiskStream_OLD(stream, block, numBlocks, rate, flags, disposeAfterUse);
-
-	delete[] block;
-
-	return audioStream;
-}
-
-#endif
-
 SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) {
-#ifdef STREAM_AUDIO_FROM_DISK
-	return makeVOCDiskStreamNoLoop(stream, flags, disposeAfterUse);
-#else
-	int size, rate;
-
-	byte *data = loadVOCFromStream(*stream, size, rate);
-
-	if (!data) {
+	if (!checkVOCHeader(*stream)) {
 		if (disposeAfterUse == DisposeAfterUse::YES)
 			delete stream;
 		return 0;
 	}
 
-	return makeRawStream(data, size, rate, flags);
-#endif
+	SeekableAudioStream *audioStream = new VocStream(stream, (flags & Audio::FLAG_UNSIGNED) != 0, disposeAfterUse);
+
+	if (audioStream && audioStream->endOfData()) {
+		delete audioStream;
+		return 0;
+	} else {
+		return audioStream;
+	}
 }
 
 } // End of namespace Audio
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp
index 5195271..a1af1ad 100644
--- a/engines/kyra/sound.cpp
+++ b/engines/kyra/sound.cpp
@@ -334,16 +334,7 @@ namespace {
 // 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::SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
-
-#ifdef STREAM_AUDIO_FROM_DISK
 	Audio::SeekableAudioStream *as = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, disposeAfterUse);
-#else
-	Audio::SeekableAudioStream *as = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
-
-	if (disposeAfterUse)
-		delete stream;
-#endif
-
 	return as;
 }
 


Commit: 09708f7224cc388ef45ec570a477a8ace0e61bec
    https://github.com/scummvm/scummvm/commit/09708f7224cc388ef45ec570a477a8ace0e61bec
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2011-11-06T04:33:55-08:00

Commit Message:
SCUMM: Stream sfx/voice sounds from mouster.sou.

This fixes sound corruption when using the new VOC streaming code. It also
reduces the runtime memory needed for compressed sound files slightly, since
it does not preload them into memory anymore. This comes at the expense of one
file descriptor needed per sfx being played though.

Thanks to Kirben for his review and feedback.

Changed paths:
    engines/scumm/he/sound_he.cpp
    engines/scumm/sound.cpp
    engines/scumm/sound.h



diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 5c15a85..85e2a2f 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -770,24 +770,30 @@ void SoundHE::startHETalkSound(uint32 offset) {
 	if (ConfMan.getBool("speech_mute"))
 		return;
 
-	assert(_sfxFile);
-	if (!_sfxFile->isOpen()) {
+	if (_sfxFilename.empty()) {
 		// This happens in the Pajama Sam's Lost & Found demo, on the
 		// main menu screen, so don't make it a fatal error.
-		warning("startHETalkSound: Speech file is not open");
+		warning("startHETalkSound: Speech file is not found");
 		return;
 	}
 
+	ScummFile file;
+	if (!_vm->openFile(file, _sfxFilename)) {
+		warning("startHETalkSound: Could not open speech file %s", _sfxFilename.c_str());
+		return;
+	}
+	file.setEnc(_sfxFileEncByte);
+
 	_sfxMode |= 2;
 	_vm->_res->nukeResource(rtSound, 1);
 
-	_sfxFile->seek(offset + 4, SEEK_SET);
-	 size = _sfxFile->readUint32BE();
-	_sfxFile->seek(offset, SEEK_SET);
+	file.seek(offset + 4, SEEK_SET);
+	 size = file.readUint32BE();
+	file.seek(offset, SEEK_SET);
 
 	_vm->_res->createResource(rtSound, 1, size);
 	ptr = _vm->getResourceAddress(rtSound, 1);
-	_sfxFile->read(ptr, size);
+	file.read(ptr, size);
 
 	int channel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
 	addSoundToQueue2(1, 0, channel, 0);
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 8178fc2..f058ef1 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -23,6 +23,8 @@
 #include "common/config-manager.h"
 #include "common/timer.h"
 #include "common/util.h"
+#include "common/ptr.h"
+#include "common/substream.h"
 
 #include "scumm/actor.h"
 #include "scumm/file.h"
@@ -62,7 +64,8 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer)
 	_mixer(mixer),
 	_soundQuePos(0),
 	_soundQue2Pos(0),
-	_sfxFile(0),
+	_sfxFilename(),
+	_sfxFileEncByte(0),
 	_offsetTable(0),
 	_numSoundEffects(0),
 	_soundMode(kVOCMode),
@@ -91,7 +94,6 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer)
 Sound::~Sound() {
 	stopCDTimer();
 	g_system->getAudioCDManager()->stop();
-	delete _sfxFile;
 }
 
 void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) {
@@ -490,6 +492,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 	int num = 0, i;
 	int size = 0;
 	int id = -1;
+	Common::ScopedPtr<ScummFile> file;
 
 	if (_vm->_game.id == GID_CMI) {
 		_sfxMode |= mode;
@@ -523,25 +526,29 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 			return;
 		}
 
-		_sfxFile->close();
+		file.reset(new ScummFile());
+		if (!file)
+			error("startTalkSound: Out of memory");
+
 		sprintf(filename, "audio/%s.%u/%u.voc", roomname, offset, b);
-		_vm->openFile(*_sfxFile, filename);
-		if (!_sfxFile->isOpen()) {
+		if (!_vm->openFile(*file, filename)) {
 			sprintf(filename, "audio/%s_%u/%u.voc", roomname, offset, b);
-			_vm->openFile(*_sfxFile, filename);
+			_vm->openFile(*file, filename);
 		}
-		if (!_sfxFile->isOpen()) {
+
+		if (!file->isOpen()) {
 			sprintf(filename, "%u.%u.voc", offset, b);
-			_vm->openFile(*_sfxFile, filename);
+			_vm->openFile(*file, filename);
 		}
-		if (!_sfxFile->isOpen()) {
+
+		if (!file->isOpen()) {
 			warning("startTalkSound: dig demo: voc file not found");
 			return;
 		}
 	} else {
 
-		if (!_sfxFile->isOpen()) {
-			warning("startTalkSound: SFX file is not open");
+		if (_sfxFilename.empty()) {
+			warning("startTalkSound: SFX file not found");
 			return;
 		}
 
@@ -581,11 +588,30 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 			size = -1;
 		}
 
-		_sfxFile->seek(offset, SEEK_SET);
+		file.reset(new ScummFile());
+		if (!file)
+			error("startTalkSound: Out of memory");
+
+		if (!_vm->openFile(*file, _sfxFilename)) {
+			warning("startTalkSound: could not open sfx file %s", _sfxFilename.c_str());
+			return;
+		}
+
+		file->setEnc(_sfxFileEncByte);
+		file->seek(offset, SEEK_SET);
 
 		assert(num + 1 < (int)ARRAYSIZE(_mouthSyncTimes));
 		for (i = 0; i < num; i++)
-			_mouthSyncTimes[i] = _sfxFile->readUint16BE();
+			_mouthSyncTimes[i] = file->readUint16BE();
+
+		// Adjust offset to account for the mouth sync times. It is noteworthy
+		// that we do not adjust the size here for compressed streams, since
+		// they only set size to the size of the compressed sound data.
+		offset += num * 2;
+		// TODO: In case we ever set up the size for VOC streams, we should
+		// really check whether the size contains the _mouthSyncTimes.
+		//if (_soundMode == kVOCMode)
+		//	size -= num * 2;
 
 		_mouthSyncTimes[i] = 0xFFFF;
 		_sfxMode |= mode;
@@ -601,9 +627,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 #ifdef USE_MAD
 			{
 			assert(size > 0);
-			Common::SeekableReadStream *tmp = _sfxFile->readStream(size);
-			assert(tmp);
-			input = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
+			input = Audio::makeMP3Stream(new Common::SeekableSubReadStream(file.release(), offset, offset + size, DisposeAfterUse::YES), DisposeAfterUse::YES);
 			}
 #endif
 			break;
@@ -611,9 +635,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 #ifdef USE_VORBIS
 			{
 			assert(size > 0);
-			Common::SeekableReadStream *tmp = _sfxFile->readStream(size);
-			assert(tmp);
-			input = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
+			input = Audio::makeVorbisStream(new Common::SeekableSubReadStream(file.release(), offset, offset + size, DisposeAfterUse::YES), DisposeAfterUse::YES);
 			}
 #endif
 			break;
@@ -621,14 +643,12 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 #ifdef USE_FLAC
 			{
 			assert(size > 0);
-			Common::SeekableReadStream *tmp = _sfxFile->readStream(size);
-			assert(tmp);
-			input = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
+			input = Audio::makeFLACStream(new Common::SeekableSubReadStream(file.release(), offset, offset + size, DisposeAfterUse::YES), DisposeAfterUse::YES);
 			}
 #endif
 			break;
 		default:
-			input = Audio::makeVOCStream(_sfxFile, Audio::FLAG_UNSIGNED);
+			input = Audio::makeVOCStream(file.release(), Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 			break;
 		}
 
@@ -848,12 +868,10 @@ void Sound::talkSound(uint32 a, uint32 b, int mode, int channel) {
 }
 
 void Sound::setupSound() {
-	delete _sfxFile;
-
-	_sfxFile = openSfxFile();
+	setupSfxFile();
 
 	if (_vm->_game.id == GID_FT) {
-		_vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = _sfxFile->isOpen();
+		_vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = _sfxFilename.empty() ? 0 : 1;
 	}
 }
 
@@ -885,7 +903,7 @@ void Sound::pauseSounds(bool pause) {
 	}
 }
 
-BaseScummFile *Sound::openSfxFile() {
+void Sound::setupSfxFile() {
 	struct SoundFileExtensions {
 		const char *ext;
 		SoundMode mode;
@@ -905,8 +923,10 @@ BaseScummFile *Sound::openSfxFile() {
 		{ 0, kVOCMode }
 	};
 
-	ScummFile *file = new ScummFile();
+	ScummFile file;
 	_offsetTable = NULL;
+	_sfxFileEncByte = 0;
+	_sfxFilename.clear();
 
 	/* Try opening the file <baseName>.sou first, e.g. tentacle.sou.
 	 * That way, you can keep .sou files for multiple games in the
@@ -931,15 +951,20 @@ BaseScummFile *Sound::openSfxFile() {
 			tmp = basename[0] + "tlk";
 		}
 
-		if (file->open(tmp) && _vm->_game.heversion <= 74)
-			file->setEnc(0x69);
+		if (file.open(tmp))
+			_sfxFilename = tmp;
+	
+		if (_vm->_game.heversion <= 74)
+			_sfxFileEncByte = 0x69;
+
 		_soundMode = kVOCMode;
 	} else {
-		for (uint j = 0; j < 2 && !file->isOpen(); ++j) {
+		for (uint j = 0; j < 2 && !file.isOpen(); ++j) {
 			for (int i = 0; extensions[i].ext; ++i) {
 				tmp = basename[j] + extensions[i].ext;
-				if (_vm->openFile(*file, tmp)) {
+				if (_vm->openFile(file, tmp)) {
 					_soundMode = extensions[i].mode;
+					_sfxFilename = tmp;
 					break;
 				}
 			}
@@ -963,23 +988,21 @@ BaseScummFile *Sound::openSfxFile() {
 		 */
 		int size, compressed_offset;
 		MP3OffsetTable *cur;
-		compressed_offset = file->readUint32BE();
+		compressed_offset = file.readUint32BE();
 		_offsetTable = (MP3OffsetTable *) malloc(compressed_offset);
 		_numSoundEffects = compressed_offset / 16;
 
 		size = compressed_offset;
 		cur = _offsetTable;
 		while (size > 0) {
-			cur->org_offset = file->readUint32BE();
-			cur->new_offset = file->readUint32BE() + compressed_offset + 4; /* The + 4 is to take into accound the 'size' field */
-			cur->num_tags = file->readUint32BE();
-			cur->compressed_size = file->readUint32BE();
+			cur->org_offset = file.readUint32BE();
+			cur->new_offset = file.readUint32BE() + compressed_offset + 4; /* The + 4 is to take into accound the 'size' field */
+			cur->num_tags = file.readUint32BE();
+			cur->compressed_size = file.readUint32BE();
 			size -= 4 * 4;
 			cur++;
 		}
 	}
-
-	return file;
 }
 
 bool Sound::isSfxFinished() const {
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index e9a37ac..48f28d5 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -69,7 +69,8 @@ protected:
 		int16 flags;
 	} _soundQue2[10];
 
-	BaseScummFile *_sfxFile;
+	Common::String _sfxFilename;
+	byte _sfxFileEncByte;
 	SoundMode _soundMode;
 	MP3OffsetTable *_offsetTable;	// For compressed audio
 	int _numSoundEffects;		// For compressed audio
@@ -126,7 +127,7 @@ public:
 	void saveLoadWithSerializer(Serializer *ser);
 
 protected:
-	BaseScummFile *openSfxFile();
+	void setupSfxFile();
 	bool isSfxFinished() const;
 	void processSfxQueues();
 






More information about the Scummvm-git-logs mailing list