aboutsummaryrefslogtreecommitdiff
path: root/sound/adpcm.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2009-12-29 23:20:23 +0000
committerEugene Sandulenko2009-12-29 23:20:23 +0000
commitfb8ecae7f1904f9d992ce3a01356febcd94d9e36 (patch)
treece5c3960a99c07d4cc8d6b803b49cf17f213e07e /sound/adpcm.cpp
parent0ea022d076c491d802431ee90b658d5e8c06d0e0 (diff)
downloadscummvm-rg350-fb8ecae7f1904f9d992ce3a01356febcd94d9e36.tar.gz
scummvm-rg350-fb8ecae7f1904f9d992ce3a01356febcd94d9e36.tar.bz2
scummvm-rg350-fb8ecae7f1904f9d992ce3a01356febcd94d9e36.zip
Add Mohawk engine code. Part 2/3: common code changes.
svn-id: r46728
Diffstat (limited to 'sound/adpcm.cpp')
-rw-r--r--sound/adpcm.cpp125
1 files changed, 102 insertions, 23 deletions
diff --git a/sound/adpcm.cpp b/sound/adpcm.cpp
index 66ca216c6e..6b50c22f04 100644
--- a/sound/adpcm.cpp
+++ b/sound/adpcm.cpp
@@ -40,7 +40,7 @@ private:
int _channels;
typesADPCM _type;
uint32 _blockAlign;
- uint32 _blockPos;
+ uint32 _blockPos[2];
uint8 _chunkPos;
uint16 _chunkData;
int _blockLen;
@@ -64,6 +64,9 @@ private:
int32 stepIndex;
} ima_ch[2];
+ // Apple QuickTime IMA ADPCM
+ int32 streamPos[2];
+
// MS ADPCM
ADPCMChannelStatus ch[2];
@@ -94,6 +97,7 @@ public:
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);
+ int readBufferApple(int16 *buffer, const int numSamples);
bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); }
bool isStereo() const { return _channels == 2; }
@@ -135,7 +139,6 @@ ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, bool disp
_startpos = stream->pos();
_endpos = _startpos + size;
_curLoop = 0;
- _blockPos = 0;
reset();
}
@@ -147,7 +150,9 @@ ADPCMInputStream::~ADPCMInputStream() {
void ADPCMInputStream::reset() {
memset(&_status, 0, sizeof(_status));
_blockLen = 0;
- _blockPos = _blockAlign; // To make sure first header is read
+ _blockPos[0] = _blockPos[1] = _blockAlign; // To make sure first header is read
+ _status.streamPos[0] = 0;
+ _status.streamPos[1] = _blockAlign;
_chunkPos = 0;
}
@@ -178,6 +183,9 @@ int ADPCMInputStream::readBuffer(int16 *buffer, const int numSamples) {
case kADPCMIma:
samplesDecoded = readBufferIMA(buffer, numSamples);
break;
+ case kADPCMApple:
+ samplesDecoded = readBufferApple(buffer, numSamples);
+ break;
default:
error("Unsupported ADPCM encoding");
break;
@@ -224,6 +232,77 @@ int ADPCMInputStream::readBufferIMA(int16 *buffer, const int numSamples) {
return samples;
}
+int ADPCMInputStream::readBufferApple(int16 *buffer, const int numSamples) {
+ // Need to write 2 samples per channel
+ assert(numSamples % (2 * _channels) == 0);
+
+ // Current sample positions
+ int samples[2] = { 0, 0};
+ // Current data bytes
+ byte data[2] = { 0, 0};
+ // Current nibble selectors
+ bool lowNibble[2] = {true, true};
+
+ // Number of samples per channel
+ int chanSamples = numSamples / _channels;
+
+ for (int i = 0; i < _channels; i++) {
+ _stream->seek(_status.streamPos[i]);
+
+ while ((samples[i] < chanSamples) &&
+ // Last byte read and a new one needed
+ !((_stream->eos() || (_stream->pos() >= _endpos)) && lowNibble[i])) {
+
+ if (_blockPos[i] == _blockAlign) {
+ // 2 byte header per block
+ uint16 temp = _stream->readUint16BE();
+
+ // First 9 bits are the upper bits of the predictor
+ _status.ima_ch[i].last = (int16) (temp & 0xFF80);
+ // Lower 7 bits are the step index
+ _status.ima_ch[i].stepIndex = temp & 0x007F;
+
+ // Clip the step index
+ _status.ima_ch[i].stepIndex = CLIP(_status.ima_ch[i].stepIndex, 0, 88);
+
+ _blockPos[i] = 2;
+ }
+
+ // First decode the lower nibble, then the upper
+ if (lowNibble[i])
+ data[i] = _stream->readByte();
+
+ int16 sample;
+ if (lowNibble[i])
+ sample = decodeIMA(data[i] & 0x0F, i);
+ else
+ sample = decodeIMA(data[i] >> 4, i);
+
+ // The original is interleaved block-wise, we want it sample-wise
+ buffer[_channels * samples[i] + i] = sample;
+
+ samples[i]++;
+
+ // Different nibble
+ lowNibble[i] = !lowNibble[i];
+
+ // We're about to decode a new lower nibble again, so advance the block position
+ if (lowNibble[i])
+ _blockPos[i]++;
+
+ if (_channels == 2)
+ if (_blockPos[i] == _blockAlign)
+ // We're at the end of the block.
+ // Since the channels are interleaved, skip the next block
+ _stream->skip(MIN<uint32>(_blockAlign, _endpos - _stream->pos()));
+
+ _status.streamPos[i] = _stream->pos();
+ }
+ }
+
+ return samples[0] + samples[1];
+}
+
int ADPCMInputStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
int samples = 0;
byte data;
@@ -231,16 +310,16 @@ int ADPCMInputStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
assert(numSamples % 2 == 0);
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
- if (_blockPos == _blockAlign) {
+ if (_blockPos[0] == _blockAlign) {
// read block header
_status.ima_ch[0].last = _stream->readSint16LE();
_status.ima_ch[0].stepIndex = _stream->readSint16LE();
- _blockPos = 4;
+ _blockPos[0] = 4;
}
- for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+ for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
data = _stream->readByte();
- _blockPos++;
+ _blockPos[0]++;
buffer[samples] = decodeIMA(data & 0x0f);
buffer[samples + 1] = decodeIMA((data >> 4) & 0x0f);
}
@@ -288,7 +367,7 @@ int ADPCMInputStream::readBufferMS(int channels, int16 *buffer, const int numSam
samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
- if (_blockPos == _blockAlign) {
+ if (_blockPos[0] == _blockAlign) {
// read block header
for (i = 0; i < channels; i++) {
_status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6);
@@ -308,12 +387,12 @@ int ADPCMInputStream::readBufferMS(int channels, int16 *buffer, const int numSam
for (i = 0; i < channels; i++)
buffer[samples++] = _status.ch[i].sample1;
- _blockPos = channels * 7;
+ _blockPos[0] = channels * 7;
}
- for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+ for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
data = _stream->readByte();
- _blockPos++;
+ _blockPos[0]++;
buffer[samples] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f);
buffer[samples + 1] = decodeMS(&_status.ch[channels - 1], data & 0x0f);
}
@@ -363,12 +442,12 @@ int ADPCMInputStream::readBufferTinsel4(int channels, int16 *buffer, const int n
assert(numSamples % 2 == 0);
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
- if (_blockPos == _blockAlign) {
+ if (_blockPos[0] == _blockAlign) {
readBufferTinselHeader();
- _blockPos = 0;
+ _blockPos[0] = 0;
}
- for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos++) {
+ 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);
@@ -386,13 +465,13 @@ int ADPCMInputStream::readBufferTinsel6(int channels, int16 *buffer, const int n
samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
- if (_blockPos == _blockAlign) {
+ if (_blockPos[0] == _blockAlign) {
readBufferTinselHeader();
- _blockPos = 0;
+ _blockPos[0] = 0;
_chunkPos = 0;
}
- for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) {
+ for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) {
switch (_chunkPos) {
case 0:
@@ -402,17 +481,17 @@ int ADPCMInputStream::readBufferTinsel6(int channels, int16 *buffer, const int n
case 1:
_chunkData = (_chunkData << 8) | (_stream->readByte());
buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal);
- _blockPos++;
+ _blockPos[0]++;
break;
case 2:
_chunkData = (_chunkData << 8) | (_stream->readByte());
buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal);
- _blockPos++;
+ _blockPos[0]++;
break;
case 3:
_chunkData = (_chunkData << 8);
buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal);
- _blockPos++;
+ _blockPos[0]++;
break;
}
@@ -431,12 +510,12 @@ int ADPCMInputStream::readBufferTinsel8(int channels, int16 *buffer, const int n
samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
- if (_blockPos == _blockAlign) {
+ if (_blockPos[0] == _blockAlign) {
readBufferTinselHeader();
- _blockPos = 0;
+ _blockPos[0] = 0;
}
- for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos++) {
+ 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);