[Scummvm-cvs-logs] SF.net SVN: scummvm: [31100] residual/trunk/mixer

aquadran at users.sourceforge.net aquadran at users.sourceforge.net
Tue Mar 11 00:04:55 CET 2008


Revision: 31100
          http://scummvm.svn.sourceforge.net/scummvm/?rev=31100&view=rev
Author:   aquadran
Date:     2008-03-10 16:04:55 -0700 (Mon, 10 Mar 2008)

Log Message:
-----------
synced imuse digital with scummvm imuse and related stuff 

Modified Paths:
--------------
    residual/trunk/mixer/audiostream.cpp
    residual/trunk/mixer/audiostream.h
    residual/trunk/mixer/mixer.cpp
    residual/trunk/mixer/mixer.h
    residual/trunk/mixer/rate.cpp
    residual/trunk/mixer/rate.h

Modified: residual/trunk/mixer/audiostream.cpp
===================================================================
--- residual/trunk/mixer/audiostream.cpp	2008-03-10 20:34:34 UTC (rev 31099)
+++ residual/trunk/mixer/audiostream.cpp	2008-03-10 23:04:55 UTC (rev 31100)
@@ -1,19 +1,19 @@
 /* Residual - Virtual machine to run LucasArts' 3D adventure games
- * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
+ * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org)
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
 
- * This library is distributed in the hope that it will be useful,
+ * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * $URL$
  * $Id$
@@ -22,18 +22,39 @@
 
 #include "common/sys.h"
 #include "common/debug.h"
+#include "common/list.h"
 
 #include "engine/backend/driver.h"
 
 #include "mixer/mixer.h"
 #include "mixer/audiostream.h"
 
-#define READSAMPLE(is16Bit, isUnsigned, ptr) \
-	((is16Bit ? READ_BE_UINT16(ptr) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
-
+// This used to be an inline template function, but
+// buggy template function handling in MSVC6 forced
+// us to go with the macro approach. So far this is
+// the only template function that MSVC6 seemed to
+// compile incorrectly. Knock on wood.
 #define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \
 	((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
 
+
+namespace Audio {
+
+#pragma mark -
+#pragma mark --- LinearMemoryStream ---
+#pragma mark -
+
+
+/**
+ * A simple raw audio stream, purely memory based. It operates on a single
+ * block of data, which is passed to it upon creation.
+ * Optionally supports looping the sound.
+ *
+ * Design note: This code tries to be as efficient as possible (without
+ * resorting to assembly, that is). To this end, it is written as a template
+ * class. This way the compiler can create optimized code for each special
+ * 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 {
 protected:
@@ -44,7 +65,6 @@
 	const int _rate;
 	const byte *_origPtr;
 
-	inline bool eosIntern() const	{ return _ptr >= _end; };
 public:
 	LinearMemoryStream(int rate, const byte *ptr, uint len, uint loopOffset, uint loopLen, bool autoFreeMemory)
 		: _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0), _rate(rate) {
@@ -61,57 +81,127 @@
 		}
 		if (stereo)	// Stereo requires even sized data
 			assert(len % 2 == 0);
-		
+
 		_origPtr = autoFreeMemory ? ptr : 0;
 	}
-	~LinearMemoryStream() {
+	virtual ~LinearMemoryStream() {
 		free(const_cast<byte *>(_origPtr));
 	}
 	int readBuffer(int16 *buffer, const int numSamples);
 
 	bool isStereo() const		{ return stereo; }
-	bool endOfData() const		{ return eosIntern(); }
+	bool endOfData() const		{ return _ptr >= _end; }
 
 	int getRate() const			{ return _rate; }
 };
 
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
 int LinearMemoryStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
-	int samples = 0;
-	while (samples < numSamples && !eosIntern()) {
-		const int len = MIN(numSamples, samples + (int)(_end - _ptr) / (is16Bit ? 2 : 1));
-		while (samples < len) {
+	int samples = numSamples;
+	while (samples > 0 && _ptr < _end) {
+		int len = MIN(samples, (int)(_end - _ptr) / (is16Bit ? 2 : 1));
+		samples -= len;
+		do {
 			*buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
 			_ptr += (is16Bit ? 2 : 1);
-			samples++;
-		}
+		} while (--len);
 		// Loop, if looping was specified
-		if (_loopPtr && eosIntern()) {
+		if (_loopPtr && _ptr >= _end) {
 			_ptr = _loopPtr;
 			_end = _loopEnd;
 		}
 	}
-	return samples;
+	return numSamples-samples;
 }
 
+
+#pragma mark -
+#pragma mark --- Input stream factory ---
+#pragma mark -
+
+/* In the following, we use preprocessor / macro tricks to simplify the code
+ * which instantiates the input streams. We used to use template functions for
+ * this, but MSVC6 / EVC 3-4 (used for WinCE builds) are extremely buggy when it
+ * comes to this feature of C++... so as a compromise we use macros to cut down
+ * on the (source) code duplication a bit.
+ * So while normally macro tricks are said to make maintenance harder, in this
+ * particular case it should actually help it :-)
+ */
+
+#define MAKE_LINEAR(STEREO, UNSIGNED) \
+		if (is16Bit) { \
+			if (isLE) \
+				return new LinearMemoryStream<STEREO, true, UNSIGNED, true>(rate, ptr, len, loopOffset, loopLen, autoFree); \
+			else  \
+				return new LinearMemoryStream<STEREO, true, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree); \
+		} 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) {
+	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;
+	const bool autoFree   = (flags & Audio::Mixer::FLAG_AUTOFREE) != 0;
+
+	uint loopOffset = 0, loopLen = 0;
+	if (flags & Audio::Mixer::FLAG_LOOP) {
+		if (loopEnd == 0)
+			loopEnd = len;
+		assert(loopStart <= loopEnd);
+		assert(loopEnd <= len);
+
+		loopOffset = loopStart;
+		loopLen = loopEnd - loopStart;
+	}
+
+	if (isStereo) {
+		if (isUnsigned) {
+			MAKE_LINEAR(true, true);
+		} else {
+			MAKE_LINEAR(true, false);
+		}
+	} else {
+		if (isUnsigned) {
+			MAKE_LINEAR(false, true);
+		} else {
+			MAKE_LINEAR(false, false);
+		}
+	}
+}
+
+
+#pragma mark -
+#pragma mark --- Appendable audio stream ---
+#pragma mark -
+
+struct Buffer {
+	byte *start;
+	byte *end;
+};
+
 /**
  * Wrapped memory stream.
  */
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
 class AppendableMemoryStream : public AppendableAudioStream {
 protected:
-	MutexRef _mutex;
 
-	byte *_bufferStart;
-	byte *_bufferEnd;
-	byte *_pos;
-	byte *_end;
+	// A mutex to avoid access problems (causing e.g. corruption of
+	// the linked list) in thread aware environments.
+	Common::Mutex _mutex;
+
+	// List of all queued buffers
+	Common::List<Buffer> _bufferQueue;
+
+	// Position in the front buffer, if any
 	bool _finalized;
 	const int _rate;
+	byte *_pos;
 
-	inline bool eosIntern() const { return _end == _pos; };
+	inline bool eosIntern() const { return _bufferQueue.empty(); };
 public:
-	AppendableMemoryStream(int rate, uint bufferSize);
+	AppendableMemoryStream(int rate);
 	~AppendableMemoryStream();
 	int readBuffer(int16 *buffer, const int numSamples);
 
@@ -121,135 +211,99 @@
 
 	int getRate() const			{ return _rate; }
 
-	void append(const byte *data, uint32 len);
+	void queueBuffer(byte *data, uint32 size);
 	void finish()				{ _finalized = true; }
 };
 
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
-AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::AppendableMemoryStream(int rate, uint bufferSize)
-	: _finalized(false), _rate(rate) {
+AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::AppendableMemoryStream(int rate)
+ : _finalized(false), _rate(rate), _pos(0) {
 
-	// Verify the buffer size is sane
-	if (is16Bit && stereo)
-		assert((bufferSize & 3) == 0);
-	else if (is16Bit || stereo)
-		assert((bufferSize & 1) == 0);
-
-	_bufferStart = (byte *)malloc(bufferSize);
-	_pos = _end = _bufferStart;
-	_bufferEnd = _bufferStart + bufferSize;
-
-	_mutex = g_driver->createMutex();
 }
 
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
 AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::~AppendableMemoryStream() {
-	free(_bufferStart);
-	g_driver->deleteMutex(_mutex);
+	// Clear the queue
+	Common::List<Buffer>::iterator iter;
+	for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter)
+		delete[] iter->start;
 }
 
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
 int AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
-	StackLock lock(_mutex);
+	Common::StackLock lock(_mutex);
 
-	int samples = 0;
-	while (samples < numSamples && !eosIntern()) {
-		// Wrap around?
-		if (_pos >= _bufferEnd)
-			_pos = _pos - (_bufferEnd - _bufferStart);
+	int samples = numSamples;
+	while (samples > 0 && !eosIntern()) {
+		Buffer buf = *_bufferQueue.begin();
+		if (_pos == 0)
+			_pos = buf.start;
 
-		const byte *endMarker = (_pos > _end) ? _bufferEnd : _end;
-		const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1));
-		while (samples < len) {
+		assert(buf.start <= _pos && _pos <= buf.end);
+		const int samplesLeftInCurBuffer = buf.end - _pos;
+		if (samplesLeftInCurBuffer == 0) {
+			delete [] buf.start;
+			_bufferQueue.erase(_bufferQueue.begin());
+			_pos = 0;
+			continue;
+		}
+
+		int len = MIN(samples, samplesLeftInCurBuffer / (is16Bit ? 2 : 1));
+		samples -= len;
+		do {
 			*buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _pos, isLE);
 			_pos += (is16Bit ? 2 : 1);
-			samples++;
-		}
+		} while (--len);
 	}
 
-	return samples;
+	return numSamples-samples;
 }
 
 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
-void AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::append(const byte *data, uint32 len) {
-	StackLock lock(_mutex);
+void AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::queueBuffer(byte *data, uint32 size) {
+	Common::StackLock lock(_mutex);
 
 	// Verify the buffer size is sane
 	if (is16Bit && stereo)
-		assert((len & 3) == 0);
+		assert((size & 3) == 0);
 	else if (is16Bit || stereo)
-		assert((len & 1) == 0);
-	
+		assert((size & 1) == 0);
+
 	// Verify that the stream has not yet been finalized (by a call to finish())
 	assert(!_finalized);
 
-	if (_end + len > _bufferEnd) {
-		// Wrap-around case
-		uint32 size_to_end_of_buffer = _bufferEnd - _end;
-		len -= size_to_end_of_buffer;
-		if ((_end < _pos) || (_bufferStart + len >= _pos)) {
-			warning("AppendableMemoryStream: buffer overflow (A)");
-			return;
-		}
-		memcpy(_end, data, size_to_end_of_buffer);
-		memcpy(_bufferStart, data + size_to_end_of_buffer, len);
-		_end = _bufferStart + len;
-	} else {
-		if ((_end < _pos) && (_end + len >= _pos)) {
-			warning("AppendableMemoryStream: buffer overflow (B)");
-			return;
-		}
-		memcpy(_end, data, len);
-		_end += len;
-	}
-}
+	// Queue the buffer
+	Buffer buf = {data, data+size};
+	_bufferQueue.push_back(buf);
 
-#define MAKE_LINEAR(STEREO, UNSIGNED) \
-		if (is16Bit) { \
-			if (isLE) \
-				return new LinearMemoryStream<STEREO, true, UNSIGNED, true>(rate, ptr, len, loopOffset, loopLen, autoFree); \
-			else  \
-				return new LinearMemoryStream<STEREO, true, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree); \
-		} else \
-			return new LinearMemoryStream<STEREO, false, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree)
 
-AudioStream *makeLinearInputStream(int rate, byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen) {
-	const bool isStereo   = (_flags & SoundMixer::FLAG_STEREO) != 0;
-	const bool is16Bit    = (_flags & SoundMixer::FLAG_16BITS) != 0;
-	const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
-	const bool isLE       = (_flags & SoundMixer::FLAG_LITTLE_ENDIAN) != 0;
-	const bool autoFree   = (_flags & SoundMixer::FLAG_AUTOFREE) != 0;
-	
-	if (isStereo) {
-		if (isUnsigned) {
-			MAKE_LINEAR(true, true);
-		} else {
-			MAKE_LINEAR(true, false);
-		}
-	} else {
-		if (isUnsigned) {
-			MAKE_LINEAR(false, true);
-		} else {
-			MAKE_LINEAR(false, false);
-		}
-	}
+#if 0
+	// Output some stats
+	uint totalSize = 0;
+	Common::List<Buffer>::iterator iter;
+	for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter)
+		totalSize += iter->end - iter->start;
+	printf("AppendableMemoryStream::queueBuffer: added a %d byte buf, a total of %d bytes are queued\n",
+				size, totalSize);
+#endif
 }
 
+
 #define MAKE_WRAPPED(STEREO, UNSIGNED) \
 		if (is16Bit) { \
 			if (isLE) \
-				return new AppendableMemoryStream<STEREO, true, UNSIGNED, true>(rate, len); \
+				return new AppendableMemoryStream<STEREO, true, UNSIGNED, true>(rate); \
 			else  \
-				return new AppendableMemoryStream<STEREO, true, UNSIGNED, false>(rate, len); \
+				return new AppendableMemoryStream<STEREO, true, UNSIGNED, false>(rate); \
 		} else \
-			return new AppendableMemoryStream<STEREO, false, UNSIGNED, false>(rate, len)
+			return new AppendableMemoryStream<STEREO, false, UNSIGNED, false>(rate)
 
-AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len) {
-	const bool isStereo = (_flags & SoundMixer::FLAG_STEREO) != 0;
-	const bool is16Bit = (_flags & SoundMixer::FLAG_16BITS) != 0;
-	const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
-	const bool isLE       = (_flags & SoundMixer::FLAG_LITTLE_ENDIAN) != 0;
-	
+AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags) {
+	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_WRAPPED(true, true);
@@ -264,3 +318,6 @@
 		}
 	}
 }
+
+
+} // End of namespace Audio

Modified: residual/trunk/mixer/audiostream.h
===================================================================
--- residual/trunk/mixer/audiostream.h	2008-03-10 20:34:34 UTC (rev 31099)
+++ residual/trunk/mixer/audiostream.h	2008-03-10 23:04:55 UTC (rev 31100)
@@ -1,19 +1,19 @@
 /* Residual - Virtual machine to run LucasArts' 3D adventure games
- * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
+ * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org)
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
 
- * This library is distributed in the hope that it will be useful,
+ * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * $URL$
  * $Id$
@@ -27,8 +27,11 @@
 #include "common/platform.h"
 
 
+namespace Audio {
+
 /**
- * Generic input stream for the resampling code.
+ * Generic audio input stream. Subclasses of this are used to feed arbitrary
+ * sampled audio data into ScummVM's audio mixer.
  */
 class AudioStream {
 public:
@@ -40,14 +43,19 @@
 	 * a critical error occured (note: you *must* check if
 	 * this value is less than what you requested, this can
 	 * happen when the stream is fully used up).
+	 *
+	 * Data has to be in native endianess, 16 bit per sample, signed.
 	 * For stereo stream, buffer will be filled with interleaved
-	 * left and right channel samples.
+	 * left and right channel samples, starting with a left sample.
+	 * Furthermore, the samples in the left and right are summed up.
+	 * So if you request 4 samples from a stereo stream, you will get
+	 * a total of two left channel and two right channel samples.
 	 */
 	virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
 
 	/** Is this a stereo stream? */
 	virtual bool isStereo() const = 0;
-	
+
 	/**
 	 * End of data reached? If this returns true, it means that at this
 	 * time there is no data available in the stream. However there may be
@@ -56,7 +64,7 @@
 	 * converting data or stop.
 	 */
 	virtual bool endOfData() const = 0;
-	
+
 	/**
 	 * End of stream reached? If this returns true, it means that all data
 	 * in this stream is used up and no additional data will appear in it
@@ -71,30 +79,46 @@
 	virtual int getRate() const = 0;
 };
 
-class AppendableAudioStream : public AudioStream {
+/**
+ * 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
+ * format. It will then simply pass this data directly to the mixer, after converting
+ * it to the sample format used by the mixer (i.e. 16 bit signed native endian).
+ * Optionally supports (infinite) looping of a portion of the data.
+ */
+AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd);
+
+/**
+ * An audio stream to which additional data can be appended on-the-fly.
+ * Used by SMUSH, iMuseDigital, and the Kyrandia 3 VQA player.
+ */
+class AppendableAudioStream : public Audio::AudioStream {
 public:
-	virtual void append(const byte *data, uint32 len) = 0;
+
+	/**
+	 * Queue another audio data buffer for playback. The stream
+	 * will playback all queued buffers, in the order they were
+	 * queued. After all data contained in them has been played,
+	 * the buffer will be delete[]'d (so make sure to allocate them
+	 * with new[], not with malloc).
+	 */
+	virtual void queueBuffer(byte *data, uint32 size) = 0;
+
+	/**
+	 * Mark the stream as finished, that is, signal that no further data
+	 * will be appended to it. Only after this has been done can the
+	 * AppendableAudioStream ever 'end' (
+	 */
 	virtual void finish() = 0;
 };
 
-class ZeroInputStream : public AudioStream {
-private:
-	int _len;
-public:
-	ZeroInputStream(uint len) : _len(len) { }
-	int readBuffer(int16 *buffer, const int numSamples) {
-		int samples = MIN(_len, numSamples);
-		memset(buffer, 0, samples * 2);
-		_len -= samples;
-		return samples;
-	}
-	bool isStereo() const { return false; }
-	bool eos() const { return _len <= 0; }
-	
-	int getRate() const { return -1; }
-};
+/**
+ * Factory function for an AppendableAudioStream. The rate and flags
+ * parameters are analog to those used in makeLinearInputStream.
+ */
+AppendableAudioStream *makeAppendableAudioStream(int rate, byte flags);
 
-AudioStream *makeLinearInputStream(int rate, byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen);
-AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len);
 
+} // End of namespace Audio
+
 #endif

Modified: residual/trunk/mixer/mixer.cpp
===================================================================
--- residual/trunk/mixer/mixer.cpp	2008-03-10 20:34:34 UTC (rev 31099)
+++ residual/trunk/mixer/mixer.cpp	2008-03-10 23:04:55 UTC (rev 31100)
@@ -1,19 +1,19 @@
 /* Residual - Virtual machine to run LucasArts' 3D adventure games
- * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
+ * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org)
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
 
- * This library is distributed in the hope that it will be useful,
+ * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * $URL$
  * $Id$
@@ -30,32 +30,38 @@
 #include "mixer/rate.h"
 #include "mixer/audiostream.h"
 
-SoundMixer *g_mixer = NULL;
+namespace Audio {
 
+#pragma mark -
+#pragma mark --- Channel classes ---
+#pragma mark -
+
+
 /**
  * Channels used by the sound mixer.
  */
 class Channel {
+public:
+	const Mixer::SoundType	_type;
+	SoundHandle _handle;
 private:
-	SoundMixer *_mixer;
-	PlayingSoundHandle *_handle;
+	Mixer *_mixer;
 	bool _autofreeStream;
 	bool _permanent;
 	byte _volume;
 	int8 _balance;
-	bool _paused;
+	int _pauseLevel;
 	int _id;
 	uint32 _samplesConsumed;
 	uint32 _samplesDecoded;
+	uint32 _mixerTimeStamp;
 
 protected:
 	RateConverter *_converter;
 	AudioStream *_input;
 
 public:
-
-	Channel(SoundMixer *mixer, PlayingSoundHandle *handle, bool isMusic, int id = -1);
-	Channel(SoundMixer *mixer, PlayingSoundHandle *handle, AudioStream *input, bool autofreeStream, bool isMusic, bool reverseStereo = false, int id = -1, bool permanent = false);
+	Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, bool autofreeStream, bool reverseStereo = false, int id = -1, bool permanent = false);
 	virtual ~Channel();
 
 	void mix(int16 *data, uint len);
@@ -67,10 +73,15 @@
 		return _input->endOfStream();
 	}
 	void pause(bool paused) {
-		_paused = paused;
+		//assert((paused && _pauseLevel >= 0) || (!paused && _pauseLevel));
+
+		if (paused)
+			_pauseLevel++;
+		else if (_pauseLevel > 0)
+			_pauseLevel--;
 	}
 	bool isPaused() {
-		return _paused;
+		return _pauseLevel != 0;
 	}
 	void setVolume(const byte volume) {
 		_volume = volume;
@@ -85,116 +96,91 @@
 };
 
 
-SoundMixer::SoundMixer() {
-	_mutex = g_driver->createMutex();
-	_premixChannel = NULL;
-	_globalVolume = 0;
-	_paused = false;
+#pragma mark -
+#pragma mark --- Mixer ---
+#pragma mark -
 
-	for (int i = 0; i != NUM_CHANNELS; i++)
-		_channels[i] = NULL;
 
-	_mixerReady = g_driver->setSoundCallback(mixCallback, this);
-	_outputRate = (uint)g_driver->getOutputSampleRate();
+Mixer::Mixer() {
+	_handleSeed = 0;
 
-	if (_outputRate == 0)
-		error("OSystem returned invalid sample rate");
-}
+	int i = 0;
 
-SoundMixer::~SoundMixer() {
-	g_driver->clearSoundCallback();
-	stopAll(true);
+	for (i = 0; i < ARRAYSIZE(_volumeForSoundType); i++)
+		_volumeForSoundType[i] = kMaxMixerVolume;
 
-	delete _premixChannel;
-	_premixChannel = NULL;
+	for (i = 0; i != NUM_CHANNELS; i++)
+		_channels[i] = 0;
 
-	g_driver->deleteMutex(_mutex);
+	_mixerReady = false;
 }
 
-bool SoundMixer::isPaused() {
-	return _paused;
+Mixer::~Mixer() {
+	for (int i = 0; i != NUM_CHANNELS; i++)
+		delete _channels[i];
 }
 
-void SoundMixer::setupPremix(AudioStream *stream) {
-	StackLock lock(_mutex);
+uint Mixer::getOutputRate() const {
+	return (uint)g_driver->getOutputSampleRate();
+}
 
-	delete _premixChannel;
-	_premixChannel = NULL;
+void Mixer::insertChannel(SoundHandle *handle, Channel *chan) {
 
-	if (stream == NULL)
-		return;
-
-	// Create the channel
-	_premixChannel = new Channel(this, NULL, stream, false, true);
-}
-
-void SoundMixer::insertChannel(PlayingSoundHandle *handle, Channel *chan) {
 	int index = -1;
-
 	for (int i = 0; i != NUM_CHANNELS; i++) {
-		if (_channels[i] == NULL) {
+		if (_channels[i] == 0) {
 			index = i;
 			break;
 		}
 	}
 	if (index == -1) {
-		warning("SoundMixer::out of mixer slots");
+		warning("Mixer::out of mixer slots");
 		delete chan;
 		return;
 	}
 
 	_channels[index] = chan;
-	if (handle)
-		handle->setIndex(index);
+	 chan->_handle._val = index + (_handleSeed * NUM_CHANNELS);
+	_handleSeed++;
+	if (handle) {
+		*handle = chan->_handle;
+	}
 }
 
-void SoundMixer::playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags,
-			int id, byte volume, int8 balance, uint32 loopStart, uint32 loopEnd) {
-	StackLock lock(_mutex);
+void Mixer::playRaw(
+			SoundType type,
+			SoundHandle *handle,
+			void *sound,
+			uint32 size, uint rate, byte flags,
+			int id, byte volume, int8 balance,
+			uint32 loopStart, uint32 loopEnd) {
 
-	// Prevent duplicate sounds
-	if (id != -1) {
-		for (int i = 0; i != NUM_CHANNELS; i++)
-			if (_channels[i] != NULL && _channels[i]->getId() == id) {
-				if ((flags & SoundMixer::FLAG_AUTOFREE) != 0)
-					free(sound);
-				return;
-			}
-	}
-
 	// Create the input stream
-	AudioStream *input;
-	if (flags & SoundMixer::FLAG_LOOP) {
-		if (loopEnd == 0) {
-			input = makeLinearInputStream(rate, flags, (byte *)sound, size, 0, size);
-		} else {
-			assert(loopStart < loopEnd && loopEnd <= size);
-			input = makeLinearInputStream(rate, flags, (byte *)sound, size, loopStart, loopEnd - loopStart);
-		}
-	} else {
-		input = makeLinearInputStream(rate, flags, (byte *)sound, size, 0, 0);
-	}
+	AudioStream *input = makeLinearInputStream((byte *)sound, size, rate, flags, loopStart, loopEnd);
 
-	// Create the channel
-	Channel *chan = new Channel(this, handle, input, true, false, (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0, id);
-	chan->setVolume(volume);
-	chan->setBalance(balance);
-	insertChannel(handle, chan);
+	// Play it
+	playInputStream(type, handle, input, id, volume, balance, true, false, ((flags & Mixer::FLAG_REVERSE_STEREO) != 0));
 }
 
-void SoundMixer::playInputStream(PlayingSoundHandle *handle, AudioStream *input, bool isMusic,
-			int id, byte volume, int8 balance, bool autofreeStream, bool permanent) {
-	StackLock lock(_mutex);
+void Mixer::playInputStream(
+			SoundType type,
+			SoundHandle *handle,
+			AudioStream *input,
+			int id, byte volume, int8 balance,
+			bool autofreeStream,
+			bool permanent,
+			bool reverseStereo) {
+	Common::StackLock lock(_mutex);
 
-	if (input == NULL) {
-		warning("input stream is NULL");
+	if (input == 0) {
+		warning("input stream is 0");
 		return;
 	}
 
 	// Prevent duplicate sounds
 	if (id != -1) {
 		for (int i = 0; i != NUM_CHANNELS; i++)
-			if (_channels[i] != NULL && _channels[i]->getId() == id) {
+			if (_channels[i] != 0 && _channels[i]->getId() == id) {
 				if (autofreeStream)
 					delete input;
 				return;
@@ -202,182 +188,194 @@
 	}
 
 	// Create the channel
-	Channel *chan = new Channel(this, handle, input, autofreeStream, isMusic, false, id, permanent);
+	Channel *chan = new Channel(this, type, input, autofreeStream, reverseStereo, id, permanent);
 	chan->setVolume(volume);
 	chan->setBalance(balance);
 	insertChannel(handle, chan);
 }
 
-void SoundMixer::mix(int16 *buf, uint len) {
-	StackLock lock(_mutex);
+void Mixer::mix(int16 *buf, uint len) {
+	Common::StackLock lock(_mutex);
 
+	// Since the mixer callback has been called, the mixer must be ready...
+	_mixerReady = true;
+
 	//  zero the buf
 	memset(buf, 0, 2 * len * sizeof(int16));
 
-	if (!_paused) {
-		if (_premixChannel)
-			_premixChannel->mix(buf, len);
-
-		// now mix all channels
-		for (int i = 0; i != NUM_CHANNELS; i++) {
-			if (_channels[i]) {
-				if (_channels[i]->isFinished()) {
-					delete _channels[i];
-					_channels[i] = NULL;
-				} else if (!_channels[i]->isPaused())
-					_channels[i]->mix(buf, len);
-			}
+	// mix all channels
+	for (int i = 0; i != NUM_CHANNELS; i++)
+		if (_channels[i]) {
+			if (_channels[i]->isFinished()) {
+				delete _channels[i];
+				_channels[i] = 0;
+			} else if (!_channels[i]->isPaused())
+				_channels[i]->mix(buf, len);
 		}
-	}
 }
 
-void SoundMixer::mixCallback(void *s, byte *samples, int len) {
+void Mixer::mixCallback(void *s, byte *samples, int len) {
 	assert(s);
 	assert(samples);
 	// Len is the number of bytes in the buffer; we divide it by
 	// four to get the number of samples (stereo 16 bit).
-	((SoundMixer *)s)->mix((int16 *)samples, len >> 2);
+	((Mixer *)s)->mix((int16 *)samples, len >> 2);
 }
 
-void SoundMixer::stopAll(bool force) {
-	StackLock lock(_mutex);
+void Mixer::stopAll() {
+	Common::StackLock lock(_mutex);
 	for (int i = 0; i != NUM_CHANNELS; i++) {
-		if (_channels[i] != NULL) {
-			if (force || !_channels[i]->isPermanent()) {
-				delete _channels[i];
-				_channels[i] = NULL;
-			}
+		if (_channels[i] != 0 && !_channels[i]->isPermanent()) {
+			delete _channels[i];
+			_channels[i] = 0;
 		}
 	}
 }
 
-void SoundMixer::stopID(int id) {
-	StackLock lock(_mutex);
+void Mixer::stopID(int id) {
+	Common::StackLock lock(_mutex);
 	for (int i = 0; i != NUM_CHANNELS; i++) {
-		if (_channels[i] != NULL && _channels[i]->getId() == id) {
+		if (_channels[i] != 0 && _channels[i]->getId() == id) {
 			delete _channels[i];
-			_channels[i] = NULL;
+			_channels[i] = 0;
 		}
 	}
 }
 
-void SoundMixer::stopHandle(PlayingSoundHandle handle) {
-	StackLock lock(_mutex);
+void Mixer::stopHandle(SoundHandle handle) {
+	Common::StackLock lock(_mutex);
 
 	// Simply ignore stop requests for handles of sounds that already terminated
-	if (!handle.isActive())
+	const int index = handle._val % NUM_CHANNELS;
+	if (!_channels[index] || _channels[index]->_handle._val != handle._val)
 		return;
 
-	int index = handle.getIndex();
+	delete _channels[index];
+	_channels[index] = 0;
+}
 
-	if ((index < 0) || (index >= NUM_CHANNELS)) {
-		warning("soundMixer::stopHandle has invalid index %d", index);
+void Mixer::setChannelVolume(SoundHandle handle, byte volume) {
+	Common::StackLock lock(_mutex);
+
+	const int index = handle._val % NUM_CHANNELS;
+	if (!_channels[index] || _channels[index]->_handle._val != handle._val)
 		return;
-	}
 
-	if (_channels[index]) {
-		delete _channels[index];
-		_channels[index] = NULL;
-	}
+	_channels[index]->setVolume(volume);
 }
 
-void SoundMixer::setChannelVolume(PlayingSoundHandle handle, byte volume) {
-	StackLock lock(_mutex);
+void Mixer::setChannelBalance(SoundHandle handle, int8 balance) {
+	Common::StackLock lock(_mutex);
 
-	if (!handle.isActive())
+	const int index = handle._val % NUM_CHANNELS;
+	if (!_channels[index] || _channels[index]->_handle._val != handle._val)
 		return;
 
-	int index = handle.getIndex();
-
-	if ((index < 0) || (index >= NUM_CHANNELS)) {
-		warning("soundMixer::setChannelVolume has invalid index %d", index);
-		return;
-	}
-
-	if (_channels[index])
-		_channels[index]->setVolume(volume);
+	_channels[index]->setBalance(balance);
 }
 
-void SoundMixer::setChannelBalance(PlayingSoundHandle handle, int8 balance) {
-	StackLock lock(_mutex);
+uint32 Mixer::getSoundElapsedTime(SoundHandle handle) {
+	Common::StackLock lock(_mutex);
 
-	if (!handle.isActive())
-		return;
+	const int index = handle._val % NUM_CHANNELS;
+	if (!_channels[index] || _channels[index]->_handle._val != handle._val)
+		return 0;
 
-	int index = handle.getIndex();
+	return _channels[index]->getElapsedTime();
+}
 
-	if ((index < 0) || (index >= NUM_CHANNELS)) {
-		warning("soundMixer::setChannelBalance has invalid index %d", index);
-		return;
+void Mixer::pauseAll(bool paused) {
+	Common::StackLock lock(_mutex);
+	for (int i = 0; i != NUM_CHANNELS; i++) {
+		if (_channels[i] != 0) {
+			_channels[i]->pause(paused);
+		}
 	}
-
-	if (_channels[index])
-		_channels[index]->setBalance(balance);
 }
 
-void SoundMixer::pauseAll(bool paused) {
-	_paused = paused;
-}
-
-void SoundMixer::pauseID(int id, bool paused) {
-	StackLock lock(_mutex);
+void Mixer::pauseID(int id, bool paused) {
+	Common::StackLock lock(_mutex);
 	for (int i = 0; i != NUM_CHANNELS; i++) {
-		if (_channels[i] != NULL && _channels[i]->getId() == id) {
+		if (_channels[i] != 0 && _channels[i]->getId() == id) {
 			_channels[i]->pause(paused);
 			return;
 		}
 	}
 }
 
-void SoundMixer::pauseHandle(PlayingSoundHandle handle, bool paused) {
-	StackLock lock(_mutex);
+void Mixer::pauseHandle(SoundHandle handle, bool paused) {
+	Common::StackLock lock(_mutex);
 
-	// Simply ignore pause/unpause requests for handles of sound that alreayd terminated
-	if (!handle.isActive())
+	// Simply ignore (un)pause requests for sounds that already terminated
+	const int index = handle._val % NUM_CHANNELS;
+	if (!_channels[index] || _channels[index]->_handle._val != handle._val)
 		return;
 
-	int index = handle.getIndex();
+	_channels[index]->pause(paused);
+}
 
-	if ((index < 0) || (index >= NUM_CHANNELS)) {
-		warning("soundMixer::pauseHandle has invalid index %d", index);
-		return;
-	}
+bool Mixer::isSoundIDActive(int id) {
+	Common::StackLock lock(_mutex);
+	for (int i = 0; i != NUM_CHANNELS; i++)
+		if (_channels[i] && _channels[i]->getId() == id)
+			return true;
+	return false;
+}
 
-	if (_channels[index])
-		_channels[index]->pause(paused);
+int Mixer::getSoundID(SoundHandle handle) {
+	Common::StackLock lock(_mutex);
+	const int index = handle._val % NUM_CHANNELS;
+	if (_channels[index] && _channels[index]->_handle._val == handle._val)
+		return _channels[index]->getId();
+	return 0;
 }
 
-bool SoundMixer::isSoundIDActive(int id) {
-	StackLock lock(_mutex);
+bool Mixer::isSoundHandleActive(SoundHandle handle) {
+	Common::StackLock lock(_mutex);
+	const int index = handle._val % NUM_CHANNELS;
+	return _channels[index] && _channels[index]->_handle._val == handle._val;
+}
+
+bool Mixer::hasActiveChannelOfType(SoundType type) {
+	Common::StackLock lock(_mutex);
 	for (int i = 0; i != NUM_CHANNELS; i++)
-		if (_channels[i] && _channels[i]->getId() == id)
+		if (_channels[i] && _channels[i]->_type == type)
 			return true;
 	return false;
 }
 
-void SoundMixer::setVolume(int volume) {
+void Mixer::setVolumeForSoundType(SoundType type, int volume) {
+	assert(0 <= type && type < ARRAYSIZE(_volumeForSoundType));
+
 	// Check range
-	if (volume > 256)
-		volume = 256;
+	if (volume > kMaxMixerVolume)
+		volume = kMaxMixerVolume;
 	else if (volume < 0)
 		volume = 0;
-	
-	_globalVolume = volume;
+
+	// TODO: Maybe we should do logarithmic (not linear) volume
+	// scaling? See also Player_V2::setMasterVolume
+
+	_volumeForSoundType[type] = volume;
 }
 
+int Mixer::getVolumeForSoundType(SoundType type) const {
+	assert(0 <= type && type < ARRAYSIZE(_volumeForSoundType));
 
-Channel::Channel(SoundMixer *mixer, PlayingSoundHandle *handle, bool /*isMusic*/, int id)
-	: _mixer(mixer), _handle(handle), _autofreeStream(true),
-	  _volume(255), _balance(0), _paused(false), _id(id), _samplesConsumed(0),
-	  _samplesDecoded(0), _converter(0), _input(NULL) {
-	assert(mixer);
+	return _volumeForSoundType[type];
 }
 
-Channel::Channel(SoundMixer *mixer, PlayingSoundHandle *handle, AudioStream *input,
-				bool autofreeStream, bool /*isMusic*/, bool reverseStereo, int id, bool permanent)
-	: _mixer(mixer), _handle(handle), _autofreeStream(autofreeStream),
-	  _permanent(permanent), _volume(255), _balance(0), _paused(false), _id(id),
-	  _samplesConsumed(0), _samplesDecoded(0), _converter(0), _input(input) {
+
+#pragma mark -
+#pragma mark --- Channel implementations ---
+#pragma mark -
+
+
+Channel::Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *input,
+				bool autofreeStream, bool reverseStereo, int id, bool permanent)
+	: _type(type), _mixer(mixer), _autofreeStream(autofreeStream),
+	  _volume(Mixer::kMaxChannelVolume), _balance(0), _pauseLevel(0), _id(id), _samplesConsumed(0),
+	  _samplesDecoded(0), _mixerTimeStamp(0), _converter(0), _input(input), _permanent(permanent) {
 	assert(mixer);
 	assert(input);
 
@@ -389,8 +387,6 @@
 	delete _converter;
 	if (_autofreeStream)
 		delete _input;
-	if (_handle)
-		_handle->resetIndex();
 }
 
 /* len indicates the number of sample *pairs*. So a value of
@@ -410,27 +406,55 @@
 		// slightly odd divisor: the 255 reflects the fact that the maximal
 		// value for _volume is 255, while the 127 is there because the
 		// balance value ranges from -127 to 127.  The mixer (music/sound)
-		// volume is in the range 0 - 256.
+		// volume is in the range 0 - kMaxMixerVolume.
 		// Hence, the vol_l/vol_r values will be in that range, too
-		
-		int vol = _mixer->getVolume() * _volume;
+
+		int vol = _mixer->getVolumeForSoundType(_type) * _volume;
 		st_volume_t vol_l, vol_r;
 
 		if (_balance == 0) {
-			vol_l = vol / 255;
-			vol_r = vol / 255;
+			vol_l = vol / Mixer::kMaxChannelVolume;
+			vol_r = vol / Mixer::kMaxChannelVolume;
 		} else if (_balance < 0) {
-			vol_l = vol / 255;
-			vol_r = ((127 + _balance) * vol) / (255 * 127);
+			vol_l = vol / Mixer::kMaxChannelVolume;
+			vol_r = ((127 + _balance) * vol) / (Mixer::kMaxChannelVolume * 127);
 		} else {
-			vol_l = ((127 - _balance) * vol) / (255 * 127);
-			vol_r = vol / 255;
+			vol_l = ((127 - _balance) * vol) / (Mixer::kMaxChannelVolume * 127);
+			vol_r = vol / Mixer::kMaxChannelVolume;
 		}
 
 		_samplesConsumed = _samplesDecoded;
+		_mixerTimeStamp = g_driver->getMillis();
 
 		_converter->flow(*_input, data, len, vol_l, vol_r);
 
 		_samplesDecoded += len;
 	}
 }
+
+uint32 Channel::getElapsedTime() {
+	if (_mixerTimeStamp == 0)
+		return 0;
+
+	// Convert the number of samples into a time duration. To avoid
+	// overflow, this has to be done in a somewhat non-obvious way.
+
+	uint rate = _mixer->getOutputRate();
+
+	uint32 seconds = _samplesConsumed / rate;
+	uint32 milliseconds = (1000 * (_samplesConsumed % rate)) / rate;
+
+	uint32 delta = g_driver->getMillis() - _mixerTimeStamp;
+
+	// In theory it would seem like a good idea to limit the approximation
+	// so that it never exceeds the theoretical upper bound set by
+	// _samplesDecoded. Meanwhile, back in the real world, doing so makes
+	// the Broken Sword cutscenes noticeably jerkier. I guess the mixer
+	// isn't invoked at the regular intervals that I first imagined.
+
+	// FIXME: This won't work very well if the sound is paused.
+	return 1000 * seconds + milliseconds + delta;
+}
+
+
+} // End of namespace Audio

Modified: residual/trunk/mixer/mixer.h
===================================================================
--- residual/trunk/mixer/mixer.h	2008-03-10 20:34:34 UTC (rev 31099)
+++ residual/trunk/mixer/mixer.h	2008-03-10 23:04:55 UTC (rev 31100)
@@ -1,19 +1,19 @@
 /* Residual - Virtual machine to run LucasArts' 3D adventure games
- * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
+ * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org)
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
 
- * This library is distributed in the hope that it will be useful,
+ * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * $URL$
  * $Id$
@@ -25,32 +25,51 @@
 
 #include "common/sys.h"
 #include "common/platform.h"
+#include "common/mutex.h"
 
+namespace Audio {
+
 class AudioStream;
 class Channel;
+class Mixer;
 
-class PlayingSoundHandle {
+/**
+ * A SoundHandle instances corresponds to a specific sound
+ * being played via the mixer. It can be used to control that
+ * sound (pause it, stop it, etc.).
+ * @see The Mixer class
+ */
+class SoundHandle {
 	friend class Channel;
-	friend class SoundMixer;
-	int val;
-	int getIndex() const { return val - 1; }
-	void setIndex(int i) { val = i + 1; }
-	void resetIndex() { val = 0; }
+	friend class Mixer;
+	uint32 _val;
 public:
-	PlayingSoundHandle() { resetIndex(); }
-	bool isActive() const { return val > 0; }
+	inline SoundHandle() : _val(0xFFFFFFFF) {}
 };
 
-class SoundMixer {
+/**
+ * The main audio mixer handles mixing of an arbitrary number of
+ * input audio streams (in the form of AudioStream instances).
+ */
+class Mixer {
 public:
-	enum {
+	/**
+	 * Various flags which can be bit-ORed and then passed to
+	 * Mixer::playRaw resp. makeLinearInputStream to control their
+	 * behavior.
+	 *
+	 * Engine authors are advised not to rely on a certain value or
+	 * order of these flags (in particular, do not store them verbatim
+	 * in savestates).
+	 */
+	enum RawFlags {
 		/** unsigned samples (default: signed) */
 		FLAG_UNSIGNED = 1 << 0,
 
 		/** sound is 16 bits wide (default: 8bit) */
 		FLAG_16BITS = 1 << 1,
 
-		/** sample is little endian (default: big endian) */
+		/** samples are little endian (default: big endian) */
 		FLAG_LITTLE_ENDIAN = 1 << 2,
 
 		/** sound is in stereo (default: mono) */
@@ -66,75 +85,99 @@
 		FLAG_LOOP = 1 << 6
 	};
 
+	enum SoundType {
+		kPlainSoundType = 0,
+
+		kMusicSoundType = 1,
+		kSFXSoundType = 2,
+		kSpeechSoundType = 3
+	};
+
+	enum {
+		kMaxChannelVolume = 255,
+		kMaxMixerVolume = 256
+	};
+
 private:
 	enum {
 		NUM_CHANNELS = 32
 	};
 
-	MutexRef _mutex;
+	Common::Mutex _mutex;
 
-	Channel *_premixChannel;
+	int _volumeForSoundType[4];
 
-	uint _outputRate;
-
-	int _globalVolume;
-
-	bool _paused;
-	
+	uint32 _handleSeed;
 	Channel *_channels[NUM_CHANNELS];
 
 	bool _mixerReady;
 
 public:
-	SoundMixer();
-	~SoundMixer();
+	Mixer();
+	~Mixer();
 
 
 
 	/**
 	 * Is the mixer ready and setup? This may not be the case on systems which
 	 * don't support digital sound output. In that case, the mixer proc may
-	 * never be called. That in turn can cause breakage in games which use the
-	 * premix callback for syncing. In particular, the Adlib MIDI emulation...
+	 * never be called. That in turn can cause breakage in games which try to
+	 * sync with an audio stream. In particular, the Adlib MIDI emulation...
 	 *
 	 * @return whether the mixer is ready and setup
 	 */
-	bool isReady() const { return _mixerReady; };
+	bool isReady() const { return _mixerReady; }
 
 
 
 	/**
-	 * Set the premix stream. This is mainly used for the adlib music, but
-	 * is not limited to it. The premix stream is invoked by the mixer whenever
-	 * it needs to generate any data, before any other mixing takes place.
-	 */
-	void setupPremix(AudioStream *stream);
-
-
-
-	/**
 	 * Start playing the given raw sound data.
 	 * Internally, this simply creates an audio input stream wrapping the data
 	 * (using the makeLinearInputStream factory function), which is then
 	 * passed on to playInputStream.
 	 */
-	void playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags,
-				int id = -1, byte volume = 255, int8 balance = 0,
-				uint32 loopStart = 0, uint32 loopEnd = 0);
+	void playRaw(
+		SoundType type,
+		SoundHandle *handle,
+		void *sound, uint32 size, uint rate, byte flags,
+		int id = -1, byte volume = 255, int8 balance = 0,
+		uint32 loopStart = 0, uint32 loopEnd = 0);
 
 	/**
 	 * Start playing the given audio input stream.
+	 *
+	 * Note that the sound id assigned below is unique. At most one stream
+	 * with a given idea can play at any given time. Trying to play a sound
+	 * with an id that is already in use causes the new sound to be not played.
+	 *
+	 * @param type	the type (voice/sfx/music) of the stream
+	 * @param handle	a SoundHandle which can be used to reference and control
+	 *                  the stream via suitable mixer methods
+	 * @param input	the actual AudioStream to be played
+	 * @param id	a unique id assigned to this stream
+	 * @param volume	the volume with which to play the sound, ranging from 0 to 255
+	 * @param balance	the balance with which to play the sound, ranging from -128 to 127
+	 * @param autofreeStream	a flag indicating whether the stream should be
+	 *                          freed after playback finished
+	 * @param permanent	a flag indicating whether a plain stopAll call should
+	 *                  not stop this particular stream
+	 * @param reverseStereo	a flag indicating whether left and right channels shall be swapped
 	 */
-	void playInputStream(PlayingSoundHandle *handle, AudioStream *input, bool isMusic,
-				int id = -1, byte volume = 255, int8 balance = 0,
-				bool autofreeStream = true, bool permanent = false);
+	void playInputStream(
+		SoundType type,
+		SoundHandle *handle,
+		AudioStream *input,
+		int id = -1, byte volume = 255, int8 balance = 0,
+		bool autofreeStream = true,
+		bool permanent = false,
+		bool reverseStereo = false);
 
 
 
 	/**
 	 * Stop all currently playing sounds.
 	 */
-	void stopAll(bool force = false);
+	void stopAll();
 
 	/**
 	 * Stop playing the sound with given ID.
@@ -148,15 +191,15 @@
 	 *
 	 * @param handle the sound to affect
 	 */
-	void stopHandle(PlayingSoundHandle handle);
+	void stopHandle(SoundHandle handle);
 
 
 
 	/**
-	 * Pause/unpause the mixer (this temporarily stops all audio processing,
-	 * including all regular channels and the premix channel).
+	 * Pause/unpause all sounds, including all regular and permanent
+	 * channels
 	 *
-	 * @param paused true to pause the mixer, false to unpause it
+	 * @param paused true to pause everything, false to unpause
 	 */
 	void pauseAll(bool paused);
 
@@ -174,7 +217,7 @@
 	 * @param handle the sound to affect
 	 * @param paused true to pause the sound, false to unpause it
 	 */
-	void pauseHandle(PlayingSoundHandle handle, bool paused);
+	void pauseHandle(SoundHandle handle, bool paused);
 
 
 
@@ -187,21 +230,30 @@
 	bool isSoundIDActive(int id);
 
 	/**
-	 * Check if the mixer is paused (using pauseAll).
+	 * Get the sound ID of handle sound
 	 *
-	 * @return true if the mixer is paused
+	 * @param handle sound to query
+	 * @return sound ID if active
 	 */
-	bool isPaused();
+	int getSoundID(SoundHandle handle);
 
+	/**
+	 * Check if a sound with the given handle is active.
+	 *
+	 * @param handle sound to query
+	 * @return true if the sound is active
+	 */
+	bool isSoundHandleActive(SoundHandle handle);
 
 
+
 	/**
 	 * Set the channel volume for the given handle.
 	 *
 	 * @param handle the sound to affect
 	 * @param volume the new channel volume (0 - 255)
 	 */
-	void setChannelVolume(PlayingSoundHandle handle, byte volume);
+	void setChannelVolume(SoundHandle handle, byte volume);
 
 	/**
 	 * Set the channel balance for the given handle.
@@ -210,26 +262,38 @@
 	 * @param balance the new channel balance:
 	 *        (-127 ... 0 ... 127) corresponds to (left ... center ... right)
 	 */
-	void setChannelBalance(PlayingSoundHandle handle, int8 balance);
+	void setChannelBalance(SoundHandle handle, int8 balance);
 
 	/**
 	 * Get approximation of for how long the channel has been playing.
 	 */
-	uint32 getSoundElapsedTime(PlayingSoundHandle handle);
+	uint32 getSoundElapsedTime(SoundHandle handle);
 
 	/**
-	 * Set the global volume.
+	 * Check whether any channel of the given sound type is active.
+	 * For example, this can be used to check whether any SFX sound
+	 * is currently playing, by checking for type kSFXSoundType.
 	 *
-	 * @param volume the new global volume, 0-256
+	 * @param  type the sound type to look for
+	 * @return true if any channels of the specified type are active.
 	 */
-	void setVolume(int volume);
+	bool hasActiveChannelOfType(SoundType type);
 
 	/**
+	 * Set the volume for the given sound type.
+	 *
+	 * @param type the sound type
+	 * @param volume the new global volume, 0-kMaxMixerVolume
+	 */
+	void setVolumeForSoundType(SoundType type, int volume);
+
+	/**
 	 * Query the global volume.
 	 *
-	 * @return the global music volume, 0-256
+	 * @param type the sound type
+	 * @return the global music volume, 0-kMaxMixerVolume
 	 */
-	int getVolume() const { return _globalVolume; }
+	int getVolumeForSoundType(SoundType type) const;
 
 	/**
 	 * Query the system's audio output sample rate. This returns
@@ -237,23 +301,29 @@
 	 *
 	 * @return the output sample rate in Hz
 	 */
-	uint getOutputRate() const { return _outputRate; }
+	uint getOutputRate() const;
 
-private:
-	void insertChannel(PlayingSoundHandle *handle, Channel *chan);
+protected:
+	void insertChannel(SoundHandle *handle, Channel *chan);
 
 	/**
 	 * Internal main method -- all the actual mixing work is done from here.
 	 */
 	void mix(int16 * buf, uint len);
 
+	// FIXME: temporary "public" to allow access to mixCallback
+	// from within OSystem::makeMixer()
+public:
 	/**
 	 * The mixer callback function, passed on to OSystem::setSoundCallback().
 	 * This simply calls the mix() method.
 	 */
 	static void mixCallback(void *s, byte *samples, int len);
+
+	void setReady(bool ready) { _mixerReady = ready; }
 };
 
-extern SoundMixer *g_mixer;
 
+} // End of namespace Audio
+
 #endif

Modified: residual/trunk/mixer/rate.cpp
===================================================================
--- residual/trunk/mixer/rate.cpp	2008-03-10 20:34:34 UTC (rev 31099)
+++ residual/trunk/mixer/rate.cpp	2008-03-10 23:04:55 UTC (rev 31100)
@@ -1,19 +1,19 @@
 /* Residual - Virtual machine to run LucasArts' 3D adventure games
- * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
+ * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org)
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
 
- * This library is distributed in the hope that it will be useful,
+ * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * $URL$
  * $Id$
@@ -30,16 +30,15 @@
 
 #include "common/sys.h"
 #include "common/debug.h"
+#include "common/frac.h"
 
+#include "mixer/audiostream.h"
 #include "mixer/rate.h"
-#include "mixer/audiostream.h"
+#include "mixer/mixer.h"
 
-/**
- * The precision of the fractional computations used by the rate converter.
- * Normally you should never have to modify this value.
- */
-#define FRAC_BITS 16
+namespace Audio {
 
+
 /**
  * The size of the intermediate input cache. Bigger values may increase
  * performance, but only until some point (depends largely on cache size,
@@ -48,7 +47,105 @@
  */
 #define INTERMEDIATE_BUFFER_SIZE 512
 
+
 /**
+ * Audio rate converter based on simple resampling. Used when no
+ * interpolation is required.
+ *
+ * Limited to sampling frequency <= 65535 Hz.
+ */
+template<bool stereo, bool reverseStereo>
+class SimpleRateConverter : public RateConverter {
+protected:
+	st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
+	const st_sample_t *inPtr;
+	int inLen;
+
+	/** position of how far output is ahead of input */
+	/** Holds what would have been opos-ipos */
+	long opos;
+
+	/** fractional position increment in the output stream */
+	long opos_inc;
+
+public:
+	SimpleRateConverter(st_rate_t inrate, st_rate_t outrate);
+	int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
+	int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
+		return ST_SUCCESS;
+	}
+};
+
+
+/*
+ * Prepare processing.
+ */
+template<bool stereo, bool reverseStereo>
+SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) {
+	if ((inrate % outrate) != 0) {
+		error("Input rate must be a multiple of output rate to use rate effect");
+	}
+
+	if (inrate >= 65536 || outrate >= 65536) {
+		error("rate effect can only handle rates < 65536");
+	}
+
+	opos = 1;
+
+	/* increment */
+	opos_inc = inrate / outrate;
+
+	inLen = 0;
+}
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+template<bool stereo, bool reverseStereo>
+int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
+	st_sample_t *ostart, *oend;
+
+	ostart = obuf;
+	oend = obuf + osamp * 2;
+
+	while (obuf < oend) {
+
+		// read enough input samples so that opos >= 0
+		do {
+			// Check if we have to refill the buffer
+			if (inLen == 0) {
+				inPtr = inBuf;
+				inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
+				if (inLen <= 0)
+					return ST_EOF;
+			}
+			inLen -= (stereo ? 2 : 1);
+			opos--;
+			if (opos >= 0) {
+				inPtr += (stereo ? 2 : 1);
+			}
+		} while (opos >= 0);
+
+		st_sample_t out0, out1;
+		out0 = *inPtr++;
+		out1 = (stereo ? *inPtr++ : out0);
+
+		// Increment output position
+		opos += opos_inc;
+
+		// output left channel
+		clampedAdd(obuf[reverseStereo    ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
+
+		// output right channel
+		clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
+
+		obuf += 2;
+	}
+	return ST_SUCCESS;
+}
+
+/**
  * Audio rate converter based on simple linear Interpolation.
  *
  * The use of fractional increment allows us to use no buffer. It
@@ -67,56 +164,46 @@
 	int inLen;
 
 	/** fractional position of the output stream in input stream unit */
-	unsigned long opos, opos_frac;
+	frac_t opos;
 
 	/** fractional position increment in the output stream */
-	unsigned long opos_inc, opos_inc_frac;
+	frac_t opos_inc;
 
-	/** position in the input stream (integer) */
-	unsigned long ipos;
-
 	/** last sample(s) in the input stream (left/right channel) */
-	st_sample_t ilast[2];
+	st_sample_t ilast0, ilast1;
 	/** current sample(s) in the input stream (left/right channel) */
-	st_sample_t icur[2];
+	st_sample_t icur0, icur1;
 
 public:
 	LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
 	int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
-	int drain(st_sample_t * /*obuf*/, st_size_t /*osamp*/, st_volume_t /*vol*/) {
-		return (ST_SUCCESS);
+	int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
+		return ST_SUCCESS;
 	}
 };
 
+
 /*
  * Prepare processing.
  */
 template<bool stereo, bool reverseStereo>
 LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
-	unsigned long incr;
-
-	if (inrate == outrate) {
-		error("Input and Output rates must be different to use rate effect");
-	}
-
 	if (inrate >= 65536 || outrate >= 65536) {
 		error("rate effect can only handle rates < 65536");
 	}
 
-	opos_frac = 0;
-	opos = 1;
+	opos = FRAC_ONE;
 
-	/* increment */
-	incr = (inrate << FRAC_BITS) / outrate;
+	// Compute the linear interpolation increment.
+	// This will overflow if inrate >= 2^16, and underflow if outrate >= 2^16.
+	// Also, if the quotient of the two rate becomes too small / too big, that
+	// would cause problems, but since we rarely scale from 1 to 65536 Hz or vice
+	// versa, I think we can live with that limiation ;-).
+	opos_inc = (inrate << FRAC_BITS) / outrate;
 
-	opos_inc_frac = incr & ((1UL << FRAC_BITS) - 1);
-	opos_inc = incr >> FRAC_BITS;
+	ilast0 = ilast1 = 0;
+	icur0 = icur1 = 0;
 
-	ipos = 0;
-
-	ilast[0] = ilast[1] = 0;
-	icur[0] = icur[1] = 0;
-
 	inLen = 0;
 }
 
@@ -127,66 +214,60 @@
 template<bool stereo, bool reverseStereo>
 int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
 	st_sample_t *ostart, *oend;
-	st_sample_t out[2];
 
-	const int numChannels = stereo ? 2 : 1;
-	int i;
-
 	ostart = obuf;
 	oend = obuf + osamp * 2;
 
 	while (obuf < oend) {
 
-		// read enough input samples so that ipos > opos
-		while (ipos <= opos) {
+		// read enough input samples so that opos < 0
+		while ((frac_t)FRAC_ONE <= opos) {
 			// Check if we have to refill the buffer
 			if (inLen == 0) {
 				inPtr = inBuf;
 				inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
 				if (inLen <= 0)
-					goto the_end;
+					return ST_EOF;
 			}
-			for (i = 0; i < numChannels; i++) {
-				ilast[i] = icur[i];
-				icur[i] = *inPtr++;
-				inLen--;
+			inLen -= (stereo ? 2 : 1);
+			ilast0 = icur0;
+			icur0 = *inPtr++;
+			if (stereo) {
+				ilast1 = icur1;
+				icur1 = *inPtr++;
 			}
-			ipos++;
+			opos -= FRAC_ONE;
 		}
 
 		// Loop as long as the outpos trails behind, and as long as there is
 		// still space in the output buffer.
-		while (ipos > opos) {
-
+		while (opos < (frac_t)FRAC_ONE && obuf < oend) {
 			// interpolate
-			out[0] = out[1] = (st_sample_t)(ilast[0] + (((icur[0] - ilast[0]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
+			st_sample_t out0, out1;
+			out0 = (st_sample_t)(ilast0 + (((icur0 - ilast0) * opos + FRAC_HALF) >> FRAC_BITS));
+			out1 = (stereo ?
+						  (st_sample_t)(ilast1 + (((icur1 - ilast1) * opos + FRAC_HALF) >> FRAC_BITS)) :
+						  out0);
 
-			if (stereo) {
-				// interpolate
-				out[reverseStereo ? 0 : 1] = (st_sample_t)(ilast[1] + (((icur[1] - ilast[1]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
-			}
-
 			// output left channel
-			clampedAdd(*obuf++, (out[0] * (int)vol_l) >> 8);
+			clampedAdd(obuf[reverseStereo    ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
 
 			// output right channel
-			clampedAdd(*obuf++, (out[1] * (int)vol_r) >> 8);
+			clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
 
+			obuf += 2;
+
 			// Increment output position
-			unsigned long tmp = opos_frac + opos_inc_frac;
-			opos += opos_inc + (tmp >> FRAC_BITS);
-			opos_frac = tmp & ((1UL << FRAC_BITS) - 1);
-
-			// Abort if we reached the end of the output buffer
-			if (obuf >= oend)
-				goto the_end;
+			opos += opos_inc;
 		}
 	}
-
-the_end:
-	return (ST_SUCCESS);
+	return ST_SUCCESS;
 }
 
+
+#pragma mark -
+
+
 /**
  * Simple audio rate converter for the case that the inrate equals the outrate.
  */
@@ -202,10 +283,10 @@
 
 	virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
 		assert(input.isStereo() == stereo);
-		
+
 		st_sample_t *ptr;
 		st_size_t len;
-		
+
 		if (stereo)
 			osamp *= 2;
 
@@ -218,53 +299,57 @@
 
 		// Read up to 'osamp' samples into our temporary buffer
 		len = input.readBuffer(_buffer, osamp);
-		
+
 		// Mix the data into the output buffer
 		ptr = _buffer;
-		while (len--) {
-			st_sample_t tmp0, tmp1;
-			tmp0 = tmp1 = *ptr++;
-			if (stereo) {
-				if (reverseStereo)
-					tmp0 = *ptr++;
-				else
-					tmp1 = *ptr++;
-				len--;
-			}
+		for (; len > 0; len -= (stereo ? 2 : 1)) {
+			st_sample_t out0, out1;
+			out0 = *ptr++;
+			out1 = (stereo ? *ptr++ : out0);
 
 			// output left channel
-			clampedAdd(*obuf++, (tmp0 * (int)vol_l) >> 8);
-	
+			clampedAdd(obuf[reverseStereo    ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
+
 			// output right channel
-			clampedAdd(*obuf++, (tmp1 * (int)vol_r) >> 8);
+			clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
+
+			obuf += 2;
 		}
-		return (ST_SUCCESS);
+		return ST_SUCCESS;
 	}
-	virtual int drain(st_sample_t * /*obuf*/, st_size_t /*osamp*/, st_volume_t /*vol*/) {
-		return (ST_SUCCESS);
+
+	virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
+		return ST_SUCCESS;
 	}
 };
 
+
+#pragma mark -
+
+template<bool stereo, bool reverseStereo>
+RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate) {
+	if (inrate != outrate) {
+		if ((inrate % outrate) == 0) {
+			return new SimpleRateConverter<stereo, reverseStereo>(inrate, outrate);
+		} else {
+			return new LinearRateConverter<stereo, reverseStereo>(inrate, outrate);
+		}
+	} else {
+		return new CopyRateConverter<stereo, reverseStereo>();
+	}
+}
+
 /**
  * Create and return a RateConverter object for the specified input and output rates.
  */
 RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) {
-	if (inrate != outrate) {
-		if (stereo) {
-			if (reverseStereo)
-				return new LinearRateConverter<true, true>(inrate, outrate);
-			else
-				return new LinearRateConverter<true, false>(inrate, outrate);
-		} else
-			return new LinearRateConverter<false, false>(inrate, outrate);
-		//return new ResampleRateConverter(inrate, outrate, 1);
-	} else {
-		if (stereo) {
-			if (reverseStereo)
-				return new CopyRateConverter<true, true>();
-			else
-				return new CopyRateConverter<true, false>();
-		} else
-			return new CopyRateConverter<false, false>();
-	}
+	if (stereo) {
+		if (reverseStereo)
+			return makeRateConverter<true, true>(inrate, outrate);
+		else
+			return makeRateConverter<true, false>(inrate, outrate);
+	} else
+		return makeRateConverter<false, false>(inrate, outrate);
 }
+
+} // End of namespace Audio

Modified: residual/trunk/mixer/rate.h
===================================================================
--- residual/trunk/mixer/rate.h	2008-03-10 20:34:34 UTC (rev 31099)
+++ residual/trunk/mixer/rate.h	2008-03-10 23:04:55 UTC (rev 31100)
@@ -1,19 +1,19 @@
 /* Residual - Virtual machine to run LucasArts' 3D adventure games
- * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
+ * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org)
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
 
- * This library is distributed in the hope that it will be useful,
+ * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * $URL$
  * $Id$
@@ -27,17 +27,24 @@
 
 class AudioStream;
 
+
+namespace Audio {
+
 typedef int16 st_sample_t;
 typedef uint16 st_volume_t;
 typedef uint32 st_size_t;
 typedef uint32 st_rate_t;
 
 /* Minimum and maximum values a sample can hold. */
-#define ST_SAMPLE_MAX 0x7fffL
-#define ST_SAMPLE_MIN (-ST_SAMPLE_MAX - 1L)
+enum {
+	ST_SAMPLE_MAX = 0x7fffL,
+	ST_SAMPLE_MIN = (-ST_SAMPLE_MAX - 1L)
+};
 
-#define ST_EOF (-1)
-#define ST_SUCCESS (0)
+enum {
+	ST_EOF = -1,
+	ST_SUCCESS = 0
+};
 
 static inline void clampedAdd(int16& a, int b) {
 	register int val;
@@ -59,12 +66,6 @@
 #endif
 }
 
-// Q&D hack to get this SOX stuff to work
-#define st_report warning
-#define st_warn warning
-#define st_fail error
-
-
 class RateConverter {
 public:
 	RateConverter() {}
@@ -75,4 +76,6 @@
 
 RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo = false);
 
+} // End of namespace Audio
+
 #endif


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