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

mthreepwood at users.sourceforge.net mthreepwood at users.sourceforge.net
Tue Jan 27 12:46:54 CET 2009


Revision: 36097
          http://scummvm.svn.sourceforge.net/scummvm/?rev=36097&view=rev
Author:   mthreepwood
Date:     2009-01-27 11:46:54 +0000 (Tue, 27 Jan 2009)

Log Message:
-----------
Add support for looping ADPCM sounds. I'm also adding decoding of the "standard" IMA ADPCM code (aka Intel DVI ADPCM). This is from Mohawk. ;)

Modified Paths:
--------------
    scummvm/trunk/sound/adpcm.cpp
    scummvm/trunk/sound/adpcm.h

Modified: scummvm/trunk/sound/adpcm.cpp
===================================================================
--- scummvm/trunk/sound/adpcm.cpp	2009-01-27 10:27:53 UTC (rev 36096)
+++ scummvm/trunk/sound/adpcm.cpp	2009-01-27 11:46:54 UTC (rev 36097)
@@ -31,13 +31,11 @@
 
 namespace Audio {
 
-// TODO: Switch from a SeekableReadStream to a plain ReadStream. This requires
-// some internal refactoring but is definitely possible and will increase the
-// flexibility of this code.
 class ADPCMInputStream : public AudioStream {
 private:
 	Common::SeekableReadStream *_stream;
 	bool _disposeAfterUse;
+	int32 _startpos;
 	int32 _endpos;
 	int _channels;
 	typesADPCM _type;
@@ -47,6 +45,8 @@
 	uint16 _chunkData;
 	int _blockLen;
 	int _rate;
+	uint _numLoops;
+	uint _curLoop;
 
 	struct ADPCMChannelStatus {
 		byte predictor;
@@ -58,9 +58,11 @@
 	};
 
 	struct adpcmStatus {
-		// IMA
-		int32 last;
-		int32 stepIndex;
+		// OKI/IMA
+		struct {
+			int32 last;
+			int32 stepIndex;
+		} ima_ch[2];
 
 		// MS ADPCM
 		ADPCMChannelStatus ch[2];
@@ -71,18 +73,20 @@
 		double d0, d1;
 	} _status;
 
+	void reset();
 	int16 stepAdjust(byte);
 	int16 decodeOKI(byte);
-	int16 decodeMSIMA(byte);
+	int16 decodeIMA(byte code, bool right = false); // Default to using the left channel/using one channel
 	int16 decodeMS(ADPCMChannelStatus *c, byte);
 	int16 decodeTinsel(int16, double);
 
 public:
-	ADPCMInputStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels = 2, uint32 blockAlign = 0);
+	ADPCMInputStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels = 2, uint32 blockAlign = 0, uint numLoops = 1);
 	~ADPCMInputStream();
 
 	int readBuffer(int16 *buffer, const int numSamples);
 	int readBufferOKI(int16 *buffer, const int numSamples);
+	int readBufferIMA(int16 *buffer, const int numSamples);
 	int readBufferMSIMA1(int16 *buffer, const int numSamples);
 	int readBufferMSIMA2(int16 *buffer, const int numSamples);
 	int readBufferMS(int channels, int16 *buffer, const int numSamples);
@@ -92,7 +96,7 @@
 	int readBufferTinsel8(int channels, int16 *buffer, const int numSamples);
 
 	bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); }
-	bool isStereo() const	{ return false; }
+	bool isStereo() const	{ return _channels == 2; }
 	int getRate() const	{ return _rate; }
 };
 
@@ -100,32 +104,29 @@
 // Dialogic or Oki ADPCM coding format aka VOX.
 // See also <http://www.comptek.ru/telephony/tnotes/tt1-13.html>
 //
+// IMA ADPCM support is based on
+//   <http://wiki.multimedia.cx/index.php?title=IMA_ADPCM>
+//
+// Apple IMA ADPCM support is based on
+//   <http://wiki.multimedia.cx/index.php?title=Apple_QuickTime_IMA_ADPCM>
+//
 // In addition, also MS IMA ADPCM is supported. See
 //   <http://wiki.multimedia.cx/index.php?title=Microsoft_IMA_ADPCM>.
 
-ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign)
-	: _stream(stream), _disposeAfterUse(disposeAfterUse), _channels(channels), _type(type), _blockAlign(blockAlign), _rate(rate) {
-
-	_status.last = 0;
-	_status.stepIndex = 0;
-	_status.d0 = _status.d1 = 0;
-	memset(_status.ch, 0, sizeof(_status.ch));
-	_endpos = stream->pos() + size;
-	_blockLen = 0;
-	_blockPos = _blockAlign; // To make sure first header is read
-	_chunkPos = 0;
-	_chunkData = 0;
-
+ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign, uint numLoops)
+	: _stream(stream), _disposeAfterUse(disposeAfterUse), _channels(channels), _type(type), _blockAlign(blockAlign), _rate(rate), _numLoops(numLoops) {
+	
 	if (type == kADPCMMSIma && blockAlign == 0)
-		error("ADPCMInputStream(): blockAlign isn't specifiled for MS IMA ADPCM");
+		error("ADPCMInputStream(): blockAlign isn't specified for MS IMA ADPCM");
 	if (type == kADPCMMS && blockAlign == 0)
-		error("ADPCMInputStream(): blockAlign isn't specifiled for MS ADPCM");
+		error("ADPCMInputStream(): blockAlign isn't specified for MS ADPCM");
+	
 	if (type == kADPCMTinsel4 && blockAlign == 0)
-		error("ADPCMInputStream(): blockAlign isn't specifiled for Tinsel 4-bit ADPCM");
+		error("ADPCMInputStream(): blockAlign isn't specified for Tinsel 4-bit ADPCM");
 	if (type == kADPCMTinsel6 && blockAlign == 0)
-		error("ADPCMInputStream(): blockAlign isn't specifiled for Tinsel 6-bit ADPCM");
+		error("ADPCMInputStream(): blockAlign isn't specified for Tinsel 6-bit ADPCM");
 	if (type == kADPCMTinsel8 && blockAlign == 0)
-		error("ADPCMInputStream(): blockAlign isn't specifiled for Tinsel 8-bit ADPCM");
+		error("ADPCMInputStream(): blockAlign isn't specified for Tinsel 8-bit ADPCM");
 
 	if (type == kADPCMTinsel4 && channels != 1)
 		error("ADPCMInputStream(): Tinsel 4-bit ADPCM only supports mono");
@@ -133,6 +134,11 @@
 		error("ADPCMInputStream(): Tinsel 6-bit ADPCM only supports mono");
 	if (type == kADPCMTinsel8 && channels != 1)
 		error("ADPCMInputStream(): Tinsel 8-bit ADPCM only supports mono");
+	
+	_startpos = stream->pos();
+	_endpos = _startpos + size;
+	_curLoop = 0;	
+	reset();
 }
 
 ADPCMInputStream::~ADPCMInputStream() {
@@ -140,28 +146,57 @@
 		delete _stream;
 }
 
+void ADPCMInputStream::reset() {
+	memset(&_status, 0, sizeof(_status));
+	_blockLen = 0;
+	_blockPos = _blockAlign; // To make sure first header is read
+	_chunkPos = 0;
+	_blockPos = 0;
+}
+
 int ADPCMInputStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samplesDecoded = 0;
 	switch (_type) {
 	case kADPCMOki:
-		return readBufferOKI(buffer, numSamples);
+		samplesDecoded = readBufferOKI(buffer, numSamples);
+		break;
 	case kADPCMMSIma:
 		if (_channels == 1)
-			return readBufferMSIMA1(buffer, numSamples);
+			samplesDecoded = readBufferMSIMA1(buffer, numSamples);
 		else
-			return readBufferMSIMA2(buffer, numSamples);
+			samplesDecoded = readBufferMSIMA2(buffer, numSamples);
+		break;
 	case kADPCMMS:
-		return readBufferMS(_channels, buffer, numSamples);
+		samplesDecoded = readBufferMS(_channels, buffer, numSamples);
+		break;
 	case kADPCMTinsel4:
-		return readBufferTinsel4(_channels, buffer, numSamples);
+		samplesDecoded = readBufferTinsel4(_channels, buffer, numSamples);
+		break;
 	case kADPCMTinsel6:
-		return readBufferTinsel6(_channels, buffer, numSamples);
+		samplesDecoded = readBufferTinsel6(_channels, buffer, numSamples);
+		break;
 	case kADPCMTinsel8:
-		return readBufferTinsel8(_channels, buffer, numSamples);
+		samplesDecoded = readBufferTinsel8(_channels, buffer, numSamples);
+		break;
+	case kADPCMIma:
+		samplesDecoded = readBufferIMA(buffer, numSamples);
+		break;
 	default:
 		error("Unsupported ADPCM encoding");
 		break;
 	}
-	return 0;
+	
+	// Loop if necessary
+	if (samplesDecoded < numSamples || _stream->pos() == _endpos) {
+		_curLoop++;
+		if (_numLoops == 0 || _curLoop < _numLoops) {
+			reset();
+			_stream->seek(_startpos);
+			return samplesDecoded + readBuffer(buffer + samplesDecoded, numSamples - samplesDecoded);
+		}
+	}
+	
+	return samplesDecoded;
 }
 
 int ADPCMInputStream::readBufferOKI(int16 *buffer, const int numSamples) {
@@ -178,7 +213,20 @@
 	return samples;
 }
 
+int ADPCMInputStream::readBufferIMA(int16 *buffer, const int numSamples) {
+	int samples;
+	byte data;
 
+	assert(numSamples % 2 == 0);
+
+	for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+		data = _stream->readByte();
+		buffer[samples] = TO_LE_16(decodeIMA((data >> 4) & 0x0f));
+		buffer[samples + 1] = TO_LE_16(decodeIMA(data & 0x0f, _channels == 2));
+	}
+	return samples;
+}
+
 int ADPCMInputStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
 	int samples = 0;
 	byte data;
@@ -188,16 +236,16 @@
 	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
 		if (_blockPos == _blockAlign) {
 			// read block header
-			_status.last = _stream->readSint16LE();
-			_status.stepIndex = _stream->readSint16LE();
+			_status.ima_ch[0].last = _stream->readSint16LE();
+			_status.ima_ch[0].stepIndex = _stream->readSint16LE();
 			_blockPos = 4;
 		}
 
 		for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
 			data = _stream->readByte();
 			_blockPos++;
-			buffer[samples] = TO_LE_16(decodeMSIMA(data & 0x0f));
-			buffer[samples + 1] = TO_LE_16(decodeMSIMA((data >> 4) & 0x0f));
+			buffer[samples] = TO_LE_16(decodeIMA(data & 0x0f));
+			buffer[samples + 1] = TO_LE_16(decodeIMA((data >> 4) & 0x0f));
 		}
 	}
 	return samples;
@@ -218,7 +266,7 @@
 
 			for (nibble = 0; nibble < 8; nibble++) {
 				k = ((data & 0xf0000000) >> 28);
-				buffer[samples + channel + nibble * 2] = TO_LE_16(decodeMSIMA(k));
+				buffer[samples + channel + nibble * 2] = TO_LE_16(decodeIMA(k));
 				data <<= 4;
 			}
 		}
@@ -446,21 +494,20 @@
 int16 ADPCMInputStream::decodeOKI(byte code) {
 	int16 diff, E, samp;
 
-	E = (2 * (code & 0x7) + 1) * okiStepSize[_status.stepIndex] / 8;
+	E = (2 * (code & 0x7) + 1) * okiStepSize[_status.ima_ch[0].stepIndex] / 8;
 	diff = (code & 0x08) ? -E : E;
-	samp = _status.last + diff;
+	samp = _status.ima_ch[0].last + diff;
 	// Clip the values to +/- 2^11 (supposed to be 12 bits)
 	samp = CLIP<int16>(samp, -2048, 2047);
 
-	_status.last = samp;
-	_status.stepIndex += stepAdjust(code);
-	_status.stepIndex = CLIP<int32>(_status.stepIndex, 0, ARRAYSIZE(okiStepSize) - 1);
+	_status.ima_ch[0].last = samp;
+	_status.ima_ch[0].stepIndex += stepAdjust(code);
+	_status.ima_ch[0].stepIndex = CLIP<int32>(_status.ima_ch[0].stepIndex, 0, ARRAYSIZE(okiStepSize) - 1);
 
 	// * 16 effectively converts 12-bit input to 16-bit output
 	return samp * 16;
 }
 
-
 static const uint16 imaStepTable[89] = {
 		7,    8,    9,   10,   11,   12,   13,   14,
 	   16,   17,   19,   21,   23,   25,   28,   31,
@@ -476,14 +523,14 @@
 	32767
 };
 
-int16 ADPCMInputStream::decodeMSIMA(byte code) {
-	int32 E = (2 * (code & 0x7) + 1) * imaStepTable[_status.stepIndex] / 8;
+int16 ADPCMInputStream::decodeIMA(byte code, bool right) {
+	int32 E = (2 * (code & 0x7) + 1) * imaStepTable[_status.ima_ch[right].stepIndex] / 8;
 	int32 diff = (code & 0x08) ? -E : E;
-	int32 samp = CLIP<int32>(_status.last + diff, -32768, 32767);
+	int32 samp = CLIP<int32>(_status.ima_ch[right].last + diff, -32768, 32767);
 
-	_status.last = samp;
-	_status.stepIndex += stepAdjust(code);
-	_status.stepIndex = CLIP<int32>(_status.stepIndex, 0, ARRAYSIZE(imaStepTable) - 1);
+	_status.ima_ch[right].last = samp;
+	_status.ima_ch[right].stepIndex += stepAdjust(code);
+	_status.ima_ch[right].stepIndex = CLIP<int32>(_status.ima_ch[right].stepIndex, 0, ARRAYSIZE(imaStepTable) - 1);
 
 	return samp;
 }
@@ -501,9 +548,8 @@
 	return (int16) CLIP<double>(sample, -32768.0, 32767.0);
 }
 
-// TODO: Add optional support for sound looping
-AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) {
-	return new ADPCMInputStream(stream, disposeAfterUse, size, type, rate, channels, blockAlign);
+AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign, uint numLoops) {
+	return new ADPCMInputStream(stream, disposeAfterUse, size, type, rate, channels, blockAlign, numLoops);
 }
 
 } // End of namespace Audio

Modified: scummvm/trunk/sound/adpcm.h
===================================================================
--- scummvm/trunk/sound/adpcm.h	2009-01-27 10:27:53 UTC (rev 36096)
+++ scummvm/trunk/sound/adpcm.h	2009-01-27 11:46:54 UTC (rev 36097)
@@ -45,7 +45,8 @@
 	kADPCMMS,		// Microsoft ADPCM
 	kADPCMTinsel4,	// 4-bit ADPCM used by the Tinsel engine
 	kADPCMTinsel6,	// 6-bit ADPCM used by the Tinsel engine
-	kADPCMTinsel8	// 8-bit ADPCM used by the Tinsel engine
+	kADPCMTinsel8,	// 8-bit ADPCM used by the Tinsel engine
+	kADPCMIma		// Standard IMA ADPCM
 };
 
 /**
@@ -59,6 +60,7 @@
  * @param rate				the sampling rate (default = 22050)
  * @param channels			the number of channels (default = 2)
  * @param blockAlign		block alignment ??? (default = 0)
+ * @param numLoop		how many types the sounds should loop, 0 for infinite loop (default = 1)
  * @return	a new AudioStream, or NULL, if an error occured
  */
 AudioStream *makeADPCMStream(
@@ -68,7 +70,8 @@
 	typesADPCM type,
 	int rate = 22050,
 	int channels = 2,
-	uint32 blockAlign = 0);
+	uint32 blockAlign = 0,
+	uint numLoops = 1);
 
 } // End of namespace Audio
 


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




More information about the Scummvm-git-logs mailing list