Initial commit
This commit is contained in:
341
audio/decoders/3do.cpp
Normal file
341
audio/decoders/3do.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/decoders/3do.h"
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// Reuses ADPCM table
|
||||
#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable
|
||||
#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable
|
||||
|
||||
RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) {
|
||||
if (stereo) {
|
||||
warning("make3DO_ADP4Stream(): stereo currently not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (audioLengthMSecsPtr) {
|
||||
// Caller requires the milliseconds of audio
|
||||
uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample
|
||||
if (stereo) {
|
||||
audioLengthMSecs /= 2;
|
||||
}
|
||||
*audioLengthMSecsPtr = audioLengthMSecs;
|
||||
}
|
||||
|
||||
return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
|
||||
}
|
||||
|
||||
Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace)
|
||||
: _sampleRate(sampleRate), _stereo(stereo),
|
||||
_stream(stream, disposeAfterUse) {
|
||||
|
||||
_callerDecoderData = persistentSpace;
|
||||
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
|
||||
_initialRead = true;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void Audio3DO_ADP4_Stream::reset() {
|
||||
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
|
||||
_streamBytesLeft = _stream->size();
|
||||
_stream->seek(0);
|
||||
}
|
||||
|
||||
bool Audio3DO_ADP4_Stream::rewind() {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) {
|
||||
int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex];
|
||||
int32 decodedSample = _curDecoderData.lastSample;
|
||||
int16 delta = currentStep >> 3;
|
||||
|
||||
if (compressedNibble & 1)
|
||||
delta += currentStep >> 2;
|
||||
|
||||
if (compressedNibble & 2)
|
||||
delta += currentStep >> 1;
|
||||
|
||||
if (compressedNibble & 4)
|
||||
delta += currentStep;
|
||||
|
||||
if (compressedNibble & 8) {
|
||||
decodedSample -= delta;
|
||||
} else {
|
||||
decodedSample += delta;
|
||||
}
|
||||
|
||||
_curDecoderData.lastSample = CLIP<int32>(decodedSample, -32768, 32767);
|
||||
|
||||
_curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07];
|
||||
_curDecoderData.stepIndex = CLIP<int16>(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1);
|
||||
|
||||
return _curDecoderData.lastSample;
|
||||
}
|
||||
|
||||
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
|
||||
int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
|
||||
int8 *byteCachePtr = nullptr;
|
||||
int byteCacheSize = 0;
|
||||
int requestedBytesLeft = 0;
|
||||
int decodedSamplesCount = 0;
|
||||
|
||||
int8 compressedByte = 0;
|
||||
|
||||
if (endOfData())
|
||||
return 0; // no more bytes left
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data over
|
||||
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
|
||||
if (_initialRead) {
|
||||
_initialRead = false;
|
||||
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
|
||||
}
|
||||
}
|
||||
|
||||
requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample
|
||||
if (requestedBytesLeft > _streamBytesLeft)
|
||||
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
|
||||
|
||||
// in case caller requests an uneven amount of samples, we will return an even amount
|
||||
|
||||
// buffering, so that direct decoding of files and such runs way faster
|
||||
while (requestedBytesLeft) {
|
||||
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
|
||||
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
|
||||
} else {
|
||||
byteCacheSize = requestedBytesLeft;
|
||||
}
|
||||
|
||||
requestedBytesLeft -= byteCacheSize;
|
||||
_streamBytesLeft -= byteCacheSize;
|
||||
|
||||
// Fill our byte cache
|
||||
_stream->read(byteCache, byteCacheSize);
|
||||
|
||||
byteCachePtr = byteCache;
|
||||
|
||||
// Mono
|
||||
while (byteCacheSize) {
|
||||
compressedByte = *byteCachePtr++;
|
||||
byteCacheSize--;
|
||||
|
||||
buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4);
|
||||
decodedSamplesCount++;
|
||||
buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f);
|
||||
decodedSamplesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data back
|
||||
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
|
||||
}
|
||||
|
||||
return decodedSamplesCount;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
static const int16 audio_3DO_SDX2_SquareTable[256] = {
|
||||
-32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322,
|
||||
-27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762,
|
||||
-23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602,
|
||||
-19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842,
|
||||
-15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482,
|
||||
-12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522,
|
||||
-9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962,
|
||||
-6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802,
|
||||
-4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042,
|
||||
-2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682,
|
||||
-1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722,
|
||||
-648, -578, -512, -450, -392, -338, -288, -242, -200, -162,
|
||||
-128, -98, -72, -50, -32, -18, -8, -2, 0, 2,
|
||||
8, 18, 32, 50, 72, 98, 128, 162, 200, 242,
|
||||
288, 338, 392, 450, 512, 578, 648, 722, 800, 882,
|
||||
968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922,
|
||||
2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362,
|
||||
3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202,
|
||||
5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442,
|
||||
7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082,
|
||||
10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122,
|
||||
13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562,
|
||||
16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402,
|
||||
20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642,
|
||||
25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282,
|
||||
29768, 30258, 30752, 31250, 31752, 32258
|
||||
};
|
||||
|
||||
Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace)
|
||||
: _sampleRate(sampleRate), _stereo(stereo),
|
||||
_stream(stream, disposeAfterUse) {
|
||||
|
||||
_callerDecoderData = persistentSpace;
|
||||
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
|
||||
_initialRead = true;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void Audio3DO_SDX2_Stream::reset() {
|
||||
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
|
||||
_streamBytesLeft = _stream->size();
|
||||
_stream->seek(0);
|
||||
}
|
||||
|
||||
bool Audio3DO_SDX2_Stream::rewind() {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
|
||||
int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
|
||||
int8 *byteCachePtr = nullptr;
|
||||
int byteCacheSize = 0;
|
||||
int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample
|
||||
int decodedSamplesCount = 0;
|
||||
|
||||
int8 compressedByte = 0;
|
||||
uint8 squareTableOffset = 0;
|
||||
int16 decodedSample = 0;
|
||||
|
||||
if (endOfData())
|
||||
return 0; // no more bytes left
|
||||
|
||||
if (_stereo) {
|
||||
// We expect numSamples to be even in case of Stereo audio
|
||||
assert((numSamples & 1) == 0);
|
||||
}
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data over
|
||||
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
|
||||
if (_initialRead) {
|
||||
_initialRead = false;
|
||||
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
|
||||
}
|
||||
}
|
||||
|
||||
requestedBytesLeft = numSamples;
|
||||
if (requestedBytesLeft > _streamBytesLeft)
|
||||
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
|
||||
|
||||
// buffering, so that direct decoding of files and such runs way faster
|
||||
while (requestedBytesLeft) {
|
||||
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
|
||||
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
|
||||
} else {
|
||||
byteCacheSize = requestedBytesLeft;
|
||||
}
|
||||
|
||||
requestedBytesLeft -= byteCacheSize;
|
||||
_streamBytesLeft -= byteCacheSize;
|
||||
|
||||
// Fill our byte cache
|
||||
_stream->read(byteCache, byteCacheSize);
|
||||
|
||||
byteCachePtr = byteCache;
|
||||
|
||||
if (!_stereo) {
|
||||
// Mono
|
||||
while (byteCacheSize) {
|
||||
compressedByte = *byteCachePtr++;
|
||||
byteCacheSize--;
|
||||
squareTableOffset = compressedByte + 128;
|
||||
|
||||
if (!(compressedByte & 1))
|
||||
_curDecoderData.lastSample1 = 0;
|
||||
|
||||
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
|
||||
_curDecoderData.lastSample1 = decodedSample;
|
||||
|
||||
buffer[decodedSamplesCount] = decodedSample;
|
||||
decodedSamplesCount++;
|
||||
}
|
||||
} else {
|
||||
// Stereo
|
||||
while (byteCacheSize) {
|
||||
compressedByte = *byteCachePtr++;
|
||||
byteCacheSize--;
|
||||
squareTableOffset = compressedByte + 128;
|
||||
|
||||
if (!(decodedSamplesCount & 1)) {
|
||||
// First channel
|
||||
if (!(compressedByte & 1))
|
||||
_curDecoderData.lastSample1 = 0;
|
||||
|
||||
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
|
||||
_curDecoderData.lastSample1 = decodedSample;
|
||||
} else {
|
||||
// Second channel
|
||||
if (!(compressedByte & 1))
|
||||
_curDecoderData.lastSample2 = 0;
|
||||
|
||||
decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset];
|
||||
_curDecoderData.lastSample2 = decodedSample;
|
||||
}
|
||||
|
||||
buffer[decodedSamplesCount] = decodedSample;
|
||||
decodedSamplesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data back
|
||||
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
|
||||
}
|
||||
|
||||
return decodedSamplesCount;
|
||||
}
|
||||
|
||||
RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) {
|
||||
if (stereo) {
|
||||
if (stream->size() & 1) {
|
||||
warning("make3DO_SDX2Stream(): stereo data is uneven size");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioLengthMSecsPtr) {
|
||||
// Caller requires the milliseconds of audio
|
||||
uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample
|
||||
if (stereo) {
|
||||
audioLengthMSecs /= 2;
|
||||
}
|
||||
*audioLengthMSecsPtr = audioLengthMSecs;
|
||||
}
|
||||
|
||||
return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
Reference in New Issue
Block a user