[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