[Scummvm-git-logs] scummvm master -> b76652120b5b57a4823a0d8c6de4b04e2f9a22d3

sev- sev at scummvm.org
Mon Aug 24 12:21:04 UTC 2020


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
b76652120b AUDIO: Separate the XA ADPCM decoder from the PSX video decoder


Commit: b76652120b5b57a4823a0d8c6de4b04e2f9a22d3
    https://github.com/scummvm/scummvm/commit/b76652120b5b57a4823a0d8c6de4b04e2f9a22d3
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2020-08-24T14:21:00+02:00

Commit Message:
AUDIO: Separate the XA ADPCM decoder from the PSX video decoder

Changed paths:
    audio/decoders/adpcm.cpp
    audio/decoders/adpcm.h
    audio/decoders/adpcm_intern.h
    video/psx_decoder.cpp
    video/psx_decoder.h


diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp
index e87661dbbb..1f0ac3b6cf 100644
--- a/audio/decoders/adpcm.cpp
+++ b/audio/decoders/adpcm.cpp
@@ -39,6 +39,8 @@ namespace Audio {
 //
 // In addition, also MS IMA ADPCM is supported. See
 //   <http://wiki.multimedia.cx/index.php?title=Microsoft_IMA_ADPCM>.
+//
+// XA ADPCM support is based on FFmpeg/libav
 
 ADPCMStream::ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
 	: _stream(stream, disposeAfterUse),
@@ -119,6 +121,106 @@ int16 Oki_ADPCMStream::decodeOKI(byte code) {
 #pragma mark -
 
 
+int XA_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samples;
+	byte *data = new byte[128];
+
+	for (samples = 0; samples < numSamples && !endOfData(); samples++) {
+		if (_decodedSampleCount == 0) {
+			uint32 bytesLeft = _stream->size() - _stream->pos();
+			if (bytesLeft < 128) {
+				_stream->skip(bytesLeft);
+				memset(&buffer[samples], 0, (numSamples - samples) * sizeof(uint16));
+				samples = numSamples;
+				break;
+			}
+			_stream->read(data, 128);
+			decodeXA(data);
+			_decodedSampleIndex = 0;
+		}
+
+		// _decodedSamples acts as a FIFO of depth 2 or 4;
+		buffer[samples] = _decodedSamples[_decodedSampleIndex++];
+		_decodedSampleCount--;
+	}
+
+	delete[] data;
+	return samples;
+}
+
+static const int s_xaTable[5][2] = {
+   {   0,   0 },
+   {  60,   0 },
+   { 115, -52 },
+   {  98, -55 },
+   { 122, -60 }
+};
+
+void XA_ADPCMStream::decodeXA(const byte *src) {
+	int16 *leftChannel = _decodedSamples;
+	int16 *rightChannel = _decodedSamples + 1;
+
+	for (int i = 0; i < 4; i++) {
+		int shift = 12 - (src[4 + i * 2] & 0xf);
+		int filter = src[4 + i * 2] >> 4;
+		int f0 = s_xaTable[filter][0];
+		int f1 = s_xaTable[filter][1];
+		int16 s_1 = _status.ima_ch[0].sample[0];
+		int16 s_2 = _status.ima_ch[0].sample[1];
+
+		for (int j = 0; j < 28; j++) {
+			byte d = src[16 + i + j * 4];
+			int t = (int8)(d << 4) >> 4;
+			int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
+			s_2 = s_1;
+			s_1 = CLIP<int>(s, -32768, 32767);
+			*leftChannel = s_1;
+			leftChannel += _channels;
+			_decodedSampleCount++;
+		}
+
+		if (_channels == 2) {
+			_status.ima_ch[0].sample[0] = s_1;
+			_status.ima_ch[0].sample[1] = s_2;
+			s_1 = _status.ima_ch[1].sample[0];
+			s_2 = _status.ima_ch[1].sample[1];
+		}
+
+		shift = 12 - (src[5 + i * 2] & 0xf);
+		filter = src[5 + i * 2] >> 4;
+		f0 = s_xaTable[filter][0];
+		f1 = s_xaTable[filter][1];
+
+		for (int j = 0; j < 28; j++) {
+			byte d = src[16 + i + j * 4];
+			int t = (int8)d >> 4;
+			int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
+			s_2 = s_1;
+			s_1 = CLIP<int>(s, -32768, 32767);
+
+			if (_channels == 2) {
+				*rightChannel = s_1;
+				rightChannel += 2;
+			} else {
+				*leftChannel++ = s_1;
+			}
+			_decodedSampleCount++;
+		}
+
+		if (_channels == 2) {
+			_status.ima_ch[1].sample[0] = s_1;
+			_status.ima_ch[1].sample[1] = s_2;
+		} else {
+			_status.ima_ch[0].sample[0] = s_1;
+			_status.ima_ch[0].sample[1] = s_2;
+		}
+	}
+}
+
+
+#pragma mark -
+
+
 int DVI_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
 	int samples;
 	byte data;
@@ -474,6 +576,8 @@ SeekableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispose
 		return new Apple_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
 	case kADPCMDK3:
 		return new DK3_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
+	case kADPCMXA:
+		return new XA_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
 	default:
 		error("Unsupported ADPCM encoding");
 		break;
@@ -501,6 +605,7 @@ PacketizedAudioStream *makePacketizedADPCMStream(ADPCMType type, int rate, int c
 	// Filter out types we can't support (they're not fully stateless)
 	switch (type) {
 	case kADPCMOki:
+	case kADPCMXA:
 	case kADPCMDVI:
 		return 0;
 	default:
diff --git a/audio/decoders/adpcm.h b/audio/decoders/adpcm.h
index 0f0d83c3c2..4c75915ea8 100644
--- a/audio/decoders/adpcm.h
+++ b/audio/decoders/adpcm.h
@@ -59,7 +59,8 @@ enum ADPCMType {
 	kADPCMMS,                  // Microsoft ADPCM
 	kADPCMDVI,                 // Intel DVI IMA ADPCM
 	kADPCMApple,               // Apple QuickTime IMA ADPCM
-	kADPCMDK3                  // Duck DK3 IMA ADPCM
+	kADPCMDK3,                 // Duck DK3 IMA ADPCM
+	kADPCMXA                   // XA ADPCM
 };
 
 /**
@@ -88,7 +89,7 @@ SeekableAudioStream *makeADPCMStream(
  * packets as individual AudioStreams like returned by makeADPCMStream.
  *
  * Due to the ADPCM types not necessarily supporting stateless
- * streaming, OKI and DVI are not supported by this function
+ * streaming, OKI, XA and DVI are not supported by this function
  * and will return NULL.
  *
  * @param type              the compression type used
diff --git a/audio/decoders/adpcm_intern.h b/audio/decoders/adpcm_intern.h
index 889b0a8118..a331cede5a 100644
--- a/audio/decoders/adpcm_intern.h
+++ b/audio/decoders/adpcm_intern.h
@@ -54,6 +54,7 @@ protected:
 		struct {
 			int32 last;
 			int32 stepIndex;
+			int16 sample[2];
 		} ima_ch[2];
 	} _status;
 
@@ -97,6 +98,24 @@ private:
 	int16 _decodedSamples[2];
 };
 
+class XA_ADPCMStream : public ADPCMStream {
+public:
+	XA_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
+		: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { _decodedSampleCount = 0; }
+
+	virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
+
+	virtual int readBuffer(int16 *buffer, const int numSamples);
+
+protected:
+	void decodeXA(const byte *src);
+
+private:
+	uint8 _decodedSampleCount;
+	uint8 _decodedSampleIndex;
+	int16 _decodedSamples[28 * 2 * 4];
+};
+
 class Ima_ADPCMStream : public ADPCMStream {
 protected:
 	int16 decodeIMA(byte code, int channel = 0); // Default to using the left channel/using one channel
diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp
index ef42a72873..48de547f48 100644
--- a/video/psx_decoder.cpp
+++ b/video/psx_decoder.cpp
@@ -20,11 +20,11 @@
  *
  */
 
-// PlayStation Stream demuxer and XA audio decoder based on FFmpeg/libav
+// PlayStation Stream demuxer based on FFmpeg/libav
 // MDEC video emulation based on http://kenai.com/downloads/jpsxdec/Old/PlayStation1_STR_format1-00.txt
 
 #include "audio/audiostream.h"
-#include "audio/decoders/raw.h"
+#include "audio/decoders/adpcm.h"
 #include "common/bitstream.h"
 #include "common/huffman.h"
 #include "common/stream.h"
@@ -299,14 +299,6 @@ Common::SeekableReadStream *PSXStreamDecoder::readSector() {
 #define AUDIO_DATA_CHUNK_SIZE   2304
 #define AUDIO_DATA_SAMPLE_COUNT 4032
 
-static const int s_xaTable[5][2] = {
-   {   0,   0 },
-   {  60,   0 },
-   { 115, -52 },
-   {  98, -55 },
-   { 122, -60 }
-};
-
 PSXStreamDecoder::PSXAudioTrack::PSXAudioTrack(Common::SeekableReadStream *sector, Audio::Mixer::SoundType soundType) :
 		AudioTrack(soundType) {
 	assert(sector);
@@ -314,11 +306,9 @@ PSXStreamDecoder::PSXAudioTrack::PSXAudioTrack(Common::SeekableReadStream *secto
 
 	sector->seek(19);
 	byte format = sector->readByte();
-	bool stereo = (format & (1 << 0)) != 0;
-	uint rate = (format & (1 << 2)) ? 18900 : 37800;
-	_audStream = Audio::makeQueuingAudioStream(rate, stereo);
-
-	memset(&_adpcmStatus, 0, sizeof(_adpcmStatus));
+	_stereo = (format & (1 << 0)) != 0;
+	_rate = (format & (1 << 2)) ? 18900 : 37800;
+	_audStream = Audio::makeQueuingAudioStream(_rate, _stereo);
 }
 
 PSXStreamDecoder::PSXAudioTrack::~PSXAudioTrack() {
@@ -334,86 +324,16 @@ void PSXStreamDecoder::PSXAudioTrack::queueAudioFromSector(Common::SeekableReadS
 
 	sector->seek(24);
 
-	// This XA audio is different (yet similar) from normal XA audio! Watch out!
-	// TODO: It's probably similar enough to normal XA that we can merge it somehow...
-	// TODO: RTZ PSX needs the same audio code in a regular AudioStream class. Probably
-	// will do something similar to QuickTime and creating a base class 'ISOMode2Parser'
-	// or something similar.
-	byte *buf = new byte[AUDIO_DATA_CHUNK_SIZE];
-	sector->read(buf, AUDIO_DATA_CHUNK_SIZE);
-
-	int channels = _audStream->isStereo() ? 2 : 1;
-	int16 *dst = new int16[AUDIO_DATA_SAMPLE_COUNT];
-	int16 *leftChannel = dst;
-	int16 *rightChannel = dst + 1;
-
-	for (byte *src = buf; src < buf + AUDIO_DATA_CHUNK_SIZE; src += 128) {
-		for (int i = 0; i < 4; i++) {
-			int shift = 12 - (src[4 + i * 2] & 0xf);
-			int filter = src[4 + i * 2] >> 4;
-			int f0 = s_xaTable[filter][0];
-			int f1 = s_xaTable[filter][1];
-			int16 s_1 = _adpcmStatus[0].sample[0];
-			int16 s_2 = _adpcmStatus[0].sample[1];
-
-			for (int j = 0; j < 28; j++) {
-				byte d = src[16 + i + j * 4];
-				int t = (int8)(d << 4) >> 4;
-				int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
-				s_2 = s_1;
-				s_1 = CLIP<int>(s, -32768, 32767);
-				*leftChannel = s_1;
-				leftChannel += channels;
-			}
-
-			if (channels == 2) {
-				_adpcmStatus[0].sample[0] = s_1;
-				_adpcmStatus[0].sample[1] = s_2;
-				s_1 = _adpcmStatus[1].sample[0];
-				s_2 = _adpcmStatus[1].sample[1];
-			}
-
-			shift = 12 - (src[5 + i * 2] & 0xf);
-			filter = src[5 + i * 2] >> 4;
-			f0 = s_xaTable[filter][0];
-			f1 = s_xaTable[filter][1];
-
-			for (int j = 0; j < 28; j++) {
-				byte d = src[16 + i + j * 4];
-				int t = (int8)d >> 4;
-				int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
-				s_2 = s_1;
-				s_1 = CLIP<int>(s, -32768, 32767);
-
-				if (channels == 2) {
-					*rightChannel = s_1;
-					rightChannel += 2;
-				} else {
-					*leftChannel++ = s_1;
-				}
-			}
+	// Read the specified sector into memory
+	Common::SeekableReadStream *compressedAudioStream = sector->readStream(AUDIO_DATA_CHUNK_SIZE);
 
-			if (channels == 2) {
-				_adpcmStatus[1].sample[0] = s_1;
-				_adpcmStatus[1].sample[1] = s_2;
-			} else {
-				_adpcmStatus[0].sample[0] = s_1;
-				_adpcmStatus[0].sample[1] = s_2;
-			}
-		}
+	Audio::SeekableAudioStream *audioStream = Audio::makeADPCMStream(compressedAudioStream, DisposeAfterUse::YES, AUDIO_DATA_CHUNK_SIZE, Audio::kADPCMXA, _rate, _stereo ? 2 : 1);
+	if (audioStream) {
+		_audStream->queueAudioStream(audioStream, DisposeAfterUse::YES);
+	} else {
+		// in case there was an error
+		delete compressedAudioStream;
 	}
-
-	int flags = Audio::FLAG_16BITS;
-
-	if (_audStream->isStereo())
-		flags |= Audio::FLAG_STEREO;
-
-#ifdef SCUMM_LITTLE_ENDIAN
-	flags |= Audio::FLAG_LITTLE_ENDIAN;
-#endif
-
-	_audStream->queueBuffer((byte *)dst, AUDIO_DATA_SAMPLE_COUNT * 2, DisposeAfterUse::YES, flags);
-	delete[] buf;
 }
 
 Audio::AudioStream *PSXStreamDecoder::PSXAudioTrack::getAudioStream() const {
diff --git a/video/psx_decoder.h b/video/psx_decoder.h
index 183b6da317..bf85fb11b1 100644
--- a/video/psx_decoder.h
+++ b/video/psx_decoder.h
@@ -140,11 +140,9 @@ private:
 
 		Audio::QueuingAudioStream *_audStream;
 
-		struct ADPCMStatus {
-			int16 sample[2];
-		} _adpcmStatus[2];
-
 		bool _endOfTrack;
+		bool _stereo;
+		uint _rate;
 	};
 
 	CDSpeed _speed;




More information about the Scummvm-git-logs mailing list