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

sev at users.sourceforge.net sev at users.sourceforge.net
Fri Apr 21 20:01:00 CEST 2006


Revision: 22080
Author:   sev
Date:     2006-04-21 20:00:21 -0700 (Fri, 21 Apr 2006)
ViewCVS:  http://svn.sourceforge.net/scummvm/?rev=22080&view=rev

Log Message:
-----------
- Implemented MS ADPCM WAV format decoder used in Feeble Files. Still it is
  out of sync with video. See TODO there.
- Fixed bug with MS IMA ADPCM mono to make it possible to work in real streams.

Modified Paths:
--------------
    scummvm/trunk/engines/scumm/he/sound_he.cpp
    scummvm/trunk/sound/adpcm.cpp
    scummvm/trunk/sound/adpcm.h
    scummvm/trunk/sound/wave.cpp
Modified: scummvm/trunk/engines/scumm/he/sound_he.cpp
===================================================================
--- scummvm/trunk/engines/scumm/he/sound_he.cpp	2006-04-22 02:49:27 UTC (rev 22079)
+++ scummvm/trunk/engines/scumm/he/sound_he.cpp	2006-04-22 03:00:21 UTC (rev 22080)
@@ -419,7 +419,7 @@
 		}
 
 		if (compType == 17) {
-			AudioStream *voxStream = makeADPCMStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
+			AudioStream *voxStream = makeADPCMStream(&stream, size, kADPCMMSIma, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
 
 			sound = (char *)malloc(size * 4);
 			size = voxStream->readBuffer((int16*)sound, size * 2);

Modified: scummvm/trunk/sound/adpcm.cpp
===================================================================
--- scummvm/trunk/sound/adpcm.cpp	2006-04-22 02:49:27 UTC (rev 22079)
+++ scummvm/trunk/sound/adpcm.cpp	2006-04-22 03:00:21 UTC (rev 22080)
@@ -38,46 +38,67 @@
 	int _channels;
 	typesADPCM _type;
 	uint32 _blockAlign;
+	uint32 _blockPos;
+	int _blockLen;
+	int _rate;
 
+	struct ADPCMChannelStatus {
+		byte predictor;
+		int16 delta;
+		int16 coeff1;
+		int16 coeff2;
+		int16 sample1;
+		int16 sample2;
+	};
+
 	struct adpcmStatus {
+		// IMA
 		int32 last;
 		int32 stepIndex;
+
+		// MS ADPCM
+		ADPCMChannelStatus ch[2];
 	} _status;
 
 	int16 stepAdjust(byte);
 	int16 decodeOKI(byte);
 	int16 decodeMSIMA(byte);
+	int16 decodeMS(ADPCMChannelStatus *c, byte);
 
 public:
-	ADPCMInputStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int channels = 2, uint32 blockAlign = 0);
+	ADPCMInputStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate, int channels = 2, uint32 blockAlign = 0);
 	~ADPCMInputStream() {};
 
 	int readBuffer(int16 *buffer, const int numSamples);
 	int readBufferOKI(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);
 
 	bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); }
 	bool isStereo() const	{ return false; }
-	int getRate() const	{ return 22050; }
+	int getRate() const	{ return _rate; }
 };
 
 // Routines to convert 12 bit linear samples to the
 // Dialogic or Oki ADPCM coding format aka VOX.
 // See also <http://www.comptek.ru/telephony/tnotes/tt1-13.html>
 //
-// In addition, also IMA ADPCM is supported. See
-//   <http://www.multimedia.cx/simpleaudio.html>.
+// In addition, also MS IMA ADPCM is supported. See
+//   <http://wiki.multimedia.cx/index.php?title=Microsoft_IMA_ADPCM>.
 
-ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int channels, uint32 blockAlign)
-	: _stream(stream), _channels(channels), _type(type), _blockAlign(blockAlign) {
+ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign)
+	: _stream(stream), _channels(channels), _type(type), _blockAlign(blockAlign), _rate(rate) {
 
 	_status.last = 0;
 	_status.stepIndex = 0;
 	_endpos = stream->pos() + size;
+	_blockPos = _blockLen = 0;
 
-	if (type == kADPCMIma && blockAlign == 0)
-		error("ADPCMInputStream(): blockAlign isn't specifiled for MS ADPCM IMA");
+	if (type == kADPCMMSIma && blockAlign == 0)
+		error("ADPCMInputStream(): blockAlign isn't specifiled for MS IMA ADPCM");
+	if (type == kADPCMMS && blockAlign == 0)
+		error("ADPCMInputStream(): blockAlign isn't specifiled for MS ADPCM");
 }
 
 int ADPCMInputStream::readBuffer(int16 *buffer, const int numSamples) {
@@ -85,12 +106,15 @@
 	case kADPCMOki:
 		return readBufferOKI(buffer, numSamples);
 		break;
-	case kADPCMIma:
+	case kADPCMMSIma:
 		if (_channels == 1)
 			return readBufferMSIMA1(buffer, numSamples);
 		else
 			return readBufferMSIMA2(buffer, numSamples);
 		break;
+	case kADPCMMS:
+		return readBufferMS(_channels, buffer, numSamples);
+		break;
 	default:
 		error("Unsupported ADPCM encoding");
 		break;
@@ -116,22 +140,22 @@
 int ADPCMInputStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
 	int samples;
 	byte data;
-	int blockLen;
-	int i;
 
 	assert(numSamples % 2 == 0);
 
 	samples = 0;
 
 	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
-		// read block header
-		_status.last = _stream->readSint16LE();
-		_status.stepIndex = _stream->readSint16LE();
+		if (_blockPos == _blockAlign) {
+			// read block header
+			_status.last = _stream->readSint16LE();
+			_status.stepIndex = _stream->readSint16LE();
+			_blockPos = 4;
+		}
 
-		blockLen = MIN(_endpos - _stream->pos(), _blockAlign - 4);
-
-		for (i = 0; i < blockLen && !_stream->eos() && _stream->pos() < _endpos; i++, samples += 2) {
+		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));
 		}
@@ -162,6 +186,61 @@
 	return samples;
 }
 
+static const int MSADPCMAdaptCoeff1[] = {
+	256, 512, 0, 192, 240, 460, 392
+};
+
+static const int MSADPCMAdaptCoeff2[] = {
+	0, -256, 0, 64, 0, -208, -232
+};
+
+int ADPCMInputStream::readBufferMS(int channels, int16 *buffer, const int numSamples) {
+	int samples;
+	byte data;
+	int stereo = channels - 1; // We use it in index
+
+	samples = 0;
+
+	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+		if (_blockPos == _blockAlign) {
+			// read block header
+			_status.ch[0].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6);
+			_status.ch[0].coeff1 = MSADPCMAdaptCoeff1[_status.ch[0].predictor];
+			_status.ch[0].coeff2 = MSADPCMAdaptCoeff2[_status.ch[0].predictor];
+			if (stereo) {
+				_status.ch[1].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6);
+				_status.ch[1].coeff1 = MSADPCMAdaptCoeff1[_status.ch[1].predictor];
+				_status.ch[1].coeff2 = MSADPCMAdaptCoeff2[_status.ch[1].predictor];
+			}
+
+			_status.ch[0].delta = _stream->readSint16LE();
+			if (stereo)
+				_status.ch[1].delta = _stream->readSint16LE();
+
+			buffer[samples++] = _status.ch[0].sample1 = _stream->readSint16LE();
+			if (stereo)
+				buffer[samples++] = _status.ch[1].sample1 = _stream->readSint16LE();
+
+			buffer[samples++] = _status.ch[0].sample2 = _stream->readSint16LE();
+			if (stereo)
+				buffer[samples++] = _status.ch[1].sample2 = _stream->readSint16LE();
+
+			_blockPos = channels * 7;
+		}
+
+
+		for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+			data = _stream->readByte();
+			_blockPos++;
+			buffer[samples] = TO_LE_16(decodeMS(&_status.ch[0], (data >> 4) & 0x0f));
+			buffer[samples + 1] = TO_LE_16(decodeMS(&_status.ch[stereo], data & 0x0f));
+		}
+	}
+
+	return samples;
+}
+
+
 // adjust the step for use on the next sample.
 int16 ADPCMInputStream::stepAdjust(byte code) {
 	static const int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8};
@@ -243,6 +322,33 @@
 	return samp;
 }
 
-AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int channels, uint32 blockAlign) {
-	return new ADPCMInputStream(stream, size, type, channels, blockAlign);
+static const int MSADPCMAdaptationTable[] = {
+	230, 230, 230, 230, 307, 409, 512, 614,
+	768, 614, 512, 409, 307, 230, 230, 230
+};
+
+
+int16 ADPCMInputStream::decodeMS(ADPCMChannelStatus *c, byte code) {
+	int32 predictor;
+
+	predictor = (((c->sample1) * (c->coeff1)) + ((c->sample2) * (c->coeff2))) / 256;
+	predictor += (signed)((code & 0x08) ? (code - 0x10) : (code)) * c->delta;
+
+	if (predictor < -0x8000)
+		predictor = -0x8000;
+	else if (predictor > 0x7fff)
+		predictor = 0x7fff;
+
+	c->sample2 = c->sample1;
+	c->sample1 = predictor;
+	c->delta = (MSADPCMAdaptationTable[(int)code] * c->delta) >> 8;
+
+	if (c->delta < 16)
+		c->delta = 16;
+
+	return (int16)predictor;
 }
+
+AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) {
+	return new ADPCMInputStream(stream, size, type, rate, channels, blockAlign);
+}

Modified: scummvm/trunk/sound/adpcm.h
===================================================================
--- scummvm/trunk/sound/adpcm.h	2006-04-22 02:49:27 UTC (rev 22079)
+++ scummvm/trunk/sound/adpcm.h	2006-04-22 03:00:21 UTC (rev 22080)
@@ -31,10 +31,11 @@
 
 enum typesADPCM {
 	kADPCMOki,
-	kADPCMIma
+	kADPCMMSIma,
+	kADPCMMS
 };
 
-AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int channels = 2, uint32 blockAlign = 0);
+AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate = 22050, int channels = 2, uint32 blockAlign = 0);
 
 
 #endif

Modified: scummvm/trunk/sound/wave.cpp
===================================================================
--- scummvm/trunk/sound/wave.cpp	2006-04-22 02:49:27 UTC (rev 22079)
+++ scummvm/trunk/sound/wave.cpp	2006-04-22 03:00:21 UTC (rev 22080)
@@ -95,16 +95,16 @@
 	printf("  bitsPerSample: %d\n", bitsPerSample);
 #endif
 
-	if (type != 1 && type != 17) {
-		warning("getWavInfo: only PCM or IMA ADPCM data is supported (type %d)", type);
+	if (type != 1 && type != 2 && type != 17) {
+		warning("getWavInfo: only PCM, MS ADPCM or IMA ADPCM data is supported (type %d)", type);
 		return false;
 	}
 
-	if (blockAlign != numChannels * bitsPerSample / 8) {
+	if (blockAlign != numChannels * bitsPerSample / 8 && type != 2) {
 		debug(0, "getWavInfo: blockAlign is invalid");
 	}
 
-	if (avgBytesPerSec != samplesPerSec * blockAlign) {
+	if (avgBytesPerSec != samplesPerSec * blockAlign && type != 2) {
 		debug(0, "getWavInfo: avgBytesPerSec is invalid");
 	}
 
@@ -116,8 +116,10 @@
 		flags |= Audio::Mixer::FLAG_UNSIGNED;
 	else if (bitsPerSample == 16)	// 16 bit data is signed little endian
 		flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
-	else if (bitsPerSample == 4 && type == 17)	// IMA ADPCM compressed. We decompress it
+	else if (bitsPerSample == 4 && type == 17)	// MS IMA ADPCM compressed. We decompress it
 		flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
+	else if (bitsPerSample == 4 && type == 2)	// MS ADPCM compressed. We decompress it
+		flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
 	else {
 		warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
 		return false;
@@ -160,12 +162,15 @@
 	int size, rate;
 	byte flags;
 	uint16 type;
+	int blockAlign;
 
-	if (!loadWAVFromStream(stream, size, rate, flags, &type))
+	if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign))
 		return 0;
 
-	if (type == 17) // IMA ADPCM
-		return makeADPCMStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1);
+	if (type == 17) // MS IMA ADPCM
+		return makeADPCMStream(&stream, size, kADPCMMSIma, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1);
+	if (type == 2) // MS ADPCM
+		return makeADPCMStream(&stream, size, kADPCMMS, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
 
 	byte *data = (byte *)malloc(size);
 	assert(data);


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