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

drmccoy at users.sourceforge.net drmccoy at users.sourceforge.net
Sat Nov 15 20:40:01 CET 2008


Revision: 35089
          http://scummvm.svn.sourceforge.net/scummvm/?rev=35089&view=rev
Author:   drmccoy
Date:     2008-11-15 19:40:01 +0000 (Sat, 15 Nov 2008)

Log Message:
-----------
Added support for the ADPCM used in Discworld 2

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

Modified: scummvm/trunk/sound/adpcm.cpp
===================================================================
--- scummvm/trunk/sound/adpcm.cpp	2008-11-15 19:08:35 UTC (rev 35088)
+++ scummvm/trunk/sound/adpcm.cpp	2008-11-15 19:40:01 UTC (rev 35089)
@@ -62,12 +62,18 @@
 
 		// MS ADPCM
 		ADPCMChannelStatus ch[2];
+
+		// Tinsel
+		double predictor;
+		double K0, K1;
+		double d0, d1;
 	} _status;
 
 	int16 stepAdjust(byte);
 	int16 decodeOKI(byte);
 	int16 decodeMSIMA(byte);
 	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);
@@ -78,6 +84,10 @@
 	int readBufferMSIMA1(int16 *buffer, const int numSamples);
 	int readBufferMSIMA2(int16 *buffer, const int numSamples);
 	int readBufferMS(int channels, int16 *buffer, const int numSamples);
+	void readBufferTinselHeader();
+	int readBufferTinsel4(int channels, int16 *buffer, const int numSamples);
+	int readBufferTinsel6(int channels, int16 *buffer, const int numSamples);
+	int readBufferTinsel8(int channels, int16 *buffer, const int numSamples);
 
 	bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); }
 	bool isStereo() const	{ return false; }
@@ -96,6 +106,7 @@
 
 	_status.last = 0;
 	_status.stepIndex = 0;
+	_status.d0 = _status.d1 = 0;
 	memset(_status.ch, 0, sizeof(_status.ch));
 	_endpos = stream->pos() + size;
 	_blockLen = 0;
@@ -105,6 +116,19 @@
 		error("ADPCMInputStream(): blockAlign isn't specifiled for MS IMA ADPCM");
 	if (type == kADPCMMS && blockAlign == 0)
 		error("ADPCMInputStream(): blockAlign isn't specifiled for MS ADPCM");
+	if (type == kADPCMTinsel4 && blockAlign == 0)
+		error("ADPCMInputStream(): blockAlign isn't specifiled for Tinsel 4-bit ADPCM");
+	if (type == kADPCMTinsel6 && blockAlign == 0)
+		error("ADPCMInputStream(): blockAlign isn't specifiled for Tinsel 6-bit ADPCM");
+	if (type == kADPCMTinsel8 && blockAlign == 0)
+		error("ADPCMInputStream(): blockAlign isn't specifiled for Tinsel 8-bit ADPCM");
+
+	if (type == kADPCMTinsel4 && channels != 1)
+		error("ADPCMInputStream(): Tinsel 4-bit ADPCM only supports mono");
+	if (type == kADPCMTinsel6 && channels != 1)
+		error("ADPCMInputStream(): Tinsel 6-bit ADPCM only supports mono");
+	if (type == kADPCMTinsel8 && channels != 1)
+		error("ADPCMInputStream(): Tinsel 8-bit ADPCM only supports mono");
 }
 
 ADPCMInputStream::~ADPCMInputStream() {
@@ -123,6 +147,12 @@
 			return readBufferMSIMA2(buffer, numSamples);
 	case kADPCMMS:
 		return readBufferMS(_channels, buffer, numSamples);
+	case kADPCMTinsel4:
+		return readBufferTinsel4(_channels, buffer, numSamples);
+	case kADPCMTinsel6:
+		return readBufferTinsel6(_channels, buffer, numSamples);
+	case kADPCMTinsel8:
+		return readBufferTinsel8(_channels, buffer, numSamples);
 	default:
 		error("Unsupported ADPCM encoding");
 		break;
@@ -243,7 +273,116 @@
 	return samples;
 }
 
+static const double TinselFilterTable[4][2] = {
+	{0, 0 },
+	{0.9375, 0},
+	{1.796875, -0.8125},
+	{1.53125, -0.859375}
+};
 
+void ADPCMInputStream::readBufferTinselHeader() {
+	uint8 start = _stream->readByte();
+	uint8 filterVal = (start & 0xC0) >> 6;
+
+	if ((start & 0x20) != 0) {
+		//Lower 6 bit are negative
+
+		// Negate
+		start = ~(start | 0xC0) + 1;
+
+		_status.predictor = 1 << start;
+	} else {
+		// Lower 6 bit are positive
+
+		// Truncate
+		start &= 0x1F;
+
+		_status.predictor = ((double) 1.0) / (1 << start);
+	}
+
+	_status.K0 = TinselFilterTable[filterVal][0];
+	_status.K1 = TinselFilterTable[filterVal][1];
+}
+
+int ADPCMInputStream::readBufferTinsel4(int channels, int16 *buffer, const int numSamples) {
+	int samples;
+	uint16 data;
+	const double eVal = 1.142822265;
+
+	samples = 0;
+
+	assert(numSamples % 2 == 0);
+
+	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+		if (_blockPos == _blockAlign) {
+			readBufferTinselHeader();
+			_blockPos = 0;
+		}
+
+		for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos++) {
+
+			data = _stream->readByte();
+			buffer[samples] = TO_LE_16(decodeTinsel((data << 8) & 0xF000, eVal));
+			buffer[samples+1] = TO_LE_16(decodeTinsel((data << 12) & 0xF000, eVal));
+		}
+	}
+
+	return samples;
+}
+
+int ADPCMInputStream::readBufferTinsel6(int channels, int16 *buffer, const int numSamples) {
+	int samples;
+	uint16 data;
+	const double eVal = 1.032226562;
+
+	samples = 0;
+
+	assert(numSamples % 4 == 0);
+
+	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+		if (_blockPos == _blockAlign) {
+			readBufferTinselHeader();
+			_blockPos = 0;
+		}
+
+		for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 4, _blockPos += 3) {
+
+			data = _stream->readByte();
+			buffer[samples] = TO_LE_16(decodeTinsel((data << 8) & 0xFC00, eVal));
+			data = (data << 8) | (_stream->readByte());
+			buffer[samples+1] = TO_LE_16(decodeTinsel((data << 6) & 0xFC00, eVal));
+			data = (data << 8) | (_stream->readByte());
+			buffer[samples+2] = TO_LE_16(decodeTinsel((data << 4) & 0xFC00, eVal));
+			data = (data << 8);
+			buffer[samples+3] = TO_LE_16(decodeTinsel((data << 2) & 0xFC00, eVal));
+		}
+	}
+
+	return samples;
+}
+
+int ADPCMInputStream::readBufferTinsel8(int channels, int16 *buffer, const int numSamples) {
+	int samples;
+	byte data;
+	const double eVal = 1.007843258;
+
+	samples = 0;
+
+	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+		if (_blockPos == _blockAlign) {
+			readBufferTinselHeader();
+			_blockPos = 0;
+		}
+
+		for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos++) {
+			data = _stream->readByte();
+			buffer[samples] = TO_LE_16(decodeTinsel(data << 8, eVal));
+		}
+	}
+
+	return samples;
+}
+
 static const int MSADPCMAdaptationTable[] = {
 	230, 230, 230, 230, 307, 409, 512, 614,
 	768, 614, 512, 409, 307, 230, 230, 230
@@ -331,6 +470,19 @@
 	return samp;
 }
 
+int16 ADPCMInputStream::decodeTinsel(int16 code, double eVal) {
+	double sample;
+
+	sample = (double) code;
+	sample *= eVal * _status.predictor;
+	sample += (_status.d0 * _status.K0) + (_status.d1 * _status.K1);
+
+	_status.d1 = _status.d0;
+	_status.d0 = sample;
+
+	return (int16) CLIP(sample, -32768.0, 32767.0);
+}
+
 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);
 }

Modified: scummvm/trunk/sound/adpcm.h
===================================================================
--- scummvm/trunk/sound/adpcm.h	2008-11-15 19:08:35 UTC (rev 35088)
+++ scummvm/trunk/sound/adpcm.h	2008-11-15 19:40:01 UTC (rev 35089)
@@ -42,7 +42,10 @@
 enum typesADPCM {
 	kADPCMOki,		// Dialogic/Oki ADPCM (aka VOX)
 	kADPCMMSIma,	// Microsoft IMA ADPCM
-	kADPCMMS		// Microsoft ADPCM
+	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
 };
 
 /**


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