diff options
Diffstat (limited to 'audio/decoders/adpcm.cpp')
| -rw-r--r-- | audio/decoders/adpcm.cpp | 492 | 
1 files changed, 47 insertions, 445 deletions
diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp index 7def89b688..d28ed222a5 100644 --- a/audio/decoders/adpcm.cpp +++ b/audio/decoders/adpcm.cpp @@ -18,52 +18,18 @@   * 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$ - *   */ -#include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/util.h"  #include "audio/decoders/adpcm.h" -#include "audio/audiostream.h" +#include "audio/decoders/adpcm_intern.h"  namespace Audio { -class ADPCMStream : public RewindableAudioStream { -protected: -	Common::SeekableReadStream *_stream; -	const DisposeAfterUse::Flag _disposeAfterUse; -	const int32 _startpos; -	const int32 _endpos; -	const int _channels; -	const uint32 _blockAlign; -	uint32 _blockPos[2]; -	const int _rate; - -	struct { -		// OKI/IMA -		struct { -			int32 last; -			int32 stepIndex; -		} ima_ch[2]; -	} _status; - -	virtual void reset(); -	int16 stepAdjust(byte); - -public: -	ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign); -	~ADPCMStream(); - -	virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); } -	virtual bool isStereo() const	{ return _channels == 2; } -	virtual int getRate() const	{ return _rate; } - -	virtual bool rewind(); -}; -  // 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> @@ -107,17 +73,6 @@ bool ADPCMStream::rewind() {  #pragma mark - -class Oki_ADPCMStream : public ADPCMStream { -public: -	Oki_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - -	virtual int readBuffer(int16 *buffer, const int numSamples); - -protected: -	int16 decodeOKI(byte); -}; -  int Oki_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  	int samples;  	byte data; @@ -153,7 +108,7 @@ int16 Oki_ADPCMStream::decodeOKI(byte code) {  	samp = CLIP<int16>(samp, -2048, 2047);  	_status.ima_ch[0].last = samp; -	_status.ima_ch[0].stepIndex += stepAdjust(code); +	_status.ima_ch[0].stepIndex += _stepAdjustTable[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 @@ -164,20 +119,7 @@ int16 Oki_ADPCMStream::decodeOKI(byte code) {  #pragma mark - -class Ima_ADPCMStream : public ADPCMStream { -protected: -	int16 decodeIMA(byte code, int channel = 0); // Default to using the left channel/using one channel - -public: -	Ima_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { -		memset(&_status, 0, sizeof(_status)); -	} - -	virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Ima_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { +int DVI_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  	int samples;  	byte data; @@ -194,34 +136,6 @@ int Ima_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  #pragma mark - -class Apple_ADPCMStream : public Ima_ADPCMStream { -protected: -	// Apple QuickTime IMA ADPCM -	int32 _streamPos[2]; -	int16 _buffer[2][2]; -	uint8 _chunkPos[2]; - -	void reset() { -		Ima_ADPCMStream::reset(); -		_chunkPos[0] = 0; -		_chunkPos[1] = 0; -		_streamPos[0] = 0; -		_streamPos[1] = _blockAlign; -	} - -public: -	Apple_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { -		_chunkPos[0] = 0; -		_chunkPos[1] = 0; -		_streamPos[0] = 0; -		_streamPos[1] = _blockAlign; -	} - -	virtual int readBuffer(int16 *buffer, const int numSamples); - -}; -  int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  	// Need to write at least one samples per channel  	assert((numSamples % _channels) == 0); @@ -285,82 +199,49 @@ int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  	return samples[0] + samples[1];  } -#pragma mark - - -class MSIma_ADPCMStream : public Ima_ADPCMStream { -public: -	MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign, bool invertSamples = false) -		: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign), _invertSamples(invertSamples) { -		if (blockAlign == 0) -			error("ADPCMStream(): blockAlign isn't specified for MS IMA ADPCM"); -	} - -	virtual int readBuffer(int16 *buffer, const int numSamples) { -		if (_channels == 1) -			return readBufferMSIMA1(buffer, numSamples); -		else -			return readBufferMSIMA2(buffer, numSamples); -	} +#pragma mark - -	int readBufferMSIMA1(int16 *buffer, const int numSamples); -	int readBufferMSIMA2(int16 *buffer, const int numSamples); -private: -	bool _invertSamples;    // Some implementations invert the way samples are decoded -}; +int MSIma_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { +	// Need to write at least one sample per channel +	assert((numSamples % _channels) == 0); -int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {  	int samples = 0; -	byte data; - -	assert(numSamples % 2 == 0);  	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {  		if (_blockPos[0] == _blockAlign) { -			// read block header -			_status.ima_ch[0].last = _stream->readSint16LE(); -			_status.ima_ch[0].stepIndex = _stream->readSint16LE(); -			_blockPos[0] = 4; -		} +			for (int i = 0; i < _channels; i++) { +				// read block header +				_status.ima_ch[i].last = _stream->readSint16LE(); +				_status.ima_ch[i].stepIndex = _stream->readSint16LE(); +			} -		for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { -			data = _stream->readByte(); -			_blockPos[0]++; -			buffer[samples] = decodeIMA(_invertSamples ? (data >> 4) & 0x0f : data & 0x0f); -			buffer[samples + 1] = decodeIMA(_invertSamples ? data & 0x0f : (data >> 4) & 0x0f); +			_blockPos[0] = _channels * 4;  		} -	} -	return samples; -} +		// Decode a set of samples +		for (int i = 0; i < _channels; i++) { +			// The stream encodes four bytes per channel at a time +			for (int j = 0; j < 4; j++) { +				byte data = _stream->readByte(); +				_blockPos[0]++; +				_buffer[i][j * 2] = decodeIMA(data & 0x0f, i); +				_buffer[i][j * 2 + 1] = decodeIMA((data >> 4) & 0x0f, i); +				_samplesLeft[i] += 2; +			} +		} -// Microsoft as usual tries to implement it differently. This method -// is used for stereo data. -int MSIma_ADPCMStream::readBufferMSIMA2(int16 *buffer, const int numSamples) { -	int samples; -	uint32 data; -	int nibble; -	byte k; - -	// TODO: Currently this implementation only supports -	// reading a multiple of 16 samples at once. We might -	// consider changing that so it could read an arbitrary -	// sample pair count. -	assert(numSamples % 16 == 0); - -	for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos;) { -		for (int channel = 0; channel < 2; channel++) { -			data = _stream->readUint32LE(); - -			for (nibble = 0; nibble < 8; nibble++) { -				k = ((data & 0xf0000000) >> 28); -				buffer[samples + channel + nibble * 2] = decodeIMA(k); -				data <<= 4; +		while (samples < numSamples && _samplesLeft[0] != 0) { +			for (int i = 0; i < _channels; i++) { +				buffer[samples] = _buffer[i][8 - _samplesLeft[i]]; +				_samplesLeft[i]--;  			} + +			samples += _channels;  		} -		samples += 16;  	} +  	return samples;  } @@ -382,41 +263,6 @@ static const int MSADPCMAdaptationTable[] = {  }; -class MS_ADPCMStream : public ADPCMStream { -protected: -	struct ADPCMChannelStatus { -		byte predictor; -		int16 delta; -		int16 coeff1; -		int16 coeff2; -		int16 sample1; -		int16 sample2; -	}; - -	struct { -		// MS ADPCM -		ADPCMChannelStatus ch[2]; -	} _status; - -	void reset() { -		ADPCMStream::reset(); -		memset(&_status, 0, sizeof(_status)); -	} - -public: -	MS_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { -		if (blockAlign == 0) -			error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM"); -		memset(&_status, 0, sizeof(_status)); -	} - -	virtual int readBuffer(int16 *buffer, const int numSamples); - -protected: -	int16 decodeMS(ADPCMChannelStatus *c, byte); -}; -  int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) {  	int32 predictor; @@ -478,245 +324,8 @@ int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  } - -#pragma mark - - - -class Tinsel_ADPCMStream : public ADPCMStream { -protected: -	struct { -		// Tinsel -		double predictor; -		double K0, K1; -		double d0, d1; -	} _status; - -	void reset() { -		ADPCMStream::reset(); -		memset(&_status, 0, sizeof(_status)); -	} - -	int16 decodeTinsel(int16, double); -	void readBufferTinselHeader(); - -public: -	Tinsel_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - -		if (blockAlign == 0) -			error("Tinsel_ADPCMStream(): blockAlign isn't specified"); - -		if (channels != 1) -			error("Tinsel_ADPCMStream(): Tinsel ADPCM only supports mono"); - -		memset(&_status, 0, sizeof(_status)); -	} - -}; - -static const double TinselFilterTable[4][2] = { -	{0, 0 }, -	{0.9375, 0}, -	{1.796875, -0.8125}, -	{1.53125, -0.859375} -}; - -void Tinsel_ADPCMStream::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]; -} - -int16 Tinsel_ADPCMStream::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<double>(sample, -32768.0, 32767.0); -} - -class Tinsel4_ADPCMStream : public Tinsel_ADPCMStream { -public: -	Tinsel4_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - -	virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel4_ADPCMStream::readBuffer(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[0] == _blockAlign) { -			readBufferTinselHeader(); -			_blockPos[0] = 0; -		} - -		for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos[0]++) { -			// Read 1 byte = 8 bits = two 4 bit blocks -			data = _stream->readByte(); -			buffer[samples] = decodeTinsel((data << 8) & 0xF000, eVal); -			buffer[samples+1] = decodeTinsel((data << 12) & 0xF000, eVal); -		} -	} - -	return samples; -} - -class Tinsel6_ADPCMStream : public Tinsel_ADPCMStream { -protected: -	uint8 _chunkPos; -	uint16 _chunkData; - -	void reset() { -		ADPCMStream::reset(); -		_chunkPos = 0; -		_chunkData = 0; -	} - -public: -	Tinsel6_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { -		_chunkPos = 0; -		_chunkData = 0; -	} - -	virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel6_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { -	int samples; -	const double eVal = 1.032226562; - -	samples = 0; - -	while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { -		if (_blockPos[0] == _blockAlign) { -			readBufferTinselHeader(); -			_blockPos[0] = 0; -			_chunkPos = 0; -		} - -		for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) { - -			switch (_chunkPos) { -			case 0: -				_chunkData = _stream->readByte(); -				buffer[samples] = decodeTinsel((_chunkData << 8) & 0xFC00, eVal); -				break; -			case 1: -				_chunkData = (_chunkData << 8) | (_stream->readByte()); -				buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal); -				_blockPos[0]++; -				break; -			case 2: -				_chunkData = (_chunkData << 8) | (_stream->readByte()); -				buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal); -				_blockPos[0]++; -				break; -			case 3: -				_chunkData = (_chunkData << 8); -				buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal); -				_blockPos[0]++; -				break; -			} - -		} - -	} - -	return samples; -} - -class Tinsel8_ADPCMStream : public Tinsel_ADPCMStream { -public: -	Tinsel8_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - -	virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel8_ADPCMStream::readBuffer(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[0] == _blockAlign) { -			readBufferTinselHeader(); -			_blockPos[0] = 0; -		} - -		for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos[0]++) { -			// Read 1 byte = 8 bits = one 8 bit block -			data = _stream->readByte(); -			buffer[samples] = decodeTinsel(data << 8, eVal); -		} -	} - -	return samples; -} - -  #pragma mark - -// Duck DK3 IMA ADPCM Decoder -// Based on FFmpeg's decoder and http://wiki.multimedia.cx/index.php?title=Duck_DK3_IMA_ADPCM - -class DK3_ADPCMStream : public Ima_ADPCMStream { -protected: - -	void reset() { -		Ima_ADPCMStream::reset(); -		_topNibble = false; -	} - -public: -	DK3_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) -		: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - -		// DK3 only works as a stereo stream -		assert(channels == 2); -		_topNibble = false; -	} - -	virtual int readBuffer(int16 *buffer, const int numSamples); - -private: -	byte _nibble, _lastByte; -	bool _topNibble; -};  #define DK3_READ_NIBBLE() \  do { \ @@ -782,14 +391,15 @@ int DK3_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {  #pragma mark - -// adjust the step for use on the next sample. -int16 ADPCMStream::stepAdjust(byte code) { -	static const int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8}; - -	return adjusts[code & 0x07]; -} +// This table is used to adjust the step for use on the next sample. +// We could half the table, but since the lookup index used is always +// a 4-bit nibble, it's more efficient to just keep it as it is. +const int16 ADPCMStream::_stepAdjustTable[16] = { +	-1, -1, -1, -1, 2, 4, 6, 8, +	-1, -1, -1, -1, 2, 4, 6, 8 +}; -static const uint16 imaStepTable[89] = { +const int16 Ima_ADPCMStream::_imaTable[89] = {  		7,    8,    9,   10,   11,   12,   13,   14,  	   16,   17,   19,   21,   23,   25,   28,   31,  	   34,   37,   41,   45,   50,   55,   60,   66, @@ -805,13 +415,13 @@ static const uint16 imaStepTable[89] = {  };  int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) { -	int32 E = (2 * (code & 0x7) + 1) * imaStepTable[_status.ima_ch[channel].stepIndex] / 8; +	int32 E = (2 * (code & 0x7) + 1) * _imaTable[_status.ima_ch[channel].stepIndex] / 8;  	int32 diff = (code & 0x08) ? -E : E;  	int32 samp = CLIP<int32>(_status.ima_ch[channel].last + diff, -32768, 32767);  	_status.ima_ch[channel].last = samp; -	_status.ima_ch[channel].stepIndex += stepAdjust(code); -	_status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, ARRAYSIZE(imaStepTable) - 1); +	_status.ima_ch[channel].stepIndex += _stepAdjustTable[code]; +	_status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, ARRAYSIZE(_imaTable) - 1);  	return samp;  } @@ -826,18 +436,10 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo  		return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);  	case kADPCMMSIma:  		return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); -	case kADPCMMSImaLastExpress: -		return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign, true);  	case kADPCMMS:  		return new MS_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); -	case kADPCMTinsel4: -		return new Tinsel4_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); -	case kADPCMTinsel6: -		return new Tinsel6_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); -	case kADPCMTinsel8: -		return new Tinsel8_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); -	case kADPCMIma: -		return new Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); +	case kADPCMDVI: +		return new DVI_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);  	case kADPCMApple:  		return new Apple_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);  	case kADPCMDK3:  | 
