Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

341
audio/decoders/3do.cpp Normal file
View 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

150
audio/decoders/3do.h Normal file
View File

@@ -0,0 +1,150 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - sherlock (3DO version of Serrated Scalpel)
*/
#ifndef AUDIO_3DO_SDX2_H
#define AUDIO_3DO_SDX2_H
#include "common/scummsys.h"
#include "common/types.h"
#include "common/stream.h"
#include "audio/audiostream.h"
namespace Audio {
// amount of bytes to be used within the decoder classes as buffers
#define AUDIO_3DO_CACHE_SIZE 1024
// persistent spaces
struct audio_3DO_ADP4_PersistentSpace {
int16 lastSample;
int16 stepIndex;
};
struct audio_3DO_SDX2_PersistentSpace {
int16 lastSample1;
int16 lastSample2;
};
class Audio3DO_ADP4_Stream : public RewindableAudioStream {
public:
Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace);
protected:
const uint16 _sampleRate;
const bool _stereo;
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _streamBytesLeft;
void reset();
bool rewind();
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
int readBuffer(int16 *buffer, const int numSamples);
bool _initialRead;
audio_3DO_ADP4_PersistentSpace *_callerDecoderData;
audio_3DO_ADP4_PersistentSpace _initialDecoderData;
audio_3DO_ADP4_PersistentSpace _curDecoderData;
private:
int16 decodeSample(byte compressedNibble);
};
class Audio3DO_SDX2_Stream : public RewindableAudioStream {
public:
Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr);
protected:
const uint16 _sampleRate;
const bool _stereo;
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _streamBytesLeft;
void reset();
bool rewind();
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
int readBuffer(int16 *buffer, const int numSamples);
bool _initialRead;
audio_3DO_SDX2_PersistentSpace *_callerDecoderData;
audio_3DO_SDX2_PersistentSpace _initialDecoderData;
audio_3DO_SDX2_PersistentSpace _curDecoderData;
};
/**
* Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
* @sampleRate sample rate
* @stereo if it's stereo or mono
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
* @persistentSpacePtr pointer to the persistent space structure
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *make3DO_ADP4AudioStream(
Common::SeekableReadStream *stream,
uint16 sampleRate,
bool stereo,
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL
);
/**
* Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
* @sampleRate sample rate
* @stereo if it's stereo or mono
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
* @persistentSpacePtr pointer to the persistent space structure
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *make3DO_SDX2AudioStream(
Common::SeekableReadStream *stream,
uint16 sampleRate,
bool stereo,
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL
);
} // End of namespace Audio
#endif

130
audio/decoders/aac.cpp Normal file
View File

@@ -0,0 +1,130 @@
/* 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 "audio/decoders/aac.h"
#ifdef USE_FAAD
#include "common/debug.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include "audio/decoders/codec.h"
#include "audio/decoders/raw.h"
#include <neaacdec.h>
namespace Audio {
class AACDecoder : public Codec {
public:
AACDecoder(Common::SeekableReadStream *extraData,
DisposeAfterUse::Flag disposeExtraData);
~AACDecoder();
AudioStream *decodeFrame(Common::SeekableReadStream &stream) override;
private:
NeAACDecHandle _handle;
byte _channels;
unsigned long _rate;
};
AACDecoder::AACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
// Open the library
_handle = NeAACDecOpen();
// Configure the library to our needs
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(_handle);
conf->outputFormat = FAAD_FMT_16BIT; // We only support 16bit audio
conf->downMatrix = 1; // Convert from 5.1 to stereo if required
NeAACDecSetConfiguration(_handle, conf);
// Copy the extra data to a buffer
extraData->seek(0);
byte *extraDataBuf = new byte[extraData->size()];
extraData->read(extraDataBuf, extraData->size());
// Initialize with our extra data
// NOTE: This code assumes the extra data is coming from an MPEG-4 file!
int err = NeAACDecInit2(_handle, extraDataBuf, extraData->size(), &_rate, &_channels);
delete[] extraDataBuf;
if (err < 0)
error("Could not initialize AAC decoder: %s", NeAACDecGetErrorMessage(err));
if (disposeExtraData == DisposeAfterUse::YES)
delete extraData;
}
AACDecoder::~AACDecoder() {
NeAACDecClose(_handle);
}
AudioStream *AACDecoder::decodeFrame(Common::SeekableReadStream &stream) {
// read everything into a buffer
uint32 inBufferPos = 0;
uint32 inBufferSize = stream.size();
byte *inBuffer = new byte[inBufferSize];
stream.read(inBuffer, inBufferSize);
QueuingAudioStream *audioStream = makeQueuingAudioStream(_rate, _channels == 2);
// Decode until we have enough samples (or there's no more left)
while (inBufferPos < inBufferSize) {
NeAACDecFrameInfo frameInfo;
void *decodedSamples = NeAACDecDecode(_handle, &frameInfo, inBuffer + inBufferPos, inBufferSize - inBufferPos);
if (frameInfo.error != 0)
error("Failed to decode AAC frame: %s", NeAACDecGetErrorMessage(frameInfo.error));
byte *buffer = (byte *)malloc(frameInfo.samples * 2);
memcpy(buffer, decodedSamples, frameInfo.samples * 2);
byte flags = FLAG_16BITS;
if (_channels == 2)
flags |= FLAG_STEREO;
#ifdef SCUMM_LITTLE_ENDIAN
flags |= FLAG_LITTLE_ENDIAN;
#endif
audioStream->queueBuffer(buffer, frameInfo.samples * 2, DisposeAfterUse::YES, flags);
inBufferPos += frameInfo.bytesconsumed;
}
audioStream->finish();
return audioStream;
}
// Factory function
Codec *makeAACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
return new AACDecoder(extraData, disposeExtraData);
}
} // End of namespace Audio
#endif // #ifdef USE_FAAD

61
audio/decoders/aac.h Normal file
View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - groovie
*/
#ifndef AUDIO_AAC_H
#define AUDIO_AAC_H
#include "common/scummsys.h"
#include "common/types.h"
#ifdef USE_FAAD
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class Codec;
/**
* Create a new Codec for decoding AAC data of an MPEG-4 file in the given stream.
*
* @note This should *only* be called by our QuickTime/MPEG-4 decoder since it relies
* on the MPEG-4 extra data. If you want to decode a file using AAC, go use
* makeQuickTimeStream() instead!
* @param extraData the SeekableReadStream from which to read the AAC extra data
* @param disposeExtraData whether to delete the extra data stream after use
* @return a new Codec, or NULL, if an error occurred
*/
Codec *makeAACDecoder(
Common::SeekableReadStream *extraData,
DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO);
} // End of namespace Audio
#endif // #ifdef USE_FAAD
#endif // #ifndef AUDIO_AAC_H

202
audio/decoders/ac3.cpp Normal file
View File

@@ -0,0 +1,202 @@
/* 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/ptr.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include "audio/decoders/ac3.h"
#include "audio/decoders/raw.h"
extern "C" {
#include <a52dec/a52.h>
}
namespace Audio {
class AC3Stream : public PacketizedAudioStream {
public:
AC3Stream(double decibel);
~AC3Stream();
bool init(Common::SeekableReadStream &firstPacket);
void deinit();
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples) override { return _audStream->readBuffer(buffer, numSamples); }
bool isStereo() const override { return _audStream->isStereo(); }
int getRate() const override { return _audStream->getRate(); }
bool endOfData() const override { return _audStream->endOfData(); }
bool endOfStream() const override { return _audStream->endOfStream(); }
// PacketizedAudioStream API
void queuePacket(Common::SeekableReadStream *data) override;
void finish() override { _audStream->finish(); }
private:
Common::ScopedPtr<QueuingAudioStream> _audStream;
a52_state_t *_a52State;
uint32 _frameSize;
byte _inBuf[4096];
byte *_inBufPtr;
int _flags;
int _sampleRate;
double _audioGain;
};
AC3Stream::AC3Stream(double decibel) : _a52State(nullptr), _frameSize(0), _inBufPtr(nullptr), _flags(0), _sampleRate(0) {
_audioGain = pow(2, decibel / 6);
}
AC3Stream::~AC3Stream() {
deinit();
}
enum {
HEADER_SIZE = 7
};
bool AC3Stream::init(Common::SeekableReadStream &firstPacket) {
deinit();
// In theory, I should pass mm_accel() to a52_init(), but I don't know
// where that's supposed to be defined.
_a52State = a52_init(0);
// Go through the header to find sync
byte buf[HEADER_SIZE];
_sampleRate = -1;
for (uint i = 0; i < firstPacket.size() - sizeof(buf); i++) {
int flags, bitRate;
firstPacket.seek(i);
firstPacket.read(buf, sizeof(buf));
if (a52_syncinfo(buf, &flags, &_sampleRate, &bitRate) > 0)
break;
}
// Ensure we have a valid sample rate
if (_sampleRate <= 0) {
deinit();
return false;
}
_audStream.reset(makeQueuingAudioStream(_sampleRate, true));
_inBufPtr = _inBuf;
_flags = 0;
_frameSize = 0;
return true;
}
void AC3Stream::deinit() {
if (!_a52State)
return;
_audStream.reset();
a52_free(_a52State);
_a52State = nullptr;
}
void AC3Stream::queuePacket(Common::SeekableReadStream *data) {
Common::ScopedPtr<Common::SeekableReadStream> packet(data);
while (packet->pos() < packet->size()) {
uint32 leftSize = packet->size() - packet->pos();
uint32 len = _inBufPtr - _inBuf;
if (_frameSize == 0) {
// No header seen: find one
len = HEADER_SIZE - len;
if (len > leftSize)
len = leftSize;
packet->read(_inBufPtr, len);
leftSize -= len;
_inBufPtr += len;
if ((_inBufPtr - _inBuf) == HEADER_SIZE) {
int sampleRate, bitRate;
len = a52_syncinfo(_inBuf, &_flags, &sampleRate, &bitRate);
if (len == 0) {
memmove(_inBuf, _inBuf + 1, HEADER_SIZE - 1);
_inBufPtr--;
} else {
_frameSize = len;
}
}
} else if (len < _frameSize) {
len = _frameSize - len;
if (len > leftSize)
len = leftSize;
assert(len < sizeof(_inBuf) - (_inBufPtr - _inBuf));
packet->read(_inBufPtr, len);
leftSize -= len;
_inBufPtr += len;
} else {
// TODO: Eventually support more than just stereo max
int flags = A52_STEREO | A52_ADJUST_LEVEL;
sample_t level = 32767 * _audioGain;
if (a52_frame(_a52State, _inBuf, &flags, &level, 0) != 0)
error("Frame fail");
int16 *outputBuffer = (int16 *)malloc(6 * 256 * 2 * 2);
int16 *outputPtr = outputBuffer;
int outputLength = 0;
for (int i = 0; i < 6; i++) {
if (a52_block(_a52State) == 0) {
sample_t *samples = a52_samples(_a52State);
for (int j = 0; j < 256; j++) {
*outputPtr++ = (int16)CLIP<sample_t>(samples[j], -32768, 32767);
*outputPtr++ = (int16)CLIP<sample_t>(samples[j + 256], -32768, 32767);
}
outputLength += 1024;
}
}
if (outputLength > 0) {
flags = FLAG_STEREO | FLAG_16BITS;
#ifdef SCUMM_LITTLE_ENDIAN
flags |= FLAG_LITTLE_ENDIAN;
#endif
_audStream->queueBuffer((byte *)outputBuffer, outputLength, DisposeAfterUse::YES, flags);
}
_inBufPtr = _inBuf;
_frameSize = 0;
}
}
}
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket, double decibel) {
Common::ScopedPtr<AC3Stream> stream(new AC3Stream(decibel));
if (!stream->init(firstPacket))
return nullptr;
return stream.release();
}
} // End of namespace Audio

50
audio/decoders/ac3.h Normal file
View File

@@ -0,0 +1,50 @@
/* 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/>.
*
*/
#ifndef AUDIO_DECODERS_AC3_H
#define AUDIO_DECODERS_AC3_H
#include "common/scummsys.h"
#ifdef USE_A52
namespace Common {
class SeekableReadStream;
} // End of namespace Common
namespace Audio {
class PacketizedAudioStream;
/**
* Create a PacketizedAudioStream that decodes AC-3 sound
*
* @param firstPacket The stream containing the first packet of data
* @return A new PacketizedAudioStream, or NULL on error
*/
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket, double decibel = 0.0);
} // End of namespace Audio
#endif
#endif

619
audio/decoders/adpcm.cpp Normal file
View File

@@ -0,0 +1,619 @@
/* 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/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/decoders/adpcm.h"
#include "audio/decoders/adpcm_intern.h"
namespace Audio {
// Routines to convert 12 bit linear samples to the
// Dialogic or Oki ADPCM coding format aka VOX.
// See also
// <https://web.archive.org/web/20050421220515/http://www.comptek.ru/telephony/tnotes/tt1-13.html>
//
// IMA ADPCM support is based on
// <https://wiki.multimedia.cx/index.php?title=IMA_ADPCM>
//
// In addition, also MS IMA ADPCM is supported. See
// <https://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),
_startpos(stream->pos()),
_endpos(_startpos + size),
_channels(channels),
_blockAlign(blockAlign),
_rate(rate) {
reset();
}
void ADPCMStream::reset() {
memset(&_status, 0, sizeof(_status));
_blockPos[0] = _blockPos[1] = _blockAlign; // To make sure first header is read
}
bool ADPCMStream::rewind() {
// TODO: Error checking.
reset();
_stream->seek(_startpos);
return true;
}
#pragma mark -
int Oki_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
int samples;
byte data;
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
if (_decodedSampleCount == 0) {
data = _stream->readByte();
_decodedSamples[0] = decodeOKI((data >> 4) & 0x0f);
_decodedSamples[1] = decodeOKI((data >> 0) & 0x0f);
_decodedSampleCount = 2;
}
// (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)];
_decodedSampleCount--;
}
return samples;
}
static const int16 okiStepSize[49] = {
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552
};
// Decode Linear to ADPCM
int16 Oki_ADPCMStream::decodeOKI(byte code) {
int16 diff, E, samp;
E = (2 * (code & 0x7) + 1) * okiStepSize[_status.ima_ch[0].stepIndex] / 8;
diff = (code & 0x08) ? -E : E;
samp = _status.ima_ch[0].last + diff;
// Clip the values to +/- 2^11 (supposed to be 12 bits)
samp = CLIP<int16>(samp, -2048, 2047);
_status.ima_ch[0].last = samp;
_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
return samp * 16;
}
#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;
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
if (_decodedSampleCount == 0) {
data = _stream->readByte();
_decodedSamples[0] = decodeIMA((data >> 4) & 0x0f, 0);
_decodedSamples[1] = decodeIMA((data >> 0) & 0x0f, _channels == 2 ? 1 : 0);
_decodedSampleCount = 2;
}
// (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)];
_decodedSampleCount--;
}
return samples;
}
#pragma mark -
int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
// Need to write at least one samples per channel
assert((numSamples % _channels) == 0);
// Current sample positions
int samples[2] = { 0, 0};
// Number of samples per channel
int chanSamples = numSamples / _channels;
for (int i = 0; i < _channels; i++) {
_stream->seek(_streamPos[i]);
while ((samples[i] < chanSamples) &&
// Last byte read and a new one needed
!((_stream->eos() || (_stream->pos() >= _endpos)) && (_chunkPos[i] == 0))) {
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<int32>(_status.ima_ch[i].stepIndex, 0, 88);
_blockPos[i] = 2;
}
if (_chunkPos[i] == 0) {
// Decode data
byte data = _stream->readByte();
_buffer[i][0] = decodeIMA(data & 0x0F, i);
_buffer[i][1] = decodeIMA(data >> 4, i);
}
// The original is interleaved block-wise, we want it sample-wise
buffer[_channels * samples[i] + i] = _buffer[i][_chunkPos[i]];
if (++_chunkPos[i] > 1) {
// We're about to decode the next byte, so advance the block position
_chunkPos[i] = 0;
_blockPos[i]++;
}
samples[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()));
_streamPos[i] = _stream->pos();
}
}
return samples[0] + samples[1];
}
#pragma mark -
int MSIma_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
// Need to write at least one sample per channel
assert((numSamples % _channels) == 0);
int samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
if (_blockPos[0] == _blockAlign) {
for (int i = 0; i < _channels; i++) {
// read block header
_status.ima_ch[i].last = _stream->readSint16LE();
_status.ima_ch[i].stepIndex = _stream->readSint16LE();
}
_blockPos[0] = _channels * 4;
}
// 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;
}
}
while (samples < numSamples && _samplesLeft[0] != 0) {
for (int i = 0; i < _channels; i++) {
buffer[samples + i] = _buffer[i][8 - _samplesLeft[i]];
_samplesLeft[i]--;
}
samples += _channels;
}
}
return samples;
}
#pragma mark -
static const int MSADPCMAdaptCoeff1[] = {
256, 512, 0, 192, 240, 460, 392
};
static const int MSADPCMAdaptCoeff2[] = {
0, -256, 0, 64, 0, -208, -232
};
static const int MSADPCMAdaptationTable[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
int16 MS_ADPCMStream::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;
predictor = CLIP<int32>(predictor, -32768, 32767);
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;
}
int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
int samples;
byte data;
int i;
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
if (_decodedSampleCount == 0) {
if (_blockPos[0] == _blockAlign) {
// read block header
for (i = 0; i < _channels; i++) {
_status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6);
_status.ch[i].coeff1 = MSADPCMAdaptCoeff1[_status.ch[i].predictor];
_status.ch[i].coeff2 = MSADPCMAdaptCoeff2[_status.ch[i].predictor];
}
for (i = 0; i < _channels; i++)
_status.ch[i].delta = _stream->readSint16LE();
for (i = 0; i < _channels; i++)
_status.ch[i].sample1 = _stream->readSint16LE();
for (i = 0; i < _channels; i++)
_decodedSamples[_decodedSampleCount++] = _status.ch[i].sample2 = _stream->readSint16LE();
for (i = 0; i < _channels; i++)
_decodedSamples[_decodedSampleCount++] = _status.ch[i].sample1;
_blockPos[0] = _channels * 7;
} else {
data = _stream->readByte();
_blockPos[0]++;
_decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f);
_decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[_channels - 1], data & 0x0f);
}
_decodedSampleIndex = 0;
}
// _decodedSamples acts as a FIFO of depth 2 or 4;
buffer[samples] = _decodedSamples[_decodedSampleIndex++];
_decodedSampleCount--;
}
return samples;
}
#pragma mark -
#define DK3_READ_NIBBLE(channelNo) \
do { \
if (_topNibble) { \
_nibble = _lastByte >> 4; \
_topNibble = false; \
} else { \
_lastByte = _stream->readByte(); \
_nibble = _lastByte & 0xf; \
_topNibble = true; \
--blockBytesLeft; \
--audioBytesLeft; \
} \
decodeIMA(_nibble, channelNo); \
} while(0)
int DK3_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
assert((numSamples % 4) == 0);
const uint32 startOffset = _stream->pos() % _blockAlign;
uint32 audioBytesLeft = _endpos - _stream->pos();
uint32 blockBytesLeft;
if (startOffset != 0) {
blockBytesLeft = _blockAlign - startOffset;
} else {
blockBytesLeft = 0;
}
int samples = 0;
while (samples < numSamples && audioBytesLeft) {
if (blockBytesLeft == 0) {
blockBytesLeft = MIN(_blockAlign, audioBytesLeft);
_topNibble = false;
if (blockBytesLeft < 16) {
warning("Truncated DK3 ADPCM block header");
break;
}
_stream->skip(2);
const uint16 rate = _stream->readUint16LE();
assert(rate == getRate());
(void)rate;
_stream->skip(6);
// Get predictor for both sum/diff channels
_status.ima_ch[0].last = _stream->readSint16LE();
_status.ima_ch[1].last = _stream->readSint16LE();
// Get index for both sum/diff channels
_status.ima_ch[0].stepIndex = _stream->readByte();
_status.ima_ch[1].stepIndex = _stream->readByte();
assert(_status.ima_ch[0].stepIndex < ARRAYSIZE(_imaTable));
assert(_status.ima_ch[1].stepIndex < ARRAYSIZE(_imaTable));
blockBytesLeft -= 16;
audioBytesLeft -= 16;
}
DK3_READ_NIBBLE(0);
DK3_READ_NIBBLE(1);
*buffer++ = _status.ima_ch[0].last + _status.ima_ch[1].last;
*buffer++ = _status.ima_ch[0].last - _status.ima_ch[1].last;
DK3_READ_NIBBLE(0);
*buffer++ = _status.ima_ch[0].last + _status.ima_ch[1].last;
*buffer++ = _status.ima_ch[0].last - _status.ima_ch[1].last;
samples += 4;
// if the last sample of a block ends on an odd byte, the encoder adds
// an extra alignment byte
if (!_topNibble && blockBytesLeft == 1) {
_stream->skip(1);
--blockBytesLeft;
--audioBytesLeft;
}
}
return samples;
}
#undef DK3_READ_NIBBLE
#pragma mark -
// 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
};
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,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493,10442,11487,12635,13899,
15289,16818,18500,20350,22385,24623,27086,29794,
32767
};
int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) {
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 += _stepAdjustTable[code];
_status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, ARRAYSIZE(_imaTable) - 1);
return samp;
}
SeekableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, ADPCMType type, int rate, int channels, uint32 blockAlign) {
// If size is 0, report the entire size of the stream
if (!size)
size = stream->size();
switch (type) {
case kADPCMOki:
return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMMSIma:
return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMMS:
return new MS_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:
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;
}
}
class PacketizedADPCMStream : public StatelessPacketizedAudioStream {
public:
PacketizedADPCMStream(ADPCMType type, int rate, int channels, uint32 blockAlign) :
StatelessPacketizedAudioStream(rate, channels), _type(type), _blockAlign(blockAlign) {}
protected:
AudioStream *makeStream(Common::SeekableReadStream *data) override;
private:
ADPCMType _type;
uint32 _blockAlign;
};
AudioStream *PacketizedADPCMStream::makeStream(Common::SeekableReadStream *data) {
return makeADPCMStream(data, DisposeAfterUse::YES, data->size(), _type, getRate(), getChannels(), _blockAlign);
}
PacketizedAudioStream *makePacketizedADPCMStream(ADPCMType type, int rate, int channels, uint32 blockAlign) {
// Filter out types we can't support (they're not fully stateless)
switch (type) {
case kADPCMOki:
case kADPCMXA:
case kADPCMDVI:
return nullptr;
default:
break;
}
return new PacketizedADPCMStream(type, rate, channels, blockAlign);
}
} // End of namespace Audio

108
audio/decoders/adpcm.h Normal file
View File

@@ -0,0 +1,108 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - agos
* - lastexpress
* - mohawk
* - saga
* - sci (DK3 ADPCM for Phantasmagoria 2)
* - scumm
* - tinsel
*/
#ifndef AUDIO_ADPCM_H
#define AUDIO_ADPCM_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class PacketizedAudioStream;
class SeekableAudioStream;
// There are several types of ADPCM encoding, only some are supported here
// For all the different encodings, refer to:
// https://wiki.multimedia.cx/index.php?title=Category:ADPCM_Audio_Codecs
// Usually, if the audio stream we're trying to play has the FourCC header
// string intact, it's easy to discern which encoding is used
enum ADPCMType {
kADPCMOki, // Dialogic/Oki ADPCM (aka VOX)
kADPCMMSIma, // Microsoft IMA ADPCM
kADPCMMS, // Microsoft ADPCM
kADPCMDVI, // Intel DVI IMA ADPCM
kADPCMApple, // Apple QuickTime IMA ADPCM
kADPCMDK3, // Duck DK3 IMA ADPCM
kADPCMXA // XA ADPCM
};
/**
* Takes an input stream containing ADPCM compressed sound data and creates
* a RewindableAudioStream from that.
*
* @param stream the SeekableReadStream from which to read the ADPCM data
* @param disposeAfterUse whether to delete the stream after use
* @param size how many bytes to read from the stream (0 = all)
* @param type the compression type used
* @param rate the sampling rate
* @param channels the number of channels
* @param blockAlign block alignment ???
* @return a new RewindableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeADPCMStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse,
uint32 size, ADPCMType type,
int rate,
int channels,
uint32 blockAlign = 0);
/**
* Creates a PacketizedAudioStream that will automatically queue
* packets as individual AudioStreams like returned by makeADPCMStream.
*
* Due to the ADPCM types not necessarily supporting stateless
* streaming, OKI, XA and DVI are not supported by this function
* and will return NULL.
*
* @param type the compression type used
* @param rate the sampling rate
* @param channels the number of channels
* @param blockAlign block alignment ???
* @return The new PacketizedAudioStream or NULL, if the type isn't supported.
*/
PacketizedAudioStream *makePacketizedADPCMStream(
ADPCMType type,
int rate,
int channels,
uint32 blockAlign = 0);
} // End of namespace Audio
#endif

View File

@@ -0,0 +1,267 @@
/* 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/>.
*
*/
/**
* Internal interfaces to the ADPCM decoders.
*
* These can be used to make custom ADPCM decoder subclasses,
* or to at least share some common data tables between various
* ADPCM decoder implementations.
*/
#ifndef AUDIO_ADPCM_INTERN_H
#define AUDIO_ADPCM_INTERN_H
#include "audio/audiostream.h"
#include "common/endian.h"
#include "common/ptr.h"
#include "common/stream.h"
#include "common/textconsole.h"
namespace Audio {
class ADPCMStream : public SeekableAudioStream {
protected:
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _startpos;
const int32 _endpos;
const int _channels;
const uint32 _blockAlign;
uint32 _blockPos[2];
const int _rate;
struct ADPCMStatus {
// OKI/IMA
struct {
int32 last;
int32 stepIndex;
int16 sample[2];
} ima_ch[2];
} _status;
virtual void reset();
public:
ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign);
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();
virtual bool seek(const Timestamp &where) { return false; }
virtual Timestamp getLength() const { return Timestamp(); }
/**
* This table is used by some ADPCM variants (IMA and OKI) to adjust the
* step for use on the next sample.
* The first 8 entries are identical to the second 8 entries. Hence, we
* could half the table in size. But since the lookup index is always a
* 4-bit nibble, it is more efficient to just keep it as it is.
*/
static const int16 _stepAdjustTable[16];
};
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) { _decodedSampleCount = 0; }
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
virtual int readBuffer(int16 *buffer, const int numSamples);
protected:
int16 decodeOKI(byte);
private:
uint8 _decodedSampleCount;
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
public:
Ima_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
/**
* This table is used by decodeIMA.
*/
static const int16 _imaTable[89];
};
class DVI_ADPCMStream : public Ima_ADPCMStream {
public:
DVI_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: Ima_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);
private:
uint8 _decodedSampleCount;
int16 _decodedSamples[2];
};
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);
};
class MSIma_ADPCMStream : public Ima_ADPCMStream {
public:
MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
if (blockAlign == 0)
error("MSIma_ADPCMStream(): blockAlign isn't specified");
if (blockAlign % (_channels * 4))
error("MSIma_ADPCMStream(): invalid blockAlign");
_samplesLeft[0] = 0;
_samplesLeft[1] = 0;
}
virtual int readBuffer(int16 *buffer, const int numSamples);
void reset() {
Ima_ADPCMStream::reset();
_samplesLeft[0] = 0;
_samplesLeft[1] = 0;
}
private:
int16 _buffer[2][8];
int _samplesLeft[2];
};
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));
_decodedSampleCount = 0;
_decodedSampleIndex = 0;
}
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
virtual int readBuffer(int16 *buffer, const int numSamples);
protected:
int16 decodeMS(ADPCMChannelStatus *c, byte);
private:
uint8 _decodedSampleCount;
uint8 _decodedSampleIndex;
int16 _decodedSamples[4];
};
// Duck DK3 IMA ADPCM Decoder
// Based on FFmpeg's decoder and https://wiki.multimedia.cx/index.php?title=Duck_DK3_IMA_ADPCM
class DK3_ADPCMStream : public Ima_ADPCMStream {
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);
}
virtual int readBuffer(int16 *buffer, const int numSamples);
private:
byte _nibble, _lastByte;
bool _topNibble;
};
} // End of namespace Audio
#endif

250
audio/decoders/aiff.cpp Normal file
View File

@@ -0,0 +1,250 @@
/* 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/>.
*
*/
/*
* The code in this file is based on information found at
* https://web.archive.org/web/20070409184854/http://www.borg.com/~jglatt/tech/aiff.htm
*
* Also partially based on libav's aiffdec.c
*/
#include "common/debug.h"
#include "common/endian.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
#include "audio/decoders/aiff.h"
#include "audio/decoders/adpcm.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/3do.h"
namespace Audio {
uint32 readExtended(Common::SeekableReadStream &stream) {
// The sample rate is stored as an "80 bit IEEE Standard 754 floating
// point number (Standard Apple Numeric Environment [SANE] data type
// Extended).
byte buf[10];
uint32 mantissa;
uint32 last = 0;
byte exp;
stream.read(buf, 10);
mantissa = READ_BE_UINT32(buf + 2);
exp = 30 - buf[1];
while (exp--) {
last = mantissa;
mantissa >>= 1;
}
if (last & 0x00000001)
mantissa++;
return mantissa;
}
// AIFF versions
static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F');
static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C');
// Codecs
static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original
AIFFHeader *AIFFHeader::readAIFFHeader(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) {
warning("makeAIFFStream: No 'FORM' header");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return nullptr;
}
stream->readUint32BE(); // file size
uint32 version = stream->readUint32BE();
if (version != kVersionAIFF && version != kVersionAIFC) {
warning("makeAIFFStream: No 'AIFF' or 'AIFC' header");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return nullptr;
}
// From here on, we only care about the COMM and SSND chunks, which are
// the only required chunks.
bool foundCOMM = false;
bool foundSSND = false;
AIFFHeader *aiffHeader = new AIFFHeader;
aiffHeader->_codec = kCodecPCM; // AIFF Default;
while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) {
uint32 tag = stream->readUint32BE();
uint32 length = stream->readUint32BE();
uint32 pos = stream->pos();
if (stream->eos() || stream->err())
break;
switch (tag) {
case MKTAG('C', 'O', 'M', 'M'):
foundCOMM = true;
aiffHeader->_channels = stream->readUint16BE();
aiffHeader->_frameCount = stream->readUint32BE();
aiffHeader->_bitsPerSample = stream->readUint16BE();
aiffHeader->_rate = readExtended(*stream);
if (version == kVersionAIFC)
aiffHeader->_codec = stream->readUint32BE();
break;
case MKTAG('S', 'S', 'N', 'D'):
foundSSND = true;
/* uint32 offset = */ stream->readUint32BE();
/* uint32 blockAlign = */ stream->readUint32BE();
delete aiffHeader->_dataStream;
aiffHeader->_dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse);
break;
case MKTAG('F', 'V', 'E', 'R'):
switch (stream->readUint32BE()) {
case 0:
version = kVersionAIFF;
break;
case 0xA2805140:
version = kVersionAIFC;
break;
default:
warning("Unknown AIFF version chunk version");
break;
}
break;
case MKTAG('w', 'a', 'v', 'e'):
warning("Found unhandled AIFF-C extra data chunk");
if (!aiffHeader->_dataStream && disposeAfterUse == DisposeAfterUse::YES)
delete stream;
delete aiffHeader->_dataStream;
delete aiffHeader;
return nullptr;
default:
debug(1, "Skipping AIFF '%s' chunk", tag2str(tag));
break;
}
uint32 seekPos = pos + length;
if (seekPos < (uint32)stream->size()) {
seekPos += (length & 1); // ensure we're word-aligned
}
stream->seek(seekPos);
}
if (!foundCOMM) {
warning("makeAIFFStream: Could not find 'COMM' chunk");
if (!aiffHeader->_dataStream && disposeAfterUse == DisposeAfterUse::YES)
delete stream;
delete aiffHeader;
return nullptr;
}
if (!foundSSND) {
warning("makeAIFFStream: Could not find 'SSND' chunk");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
delete aiffHeader;
return nullptr;
}
return aiffHeader;
}
RewindableAudioStream *AIFFHeader::makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
// We only implement a subset of the AIFF standard.
if (_channels < 1 || _channels > 2) {
warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", _channels);
delete _dataStream;
return nullptr;
}
// Seek to the start of _dataStream, required for at least FileStream
_dataStream->seek(0);
switch (_codec) {
case kCodecPCM:
case MKTAG('t', 'w', 'o', 's'):
case MKTAG('s', 'o', 'w', 't'): {
// PCM samples are always signed.
byte rawFlags = 0;
if (_bitsPerSample == 16)
rawFlags |= Audio::FLAG_16BITS;
if (_channels == 2)
rawFlags |= Audio::FLAG_STEREO;
if (_codec == MKTAG('s', 'o', 'w', 't'))
rawFlags |= Audio::FLAG_LITTLE_ENDIAN;
return makeRawStream(_dataStream, _rate, rawFlags, disposeAfterUse);
}
case MKTAG('i', 'm', 'a', '4'):
// Apple QuickTime IMA ADPCM
return makeADPCMStream(_dataStream, disposeAfterUse, 0, kADPCMApple, _rate, _channels, 34);
case MKTAG('Q', 'D', 'M', '2'):
// TODO: Need to figure out how to integrate this
// (But hopefully never needed)
warning("Unhandled AIFF-C QDM2 compression");
break;
case MKTAG('A', 'D', 'P', '4'):
// ADP4 on 3DO
return make3DO_ADP4AudioStream(_dataStream, _rate, _channels == 2, NULL, disposeAfterUse);
case MKTAG('S', 'D', 'X', '2'):
// SDX2 on 3DO
return make3DO_SDX2AudioStream(_dataStream, _rate, _channels == 2, NULL, disposeAfterUse);
default:
warning("Unhandled AIFF-C compression tag '%s'", tag2str(_codec));
}
delete _dataStream;
return nullptr;
}
RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
AIFFHeader *aiffHeader = AIFFHeader::readAIFFHeader(stream, disposeAfterUse);
if (aiffHeader == nullptr) {
return nullptr;
}
auto res = aiffHeader->makeAIFFStream(stream, disposeAfterUse);
delete aiffHeader;
return res;
}
} // End of namespace Audio

77
audio/decoders/aiff.h Normal file
View File

@@ -0,0 +1,77 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - bbvs
* - pegasus
* - saga
* - sci
* - sword1
*/
#ifndef AUDIO_AIFF_H
#define AUDIO_AIFF_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class RewindableAudioStream;
class AIFFHeader {
public:
static AIFFHeader *readAIFFHeader(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
uint32 getFrameCount() const { return _frameCount; }
uint32 getFrameRate() const { return _rate; }
private:
uint16 _channels = 0;
uint32 _frameCount = 0;
uint16 _bitsPerSample = 0;
uint32 _rate = 0;
uint32 _codec = 0;
Common::SeekableReadStream *_dataStream = nullptr;
};
/**
* Try to load an AIFF from the given seekable stream and create an AudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the AIFF data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *makeAIFFStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif

185
audio/decoders/apc.cpp Normal file
View File

@@ -0,0 +1,185 @@
/* 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/ptr.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/decoders/adpcm_intern.h"
#include "audio/decoders/apc.h"
#include "audio/decoders/raw.h"
namespace Audio {
class APCStreamImpl : public APCStream {
public:
// These parameters are completely optional and only used in HNM videos to make sure data is consistent
APCStreamImpl(uint32 sampleRate = uint(-1), byte stereo = byte(-1));
bool init(Common::SeekableReadStream &header) override;
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples) override { return _audStream->readBuffer(buffer, numSamples); }
bool isStereo() const override { return _audStream->isStereo(); }
int getRate() const override { return _audStream->getRate(); }
bool endOfData() const override { return _audStream->endOfData(); }
bool endOfStream() const override { return _audStream->endOfStream(); }
// PacketizedAudioStream API
void queuePacket(Common::SeekableReadStream *data) override;
void finish() override { _audStream->finish(); }
private:
struct Status {
int32 last;
uint32 stepIndex;
int16 step;
};
FORCEINLINE static int16 decodeIMA(byte code, Status *status);
Common::ScopedPtr<QueuingAudioStream> _audStream;
byte _stereo;
uint32 _sampleRate;
Status _status[2];
};
APCStreamImpl::APCStreamImpl(uint32 sampleRate, byte stereo) :
_sampleRate(sampleRate), _stereo(stereo) {
memset(_status, 0, sizeof(_status));
if (_sampleRate != uint32(-1) && _stereo != byte(-1)) {
_audStream.reset(makeQueuingAudioStream(_sampleRate, _stereo));
}
}
bool APCStreamImpl::init(Common::SeekableReadStream &header) {
// Header size
if (header.size() < 32) {
return false;
}
// Read magic and version at once
byte magicVersion[12];
if (header.read(magicVersion, sizeof(magicVersion)) != sizeof(magicVersion)) {
return false;
}
if (memcmp(magicVersion, "CRYO_APC1.20", sizeof(magicVersion))) {
return false;
}
//uint32 num_samples = header.readUint32LE();
header.skip(4);
uint32 samplerate = header.readUint32LE();
if (_sampleRate != uint32(-1) && _sampleRate != samplerate) {
error("Samplerate mismatch");
return false;
}
_sampleRate = samplerate;
uint32 leftSample = header.readSint32LE();
uint32 rightSample = header.readSint32LE();
uint32 audioFlags = header.readUint32LE();
byte stereo = (audioFlags & 1);
if (_stereo != byte(-1) && _stereo != stereo) {
error("Channels mismatch");
return false;
}
_stereo = stereo;
_status[0].last = leftSample;
_status[1].last = rightSample;
_status[0].stepIndex = _status[1].stepIndex = 0;
_status[0].step = _status[1].step = Ima_ADPCMStream::_imaTable[0];
if (!_audStream) {
_audStream.reset(makeQueuingAudioStream(_sampleRate, _stereo));
}
return true;
}
void APCStreamImpl::queuePacket(Common::SeekableReadStream *data) {
Common::ScopedPtr<Common::SeekableReadStream> packet(data);
uint32 size = packet->size() - packet->pos();
if (size == 0) {
return;
}
// From 4-bits samples to 16-bits
int16 *outputBuffer = (int16 *)malloc(size * 4);
int16 *outputPtr = outputBuffer;
int channelOffset = (_stereo ? 1 : 0);
for (uint32 counter = size; counter > 0; counter--) {
byte word = packet->readByte();
*(outputPtr++) = decodeIMA((word >> 4) & 0x0f, _status);
*(outputPtr++) = decodeIMA((word >> 0) & 0x0f, _status + channelOffset);
}
byte flags = FLAG_16BITS;
if (_stereo) {
flags |= FLAG_STEREO;
}
#ifdef SCUMM_LITTLE_ENDIAN
flags |= Audio::FLAG_LITTLE_ENDIAN;
#endif
_audStream->queueBuffer((byte *)outputBuffer, size * 4, DisposeAfterUse::YES, flags);
}
int16 APCStreamImpl::decodeIMA(byte code, Status *status) {
int32 E = (2 * (code & 0x7) + 1) * status->step / 8;
int32 diff = (code & 0x08) ? -E : E;
// In Cryo APC data is only truncated and not CLIPed as expected
int16 samp = (status->last + diff);
int32 index = status->stepIndex + Ima_ADPCMStream::_stepAdjustTable[code];
index = CLIP<int32>(index, 0, ARRAYSIZE(Ima_ADPCMStream::_imaTable) - 1);
status->last = samp;
status->stepIndex = index;
status->step = Ima_ADPCMStream::_imaTable[index];
return samp;
}
PacketizedAudioStream *makeAPCStream(Common::SeekableReadStream &header) {
Common::ScopedPtr<APCStream> stream(new APCStreamImpl());
if (!stream->init(header)) {
return nullptr;
}
return stream.release();
}
APCStream *makeAPCStream(uint sampleRate, bool stereo) {
assert((sampleRate % 11025) == 0);
return new APCStreamImpl(sampleRate, stereo ? 1 : 0);
}
} // End of namespace Audio

61
audio/decoders/apc.h Normal file
View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
#ifndef AUDIO_DECODERS_APC_H
#define AUDIO_DECODERS_APC_H
#include "common/scummsys.h"
#include "audio/audiostream.h"
namespace Common {
class SeekableReadStream;
} // End of namespace Common
namespace Audio {
class APCStream : public PacketizedAudioStream {
public:
virtual bool init(Common::SeekableReadStream &header) = 0;
};
/**
* Create a PacketizedAudioStream that decodes Cryo APC sound from stream
*
* @param header The stream containing the header
* queuePacket must be called after
* @return A new PacketizedAudioStream, or nullptr on error
*/
PacketizedAudioStream *makeAPCStream(Common::SeekableReadStream &header);
/**
* Create a PacketizedAudioStream that decodes Cryo APC sound using predefined settings
* This is used by HNM6 video decoder and shouldn't be called elsewhere.
*
* @param sampleRate The sample rate of the stream
* @param stereo Whether the stream will be stereo
* @return A new APCStream
*/
APCStream *makeAPCStream(uint sampleRate, bool stereo);
} // End of namespace Audio
#endif

460
audio/decoders/asf.cpp Normal file
View File

@@ -0,0 +1,460 @@
/* 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/>.
*
*/
// Based on xoreos' ASF code which is in turn
// Largely based on the ASF implementation found in FFmpeg.
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include "audio/decoders/asf.h"
#include "audio/decoders/wma.h"
#include "audio/decoders/wave_types.h"
namespace Audio {
class ASFGUID {
public:
ASFGUID(Common::SeekableReadStream &stream) {
stream.read(id, 16);
}
bool operator==(const byte gid[16]) const {
return !memcmp(gid, id, 16);
}
bool operator!=(const byte gid[16]) const {
return memcmp(gid, id, 16);
}
Common::String toString() const {
return Common::String::format("%02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x",
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]);
}
private:
byte id[16];
};
// GUID's that we need
static const byte s_asfHeader[16] = { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C };
static const byte s_asfFileHeader[16] = { 0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 };
static const byte s_asfHead1[16] = { 0xb5, 0x03, 0xbf, 0x5f, 0x2E, 0xA9, 0xCF, 0x11, 0x8e, 0xe3, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 };
static const byte s_asfComment[16] = { 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
static const byte s_asfStreamHeader[16] = { 0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 };
static const byte s_asfCodecComment[16] = { 0x40, 0x52, 0xD1, 0x86, 0x1D, 0x31, 0xD0, 0x11, 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 };
static const byte s_asfDataHeader[16] = { 0x36, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
static const byte s_asfAudioStream[16] = { 0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B };
static const byte s_asfExtendedHeader[16] = { 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 };
static const byte s_asfStreamBitRate[16] = { 0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2 };
class ASFStream : public SeekableAudioStream {
public:
ASFStream(Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
~ASFStream();
int readBuffer(int16 *buffer, const int numSamples) override;
bool endOfData() const override;
bool isStereo() const override { return _channels == 2; }
int getRate() const override { return _sampleRate; }
Timestamp getLength() const override { return Audio::Timestamp(_duration / 10000, _sampleRate); }
bool seek(const Timestamp &where) override;
bool rewind() override;
private:
// Packet data
struct Packet {
Packet();
~Packet();
byte flags;
byte segmentType;
uint16 packetSize;
uint32 sendTime;
uint16 duration;
struct Segment {
byte streamID;
byte sequenceNumber;
bool isKeyframe;
Common::Array<Common::SeekableReadStream *> data;
};
Common::Array<Segment> segments;
};
Common::SeekableReadStream *_stream;
DisposeAfterUse::Flag _disposeAfterUse;
void parseStreamHeader();
void parseFileHeader();
Packet *readPacket();
Codec *createCodec();
AudioStream *createAudioStream();
uint32 _rewindPos;
uint64 _curPacket;
Packet *_lastPacket;
Codec *_codec;
AudioStream *_curAudioStream;
byte _curSequenceNumber;
// Header object variables
uint64 _packetCount;
uint64 _duration;
uint32 _minPacketSize, _maxPacketSize;
// Stream object variables
uint16 _streamID;
uint16 _compression;
uint16 _channels;
int _sampleRate;
uint32 _bitRate;
uint16 _blockAlign;
uint16 _bitsPerCodedSample;
Common::SeekableReadStream *_extraData;
};
ASFStream::Packet::Packet() {
}
ASFStream::Packet::~Packet() {
for (uint32 i = 0; i < segments.size(); i++)
for (uint32 j = 0; j < segments[i].data.size(); j++)
delete segments[i].data[j];
}
ASFStream::ASFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) {
_extraData = nullptr;
_lastPacket = nullptr;
_curPacket = 0;
_codec = nullptr;
_curAudioStream = nullptr;
_curSequenceNumber = 1; // They always start at one
ASFGUID guid = ASFGUID(*_stream);
if (guid != s_asfHeader)
error("ASFStream: Missing asf header");
_stream->readUint64LE();
_stream->readUint32LE();
_stream->readByte();
_stream->readByte();
for (;;) {
uint64 startPos = _stream->pos();
guid = ASFGUID(*_stream);
uint64 size = _stream->readUint64LE();
if (_stream->eos())
error("ASFStream: Unexpected eos");
// TODO: Parse each chunk
if (guid == s_asfFileHeader) {
parseFileHeader();
} else if (guid == s_asfHead1) {
// Should be safe to ignore
} else if (guid == s_asfComment) {
// Ignored
} else if (guid == s_asfStreamHeader) {
parseStreamHeader();
} else if (guid == s_asfCodecComment) {
// TODO
} else if (guid == s_asfDataHeader) {
// Done parsing the header
break;
} else if (guid == s_asfExtendedHeader) {
// TODO
} else if (guid == s_asfStreamBitRate) {
// Ignored
} else
warning("Found unknown ASF GUID: %s", guid.toString().c_str());
_stream->seek(startPos + size);
}
// Skip to the beginning of the packets
_stream->skip(26);
_rewindPos = _stream->pos();
}
ASFStream::~ASFStream() {
if (_disposeAfterUse)
delete _stream;
delete _lastPacket;
delete _curAudioStream;
delete _codec;
}
void ASFStream::parseFileHeader() {
_stream->skip(16); // skip client GUID
/* uint64 fileSize = */ _stream->readUint64LE();
/* uint64 creationTime = */ _stream->readUint64LE();
_packetCount = _stream->readUint64LE();
/* uint64 endTimestamp = */ _stream->readUint64LE();
_duration = _stream->readUint64LE();
/* uint32 startTimestamp = */ _stream->readUint32LE();
/* uint32 unknown = */ _stream->readUint32LE();
/* uint32 flags = */ _stream->readUint32LE();
_minPacketSize = _stream->readUint32LE();
_maxPacketSize = _stream->readUint32LE();
/* uint32 uncFrameSize = */ _stream->readUint32LE();
// We only know how to support packets of one length
if (_minPacketSize != _maxPacketSize)
error("ASFStream::parseFileHeader(): Mismatched packet sizes: Min = %d, Max = %d", _minPacketSize, _maxPacketSize);
}
void ASFStream::parseStreamHeader() {
ASFGUID guid = ASFGUID(*_stream);
if (guid != s_asfAudioStream)
error("ASFStream::parseStreamHeader(): Found non-audio stream");
_stream->skip(16); // skip a guid
_stream->readUint64LE(); // total size
uint32 typeSpecificSize = _stream->readUint32LE();
_stream->readUint32LE();
_streamID = _stream->readUint16LE();
_stream->readUint32LE();
// Parse the wave header
_compression = _stream->readUint16LE();
_channels = _stream->readUint16LE();
_sampleRate = _stream->readUint32LE();
_bitRate = _stream->readUint32LE() * 8;
_blockAlign = _stream->readUint16LE();
_bitsPerCodedSample = (typeSpecificSize == 14) ? 8 : _stream->readUint16LE();
if (typeSpecificSize >= 18) {
uint32 cbSize = _stream->readUint16LE();
cbSize = MIN<int>(cbSize, typeSpecificSize - 18);
_extraData = _stream->readStream(cbSize);
}
_codec = createCodec();
}
bool ASFStream::seek(const Timestamp &where) {
if (where == 0) {
return rewind();
}
// Seeking is not supported
return false;
}
bool ASFStream::rewind() {
// Seek back to the start of the packets
_stream->seek(_rewindPos);
// Reset our packet counter
_curPacket = 0;
delete _lastPacket; _lastPacket = nullptr;
// Delete a stream if we have one
delete _curAudioStream; _curAudioStream = nullptr;
// Reset this too
_curSequenceNumber = 1;
return true;
}
ASFStream::Packet *ASFStream::readPacket() {
if (_curPacket == _packetCount)
error("ASFStream::readPacket(): Reading too many packets");
uint32 packetStartPos = _stream->pos();
// Read a single ASF packet
if (_stream->readByte() != 0x82)
error("ASFStream::readPacket(): Missing packet header");
if (_stream->readUint16LE() != 0)
error("ASFStream::readPacket(): Unknown is not zero");
Packet *packet = new Packet();
packet->flags = _stream->readByte();
packet->segmentType = _stream->readByte();
packet->packetSize = (packet->flags & 0x40) ? _stream->readUint16LE() : 0;
uint16 paddingSize = 0;
if (packet->flags & 0x10)
paddingSize = _stream->readUint16LE();
else if (packet->flags & 0x08)
paddingSize = _stream->readByte();
packet->sendTime = _stream->readUint32LE();
packet->duration = _stream->readUint16LE();
byte segmentCount = (packet->flags & 0x01) ? _stream->readByte() : 1;
packet->segments.resize(segmentCount & 0x3F);
for (uint32 i = 0; i < packet->segments.size(); i++) {
Packet::Segment &segment = packet->segments[i];
segment.streamID = _stream->readByte();
segment.sequenceNumber = _stream->readByte();
segment.isKeyframe = (segment.streamID & 0x80) != 0;
segment.streamID &= 0x7F;
uint32 fragmentOffset = 0;
if (packet->segmentType == 0x55)
fragmentOffset = _stream->readByte();
else if (packet->segmentType == 0x59)
fragmentOffset = _stream->readUint16LE();
else if (packet->segmentType == 0x5D)
fragmentOffset = _stream->readUint32LE();
else
error("ASFStream::readPacket(): Unknown packet segment type 0x%02x", packet->segmentType);
byte flags = _stream->readByte();
if (flags == 1) {
//uint32 objectStartTime = fragmentOffset; // reused purpose
_stream->readByte(); // unknown
uint32 dataLength = (packet->segments.size() == 1) ? (_maxPacketSize - (_stream->pos() - packetStartPos) - paddingSize) : _stream->readUint16LE();
uint32 startObjectPos = _stream->pos();
while ((uint32)_stream->pos() < dataLength + startObjectPos)
segment.data.push_back(_stream->readStream(_stream->readByte()));
} else if (flags == 8) {
/* uint32 objectLength = */ _stream->readUint32LE();
/* uint32 objectStartTime = */ _stream->readUint32LE();
uint32 dataLength = 0;
if (packet->segments.size() == 1)
dataLength = _maxPacketSize - (_stream->pos() - packetStartPos) - fragmentOffset - paddingSize;
else if (segmentCount & 0x40)
dataLength = _stream->readByte();
else
dataLength = _stream->readUint16LE();
_stream->skip(fragmentOffset);
segment.data.push_back(_stream->readStream(dataLength));
} else
error("ASFStream::readPacket(): Unknown packet flags 0x%02x", flags);
}
// Skip any padding
_stream->skip(paddingSize);
// We just read a packet
_curPacket++;
if ((uint32)_stream->pos() != packetStartPos + _maxPacketSize)
error("ASFStream::readPacket(): Mismatching packet pos: %d (should be %d)", (int)_stream->pos(), _maxPacketSize + packetStartPos);
return packet;
}
Codec *ASFStream::createCodec() {
switch (_compression) {
case kWaveFormatWMAv2:
return new WMACodec(2, _sampleRate, _channels, _bitRate, _blockAlign, _extraData);
default:
error("ASFStream::createAudioStream(): Unknown compression 0x%04x", _compression);
}
return nullptr;
}
AudioStream *ASFStream::createAudioStream() {
delete _lastPacket;
_lastPacket = readPacket();
// TODO
if (_lastPacket->segments.size() != 1)
error("ASFStream::createAudioStream(): Only single segment packets supported");
Packet::Segment &segment = _lastPacket->segments[0];
// We should only have one stream in a ASF audio file
if (segment.streamID != _streamID)
error("ASFStream::createAudioStream(): Packet stream ID mismatch");
// TODO
if (segment.sequenceNumber != _curSequenceNumber)
error("ASFStream::createAudioStream(): Only one sequence number per packet supported");
// This can overflow and needs to overflow!
_curSequenceNumber++;
// TODO
if (segment.data.size() != 1)
error("ASFStream::createAudioStream(): Packet grouping not supported");
Common::SeekableReadStream *stream = segment.data[0];
if (_codec)
return _codec->decodeFrame(*stream);
return nullptr;
}
int ASFStream::readBuffer(int16 *buffer, const int numSamples) {
int samplesDecoded = 0;
for (;;) {
if (_curAudioStream) {
samplesDecoded += _curAudioStream->readBuffer(buffer + samplesDecoded, numSamples - samplesDecoded);
if (_curAudioStream->endOfData()) {
delete _curAudioStream;
_curAudioStream = nullptr;
}
}
if (samplesDecoded == numSamples || endOfData())
break;
if (!_curAudioStream) {
_curAudioStream = createAudioStream();
}
}
return samplesDecoded;
}
bool ASFStream::endOfData() const {
return _curPacket == _packetCount && !_curAudioStream;
}
SeekableAudioStream *makeASFStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new ASFStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return nullptr;
}
return s;
}
} // End of namespace Audio

45
audio/decoders/asf.h Normal file
View File

@@ -0,0 +1,45 @@
/* 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/>.
*
*/
// Based on xoreos' ASF code
#ifndef AUDIO_DECODERS_ASF_H
#define AUDIO_DECODERS_ASF_H
namespace Audio {
/**
* Try to load a ASF from the given seekable stream and create a RewindableAudioStream
* from that data.
*
* @param stream The SeekableReadStream from which to read the ASF data.
* @param disposeAfterUse Whether to delete the stream after use.
*
* @return A new SeekableAudioStream, or 0, if an error occurred.
*/
SeekableAudioStream *makeASFStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif // AUDIO_DECODERS_ASF_H

50
audio/decoders/codec.h Normal file
View File

@@ -0,0 +1,50 @@
/* 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/>.
*
*/
#ifndef AUDIO_DECODERS_CODEC_H
#define AUDIO_DECODERS_CODEC_H
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class AudioStream;
/**
* @deprecated The old method of handling audio codecs that rely
* on the state remaining the same between calls. This should
* only be used for old code.
*
* DEPRECATED; USE PacketizedAudioStream INSTEAD!
*/
class Codec {
public:
Codec() {}
virtual ~Codec() {}
virtual AudioStream *decodeFrame(Common::SeekableReadStream &data) = 0;
};
} // End of namespace Audio
#endif

643
audio/decoders/flac.cpp Normal file
View File

@@ -0,0 +1,643 @@
/* 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/>.
*
*/
// Disable symbol overrides for FILE as that is used in FLAC headers
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#include "audio/decoders/flac.h"
#ifdef USE_FLAC
#include "common/debug.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#define FLAC__NO_DLL // that MS-magic gave me headaches - just link the library you like
#include <FLAC/export.h>
#include <FLAC/stream_decoder.h>
namespace Audio {
#pragma mark -
#pragma mark --- FLAC stream ---
#pragma mark -
static const uint MAX_OUTPUT_CHANNELS = 2;
class FLACStream : public SeekableAudioStream {
protected:
Common::SeekableReadStream *_inStream;
bool _disposeAfterUse;
::FLAC__StreamDecoder *_decoder;
/** Header of the stream */
FLAC__StreamMetadata_StreamInfo _streaminfo;
/** index + 1(!) of the last sample to be played */
FLAC__uint64 _lastSample;
/** total play time */
Timestamp _length;
/** true if the last sample was decoded from the FLAC-API - there might still be data in the buffer */
bool _lastSampleWritten;
typedef int16 SampleType;
enum { BUFTYPE_BITS = 16 };
enum {
// Maximal buffer size. According to the FLAC format specification, the block size is
// a 16 bit value (in fact it seems the maximal block size is 32768, but we play it safe).
BUFFER_SIZE = 65536
};
struct {
SampleType bufData[BUFFER_SIZE];
SampleType *bufReadPos;
uint bufFill;
} _sampleCache;
SampleType *_outBuffer;
uint _requestedSamples;
typedef void (*PFCONVERTBUFFERS)(SampleType*, const FLAC__int32*[], uint, const uint, const uint8);
PFCONVERTBUFFERS _methodConvertBuffers;
public:
FLACStream(Common::SeekableReadStream *inStream, bool dispose);
virtual ~FLACStream();
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override { return _streaminfo.channels >= 2; }
int getRate() const override { return _streaminfo.sample_rate; }
bool endOfData() const override {
// End of data is reached if there either is no valid stream data available,
// or if we reached the last sample and completely emptied the sample cache.
return _streaminfo.channels == 0 || (_lastSampleWritten && _sampleCache.bufFill == 0);
}
bool seek(const Timestamp &where) override;
Timestamp getLength() const override { return _length; }
bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; }
protected:
uint getChannels() const { return MIN<uint>(_streaminfo.channels, MAX_OUTPUT_CHANNELS); }
bool allocateBuffer(uint minSamples);
inline FLAC__StreamDecoderState getStreamDecoderState() const;
inline bool processSingleBlock();
inline bool processUntilEndOfMetadata();
bool seekAbsolute(FLAC__uint64 sample);
inline ::FLAC__StreamDecoderReadStatus callbackRead(FLAC__byte buffer[], size_t *bytes);
inline ::FLAC__StreamDecoderSeekStatus callbackSeek(FLAC__uint64 absoluteByteOffset);
inline ::FLAC__StreamDecoderTellStatus callbackTell(FLAC__uint64 *absoluteByteOffset);
inline ::FLAC__StreamDecoderLengthStatus callbackLength(FLAC__uint64 *streamLength);
inline bool callbackEOF();
inline ::FLAC__StreamDecoderWriteStatus callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
inline void callbackMetadata(const ::FLAC__StreamMetadata *metadata);
inline void callbackError(::FLAC__StreamDecoderErrorStatus status);
private:
static ::FLAC__StreamDecoderReadStatus callWrapRead(const ::FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *clientData);
static ::FLAC__StreamDecoderSeekStatus callWrapSeek(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData);
static ::FLAC__StreamDecoderTellStatus callWrapTell(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData);
static ::FLAC__StreamDecoderLengthStatus callWrapLength(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData);
static FLAC__bool callWrapEOF(const ::FLAC__StreamDecoder *decoder, void *clientData);
static ::FLAC__StreamDecoderWriteStatus callWrapWrite(const ::FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData);
static void callWrapMetadata(const ::FLAC__StreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData);
static void callWrapError(const ::FLAC__StreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData);
void setBestConvertBufferMethod();
static void convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
};
FLACStream::FLACStream(Common::SeekableReadStream *inStream, bool dispose)
: _decoder(::FLAC__stream_decoder_new()),
_inStream(inStream),
_disposeAfterUse(dispose),
_length(0, 1000), _lastSample(0),
_outBuffer(nullptr), _requestedSamples(0), _lastSampleWritten(false),
_methodConvertBuffers(&FLACStream::convertBuffersGeneric)
{
assert(_inStream);
memset(&_streaminfo, 0, sizeof(_streaminfo));
_sampleCache.bufReadPos = nullptr;
_sampleCache.bufFill = 0;
_methodConvertBuffers = &FLACStream::convertBuffersGeneric;
bool success;
success = (::FLAC__stream_decoder_init_stream(
_decoder,
&FLACStream::callWrapRead,
&FLACStream::callWrapSeek,
&FLACStream::callWrapTell,
&FLACStream::callWrapLength,
&FLACStream::callWrapEOF,
&FLACStream::callWrapWrite,
&FLACStream::callWrapMetadata,
&FLACStream::callWrapError,
(void *)this
) == FLAC__STREAM_DECODER_INIT_STATUS_OK);
if (success) {
if (processUntilEndOfMetadata() && _streaminfo.channels > 0) {
_lastSample = _streaminfo.total_samples + 1;
_length = Timestamp(0, _lastSample - 1, getRate());
return; // no error occurred
}
}
warning("FLACStream: could not create audio stream");
}
FLACStream::~FLACStream() {
if (_decoder != nullptr) {
(void) ::FLAC__stream_decoder_finish(_decoder);
::FLAC__stream_decoder_delete(_decoder);
}
if (_disposeAfterUse)
delete _inStream;
}
inline FLAC__StreamDecoderState FLACStream::getStreamDecoderState() const {
assert(_decoder != nullptr);
return ::FLAC__stream_decoder_get_state(_decoder);
}
inline bool FLACStream::processSingleBlock() {
assert(_decoder != nullptr);
return 0 != ::FLAC__stream_decoder_process_single(_decoder);
}
inline bool FLACStream::processUntilEndOfMetadata() {
assert(_decoder != nullptr);
return 0 != ::FLAC__stream_decoder_process_until_end_of_metadata(_decoder);
}
bool FLACStream::seekAbsolute(FLAC__uint64 sample) {
assert(_decoder != nullptr);
const bool result = (0 != ::FLAC__stream_decoder_seek_absolute(_decoder, sample));
if (result) {
_lastSampleWritten = (_lastSample != 0 && sample >= _lastSample); // only set if we are SURE
}
return result;
}
bool FLACStream::seek(const Timestamp &where) {
_sampleCache.bufFill = 0;
_sampleCache.bufReadPos = nullptr;
// FLAC uses the sample pair number, thus we always use "false" for the isStereo parameter
// of the convertTimeToStreamPos helper.
return seekAbsolute((FLAC__uint64)convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
}
int FLACStream::readBuffer(int16 *buffer, const int numSamples) {
const uint numChannels = getChannels();
if (numChannels == 0) {
warning("FLACStream: Stream not successfully initialized, can't playback");
return -1; // streaminfo wasn't read!
}
assert(numSamples % numChannels == 0); // must be multiple of channels!
assert(buffer != nullptr);
assert(_outBuffer == nullptr);
assert(_requestedSamples == 0);
_outBuffer = buffer;
_requestedSamples = numSamples;
// If there is still data in our buffer from the last time around,
// copy that first.
if (_sampleCache.bufFill > 0) {
assert(_sampleCache.bufReadPos >= _sampleCache.bufData);
assert(_sampleCache.bufFill % numChannels == 0);
const uint copySamples = MIN((uint)numSamples, _sampleCache.bufFill);
memcpy(buffer, _sampleCache.bufReadPos, copySamples*sizeof(buffer[0]));
_outBuffer = buffer + copySamples;
_requestedSamples = numSamples - copySamples;
_sampleCache.bufReadPos += copySamples;
_sampleCache.bufFill -= copySamples;
}
bool decoderOk = true;
FLAC__StreamDecoderState state = getStreamDecoderState();
// Keep poking FLAC to process more samples until we completely satisfied the request
// respectively until we run out of data.
while (!_lastSampleWritten && _requestedSamples > 0 && state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) {
assert(_sampleCache.bufFill == 0);
assert(_requestedSamples % numChannels == 0);
processSingleBlock();
state = getStreamDecoderState();
if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
_lastSampleWritten = true;
}
// Error handling
switch (state) {
case FLAC__STREAM_DECODER_END_OF_STREAM:
_lastSampleWritten = true;
break;
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
break;
default:
decoderOk = false;
warning("FLACStream: An error occurred while decoding. DecoderState is: %d", getStreamDecoderState());
}
// Compute how many samples we actually produced
const int samples = (int)(_outBuffer - buffer);
assert(samples % numChannels == 0);
_outBuffer = nullptr; // basically unnecessary, only for the purpose of the asserts
_requestedSamples = 0; // basically unnecessary, only for the purpose of the asserts
return decoderOk ? samples : -1;
}
inline ::FLAC__StreamDecoderReadStatus FLACStream::callbackRead(FLAC__byte buffer[], size_t *bytes) {
if (*bytes == 0) {
return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
}
const uint32 bytesRead = _inStream->read(buffer, *bytes);
if (bytesRead == 0) {
return _inStream->eos() ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
*bytes = static_cast<uint>(bytesRead);
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
void FLACStream::setBestConvertBufferMethod() {
PFCONVERTBUFFERS tempMethod = &FLACStream::convertBuffersGeneric;
const uint numChannels = getChannels();
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
assert(numChannels >= 1);
assert(numBits >= 4 && numBits <=32);
if (numChannels == 1) {
if (numBits == 8)
tempMethod = &FLACStream::convertBuffersMono8Bit;
if (numBits == BUFTYPE_BITS)
tempMethod = &FLACStream::convertBuffersMonoNS;
} else if (numChannels == 2) {
if (numBits == 8)
tempMethod = &FLACStream::convertBuffersStereo8Bit;
if (numBits == BUFTYPE_BITS)
tempMethod = &FLACStream::convertBuffersStereoNS;
} /* else ... */
_methodConvertBuffers = tempMethod;
}
// 1 channel, no scaling
void FLACStream::convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 1);
assert(numBits == BUFTYPE_BITS);
FLAC__int32 const* inChannel1 = inChannels[0];
while (numSamples >= 4) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
bufDestination[1] = static_cast<SampleType>(inChannel1[1]);
bufDestination[2] = static_cast<SampleType>(inChannel1[2]);
bufDestination[3] = static_cast<SampleType>(inChannel1[3]);
bufDestination += 4;
inChannel1 += 4;
numSamples -= 4;
}
for (; numSamples > 0; --numSamples) {
*bufDestination++ = static_cast<SampleType>(*inChannel1++);
}
inChannels[0] = inChannel1;
assert(numSamples == 0); // dint copy too many samples
}
// 1 channel, scaling from 8Bit
void FLACStream::convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 1);
assert(numBits == 8);
assert(8 < BUFTYPE_BITS);
FLAC__int32 const* inChannel1 = inChannels[0];
while (numSamples >= 4) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
bufDestination[1] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
bufDestination[2] = static_cast<SampleType>(inChannel1[2]) << (BUFTYPE_BITS - 8);
bufDestination[3] = static_cast<SampleType>(inChannel1[3]) << (BUFTYPE_BITS - 8);
bufDestination += 4;
inChannel1 += 4;
numSamples -= 4;
}
for (; numSamples > 0; --numSamples) {
*bufDestination++ = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
}
inChannels[0] = inChannel1;
assert(numSamples == 0); // dint copy too many samples
}
// 2 channels, no scaling
void FLACStream::convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 2);
assert(numBits == BUFTYPE_BITS);
assert(numSamples % 2 == 0); // must be integral multiply of channels
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
while (numSamples >= 2*2) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
bufDestination[1] = static_cast<SampleType>(inChannel2[0]);
bufDestination[2] = static_cast<SampleType>(inChannel1[1]);
bufDestination[3] = static_cast<SampleType>(inChannel2[1]);
bufDestination += 2 * 2;
inChannel1 += 2;
inChannel2 += 2;
numSamples -= 2 * 2;
}
while (numSamples > 0) {
bufDestination[0] = static_cast<SampleType>(*inChannel1++);
bufDestination[1] = static_cast<SampleType>(*inChannel2++);
bufDestination += 2;
numSamples -= 2;
}
inChannels[0] = inChannel1;
inChannels[1] = inChannel2;
assert(numSamples == 0); // dint copy too many samples
}
// 2 channels, scaling from 8Bit
void FLACStream::convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 2);
assert(numBits == 8);
assert(numSamples % 2 == 0); // must be integral multiply of channels
assert(8 < BUFTYPE_BITS);
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
while (numSamples >= 2*2) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
bufDestination[1] = static_cast<SampleType>(inChannel2[0]) << (BUFTYPE_BITS - 8);
bufDestination[2] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
bufDestination[3] = static_cast<SampleType>(inChannel2[1]) << (BUFTYPE_BITS - 8);
bufDestination += 2 * 2;
inChannel1 += 2;
inChannel2 += 2;
numSamples -= 2 * 2;
}
while (numSamples > 0) {
bufDestination[0] = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
bufDestination[1] = static_cast<SampleType>(*inChannel2++) << (BUFTYPE_BITS - 8);
bufDestination += 2;
numSamples -= 2;
}
inChannels[0] = inChannel1;
inChannels[1] = inChannel2;
assert(numSamples == 0); // dint copy too many samples
}
// all Purpose-conversion - slowest of em all
void FLACStream::convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numSamples % numChannels == 0); // must be integral multiply of channels
if (numBits < BUFTYPE_BITS) {
const uint8 kPower = (uint8)(BUFTYPE_BITS - numBits);
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++)) << kPower;
}
} else if (numBits > BUFTYPE_BITS) {
const uint8 kPower = (uint8)(numBits - BUFTYPE_BITS);
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower);
}
} else {
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++));
}
}
assert(numSamples == 0); // dint copy too many samples
}
inline ::FLAC__StreamDecoderWriteStatus FLACStream::callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
assert(frame->header.channels == _streaminfo.channels);
assert(frame->header.sample_rate == _streaminfo.sample_rate);
assert(frame->header.bits_per_sample == _streaminfo.bits_per_sample);
assert(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER || _streaminfo.min_blocksize == _streaminfo.max_blocksize);
// We require that either the sample cache is empty, or that no samples were requested
assert(_sampleCache.bufFill == 0 || _requestedSamples == 0);
uint numSamples = frame->header.blocksize;
const uint numChannels = getChannels();
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
assert(_requestedSamples % numChannels == 0); // must be integral multiply of channels
const FLAC__uint64 firstSampleNumber = (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) ?
frame->header.number.sample_number : (static_cast<FLAC__uint64>(frame->header.number.frame_number)) * _streaminfo.max_blocksize;
// Check whether we are about to reach beyond the last sample we are supposed to play.
if (_lastSample != 0 && firstSampleNumber + numSamples >= _lastSample) {
numSamples = (uint)(firstSampleNumber >= _lastSample ? 0 : _lastSample - firstSampleNumber);
_lastSampleWritten = true;
}
// The value in _requestedSamples counts raw samples, so if there are more than one
// channel, we have to multiply the number of available sample "pairs" by numChannels
numSamples *= numChannels;
const FLAC__int32 *inChannels[MAX_OUTPUT_CHANNELS];
for (uint i = 0; i < numChannels; ++i)
inChannels[i] = buffer[i];
// write the incoming samples directly into the buffer provided to us by the mixer
if (_requestedSamples > 0) {
assert(_requestedSamples % numChannels == 0);
assert(_outBuffer != nullptr);
// Copy & convert the available samples (limited both by how many we have available, and
// by how many are actually needed).
const uint copySamples = MIN(_requestedSamples, numSamples);
(*_methodConvertBuffers)(_outBuffer, inChannels, copySamples, numChannels, numBits);
_requestedSamples -= copySamples;
numSamples -= copySamples;
_outBuffer += copySamples;
}
// Write all remaining samples (i.e. those which didn't fit into the mixer buffer)
// into the sample cache.
if (_sampleCache.bufFill == 0)
_sampleCache.bufReadPos = _sampleCache.bufData;
const uint cacheSpace = (_sampleCache.bufData + BUFFER_SIZE) - (_sampleCache.bufReadPos + _sampleCache.bufFill);
assert(numSamples <= cacheSpace);
(void)cacheSpace;
(*_methodConvertBuffers)(_sampleCache.bufReadPos + _sampleCache.bufFill, inChannels, numSamples, numChannels, numBits);
_sampleCache.bufFill += numSamples;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
inline ::FLAC__StreamDecoderSeekStatus FLACStream::callbackSeek(FLAC__uint64 absoluteByteOffset) {
_inStream->seek(absoluteByteOffset, SEEK_SET);
const bool result = (absoluteByteOffset == (FLAC__uint64)_inStream->pos());
return result ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
}
inline ::FLAC__StreamDecoderTellStatus FLACStream::callbackTell(FLAC__uint64 *absoluteByteOffset) {
*absoluteByteOffset = static_cast<FLAC__uint64>(_inStream->pos());
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
inline ::FLAC__StreamDecoderLengthStatus FLACStream::callbackLength(FLAC__uint64 *streamLength) {
*streamLength = static_cast<FLAC__uint64>(_inStream->size());
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
inline bool FLACStream::callbackEOF() {
return _inStream->eos();
}
inline void FLACStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) {
assert(_decoder != nullptr);
assert(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); // others arent really interesting
_streaminfo = metadata->data.stream_info;
setBestConvertBufferMethod(); // should be set after getting stream-information. FLAC always parses the info first
}
inline void FLACStream::callbackError(::FLAC__StreamDecoderErrorStatus status) {
// some of these are non-critical-Errors
debug(1, "FLACStream: An error occurred while decoding. DecoderStateError is: %d", status);
}
/* Static Callback Wrappers */
::FLAC__StreamDecoderReadStatus FLACStream::callWrapRead(const ::FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
return instance->callbackRead(buffer, bytes);
}
::FLAC__StreamDecoderSeekStatus FLACStream::callWrapSeek(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
return instance->callbackSeek(absoluteByteOffset);
}
::FLAC__StreamDecoderTellStatus FLACStream::callWrapTell(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
return instance->callbackTell(absoluteByteOffset);
}
::FLAC__StreamDecoderLengthStatus FLACStream::callWrapLength(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
return instance->callbackLength(streamLength);
}
FLAC__bool FLACStream::callWrapEOF(const ::FLAC__StreamDecoder *decoder, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
return instance->callbackEOF();
}
::FLAC__StreamDecoderWriteStatus FLACStream::callWrapWrite(const ::FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
return instance->callbackWrite(frame, buffer);
}
void FLACStream::callWrapMetadata(const ::FLAC__StreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
instance->callbackMetadata(metadata);
}
void FLACStream::callWrapError(const ::FLAC__StreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(nullptr != instance);
instance->callbackError(status);
}
#pragma mark -
#pragma mark --- FLAC factory functions ---
#pragma mark -
SeekableAudioStream *makeFLACStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new FLACStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return nullptr;
} else {
return s;
}
}
} // End of namespace Audio
#endif // #ifdef USE_FLAC

69
audio/decoders/flac.h Normal file
View File

@@ -0,0 +1,69 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - agos
* - draci
* - kyra
* - queen
* - saga
* - sci
* - scumm
* - sword1
* - sword2
* - touche
* - tucker
*/
#ifndef AUDIO_FLAC_H
#define AUDIO_FLAC_H
#include "common/scummsys.h"
#include "common/types.h"
#ifdef USE_FLAC
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
/**
* Create a new SeekableAudioStream from the FLAC data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the FLAC data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeFLACStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif // #ifdef USE_FLAC
#endif // #ifndef AUDIO_FLAC_H

127
audio/decoders/g711.cpp Normal file
View File

@@ -0,0 +1,127 @@
/* 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 "audio/decoders/g711.h"
#include "audio/audiostream.h"
#include "common/stream.h"
#include "common/util.h"
/* from g711.c by SUN microsystems (unrestricted use) */
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
#define QUANT_MASK (0xf) /* Quantization field mask. */
#define SEG_SHIFT (4) /* Left shift for segment number. */
#define SEG_MASK (0x70) /* Segment field mask. */
#define BIAS (0x84) /* Bias for linear code. */
namespace Audio {
/**
* Logarithmic PCM (G.711)
* https://en.wikipedia.org/wiki/G.711
* https://wiki.multimedia.cx/index.php/PCM#Logarithmic_PCM
*/
class G711AudioStream : public SeekableAudioStream {
Common::DisposablePtr<Common::SeekableReadStream> _stream;
const int _rate;
const int _channels;
protected:
virtual int16 decodeSample(uint8 val) = 0;
public:
G711AudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) :
_stream(stream, disposeAfterUse),
_rate(rate),
_channels(channels) {
}
int readBuffer(int16 *buffer, const int numSamples) override {
int samples;
for (samples = 0; samples < numSamples; samples++) {
uint8 val = _stream->readByte();
if (endOfData())
break;
buffer[samples] = decodeSample(val);
}
return samples;
}
bool isStereo() const override { return (_channels == 2); }
int getRate() const override { return _rate; }
bool endOfData() const override { return _stream->eos(); }
bool seek(const Timestamp &where) override {
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
return _stream->seek(seekSample, SEEK_SET);
}
Timestamp getLength() const override {
return Timestamp(0, _stream->size() / _channels, _rate);
}
};
class G711ALawStream : public G711AudioStream {
int16 decodeSample(uint8 val) override {
val ^= 0x55;
int t = val & QUANT_MASK;
int seg = ((unsigned)val & SEG_MASK) >> SEG_SHIFT;
if (seg)
t = (t + t + 1 + 32) << (seg + 2);
else
t = (t + t + 1) << 3;
return (val & SIGN_BIT) ? t : -t;
}
public:
G711ALawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) :
G711AudioStream(stream, disposeAfterUse, rate, channels) {
}
};
SeekableAudioStream *makeALawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) {
return new G711ALawStream(stream, disposeAfterUse, rate, channels);
}
class G711MuLawStream : public G711AudioStream {
int16 decodeSample(uint8 val) override {
val = ~val;
int t = ((val & QUANT_MASK) << 3) + BIAS;
t <<= ((unsigned)val & SEG_MASK) >> SEG_SHIFT;
return (val & SIGN_BIT) ? (BIAS - t) : (t - BIAS);
}
public:
G711MuLawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) :
G711AudioStream(stream, disposeAfterUse, rate, channels) {
}
};
SeekableAudioStream *makeMuLawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) {
return new G711MuLawStream(stream, disposeAfterUse, rate, channels);
}
} // End of namespace Audio

76
audio/decoders/g711.h Normal file
View File

@@ -0,0 +1,76 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - trecision
*/
#ifndef AUDIO_G711_H
#define AUDIO_G711_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
/**
* Takes an input stream containing G711 A-law compressed sound data and creates
* a SeekableAudioStream from that.
*
* @param stream the SeekableReadStream from which to read the PCM data
* @param disposeAfterUse whether to delete the stream after use
* @param rate the sampling rate
* @param channels the number of channels
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeALawStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse,
int rate,
int channels);
/**
* Takes an input stream containing G711 μ-law compressed sound data and creates
* a SeekableAudioStream from that.
*
* @param stream the SeekableReadStream from which to read the PCM data
* @param disposeAfterUse whether to delete the stream after use
* @param rate the sampling rate
* @param channels the number of channels
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeMuLawStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse,
int rate,
int channels);
} // End of namespace Audio
#endif

View File

@@ -0,0 +1,131 @@
/* 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 "audio/decoders/iff_sound.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "common/formats/iff_container.h"
#include "common/func.h"
namespace Audio {
struct Voice8Header {
uint32 oneShotHiSamples;
uint32 repeatHiSamples;
uint32 samplesPerHiCycle;
uint16 samplesPerSec;
byte octaves;
byte compression;
uint32 volume;
Voice8Header() {
memset(this, 0, sizeof(Voice8Header));
}
void load(Common::ReadStream &stream);
};
void Voice8Header::load(Common::ReadStream &stream) {
oneShotHiSamples = stream.readUint32BE();
repeatHiSamples = stream.readUint32BE();
samplesPerHiCycle = stream.readUint32BE();
samplesPerSec = stream.readUint16BE();
octaves = stream.readByte();
compression = stream.readByte();
volume = stream.readUint32BE();
}
struct A8SVXLoader {
Voice8Header _header;
int8 *_data;
uint32 _dataSize;
void load(Common::ReadStream &input) {
Common::IFFParser parser(&input);
Common::Functor1Mem< Common::IFFChunk&, bool, A8SVXLoader > c(this, &A8SVXLoader::callback);
parser.parse(c);
}
bool callback(Common::IFFChunk &chunk) {
switch (chunk._type) {
case ID_VHDR:
_header.load(*chunk._stream);
break;
case ID_BODY:
_dataSize = chunk._size;
_data = (int8 *)malloc(_dataSize);
assert(_data);
loadData(chunk._stream);
return true;
default:
break;
}
return false;
}
void loadData(Common::ReadStream *stream) {
switch (_header.compression) {
case 0:
stream->read(_data, _dataSize);
break;
case 1:
// implement other formats here
error("compressed IFF audio is not supported");
break;
default:
break;
}
}
};
AudioStream *make8SVXStream(Common::ReadStream &input, bool loop) {
A8SVXLoader loader;
loader.load(input);
SeekableAudioStream *stream = Audio::makeRawStream((byte *)loader._data, loader._dataSize, loader._header.samplesPerSec, 0);
uint32 loopStart = 0, loopEnd = 0;
if (loop) {
// the standard way to loop 8SVX audio implies use of the oneShotHiSamples and
// repeatHiSamples fields
loopStart = 0;
loopEnd = loader._header.oneShotHiSamples + loader._header.repeatHiSamples;
if (loopStart != loopEnd) {
return new SubLoopingAudioStream(stream, 0,
Timestamp(0, loopStart, loader._header.samplesPerSec),
Timestamp(0, loopEnd, loader._header.samplesPerSec));
}
}
return stream;
}
}

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - parallaction
*/
#ifndef AUDIO_IFF_H
#define AUDIO_IFF_H
namespace Common {
class ReadStream;
}
namespace Audio {
class AudioStream;
AudioStream *make8SVXStream(Common::ReadStream &stream, bool loop);
}
#endif

107
audio/decoders/mac_snd.cpp Normal file
View File

@@ -0,0 +1,107 @@
/* 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/>.
*
*/
/*
* The code in this file is based on information found at
* https://developer.apple.com/library/archive/documentation/mac/Sound/Sound-60.html#HEADING60-15
*
* We implement both type 1 and type 2 snd resources, but only those that are sampled
*/
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/substream.h"
#include "audio/decoders/mac_snd.h"
#include "audio/decoders/raw.h"
namespace Audio {
SeekableAudioStream *makeMacSndStream(Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
uint16 sndType = stream->readUint16BE();
if (sndType == 1) {
// "normal" snd resources
if (stream->readUint16BE() != 1) {
warning("makeMacSndStream(): Unsupported data type count");
return nullptr;
}
if (stream->readUint16BE() != 5) {
// 5 == sampled
warning("makeMacSndStream(): Unsupported data type");
return nullptr;
}
stream->readUint32BE(); // initialization option
} else if (sndType == 2) {
// old HyperCard snd resources
stream->readUint16BE(); // reference count (unused)
} else {
warning("makeMacSndStream(): Unknown format type %d", sndType);
return nullptr;
}
// We really should never get this as long as we have sampled data only
if (stream->readUint16BE() != 1) {
warning("makeMacSndStream(): Unsupported command count");
return nullptr;
}
uint16 command = stream->readUint16BE();
// 0x8050 - soundCmd (with dataOffsetFlag set): install a sampled sound as a voice
// 0x8051 - bufferCmd (with dataOffsetFlag set): play a sample sound
if (command != 0x8050 && command != 0x8051) {
warning("makeMacSndStream(): Unsupported command %04x", command);
return nullptr;
}
stream->readUint16BE(); // 0
uint32 soundHeaderOffset = stream->readUint32BE();
stream->seek(soundHeaderOffset);
uint32 soundDataOffset = stream->readUint32BE();
uint32 size = stream->readUint32BE();
uint16 rate = stream->readUint32BE() >> 16; // Really fixed point, but we only support integer rates
stream->readUint32BE(); // loop start
stream->readUint32BE(); // loop end
byte encoding = stream->readByte();
stream->readByte(); // base frequency
if (encoding != 0) {
// 0 == PCM
warning("makeMacSndStream(): Unsupported compression %d", encoding);
return nullptr;
}
stream->skip(soundDataOffset);
Common::SeekableReadStream *dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + size, disposeAfterUse);
// Since we allocated our own stream for the data, we must specify DisposeAfterUse::YES.
return makeRawStream(dataStream, rate, Audio::FLAG_UNSIGNED);
}
} // End of namespace Audio

57
audio/decoders/mac_snd.h Normal file
View File

@@ -0,0 +1,57 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - saga
* - sci
*/
#ifndef AUDIO_MAC_SND_H
#define AUDIO_MAC_SND_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
/**
* Try to load a Mac snd resource from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the snd data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeMacSndStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif

563
audio/decoders/mp3.cpp Normal file
View File

@@ -0,0 +1,563 @@
/* 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 "audio/decoders/mp3.h"
#ifdef USE_MAD
#include "common/debug.h"
#include "common/mutex.h"
#include "common/ptr.h"
#include "common/queue.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include <mad.h>
#if defined(__PSP__)
#include "backends/platform/psp/mp3.h"
#endif
namespace Audio {
#pragma mark -
#pragma mark --- MP3 (MAD) stream ---
#pragma mark -
class BaseMP3Stream : public virtual AudioStream {
public:
BaseMP3Stream();
virtual ~BaseMP3Stream();
bool endOfData() const override { return _state == MP3_STATE_EOS; }
bool isStereo() const override { return _channels == 2; }
int getRate() const override { return _rate; }
protected:
void decodeMP3Data(Common::ReadStream &stream);
void readMP3Data(Common::ReadStream &stream);
void initStream(Common::ReadStream &stream);
void readHeader(Common::ReadStream &stream);
void deinitStream();
int fillBuffer(Common::ReadStream &stream, int16 *buffer, const int numSamples);
enum State {
MP3_STATE_INIT, // Need to init the decoder
MP3_STATE_READY, // ready for processing data
MP3_STATE_EOS // end of data reached (may need to loop)
};
uint _posInFrame;
State _state;
mad_timer_t _curTime;
mad_stream _stream;
mad_frame _frame;
mad_synth _synth;
uint _channels;
uint _rate;
enum {
BUFFER_SIZE = 5 * 8192
};
// This buffer contains a slab of input data
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
};
class MP3Stream : private BaseMP3Stream, public SeekableAudioStream {
public:
MP3Stream(Common::SeekableReadStream *inStream,
DisposeAfterUse::Flag dispose);
int readBuffer(int16 *buffer, const int numSamples) override;
bool seek(const Timestamp &where) override;
Timestamp getLength() const override { return _length; }
protected:
Common::ScopedPtr<Common::SeekableReadStream> _inStream;
Timestamp _length;
private:
static Common::SeekableReadStream *skipID3(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose);
};
class PacketizedMP3Stream : private BaseMP3Stream, public PacketizedAudioStream {
public:
PacketizedMP3Stream(Common::SeekableReadStream &firstPacket);
PacketizedMP3Stream(uint channels, uint rate);
~PacketizedMP3Stream();
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples) override;
bool endOfData() const override;
bool endOfStream() const override;
// PacketizedAudioStream API
void queuePacket(Common::SeekableReadStream *packet) override;
void finish() override;
private:
Common::Mutex _mutex;
Common::Queue<Common::SeekableReadStream *> _queue;
bool _finished;
};
BaseMP3Stream::BaseMP3Stream() :
_posInFrame(0),
_state(MP3_STATE_INIT),
_curTime(mad_timer_zero) {
// The MAD_BUFFER_GUARD must always contain zeros (the reason
// for this is that the Layer III Huffman decoder of libMAD
// may read a few bytes beyond the end of the input buffer).
memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD);
}
BaseMP3Stream::~BaseMP3Stream() {
deinitStream();
}
void BaseMP3Stream::decodeMP3Data(Common::ReadStream &stream) {
do {
if (_state == MP3_STATE_INIT)
initStream(stream);
if (_state == MP3_STATE_EOS)
return;
// If necessary, load more data into the stream decoder
if (_stream.error == MAD_ERROR_BUFLEN)
readMP3Data(stream);
while (_state == MP3_STATE_READY) {
_stream.error = MAD_ERROR_NONE;
// Decode the next frame
if (mad_frame_decode(&_frame, &_stream) == -1) {
if (_stream.error == MAD_ERROR_BUFLEN) {
break; // Read more data
} else if (MAD_RECOVERABLE(_stream.error)) {
// Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here.
// These are normal and expected (caused by our frame skipping (i.e. "seeking")
// code above).
debug(6, "MP3Stream: Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
continue;
} else {
warning("MP3Stream: Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
break;
}
}
// Sum up the total playback time so far
mad_timer_add(&_curTime, _frame.header.duration);
// Synthesize PCM data
mad_synth_frame(&_synth, &_frame);
_posInFrame = 0;
break;
}
} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
if (_stream.error != MAD_ERROR_NONE)
_state = MP3_STATE_EOS;
}
void BaseMP3Stream::readMP3Data(Common::ReadStream &stream) {
uint32 remaining = 0;
// Give up immediately if we already used up all data in the stream
if (stream.eos()) {
_state = MP3_STATE_EOS;
return;
}
if (_stream.next_frame) {
// If there is still data in the MAD stream, we need to preserve it.
// Note that we use memmove, as we are reusing the same buffer,
// and hence the data regions we copy from and to may overlap.
remaining = _stream.bufend - _stream.next_frame;
assert(remaining < BUFFER_SIZE); // Paranoia check
memmove(_buf, _stream.next_frame, remaining);
}
// Try to read the next block
uint32 size = stream.read(_buf + remaining, BUFFER_SIZE - remaining);
if (size <= 0) {
_state = MP3_STATE_EOS;
return;
}
// Feed the data we just read into the stream decoder
_stream.error = MAD_ERROR_NONE;
mad_stream_buffer(&_stream, _buf, size + remaining);
}
void BaseMP3Stream::initStream(Common::ReadStream &stream) {
if (_state != MP3_STATE_INIT)
deinitStream();
// Init MAD
mad_stream_init(&_stream);
mad_frame_init(&_frame);
mad_synth_init(&_synth);
// Reset the stream data
_curTime = mad_timer_zero;
_posInFrame = 0;
// Update state
_state = MP3_STATE_READY;
// Read the first few sample bytes
readMP3Data(stream);
}
void BaseMP3Stream::readHeader(Common::ReadStream &stream) {
if (_state != MP3_STATE_READY)
return;
// If necessary, load more data into the stream decoder
if (_stream.error == MAD_ERROR_BUFLEN)
readMP3Data(stream);
while (_state != MP3_STATE_EOS) {
_stream.error = MAD_ERROR_NONE;
// Decode the next header. Note: mad_frame_decode would do this for us, too.
// However, for seeking we don't want to decode the full frame (else it would
// be far too slow). Hence we perform this explicitly in a separate step.
if (mad_header_decode(&_frame.header, &_stream) == -1) {
if (_stream.error == MAD_ERROR_BUFLEN) {
readMP3Data(stream); // Read more data
continue;
} else if (MAD_RECOVERABLE(_stream.error)) {
debug(6, "MP3Stream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
continue;
} else {
warning("MP3Stream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
break;
}
}
// Sum up the total playback time so far
mad_timer_add(&_curTime, _frame.header.duration);
break;
}
if (_stream.error != MAD_ERROR_NONE)
_state = MP3_STATE_EOS;
}
void BaseMP3Stream::deinitStream() {
if (_state == MP3_STATE_INIT)
return;
// Deinit MAD
mad_synth_finish(&_synth);
mad_frame_finish(&_frame);
mad_stream_finish(&_stream);
_state = MP3_STATE_EOS;
}
static inline int scaleSample(mad_fixed_t sample) {
// round
sample += (1L << (MAD_F_FRACBITS - 16));
// clip
if (sample > MAD_F_ONE - 1)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
// quantize and scale to not saturate when mixing a lot of channels
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
int BaseMP3Stream::fillBuffer(Common::ReadStream &stream, int16 *buffer, const int numSamples) {
int samples = 0;
// Keep going as long as we have input available
while (samples < numSamples && _state != MP3_STATE_EOS) {
const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * MAD_NCHANNELS(&_frame.header));
while (samples < len) {
*buffer++ = (int16)scaleSample(_synth.pcm.samples[0][_posInFrame]);
samples++;
if (MAD_NCHANNELS(&_frame.header) == 2) {
*buffer++ = (int16)scaleSample(_synth.pcm.samples[1][_posInFrame]);
samples++;
}
_posInFrame++;
}
if (_posInFrame >= _synth.pcm.length) {
// We used up all PCM data in the current frame -- read & decode more
decodeMP3Data(stream);
}
}
return samples;
}
MP3Stream::MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
BaseMP3Stream(),
_inStream(skipID3(inStream, dispose)),
_length(0, 1000) {
// Initialize the stream with some data and set the channels and rate
// variables
decodeMP3Data(*_inStream);
_channels = MAD_NCHANNELS(&_frame.header);
_rate = _frame.header.samplerate;
// Calculate the length of the stream
while (_state != MP3_STATE_EOS)
readHeader(*_inStream);
// To rule out any invalid sample rate to be encountered here, say in case the
// MP3 stream is invalid, we just check the MAD error code here.
// We need to assure this, since else we might trigger an assertion in Timestamp
// (When getRate() returns 0 or a negative number to be precise).
// Note that we allow "MAD_ERROR_BUFLEN" as error code here, since according
// to mad.h it is also set on EOF.
if ((_stream.error == MAD_ERROR_NONE || _stream.error == MAD_ERROR_BUFLEN) && getRate() > 0)
_length = Timestamp(mad_timer_count(_curTime, MAD_UNITS_MILLISECONDS), getRate());
deinitStream();
// Reinit stream
_state = MP3_STATE_INIT;
_inStream->seek(0);
// Decode the first chunk of data to set up the stream again.
decodeMP3Data(*_inStream);
}
int MP3Stream::readBuffer(int16 *buffer, const int numSamples) {
return fillBuffer(*_inStream, buffer, numSamples);
}
bool MP3Stream::seek(const Timestamp &where) {
if (where == _length) {
_state = MP3_STATE_EOS;
return true;
} else if (where > _length) {
return false;
}
const uint32 time = where.msecs();
mad_timer_t destination;
mad_timer_set(&destination, time / 1000, time % 1000, 1000);
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _curTime) < 0) {
_inStream->seek(0);
initStream(*_inStream);
}
while (mad_timer_compare(destination, _curTime) > 0 && _state != MP3_STATE_EOS)
readHeader(*_inStream);
decodeMP3Data(*_inStream);
return (_state != MP3_STATE_EOS);
}
Common::SeekableReadStream *MP3Stream::skipID3(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose) {
// Skip ID3 TAG if any
// ID3v1 (beginning with with 'TAG') is located at the end of files. So we can ignore those.
// ID3v2 can be located at the start of files and begins with a 10 bytes header, the first 3 bytes being 'ID3'.
// The tag size is coded on the last 4 bytes of the 10 bytes header as a 32 bit synchsafe integer.
// See https://id3.org/id3v2.4.0-structure for details.
char data[10];
stream->read(data, sizeof(data));
uint32 offset = 0;
if (!stream->eos() && data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
uint32 size = data[9] + 128 * (data[8] + 128 * (data[7] + 128 * data[6]));
// This size does not include an optional 10 bytes footer. Check if it is present.
if (data[5] & 0x10)
size += 10;
// Add in the 10 bytes we read in
size += sizeof(data);
debug(0, "Skipping ID3 TAG (%d bytes)", size);
offset = size;
}
return new Common::SeekableSubReadStream(stream, offset, stream->size(), dispose);
}
PacketizedMP3Stream::PacketizedMP3Stream(Common::SeekableReadStream &firstPacket) :
BaseMP3Stream(),
_finished(false) {
// Load some data to get the channels/rate
_queue.push(&firstPacket);
decodeMP3Data(firstPacket);
_channels = MAD_NCHANNELS(&_frame.header);
_rate = _frame.header.samplerate;
// Clear everything
deinitStream();
_state = MP3_STATE_INIT;
_queue.clear();
}
PacketizedMP3Stream::PacketizedMP3Stream(uint channels, uint rate) :
BaseMP3Stream(),
_finished(false) {
_channels = channels;
_rate = rate;
}
PacketizedMP3Stream::~PacketizedMP3Stream() {
Common::StackLock lock(_mutex);
while (!_queue.empty()) {
delete _queue.front();
_queue.pop();
}
}
int PacketizedMP3Stream::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
Common::StackLock lock(_mutex);
while (samples < numSamples) {
// Empty? Bail out for now, and mark the stream as ended
if (_queue.empty()) {
// EOS state is only valid once a packet has been received at least
// once
if (_state == MP3_STATE_READY)
_state = MP3_STATE_EOS;
return samples;
}
Common::SeekableReadStream *packet = _queue.front();
if (_state == MP3_STATE_INIT) {
// Initialize everything
decodeMP3Data(*packet);
} else if (_state == MP3_STATE_EOS) {
// Reset the end-of-stream setting
_state = MP3_STATE_READY;
}
samples += fillBuffer(*packet, buffer + samples, numSamples - samples);
// If the stream is done, kill it
if (packet->pos() >= packet->size()) {
_queue.pop();
delete packet;
}
}
// This will happen if the audio runs out just as the last sample is
// decoded. But there may still be more audio queued up.
if (_state == MP3_STATE_EOS && !_queue.empty()) {
_state = MP3_STATE_READY;
}
return samples;
}
bool PacketizedMP3Stream::endOfData() const {
Common::StackLock lock(_mutex);
return BaseMP3Stream::endOfData();
}
bool PacketizedMP3Stream::endOfStream() const {
Common::StackLock lock(_mutex);
if (!endOfData())
return false;
if (!_queue.empty())
return false;
return _finished;
}
void PacketizedMP3Stream::queuePacket(Common::SeekableReadStream *packet) {
Common::StackLock lock(_mutex);
assert(!_finished);
_queue.push(packet);
// If the audio had finished (buffer underrun?), there is more to
// decode now.
if (_state == MP3_STATE_EOS) {
_state = MP3_STATE_READY;
}
}
void PacketizedMP3Stream::finish() {
Common::StackLock lock(_mutex);
_finished = true;
}
#pragma mark -
#pragma mark --- MP3 factory functions ---
#pragma mark -
SeekableAudioStream *makeMP3Stream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
#if defined(__PSP__)
SeekableAudioStream *s = 0;
if (Mp3PspStream::isOkToCreateStream())
s = new Mp3PspStream(stream, disposeAfterUse);
if (!s) // go to regular MAD mp3 stream if ME fails
s = new MP3Stream(stream, disposeAfterUse);
#else
SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse);
#endif
if (s && s->endOfData()) {
delete s;
return nullptr;
} else {
return s;
}
}
PacketizedAudioStream *makePacketizedMP3Stream(Common::SeekableReadStream &firstPacket) {
return new PacketizedMP3Stream(firstPacket);
}
PacketizedAudioStream *makePacketizedMP3Stream(uint channels, uint rate) {
return new PacketizedMP3Stream(channels, rate);
}
} // End of namespace Audio
#endif // #ifdef USE_MAD

93
audio/decoders/mp3.h Normal file
View File

@@ -0,0 +1,93 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - agos
* - draci
* - glk
* - kyra
* - mohawk
* - queen
* - saga
* - sci
* - scumm
* - sword1
* - sword2
* - titanic
* - touche
* - tucker
*/
#ifndef AUDIO_MP3_H
#define AUDIO_MP3_H
#include "common/scummsys.h"
#include "common/types.h"
#ifdef USE_MAD
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class PacketizedAudioStream;
class SeekableAudioStream;
/**
* Create a new SeekableAudioStream from the MP3 data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the MP3 data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeMP3Stream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
/**
* Create a new PacketizedAudioStream from the first packet in the given
* stream. It does not own the packet and must be queued again later.
*
* @param firstPacket the SeekableReadStream from which to read the MP3 data
* @return a new PacketizedAudioStream
*/
PacketizedAudioStream *makePacketizedMP3Stream(
Common::SeekableReadStream &firstPacket);
/**
* Create a new PacketizedAudioStream for a given number of channels
* and sample rate.
*
* @param firstPacket the SeekableReadStream from which to read the MP3 data
* @return a new PacketizedAudioStream
*/
PacketizedAudioStream *makePacketizedMP3Stream(
uint channels, uint rate);
} // End of namespace Audio
#endif // #ifdef USE_MAD
#endif // #ifndef AUDIO_MP3_H

338
audio/decoders/mpc.cpp Normal file
View File

@@ -0,0 +1,338 @@
/* 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/>.
*
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#include "audio/decoders/mpc.h"
#ifdef USE_MPCDEC
#ifdef USE_MPCDEC_OLD_API
#include <mpcdec/mpcdec.h>
#else
#include <mpc/mpcdec.h>
#endif
#include "common/debug.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
namespace Audio {
// These are wrapper functions to allow using a SeekableReadStream object to
// provide data to the mpc_reader object.
#ifdef USE_MPCDEC_OLD_API
static mpc_int32_t read_stream(void *data, void *ptr, mpc_int32_t size) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
#else
static mpc_int32_t read_stream(mpc_reader *p_reader, void *ptr, mpc_int32_t size) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
#endif
return stream->read(ptr, size);
}
/// Seeks to byte position offset.
#ifdef USE_MPCDEC_OLD_API
static mpc_bool_t seek_stream(void *data, mpc_int32_t offset) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
#else
static mpc_bool_t seek_stream(mpc_reader *p_reader, mpc_int32_t offset) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
#endif
return stream->seek(offset);
}
/// Returns the current byte offset in the stream.
#ifdef USE_MPCDEC_OLD_API
static mpc_int32_t tell_stream(void *data) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
#else
static mpc_int32_t tell_stream(mpc_reader *p_reader) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
#endif
return stream->pos();
}
/// Returns the total length of the source stream, in bytes.
#ifdef USE_MPCDEC_OLD_API
static mpc_int32_t get_size_stream(void *data) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
#else
static mpc_int32_t get_size_stream(mpc_reader *p_reader) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
#endif
return stream->size();
}
/// True if the stream is a seekable stream.
#ifdef USE_MPCDEC_OLD_API
static mpc_bool_t canseek_stream(void *p_reader) {
return TRUE;
}
#else
static mpc_bool_t canseek_stream(mpc_reader *p_reader) {
return MPC_TRUE;
}
#endif
#pragma mark -
#pragma mark --- Musepack stream ---
#pragma mark -
class MPCStream : public SeekableAudioStream {
protected:
Common::DisposablePtr<Common::SeekableReadStream> _inStream;
bool _isStereo;
int _rate;
Timestamp _length;
mpc_reader _reader;
mpc_streaminfo _si;
#ifdef USE_MPCDEC_OLD_API
mpc_decoder _decoder;
#else
mpc_demux *_demux;
#endif
MPC_SAMPLE_FORMAT _bufferDec[MPC_DECODER_BUFFER_LENGTH];
uint16 _buffer[MPC_DECODER_BUFFER_LENGTH];
const uint16 *_bufferEnd;
const uint16 *_pos;
public:
// startTime / duration are in milliseconds
MPCStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
~MPCStream();
int readBuffer(int16 *buffer, const int numSamples) override;
bool endOfData() const override { return _pos >= _bufferEnd; }
bool isStereo() const override { return _isStereo; }
int getRate() const override { return _rate; }
bool seek(const Timestamp &where) override;
Timestamp getLength() const override { return _length; }
protected:
bool refill();
};
MPCStream::MPCStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
_inStream(inStream, dispose),
_length(0, 1000),
_bufferEnd(ARRAYEND(_buffer)) {
_pos = _bufferEnd; // This will return endOfBuffer() if we're not properly inited
_reader.read = read_stream;
_reader.seek = seek_stream;
_reader.tell = tell_stream;
_reader.get_size = get_size_stream;
_reader.canseek = canseek_stream;
_reader.data = (void *)inStream;
#ifdef USE_MPCDEC_OLD_API
mpc_streaminfo_init(&_si);
if (mpc_streaminfo_read(&_si, &_reader) < 0) {
warning("Cannot read musepack stream info");
return;
}
mpc_decoder_setup(&_decoder, &_reader);
mpc_decoder_scale_output (&_decoder, 1.0);
if (!mpc_decoder_initialize(&_decoder, &_si)) {
warning("Cannot initialize musepack decoder");
return;
}
#else
_demux = mpc_demux_init(&_reader);
if (!_demux) {
warning("Cannot init musepack demuxer");
return;
}
mpc_demux_get_info(_demux, &_si);
#endif
_isStereo = _si.channels >= 2;
_rate = _si.sample_freq;
_length = Timestamp(uint32(mpc_streaminfo_get_length(&_si) * 1000.0), getRate());
int time = (int)mpc_streaminfo_get_length(&_si);
int minutes = time / 60;
int seconds = time % 60;
debug(9, "stream version %d", _si.stream_version);
debug(9, "encoder: %s", _si.encoder);
#ifdef USE_MPCDEC_OLD_API
debug(9, "profile: %s (q=%d)", _si.profile_name, _si.profile);
#else
debug(9, "profile: %s (q=%0.2f)", _si.profile_name, _si.profile - 5);
debug(9, "PNS: %s", _si.pns == 0xFF ? "unknow" : _si.pns ? "on" : "off");
#endif
debug(9, "mid/side stereo: %s", _si.ms ? "on" : "off");
debug(9, "gapless: %s", _si.is_true_gapless ? "on" : "off");
debug(9, "average bitrate: %6.1f kbps", _si.average_bitrate * 1.e-3);
debug(9, "samplerate: %d Hz", _si.sample_freq);
debug(9, "channels: %d", _si.channels);
debug(9, "length: %d:%.2d (%u samples)", minutes, seconds, (mpc_uint32_t)mpc_streaminfo_get_length_samples(&_si));
debug(9, "file size: %d Bytes", _si.total_file_length);
debug(9, "track peak: %2.2f dB", _si.peak_title / 256.f);
debug(9, "track gain: %2.2f dB / %2.2f dB", _si.gain_title / 256.f, _si.gain_title == 0 ? 0 : 64.82f - _si.gain_title / 256.f);
debug(9, "album peak: %2.2f dB", _si.peak_album / 256.f);
debug(9, "album gain: %2.2f dB / %2.2f dB", _si.gain_album / 256.f, _si.gain_album == 0 ? 0 : 64.82f - _si.gain_album / 256.f);
if (!refill())
return;
}
MPCStream::~MPCStream() {
#ifndef USE_MPCDEC_OLD_API
mpc_demux_exit(_demux);
#endif
}
int MPCStream::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
while (samples < numSamples && _pos < _bufferEnd) {
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
memcpy(buffer, _pos, len * 2);
buffer += len;
_pos += len;
samples += len;
if (_pos >= _bufferEnd) {
if (!refill())
break;
}
}
return samples;
}
bool MPCStream::seek(const Timestamp &where) {
#ifdef USE_MPCDEC_OLD_API
bool res = (mpc_decoder_seek_seconds(&_decoder, (double)where.msecs() / 1000.0) == TRUE);
#else
bool res = (mpc_demux_seek_second(_demux, (double)where.msecs() / 1000.0) == MPC_STATUS_OK);
#endif
if (!res) {
warning("Error seeking in musepack stream");
_pos = _bufferEnd;
return false;
}
return refill();
}
bool MPCStream::refill() {
bool result;
uint32 samples;
#ifdef USE_MPCDEC_OLD_API
uint32 vbr_update_acc, vbr_update_bits;
samples = mpc_decoder_decode(&_decoder, _bufferDec, &vbr_update_acc, &vbr_update_bits);
if (samples == 0) { // End of stream
_pos = _buffer;
_bufferEnd = _buffer;
return false;
}
if (samples == -1u) { // Corruptd stream
result = false;
samples = 0;
} else {
result = true;
}
#else
mpc_frame_info frame;
frame.buffer = _bufferDec;
result = (mpc_demux_decode(_demux, &frame) == MPC_STATUS_OK);
if (frame.bits == -1) { // End of stream
_pos = _buffer;
_bufferEnd = _buffer;
return false;
}
samples = frame.samples;
#endif
if (!result) {
// Possibly recoverable, just warn about it
warning("Corrupted data in musepack file");
}
#ifdef MPC_FIXED_POINT
for(int i = 0; i < MPC_DECODER_BUFFER_LENGTH; i++) {
int tmp = _bufferDec[i] >> MPC_FIXED_POINT_FRACTPART;
if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
if (tmp < -(1 << 15)) tmp = -(1 << 15);
_buffer[i] = tmp;
}
#else
for (int i = 0; i < MPC_DECODER_BUFFER_LENGTH; i++) {
int tmp = nearbyintf(_bufferDec[i] * (1 << 15));
if (tmp > ((1 << 15) - 1))
tmp = ((1 << 15) - 1);
if (tmp < -(1 << 15))
tmp = -(1 << 15);
_buffer[i] = (uint16)tmp;
}
#endif
_pos = _buffer;
_bufferEnd = &_buffer[samples * _si.channels];
return true;
}
#pragma mark -
#pragma mark --- Ogg Vorbis factory functions ---
#pragma mark -
SeekableAudioStream *makeMPCStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new MPCStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return nullptr;
} else {
return s;
}
}
} // End of namespace Audio
#endif // #ifdef USE_MPCDEC

59
audio/decoders/mpc.h Normal file
View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - qdengine
*/
#ifndef AUDIO_MPC_H
#define AUDIO_MPC_H
#include "common/scummsys.h"
#include "common/types.h"
#ifdef USE_MPCDEC
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
/**
* Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the Ogg Vorbis data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeMPCStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif // #ifdef USE_MPCDEC
#endif // #ifndef AUDIO_MPC_H

2611
audio/decoders/qdm2.cpp Normal file

File diff suppressed because it is too large Load Diff

53
audio/decoders/qdm2.h Normal file
View File

@@ -0,0 +1,53 @@
/* 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/>.
*
*/
#ifndef AUDIO_QDM2_H
#define AUDIO_QDM2_H
#include "common/scummsys.h"
#ifdef USE_QDM2
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class Codec;
/**
* Create a new Codec from the QDM2 data in the given stream.
*
* @param extraData the QuickTime extra data stream
* @param disposeExtraData the QuickTime extra data stream
* @return a new Codec, or NULL, if an error occurred
*/
Codec *makeQDM2Decoder(Common::SeekableReadStream *extraData,
DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO);
} // End of namespace Audio
#endif // USE_QDM2
#endif // AUDIO_QDM2_H

527
audio/decoders/qdm2data.h Normal file
View File

@@ -0,0 +1,527 @@
/* 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/>.
*
*/
#ifndef AUDIO_QDM2DATA_H
#define AUDIO_QDM2DATA_H
#include "common/scummsys.h"
namespace Audio {
/// VLC TABLES
// values in this table range from -1..23; adjust retrieved value by -1
static const uint16 vlc_tab_level_huffcodes[24] = {
0x037c, 0x0004, 0x003c, 0x004c, 0x003a, 0x002c, 0x001c, 0x001a,
0x0024, 0x0014, 0x0001, 0x0002, 0x0000, 0x0003, 0x0007, 0x0005,
0x0006, 0x0008, 0x0009, 0x000a, 0x000c, 0x00fc, 0x007c, 0x017c
};
static const byte vlc_tab_level_huffbits[24] = {
10, 6, 7, 7, 6, 6, 6, 6, 6, 5, 4, 4, 4, 3, 3, 3, 3, 4, 4, 5, 7, 8, 9, 10
};
// values in this table range from -1..36; adjust retrieved value by -1
static const uint16 vlc_tab_diff_huffcodes[37] = {
0x1c57, 0x0004, 0x0000, 0x0001, 0x0003, 0x0002, 0x000f, 0x000e,
0x0007, 0x0016, 0x0037, 0x0027, 0x0026, 0x0066, 0x0006, 0x0097,
0x0046, 0x01c6, 0x0017, 0x0786, 0x0086, 0x0257, 0x00d7, 0x0357,
0x00c6, 0x0386, 0x0186, 0x0000, 0x0157, 0x0c57, 0x0057, 0x0000,
0x0b86, 0x0000, 0x1457, 0x0000, 0x0457
};
static const byte vlc_tab_diff_huffbits[37] = {
13, 3, 3, 2, 3, 3, 4, 4, 6, 5, 6, 6, 7, 7, 8, 8,
8, 9, 8, 11, 9, 10, 8, 10, 9, 12, 10, 0, 10, 13, 11, 0,
12, 0, 13, 0, 13
};
// values in this table range from -1..5; adjust retrieved value by -1
static const byte vlc_tab_run_huffcodes[6] = {
0x1f, 0x00, 0x01, 0x03, 0x07, 0x0f
};
static const byte vlc_tab_run_huffbits[6] = {
5, 1, 2, 3, 4, 5
};
// values in this table range from -1..19; adjust retrieved value by -1
static const uint16 vlc_tab_tone_level_idx_hi1_huffcodes[20] = {
0x5714, 0x000c, 0x0002, 0x0001, 0x0000, 0x0004, 0x0034, 0x0054,
0x0094, 0x0014, 0x0114, 0x0214, 0x0314, 0x0614, 0x0e14, 0x0f14,
0x2714, 0x0714, 0x1714, 0x3714
};
static const byte vlc_tab_tone_level_idx_hi1_huffbits[20] = {
15, 4, 2, 1, 3, 5, 6, 7, 8, 10, 10, 11, 11, 12, 12, 12, 14, 14, 15, 14
};
// values in this table range from -1..23; adjust retrieved value by -1
static const uint16 vlc_tab_tone_level_idx_mid_huffcodes[24] = {
0x0fea, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x03ea, 0x00ea, 0x002a, 0x001a,
0x0006, 0x0001, 0x0000, 0x0002, 0x000a, 0x006a, 0x01ea, 0x07ea
};
static const byte vlc_tab_tone_level_idx_mid_huffbits[24] = {
12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12
};
// values in this table range from -1..23; adjust retrieved value by -1
static const uint16 vlc_tab_tone_level_idx_hi2_huffcodes[24] = {
0x0664, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0064, 0x00e4,
0x00a4, 0x0068, 0x0004, 0x0008, 0x0014, 0x0018, 0x0000, 0x0001,
0x0002, 0x0003, 0x000c, 0x0028, 0x0024, 0x0164, 0x0000, 0x0264
};
static const byte vlc_tab_tone_level_idx_hi2_huffbits[24] = {
11, 0, 0, 0, 0, 0, 10, 8, 8, 7, 6, 6, 5, 5, 4, 2, 2, 2, 4, 7, 8, 9, 0, 11
};
// values in this table range from -1..8; adjust retrieved value by -1
static const byte vlc_tab_type30_huffcodes[9] = {
0x3c, 0x06, 0x00, 0x01, 0x03, 0x02, 0x04, 0x0c, 0x1c
};
static const byte vlc_tab_type30_huffbits[9] = {
6, 3, 3, 2, 2, 3, 4, 5, 6
};
// values in this table range from -1..9; adjust retrieved value by -1
static const byte vlc_tab_type34_huffcodes[10] = {
0x18, 0x00, 0x01, 0x04, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08
};
static const byte vlc_tab_type34_huffbits[10] = {
5, 4, 3, 3, 3, 3, 3, 3, 3, 5
};
// values in this table range from -1..22; adjust retrieved value by -1
static const uint16 vlc_tab_fft_tone_offset_0_huffcodes[23] = {
0x038e, 0x0001, 0x0000, 0x0022, 0x000a, 0x0006, 0x0012, 0x0002,
0x001e, 0x003e, 0x0056, 0x0016, 0x000e, 0x0032, 0x0072, 0x0042,
0x008e, 0x004e, 0x00f2, 0x002e, 0x0036, 0x00c2, 0x018e
};
static const byte vlc_tab_fft_tone_offset_0_huffbits[23] = {
10, 1, 2, 6, 4, 5, 6, 7, 6, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 6, 6, 8, 10
};
// values in this table range from -1..27; adjust retrieved value by -1
static const uint16 vlc_tab_fft_tone_offset_1_huffcodes[28] = {
0x07a4, 0x0001, 0x0020, 0x0012, 0x001c, 0x0008, 0x0006, 0x0010,
0x0000, 0x0014, 0x0004, 0x0032, 0x0070, 0x000c, 0x0002, 0x003a,
0x001a, 0x002c, 0x002a, 0x0022, 0x0024, 0x000a, 0x0064, 0x0030,
0x0062, 0x00a4, 0x01a4, 0x03a4
};
static const byte vlc_tab_fft_tone_offset_1_huffbits[28] = {
11, 1, 6, 6, 5, 4, 3, 6, 6, 5, 6, 6, 7, 6, 6, 6,
6, 6, 6, 7, 8, 6, 7, 7, 7, 9, 10, 11
};
// values in this table range from -1..31; adjust retrieved value by -1
static const uint16 vlc_tab_fft_tone_offset_2_huffcodes[32] = {
0x1760, 0x0001, 0x0000, 0x0082, 0x000c, 0x0006, 0x0003, 0x0007,
0x0008, 0x0004, 0x0010, 0x0012, 0x0022, 0x001a, 0x0000, 0x0020,
0x000a, 0x0040, 0x004a, 0x006a, 0x002a, 0x0042, 0x0002, 0x0060,
0x00aa, 0x00e0, 0x00c2, 0x01c2, 0x0160, 0x0360, 0x0760, 0x0f60
};
static const byte vlc_tab_fft_tone_offset_2_huffbits[32] = {
13, 2, 0, 8, 4, 3, 3, 3, 4, 4, 5, 5, 6, 5, 7, 7,
7, 7, 7, 7, 8, 8, 8, 9, 8, 8, 9, 9, 10, 11, 13, 12
};
// values in this table range from -1..34; adjust retrieved value by -1
static const uint16 vlc_tab_fft_tone_offset_3_huffcodes[35] = {
0x33ea, 0x0005, 0x0000, 0x000c, 0x0000, 0x0006, 0x0003, 0x0008,
0x0002, 0x0001, 0x0004, 0x0007, 0x001a, 0x000f, 0x001c, 0x002c,
0x000a, 0x001d, 0x002d, 0x002a, 0x000d, 0x004c, 0x008c, 0x006a,
0x00cd, 0x004d, 0x00ea, 0x020c, 0x030c, 0x010c, 0x01ea, 0x07ea,
0x0bea, 0x03ea, 0x13ea
};
static const byte vlc_tab_fft_tone_offset_3_huffbits[35] = {
14, 4, 0, 10, 4, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6,
6, 5, 6, 7, 7, 7, 8, 8, 8, 8, 9, 10, 10, 10, 10, 11,
12, 13, 14
};
// values in this table range from -1..37; adjust retrieved value by -1
static const uint16 vlc_tab_fft_tone_offset_4_huffcodes[38] = {
0x5282, 0x0016, 0x0000, 0x0136, 0x0004, 0x0000, 0x0007, 0x000a,
0x000e, 0x0003, 0x0001, 0x000d, 0x0006, 0x0009, 0x0012, 0x0005,
0x0025, 0x0022, 0x0015, 0x0002, 0x0076, 0x0035, 0x0042, 0x00c2,
0x0182, 0x00b6, 0x0036, 0x03c2, 0x0482, 0x01c2, 0x0682, 0x0882,
0x0a82, 0x0082, 0x0282, 0x1282, 0x3282, 0x2282
};
static const byte vlc_tab_fft_tone_offset_4_huffbits[38] = {
15, 6, 0, 9, 3, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6,
6, 6, 6, 8, 7, 6, 8, 9, 9, 8, 9, 10, 11, 10, 11, 12,
12, 12, 14, 15, 14, 14
};
/// FFT TABLES
// values in this table range from -1..27; adjust retrieved value by -1
static const uint16 fft_level_exp_alt_huffcodes[28] = {
0x1ec6, 0x0006, 0x00c2, 0x0142, 0x0242, 0x0246, 0x00c6, 0x0046,
0x0042, 0x0146, 0x00a2, 0x0062, 0x0026, 0x0016, 0x000e, 0x0005,
0x0004, 0x0003, 0x0000, 0x0001, 0x000a, 0x0012, 0x0002, 0x0022,
0x01c6, 0x02c6, 0x06c6, 0x0ec6
};
static const byte fft_level_exp_alt_huffbits[28] = {
13, 7, 8, 9, 10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3,
3, 2, 3, 3, 4, 5, 7, 8, 9, 11, 12, 13
};
// values in this table range from -1..19; adjust retrieved value by -1
static const uint16 fft_level_exp_huffcodes[20] = {
0x0f24, 0x0001, 0x0002, 0x0000, 0x0006, 0x0005, 0x0007, 0x000c,
0x000b, 0x0014, 0x0013, 0x0004, 0x0003, 0x0023, 0x0064, 0x00a4,
0x0024, 0x0124, 0x0324, 0x0724
};
static const byte fft_level_exp_huffbits[20] = {
12, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 10, 11, 12
};
// values in this table range from -1..6; adjust retrieved value by -1
static const byte fft_stereo_exp_huffcodes[7] = {
0x3e, 0x01, 0x00, 0x02, 0x06, 0x0e, 0x1e
};
static const byte fft_stereo_exp_huffbits[7] = {
6, 1, 2, 3, 4, 5, 6
};
// values in this table range from -1..8; adjust retrieved value by -1
static const byte fft_stereo_phase_huffcodes[9] = {
0x35, 0x02, 0x00, 0x01, 0x0d, 0x15, 0x05, 0x09, 0x03
};
static const byte fft_stereo_phase_huffbits[9] = {
6, 2, 2, 4, 4, 6, 5, 4, 2
};
static const int fft_cutoff_index_table[4][2] = {
{ 1, 2 }, {-1, 0 }, {-1,-2 }, { 0, 0 }
};
static const int16 fft_level_index_table[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
};
static const byte last_coeff[3] = {
4, 7, 10
};
static const byte coeff_per_sb_for_avg[3][30] = {
{ 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
{ 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
{ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9 }
};
static const uint32 dequant_table[3][10][30] = {
{ { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 256, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 51, 102, 154, 205, 256, 238, 219, 201, 183, 165, 146, 128, 110, 91, 73, 55, 37, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256, 228, 199, 171, 142, 114, 85, 57, 28 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 85, 171, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 85, 171, 256, 219, 183, 146, 110, 73, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 73, 110, 146, 183, 219, 256, 228, 199, 171, 142, 114, 85, 57, 28, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 57, 85, 114, 142, 171, 199, 228, 256, 213, 171, 128, 85, 43 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 256, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 85, 171, 256, 192, 128, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 128, 192, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 102, 154, 205, 256, 213, 171, 128, 85, 43, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 85, 128, 171, 213, 256, 213, 171, 128, 85, 43 } }
};
static const byte coeff_per_sb_for_dequant[3][30] = {
{ 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
{ 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 },
{ 0, 1, 2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9 }
};
// first index is subband, 2nd index is 0, 1 or 3 (2 is unused)
static const int8 tone_level_idx_offset_table[30][4] = {
{ -50, -50, 0, -50 },
{ -50, -50, 0, -50 },
{ -50, -9, 0, -19 },
{ -16, -6, 0, -12 },
{ -11, -4, 0, -8 },
{ -8, -3, 0, -6 },
{ -7, -3, 0, -5 },
{ -6, -2, 0, -4 },
{ -5, -2, 0, -3 },
{ -4, -1, 0, -3 },
{ -4, -1, 0, -2 },
{ -3, -1, 0, -2 },
{ -3, -1, 0, -2 },
{ -3, -1, 0, -2 },
{ -2, -1, 0, -1 },
{ -2, -1, 0, -1 },
{ -2, -1, 0, -1 },
{ -2, 0, 0, -1 },
{ -2, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, -1 },
{ -1, 0, 0, 0 },
{ -1, 0, 0, 0 },
{ -1, 0, 0, 0 },
{ -1, 0, 0, 0 }
};
/* all my samples have 1st index 0 or 1 */
/* second index is subband, only indexes 0-29 seem to be used */
static const int8 coding_method_table[5][30] = {
{ 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
},
{ 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
},
{ 34, 30, 30, 30, 24, 24, 16, 16, 16, 16, 16, 16, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
},
{ 34, 34, 30, 30, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, 10
},
{ 34, 34, 30, 30, 30, 30, 30, 30, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
},
};
static const int vlc_stage3_values[60] = {
0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24,
28, 36, 44, 52, 60, 76, 92, 108, 124, 156, 188, 220,
252, 316, 380, 444, 508, 636, 764, 892, 1020, 1276, 1532, 1788,
2044, 2556, 3068, 3580, 4092, 5116, 6140, 7164, 8188, 10236, 12284, 14332,
16380, 20476, 24572, 28668, 32764, 40956, 49148, 57340, 65532, 81916, 98300,114684
};
static const float fft_tone_sample_table[4][16][5] = {
{ { .0100000000f,-.0037037037f,-.0020000000f,-.0069444444f,-.0018416207f },
{ .0416666667f, .0000000000f, .0000000000f,-.0208333333f,-.0123456791f },
{ .1250000000f, .0558035709f, .0330687836f,-.0164473690f,-.0097465888f },
{ .1562500000f, .0625000000f, .0370370370f,-.0062500000f,-.0037037037f },
{ .1996007860f, .0781250000f, .0462962948f, .0022727272f, .0013468013f },
{ .2000000000f, .0625000000f, .0370370373f, .0208333333f, .0074074073f },
{ .2127659619f, .0555555556f, .0329218097f, .0208333333f, .0123456791f },
{ .2173913121f, .0473484844f, .0280583613f, .0347222239f, .0205761325f },
{ .2173913121f, .0347222239f, .0205761325f, .0473484844f, .0280583613f },
{ .2127659619f, .0208333333f, .0123456791f, .0555555556f, .0329218097f },
{ .2000000000f, .0208333333f, .0074074073f, .0625000000f, .0370370370f },
{ .1996007860f, .0022727272f, .0013468013f, .0781250000f, .0462962948f },
{ .1562500000f,-.0062500000f,-.0037037037f, .0625000000f, .0370370370f },
{ .1250000000f,-.0164473690f,-.0097465888f, .0558035709f, .0330687836f },
{ .0416666667f,-.0208333333f,-.0123456791f, .0000000000f, .0000000000f },
{ .0100000000f,-.0069444444f,-.0018416207f,-.0037037037f,-.0020000000f } },
{ { .0050000000f,-.0200000000f, .0125000000f,-.3030303030f, .0020000000f },
{ .1041666642f, .0400000000f,-.0250000000f, .0333333333f,-.0200000000f },
{ .1250000000f, .0100000000f, .0142857144f,-.0500000007f,-.0200000000f },
{ .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f },
{ .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f },
{ .1250000000f,-.0500000000f,-.0200000000f, .0100000000f, .0142857144f },
{ .1041666667f, .0333333333f,-.0200000000f, .0400000000f,-.0250000000f },
{ .0050000000f,-.3030303030f, .0020000001f,-.0200000000f, .0125000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } },
{ { .1428571492f, .1250000000f,-.0285714287f,-.0357142873f, .0208333333f },
{ .1818181818f, .0588235296f, .0333333333f, .0212765951f, .0100000000f },
{ .1818181818f, .0212765951f, .0100000000f, .0588235296f, .0333333333f },
{ .1428571492f,-.0357142873f, .0208333333f, .1250000000f,-.0285714287f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } },
{ { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } }
};
static const float fft_tone_level_table[2][64] = { {
// pow ~ (i > 46) ? 0 : (((((i & 1) ? 431 : 304) << (i >> 1))) / 1024.0);
0.17677669f, 0.42677650f, 0.60355347f, 0.85355347f,
1.20710683f, 1.68359375f, 2.37500000f, 3.36718750f,
4.75000000f, 6.73437500f, 9.50000000f, 13.4687500f,
19.0000000f, 26.9375000f, 38.0000000f, 53.8750000f,
76.0000000f, 107.750000f, 152.000000f, 215.500000f,
304.000000f, 431.000000f, 608.000000f, 862.000000f,
1216.00000f, 1724.00000f, 2432.00000f, 3448.00000f,
4864.00000f, 6896.00000f, 9728.00000f, 13792.0000f,
19456.0000f, 27584.0000f, 38912.0000f, 55168.0000f,
77824.0000f, 110336.000f, 155648.000f, 220672.000f,
311296.000f, 441344.000f, 622592.000f, 882688.000f,
1245184.00f, 1765376.00f, 2490368.00f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
}, {
// pow = (i > 45) ? 0 : ((((i & 1) ? 431 : 304) << (i >> 1)) / 512.0);
0.59375000f, 0.84179688f, 1.18750000f, 1.68359375f,
2.37500000f, 3.36718750f, 4.75000000f, 6.73437500f,
9.50000000f, 13.4687500f, 19.0000000f, 26.9375000f,
38.0000000f, 53.8750000f, 76.0000000f, 107.750000f,
152.000000f, 215.500000f, 304.000000f, 431.000000f,
608.000000f, 862.000000f, 1216.00000f, 1724.00000f,
2432.00000f, 3448.00000f, 4864.00000f, 6896.00000f,
9728.00000f, 13792.0000f, 19456.0000f, 27584.0000f,
38912.0000f, 55168.0000f, 77824.0000f, 110336.000f,
155648.000f, 220672.000f, 311296.000f, 441344.000f,
622592.000f, 882688.000f, 1245184.00f, 1765376.00f,
2490368.00f, 3530752.00f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f
} };
static const float fft_tone_envelope_table[4][31] = {
{ .009607375f, .038060248f, .084265202f, .146446645f, .222214907f, .308658302f,
.402454883f, .500000060f, .597545207f, .691341758f, .777785182f, .853553414f,
.915734828f, .961939812f, .990392685f, 1.00000000f, .990392625f, .961939752f,
.915734768f, .853553295f, .777785063f, .691341639f, .597545087f, .500000000f,
.402454853f, .308658272f, .222214878f, .146446615f, .084265172f, .038060218f,
.009607345f },
{ .038060248f, .146446645f, .308658302f, .500000060f, .691341758f, .853553414f,
.961939812f, 1.00000000f, .961939752f, .853553295f, .691341639f, .500000000f,
.308658272f, .146446615f, .038060218f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f },
{ .146446645f, .500000060f, .853553414f, 1.00000000f, .853553295f, .500000000f,
.146446615f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f },
{ .500000060f, 1.00000000f, .500000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
.000000000f }
};
static const float sb_noise_attenuation[32] = {
0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 0.7f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
};
static const byte fft_subpackets[32] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0
};
// first index is joined_stereo, second index is 0 or 2 (1 is unused)
static const float dequant_1bit[2][3] = {
{-0.920000f, 0.000000f, 0.920000f },
{-0.890000f, 0.000000f, 0.890000f }
};
static const float type30_dequant[8] = {
-1.0f,-0.625f,-0.291666656732559f,0.0f,
0.25f,0.5f,0.75f,1.0f,
};
static const float type34_delta[10] = { // FIXME: covers 8 entries..
-1.0f,-0.60947573184967f,-0.333333343267441f,-0.138071194291115f,0.0f,
0.138071194291115f,0.333333343267441f,0.60947573184967f,1.0f,0.0f,
};
} // End of namespace Audio
#endif

View File

@@ -0,0 +1,712 @@
/* 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/debug.h"
#include "common/util.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "audio/decoders/codec.h"
#include "audio/decoders/quicktime.h"
#include "audio/decoders/quicktime_intern.h"
// Codecs
#include "audio/decoders/aac.h"
#include "audio/decoders/adpcm.h"
#include "audio/decoders/qdm2.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/g711.h"
namespace Audio {
/**
* An AudioStream wrapper that forces audio to be played in mono.
* It currently just ignores the right channel if stereo.
*/
class ForcedMonoAudioStream : public AudioStream {
public:
ForcedMonoAudioStream(AudioStream *parentStream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) :
_parentStream(parentStream), _disposeAfterUse(disposeAfterUse) {}
~ForcedMonoAudioStream() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _parentStream;
}
int readBuffer(int16 *buffer, const int numSamples) override {
if (!_parentStream->isStereo())
return _parentStream->readBuffer(buffer, numSamples);
int16 temp[2];
int samples = 0;
while (samples < numSamples && !endOfData()) {
_parentStream->readBuffer(temp, 2);
*buffer++ = temp[0];
samples++;
}
return samples;
}
bool endOfData() const override { return _parentStream->endOfData(); }
bool isStereo() const override { return false; }
int getRate() const override { return _parentStream->getRate(); }
private:
AudioStream *_parentStream;
DisposeAfterUse::Flag _disposeAfterUse;
};
QuickTimeAudioDecoder::QuickTimeAudioDecoder() : Common::QuickTimeParser() {
}
QuickTimeAudioDecoder::~QuickTimeAudioDecoder() {
for (uint32 i = 0; i < _audioTracks.size(); i++)
delete _audioTracks[i];
}
bool QuickTimeAudioDecoder::loadAudioFile(const Common::Path &filename) {
if (!Common::QuickTimeParser::parseFile(filename))
return false;
init();
return true;
}
bool QuickTimeAudioDecoder::loadAudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) {
if (!Common::QuickTimeParser::parseStream(stream, disposeFileHandle))
return false;
init();
return true;
}
void QuickTimeAudioDecoder::init() {
Common::QuickTimeParser::init();
// Initialize all the audio streams
// But ignore any streams we don't support
for (uint32 i = 0; i < _tracks.size(); i++)
if (_tracks[i]->codecType == CODEC_TYPE_AUDIO && ((AudioSampleDesc *)_tracks[i]->sampleDescs[0])->isAudioCodecSupported())
_audioTracks.push_back(new QuickTimeAudioTrack(this, _tracks[i]));
}
Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_AUDIO) {
debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format));
AudioSampleDesc *entry = new AudioSampleDesc(track, format);
uint16 stsdVersion = _fd->readUint16BE();
_fd->readUint16BE(); // revision level
_fd->readUint32BE(); // vendor
entry->_channels = _fd->readUint16BE(); // channel count
entry->_bitsPerSample = _fd->readUint16BE(); // sample size
_fd->readUint16BE(); // compression id = 0
_fd->readUint16BE(); // packet size = 0
entry->_sampleRate = (_fd->readUint32BE() >> 16);
debug(0, "stsd version =%d", stsdVersion);
if (stsdVersion == 0) {
// Not used, except in special cases. See below.
entry->_samplesPerFrame = entry->_bytesPerFrame = 0;
} else if (stsdVersion == 1) {
// Read QT version 1 fields. In version 0 these dont exist.
entry->_samplesPerFrame = _fd->readUint32BE();
debug(0, "stsd samples_per_frame =%d",entry->_samplesPerFrame);
_fd->readUint32BE(); // bytes per packet
entry->_bytesPerFrame = _fd->readUint32BE();
debug(0, "stsd bytes_per_frame =%d", entry->_bytesPerFrame);
_fd->readUint32BE(); // bytes per sample
} else {
warning("Unsupported QuickTime STSD audio version %d", stsdVersion);
delete entry;
return nullptr;
}
// Version 0 files don't have some variables set, so we'll do that here
if (format == MKTAG('i', 'm', 'a', '4')) {
entry->_samplesPerFrame = 64;
entry->_bytesPerFrame = 34 * entry->_channels;
}
if (entry->_sampleRate == 0 && track->timeScale > 1)
entry->_sampleRate = track->timeScale;
return entry;
}
return nullptr;
}
QuickTimeAudioDecoder::QuickTimeAudioTrack::QuickTimeAudioTrack(QuickTimeAudioDecoder *decoder, Common::QuickTimeParser::Track *parentTrack) {
_decoder = decoder;
_parentTrack = parentTrack;
_queue = createStream();
_samplesQueued = 0;
AudioSampleDesc *entry = (AudioSampleDesc *)_parentTrack->sampleDescs[0];
if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's'))
_parentTrack->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels;
// Initialize our edit parser too
_curEdit = 0;
enterNewEdit(Timestamp());
// If the edit doesn't start on a nice boundary, set us up to skip some samples
Timestamp editStartTime(0, _parentTrack->editList[_curEdit].mediaTime, _parentTrack->timeScale);
Timestamp trackPosition = getCurrentTrackTime();
if (_parentTrack->editList[_curEdit].mediaTime != -1 && trackPosition != editStartTime)
_skipSamples = editStartTime.convertToFramerate(getRate()) - trackPosition;
}
QuickTimeAudioDecoder::QuickTimeAudioTrack::~QuickTimeAudioTrack() {
delete _queue;
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &length) {
if (allDataRead() || (length.totalNumberOfFrames() != 0 && Timestamp(0, _samplesQueued, getRate()) >= length))
return;
do {
Timestamp nextEditTime(0, _parentTrack->editList[_curEdit].timeOffset + _parentTrack->editList[_curEdit].trackDuration, _decoder->_timeScale);
if (_parentTrack->editList[_curEdit].mediaTime == -1) {
// We've got an empty edit, so fill it with silence
Timestamp editLength(0, _parentTrack->editList[_curEdit].trackDuration, _decoder->_timeScale);
// If we seek into the middle of an empty edit, we need to adjust
if (_skipSamples != Timestamp()) {
editLength = editLength - _skipSamples;
_skipSamples = Timestamp();
}
queueStream(makeLimitingAudioStream(makeSilentAudioStream(getRate(), isStereo()), editLength), editLength);
_curEdit++;
enterNewEdit(nextEditTime);
} else {
// Normal audio
AudioStream *stream = readAudioChunk(_curChunk);
Timestamp chunkLength = getChunkLength(_curChunk, _skipAACPrimer);
_skipAACPrimer = false;
_curChunk++;
// If we have any samples that we need to skip (ie. we seek'ed into
// the middle of a chunk), skip them here.
if (_skipSamples != Timestamp()) {
if (_skipSamples > chunkLength) {
// If the amount we need to skip is greater than the size
// of the chunk, just skip it altogether.
_curMediaPos = _curMediaPos + chunkLength;
_skipSamples = _skipSamples - chunkLength;
delete stream;
continue;
}
skipSamples(_skipSamples, stream);
_curMediaPos = _curMediaPos + _skipSamples;
chunkLength = chunkLength - _skipSamples;
_skipSamples = Timestamp();
}
// Calculate our overall position within the media
Timestamp trackPosition = getCurrentTrackTime() + chunkLength;
// If we have reached the end of this edit (or have no more media to read),
// we move on to the next edit
if (trackPosition >= nextEditTime || _curChunk >= _parentTrack->chunkCount) {
chunkLength = nextEditTime.convertToFramerate(getRate()) - getCurrentTrackTime();
stream = makeLimitingAudioStream(stream, chunkLength);
_curEdit++;
enterNewEdit(nextEditTime);
// Next time around, we'll know how much to skip
trackPosition = getCurrentTrackTime();
if (!allDataRead() && _parentTrack->editList[_curEdit].mediaTime != -1 && nextEditTime != trackPosition)
_skipSamples = nextEditTime.convertToFramerate(getRate()) - trackPosition;
} else {
_curMediaPos = _curMediaPos + chunkLength.convertToFramerate(_curMediaPos.framerate());
}
queueStream(stream, chunkLength);
}
} while (!allDataRead() && Timestamp(0, _samplesQueued, getRate()) < length);
}
Timestamp QuickTimeAudioDecoder::QuickTimeAudioTrack::getCurrentTrackTime() const {
if (allDataRead())
return getLength().convertToFramerate(getRate());
return Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale).convertToFramerate(getRate())
+ _curMediaPos - Timestamp(0, _parentTrack->editList[_curEdit].mediaTime, _parentTrack->timeScale).convertToFramerate(getRate());
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueRemainingAudio() {
queueAudio(getLength());
}
int QuickTimeAudioDecoder::QuickTimeAudioTrack::readBuffer(int16 *buffer, const int numSamples) {
int samplesRead = _queue->readBuffer(buffer, numSamples);
_samplesQueued -= samplesRead / (isStereo() ? 2 : 1);
return samplesRead;
}
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::allDataRead() const {
return _curEdit == _parentTrack->editList.size();
}
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::endOfData() const {
return allDataRead() && _queue->endOfData();
}
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::seek(const Timestamp &where) {
// Recreate the queue
delete _queue;
_queue = createStream();
_samplesQueued = 0;
if (where >= getLength()) {
// We're done
_curEdit = _parentTrack->editList.size();
return true;
}
// Find where we are in the stream
findEdit(where);
// Now queue up some audio and skip whatever we need to skip
Timestamp samplesToSkip = where.convertToFramerate(getRate()) - getCurrentTrackTime();
queueAudio();
skipSamples(samplesToSkip, _queue);
return true;
}
Timestamp QuickTimeAudioDecoder::QuickTimeAudioTrack::getLength() const {
return Timestamp(0, _parentTrack->duration, _decoder->_timeScale);
}
QueuingAudioStream *QuickTimeAudioDecoder::QuickTimeAudioTrack::createStream() const {
AudioSampleDesc *entry = (AudioSampleDesc *)_parentTrack->sampleDescs[0];
return makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2);
}
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::isOldDemuxing() const {
return _parentTrack->timeToSampleCount == 1 && _parentTrack->timeToSample[0].duration == 1;
}
AudioStream *QuickTimeAudioDecoder::QuickTimeAudioTrack::readAudioChunk(uint chunk) {
AudioSampleDesc *entry = (AudioSampleDesc *)_parentTrack->sampleDescs[0];
Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::NO);
_decoder->_fd->seek(_parentTrack->chunkOffsets[chunk]);
// First, we have to get the sample count
uint32 sampleCount = getAudioChunkSampleCount(chunk);
assert(sampleCount != 0);
if (isOldDemuxing()) {
// Old-style audio demuxing
// Then calculate the right sizes
while (sampleCount > 0) {
uint32 samples = 0, size = 0;
if (entry->_samplesPerFrame >= 160) {
samples = entry->_samplesPerFrame;
size = entry->_bytesPerFrame;
} else if (entry->_samplesPerFrame > 1) {
samples = MIN<uint32>((1024 / entry->_samplesPerFrame) * entry->_samplesPerFrame, sampleCount);
size = (samples / entry->_samplesPerFrame) * entry->_bytesPerFrame;
} else {
samples = MIN<uint32>(1024, sampleCount);
size = samples * _parentTrack->sampleSize;
}
// Now, we read in the data for this data and output it
byte *data = (byte *)malloc(size);
_decoder->_fd->read(data, size);
wStream->write(data, size);
free(data);
sampleCount -= samples;
}
} else {
// New-style audio demuxing
// Find our starting sample
uint32 startSample = 0;
for (uint32 i = 0; i < chunk; i++)
startSample += getAudioChunkSampleCount(i);
for (uint32 i = 0; i < sampleCount; i++) {
uint32 size = (_parentTrack->sampleSize != 0) ? _parentTrack->sampleSize : _parentTrack->sampleSizes[i + startSample];
// Now, we read in the data for this data and output it
byte *data = (byte *)malloc(size);
_decoder->_fd->read(data, size);
wStream->write(data, size);
free(data);
}
}
AudioStream *audioStream = entry->createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES));
delete wStream;
return audioStream;
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::skipSamples(const Timestamp &length, AudioStream *stream) {
int32 sampleCount = length.convertToFramerate(getRate()).totalNumberOfFrames();
if (sampleCount <= 0)
return;
if (isStereo())
sampleCount *= 2;
int16 *tempBuffer = new int16[sampleCount];
uint32 result = stream->readBuffer(tempBuffer, sampleCount);
delete[] tempBuffer;
// If this is the queue, make sure we subtract this number from the
// amount queued
if (stream == _queue)
_samplesQueued -= result / (isStereo() ? 2 : 1);
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::findEdit(const Timestamp &position) {
// Go through the edits look for where we find out we need to be. As long
// as the position is >= to the edit's start time, it is considered to be in that
// edit. seek() already figured out if we reached the last edit, so we don't need
// to handle that case here.
for (_curEdit = 0; _curEdit < _parentTrack->editList.size() - 1; _curEdit++) {
Timestamp nextEditTime(0, _parentTrack->editList[_curEdit + 1].timeOffset, _decoder->_timeScale);
if (position < nextEditTime)
break;
}
enterNewEdit(position);
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::enterNewEdit(const Timestamp &position) {
_skipSamples = Timestamp(); // make sure our skip variable doesn't remain around
// If we're at the end of the edit list, there's nothing else for us to do here
if (allDataRead())
return;
// For an empty edit, we may need to adjust the start time
if (_parentTrack->editList[_curEdit].mediaTime == -1) {
// Just invalidate the current media position (and make sure the scale
// is in terms of our rate so it simplifies things later)
_curMediaPos = Timestamp(0, 0, getRate());
// Also handle shortening of the empty edit if needed
if (position != Timestamp())
_skipSamples = position.convertToFramerate(_decoder->_timeScale) - Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale);
return;
}
// I really hope I never need to implement this :P
// But, I'll throw in this error just to make sure I catch anything with this...
if (_parentTrack->editList[_curEdit].mediaRate != 1)
warning("QuickTimeAudioDecoder: Unhandled QuickTime audio rate change");
// Reinitialize the codec
((AudioSampleDesc *)_parentTrack->sampleDescs[0])->initCodec();
_skipAACPrimer = true;
// First, we need to track down what audio sample we need
// Convert our variables from the media time (position) and the edit time (based on position)
// and the media time
Timestamp curAudioTime = Timestamp(0, _parentTrack->editList[_curEdit].mediaTime, _parentTrack->timeScale)
+ position.convertToFramerate(_parentTrack->timeScale)
- Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale).convertToFramerate(_parentTrack->timeScale);
uint32 sample = curAudioTime.totalNumberOfFrames();
uint32 seekSample = sample;
if (!isOldDemuxing()) {
// For MPEG-4 style demuxing, we need to track down the sample based on the time
// The old style demuxing doesn't require this because each "sample"'s duration
// is just 1
uint32 curSample = 0;
seekSample = 0;
for (int32 i = 0; i < _parentTrack->timeToSampleCount; i++) {
uint32 sampleCount = _parentTrack->timeToSample[i].count * _parentTrack->timeToSample[i].duration;
if (sample < curSample + sampleCount) {
seekSample += (sample - curSample) / _parentTrack->timeToSample[i].duration;
break;
}
seekSample += _parentTrack->timeToSample[i].count;
curSample += sampleCount;
}
}
// Now to track down what chunk it's in
uint32 totalSamples = 0;
_curChunk = 0;
for (uint32 i = 0; i < _parentTrack->chunkCount; i++, _curChunk++) {
uint32 chunkSampleCount = getAudioChunkSampleCount(i);
if (seekSample < totalSamples + chunkSampleCount)
break;
totalSamples += chunkSampleCount;
}
// Now we get to have fun and convert *back* to an actual time
// We don't want the sample count to be modified at this point, though
if (!isOldDemuxing())
totalSamples = getAACSampleTime(totalSamples);
_curMediaPos = Timestamp(0, totalSamples, getRate());
}
void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueStream(AudioStream *stream, const Timestamp &length) {
// If the samples are stereo and the container is mono, force the samples
// to be mono.
if (stream->isStereo() && !isStereo())
_queue->queueAudioStream(new ForcedMonoAudioStream(stream, DisposeAfterUse::YES), DisposeAfterUse::YES);
else
_queue->queueAudioStream(stream, DisposeAfterUse::YES);
_samplesQueued += length.convertToFramerate(getRate()).totalNumberOfFrames();
}
uint32 QuickTimeAudioDecoder::QuickTimeAudioTrack::getAudioChunkSampleCount(uint chunk) const {
uint32 sampleCount = 0;
for (uint32 i = 0; i < _parentTrack->sampleToChunkCount; i++)
if (chunk >= _parentTrack->sampleToChunk[i].first)
sampleCount = _parentTrack->sampleToChunk[i].count;
return sampleCount;
}
Timestamp QuickTimeAudioDecoder::QuickTimeAudioTrack::getChunkLength(uint chunk, bool skipAACPrimer) const {
uint32 chunkSampleCount = getAudioChunkSampleCount(chunk);
if (isOldDemuxing())
return Timestamp(0, chunkSampleCount, getRate());
// AAC needs some extra handling, of course
return Timestamp(0, getAACSampleTime(chunkSampleCount, skipAACPrimer), getRate());
}
uint32 QuickTimeAudioDecoder::QuickTimeAudioTrack::getAACSampleTime(uint32 totalSampleCount, bool skipAACPrimer) const{
uint32 curSample = 0;
uint32 time = 0;
for (int32 i = 0; i < _parentTrack->timeToSampleCount; i++) {
uint32 sampleCount = _parentTrack->timeToSample[i].count;
if (totalSampleCount < curSample + sampleCount) {
time += (totalSampleCount - curSample) * _parentTrack->timeToSample[i].duration;
break;
}
time += _parentTrack->timeToSample[i].count * _parentTrack->timeToSample[i].duration;
curSample += sampleCount;
}
// The first chunk of AAC contains "duration" samples that are used as a primer
// We need to subtract that number from the duration for the first chunk. See:
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFAppenG/QTFFAppenG.html#//apple_ref/doc/uid/TP40000939-CH2-SW1
// The skipping of both the primer and the remainder are handled by the AAC code,
// whereas the timing of the remainder are handled by this time-to-sample chunk
// code already.
// We have to do this after each time we reinitialize the codec
if (skipAACPrimer) {
assert(_parentTrack->timeToSampleCount > 0);
time -= _parentTrack->timeToSample[0].duration;
}
return time;
}
QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
_channels = 0;
_sampleRate = 0;
_samplesPerFrame = 0;
_bytesPerFrame = 0;
_bitsPerSample = 0;
_codec = nullptr;
}
QuickTimeAudioDecoder::AudioSampleDesc::~AudioSampleDesc() {
delete _codec;
}
bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const {
// Check if the codec is a supported codec
if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ') || _codecTag == MKTAG('i', 'm', 'a', '4'))
return true;
#ifdef USE_QDM2
if (_codecTag == MKTAG('Q', 'D', 'M', '2'))
return true;
#endif
if (_codecTag == MKTAG('m', 'p', '4', 'a')) {
Common::String audioType;
switch (_objectTypeMP4) {
case 0x40: // AAC
#ifdef USE_FAAD
return true;
#else
audioType = "AAC";
break;
#endif
default:
audioType = "Unknown";
break;
}
warning("No MPEG-4 audio (%s) support", audioType.c_str());
} else {
warning("Audio Codec Not Supported: \'%s\'", tag2str(_codecTag));
}
return false;
}
AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::SeekableReadStream *stream) const {
if (!stream)
return nullptr;
if (_codec) {
// If we've loaded a codec, make sure we use first
AudioStream *audioStream = _codec->decodeFrame(*stream);
delete stream;
return audioStream;
} else if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) {
// Fortunately, most of the audio used in Myst videos is raw...
uint16 flags = 0;
if (_codecTag == MKTAG('r', 'a', 'w', ' '))
flags |= FLAG_UNSIGNED;
if (_channels == 2)
flags |= FLAG_STEREO;
if (_bitsPerSample == 16)
flags |= FLAG_16BITS;
return makeRawStream(stream, _sampleRate, flags);
} else if (_codecTag == MKTAG('i', 'm', 'a', '4')) {
// Riven uses this codec (as do some Myst ME videos)
return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34);
} else if (_codecTag == MKTAG('a', 'l', 'a', 'w')) {
return makeALawStream(stream, DisposeAfterUse::YES, _sampleRate, _channels);
} else if (_codecTag == MKTAG('u', 'l', 'a', 'w')) {
return makeMuLawStream(stream, DisposeAfterUse::YES, _sampleRate, _channels);
}
error("Unsupported audio codec");
return nullptr;
}
void QuickTimeAudioDecoder::AudioSampleDesc::initCodec() {
delete _codec; _codec = nullptr;
switch (_codecTag) {
case MKTAG('Q', 'D', 'M', '2'):
#ifdef USE_QDM2
_codec = makeQDM2Decoder(_extraData);
#endif
break;
case MKTAG('m', 'p', '4', 'a'):
#ifdef USE_FAAD
if (_objectTypeMP4 == 0x40)
_codec = makeAACDecoder(_extraData);
#endif
break;
default:
break;
}
}
/**
* A wrapper around QuickTimeAudioDecoder that implements the SeekableAudioStream API
*/
class QuickTimeAudioStream : public SeekableAudioStream, public QuickTimeAudioDecoder {
public:
QuickTimeAudioStream() {}
~QuickTimeAudioStream() {}
bool openFromFile(const Common::Path &filename) {
return QuickTimeAudioDecoder::loadAudioFile(filename) && !_audioTracks.empty();
}
bool openFromStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) {
return QuickTimeAudioDecoder::loadAudioStream(stream, disposeFileHandle) && !_audioTracks.empty();
}
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples) override {
int samples = 0;
while (samples < numSamples && !endOfData()) {
if (!_audioTracks[0]->hasDataInQueue())
_audioTracks[0]->queueAudio();
samples += _audioTracks[0]->readBuffer(buffer + samples, numSamples - samples);
}
return samples;
}
bool isStereo() const override { return _audioTracks[0]->isStereo(); }
int getRate() const override { return _audioTracks[0]->getRate(); }
bool endOfData() const override { return _audioTracks[0]->endOfData(); }
// SeekableAudioStream API
bool seek(const Timestamp &where) override { return _audioTracks[0]->seek(where); }
Timestamp getLength() const override { return _audioTracks[0]->getLength(); }
};
SeekableAudioStream *makeQuickTimeStream(const Common::Path &filename) {
QuickTimeAudioStream *audioStream = new QuickTimeAudioStream();
if (!audioStream->openFromFile(filename)) {
delete audioStream;
return nullptr;
}
return audioStream;
}
SeekableAudioStream *makeQuickTimeStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
QuickTimeAudioStream *audioStream = new QuickTimeAudioStream();
if (!audioStream->openFromStream(stream, disposeAfterUse)) {
delete audioStream;
return nullptr;
}
return audioStream;
}
} // End of namespace Audio

View File

@@ -0,0 +1,68 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - groovie
* - mohawk
* - pegasus
* - sci
*/
#ifndef AUDIO_QUICKTIME_H
#define AUDIO_QUICKTIME_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class Path;
class SeekableReadStream;
class String;
}
namespace Audio {
class SeekableAudioStream;
/**
* Try to load a QuickTime sound file from the given file name and create a SeekableAudioStream
* from that data.
*
* @param filename the filename of the file from which to read the data
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeQuickTimeStream(const Common::Path &filename);
/**
* Try to load a QuickTime sound file from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeQuickTimeStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
} // End of namespace Audio
#endif

View File

@@ -0,0 +1,142 @@
/* 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/>.
*
*/
/**
* Internal interface to the QuickTime audio decoder.
*
* This is available so that the QuickTimeVideoDecoder can use
* this directly.
*/
#ifndef AUDIO_QUICKTIME_INTERN_H
#define AUDIO_QUICKTIME_INTERN_H
#include "common/formats/quicktime.h"
#include "common/scummsys.h"
#include "common/types.h"
#include "audio/audiostream.h"
namespace Common {
class SeekableReadStream;
class String;
}
namespace Audio {
class Codec;
class QuickTimeAudioDecoder : public Common::QuickTimeParser {
public:
QuickTimeAudioDecoder();
virtual ~QuickTimeAudioDecoder();
/**
* Load a QuickTime audio file
* @param filename the filename to load
*/
bool loadAudioFile(const Common::Path &filename);
/**
* Load a QuickTime audio file from a SeekableReadStream
* @param stream the stream to load
*/
bool loadAudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle);
protected:
class QuickTimeAudioTrack : public SeekableAudioStream {
public:
QuickTimeAudioTrack(QuickTimeAudioDecoder *decoder, Track *parentTrack);
~QuickTimeAudioTrack();
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return _queue->isStereo(); }
int getRate() const { return _queue->getRate(); }
bool endOfData() const;
// SeekableAudioStream API
bool seek(const Timestamp &where);
Timestamp getLength() const;
// Queue *at least* "length" audio
// If length is zero, it queues the next logical block of audio whether
// that be a whole edit or just one chunk within an edit
void queueAudio(const Timestamp &length = Timestamp());
Track *getParent() const { return _parentTrack; }
void queueRemainingAudio();
bool hasDataInQueue() const { return _samplesQueued != 0; }
private:
QuickTimeAudioDecoder *_decoder;
Track *_parentTrack;
QueuingAudioStream *_queue;
uint _curChunk;
Timestamp _curMediaPos, _skipSamples;
uint32 _curEdit, _samplesQueued;
bool _skipAACPrimer;
QueuingAudioStream *createStream() const;
AudioStream *readAudioChunk(uint chunk);
bool isOldDemuxing() const;
void skipSamples(const Timestamp &length, AudioStream *stream);
void findEdit(const Timestamp &position);
bool allDataRead() const;
void enterNewEdit(const Timestamp &position);
void queueStream(AudioStream *stream, const Timestamp &length);
uint32 getAudioChunkSampleCount(uint chunk) const;
Timestamp getChunkLength(uint chunk, bool skipAACPrimer = false) const;
uint32 getAACSampleTime(uint32 totalSampleCount, bool skipAACPrimer = false) const;
Timestamp getCurrentTrackTime() const;
};
class AudioSampleDesc : public Common::QuickTimeParser::SampleDesc {
public:
AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
~AudioSampleDesc();
bool isAudioCodecSupported() const;
AudioStream *createAudioStream(Common::SeekableReadStream *stream) const;
void initCodec();
// TODO: Make private in the long run
uint16 _bitsPerSample;
uint16 _channels;
uint32 _sampleRate;
uint32 _samplesPerFrame;
uint32 _bytesPerFrame;
private:
Codec *_codec;
};
// Common::QuickTimeParser API
virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize);
void init();
Common::Array<QuickTimeAudioTrack *> _audioTracks;
};
} // End of namespace Audio
#endif

245
audio/decoders/raw.cpp Normal file
View File

@@ -0,0 +1,245 @@
/* 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/endian.h"
#include "common/memstream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
namespace Audio {
#pragma mark -
#pragma mark --- RawStream ---
#pragma mark -
/**
* This is a stream, which allows for playing raw PCM data from a stream.
*/
template<int bytesPerSample, bool isUnsigned, bool isLE>
class RawStream : public SeekableAudioStream {
public:
RawStream(int rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream)
: _rate(rate), _isStereo(stereo), _playtime(0, rate), _stream(stream, disposeStream), _endOfData(false), _buffer(nullptr) {
// Setup our buffer for readBuffer
_buffer = new byte[kSampleBufferLength * bytesPerSample];
assert(_buffer);
// Calculate the total playtime of the stream
_playtime = Timestamp(0, _stream->size() / (_isStereo ? 2 : 1) / bytesPerSample, rate);
}
~RawStream() {
delete[] _buffer;
}
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override { return _isStereo; }
bool endOfData() const override { return _endOfData; }
int getRate() const override { return _rate; }
Timestamp getLength() const override { return _playtime; }
bool seek(const Timestamp &where) override;
private:
const int _rate; ///< Sample rate of stream
const bool _isStereo; ///< Whether this is a stereo stream
Timestamp _playtime; ///< Calculated total play time
Common::DisposablePtr<Common::SeekableReadStream> _stream; ///< Stream to read data from
bool _endOfData; ///< Whether the stream end has been reached
byte *_buffer; ///< Buffer used in readBuffer
enum {
/**
* How many samples we can buffer at once.
*
* TODO: Check whether this size suffices
* for systems with slow disk I/O.
*/
kSampleBufferLength = 2048
};
/**
* Fill the temporary sample buffer used in readBuffer.
*
* @param maxSamples Maximum samples to read.
* @return actual count of samples read.
*/
int fillBuffer(int maxSamples);
};
template<int bytesPerSample, bool isUnsigned, bool isLE>
int RawStream<bytesPerSample, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
int samplesLeft = numSamples;
while (samplesLeft > 0) {
// Try to read up to "samplesLeft" samples.
int len = fillBuffer(samplesLeft);
// In case we were not able to read any samples
// we will stop reading here.
if (!len)
break;
// Adjust the samples left to read.
samplesLeft -= len;
// Copy the data to the caller's buffer.
const byte *src = _buffer;
while (len-- > 0) {
if (bytesPerSample == 1)
*buffer++ = (*src << 8) ^ (isUnsigned ? 0x8000 : 0);
else if (bytesPerSample == 2)
*buffer++ = ((isLE ? READ_LE_UINT16(src) : READ_BE_UINT16(src)) ^ (isUnsigned ? 0x8000 : 0));
else // if (bytesPerSample == 3)
*buffer++ = (((int16)((isLE ? READ_LE_UINT24(src) : READ_BE_UINT24(src)) >> 8)) ^ (isUnsigned ? 0x8000 : 0));
src += bytesPerSample;
}
}
return numSamples - samplesLeft;
}
template<int bytesPerSample, bool isUnsigned, bool isLE>
int RawStream<bytesPerSample, isUnsigned, isLE>::fillBuffer(int maxSamples) {
int bufferedSamples = 0;
byte *dst = _buffer;
// We can only read up to "kSampleBufferLength" samples
// so we take this into consideration, when trying to
// read up to maxSamples.
maxSamples = MIN<int>(kSampleBufferLength, maxSamples);
// We will only read up to maxSamples
while (maxSamples > 0 && !endOfData()) {
// Try to read all the sample data and update the
// destination pointer.
const int bytesRead = _stream->read(dst, maxSamples * bytesPerSample);
dst += bytesRead;
// Calculate how many samples we actually read.
const int samplesRead = bytesRead / bytesPerSample;
// Update all status variables
bufferedSamples += samplesRead;
maxSamples -= samplesRead;
// We stop stream playback, when we reached the end of the data stream.
// We also stop playback when an error occures.
if (_stream->pos() == _stream->size() || _stream->err() || _stream->eos())
_endOfData = true;
}
return bufferedSamples;
}
template<int bytesPerSample, bool isUnsigned, bool isLE>
bool RawStream<bytesPerSample, isUnsigned, isLE>::seek(const Timestamp &where) {
_endOfData = true;
if (where > _playtime)
return false;
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
_stream->seek(seekSample * bytesPerSample, SEEK_SET);
// In case of an error we will not continue stream playback.
if (!_stream->err() && !_stream->eos() && _stream->pos() != _stream->size())
_endOfData = false;
return true;
}
#pragma mark -
#pragma mark --- Raw stream factories ---
#pragma mark -
/**
* The following templated function is a helper to simplify the public makeRawStream function
*/
template <bool isUnsigned>
static FORCEINLINE SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream, int rate, bool isStereo, DisposeAfterUse::Flag disposeAfterUse, bool isLE, int bytesPerSample) {
switch (bytesPerSample) {
case 3:
if (isLE) {
return new RawStream<3, isUnsigned, true>(rate, isStereo, disposeAfterUse, stream);
} else {
return new RawStream<3, isUnsigned, false>(rate, isStereo, disposeAfterUse, stream);
}
case 2:
if (isLE) {
return new RawStream<2, isUnsigned, true>(rate, isStereo, disposeAfterUse, stream);
} else {
return new RawStream<2, isUnsigned, false>(rate, isStereo, disposeAfterUse, stream);
}
default:
return new RawStream<1, isUnsigned, false>(rate, isStereo, disposeAfterUse, stream);
}
}
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse) {
const bool isStereo = (flags & Audio::FLAG_STEREO) != 0;
const int bytesPerSample = (flags & Audio::FLAG_24BITS ? 3 : (flags & Audio::FLAG_16BITS ? 2 : 1));
const bool isUnsigned = (flags & Audio::FLAG_UNSIGNED) != 0;
const bool isLE = (flags & Audio::FLAG_LITTLE_ENDIAN) != 0;
assert(stream->size() % (bytesPerSample * (isStereo ? 2 : 1)) == 0);
if (isUnsigned) {
return makeRawStream<true>(stream, rate, isStereo, disposeAfterUse, isLE, bytesPerSample);
} else {
return makeRawStream<false>(stream, rate, isStereo, disposeAfterUse, isLE, bytesPerSample);
}
}
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse) {
return makeRawStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, flags, DisposeAfterUse::YES);
}
class PacketizedRawStream : public StatelessPacketizedAudioStream {
public:
PacketizedRawStream(int rate, byte flags) :
StatelessPacketizedAudioStream(rate, ((flags & FLAG_STEREO) != 0) ? 2 : 1), _flags(flags) {}
protected:
AudioStream *makeStream(Common::SeekableReadStream *data) override;
private:
byte _flags;
};
AudioStream *PacketizedRawStream::makeStream(Common::SeekableReadStream *data) {
return makeRawStream(data, getRate(), _flags);
}
PacketizedAudioStream *makePacketizedRawStream(int rate, byte flags) {
return new PacketizedRawStream(rate, flags);
}
} // End of namespace Audio

108
audio/decoders/raw.h Normal file
View File

@@ -0,0 +1,108 @@
/* 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/>.
*
*/
#ifndef AUDIO_RAW_H
#define AUDIO_RAW_H
#include "common/scummsys.h"
#include "common/types.h"
#include "common/list.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class PacketizedAudioStream;
class SeekableAudioStream;
/**
* Various flags which can be bit-ORed and then passed to
* makeRawStream and some other AudioStream factories
* to control their behavior.
*
* Engine authors are advised not to rely on a certain value or
* order of these flags (in particular, do not store them verbatim
* in savestates).
*/
enum RawFlags {
/** unsigned samples (default: signed) */
FLAG_UNSIGNED = 1 << 0,
/** sound is 16 bits wide (default: 8bit) */
FLAG_16BITS = 1 << 1,
/** sound is 24 bits wide (default: 8bit) */
FLAG_24BITS = 1 << 2,
/** samples are little endian (default: big endian) */
FLAG_LITTLE_ENDIAN = 1 << 3,
/** sound is in stereo (default: mono) */
FLAG_STEREO = 1 << 4
};
/**
* Creates an audio stream, which plays from the given buffer.
*
* @param buffer Buffer to play from.
* @param size Size of the buffer in bytes.
* @param rate Rate of the sound data.
* @param flags Audio flags combination.
* @see RawFlags
* @param disposeAfterUse Whether to free the buffer after use (with free!).
* @return The new SeekableAudioStream (or 0 on failure).
*/
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
/**
* Creates an audio stream, which plays from the given stream.
*
* @param stream Stream object to play from.
* @param rate Rate of the sound data.
* @param flags Audio flags combination.
* @see RawFlags
* @param disposeAfterUse Whether to delete the stream after use.
* @return The new SeekableAudioStream (or 0 on failure).
*/
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
/**
* Creates a PacketizedAudioStream that will automatically queue
* packets as individual AudioStreams like returned by makeRawStream.
*
* @param rate Rate of the sound data.
* @param flags Audio flags combination.
* @see RawFlags
* @return The new PacketizedAudioStream.
*/
PacketizedAudioStream *makePacketizedRawStream(int rate, byte flags);
} // End of namespace Audio
#endif

52
audio/decoders/util.h Normal file
View File

@@ -0,0 +1,52 @@
/* 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/>.
*
*/
#ifndef AUDIO_DECODERS_UTIL_H
#define AUDIO_DECODERS_UTIL_H
#include "common/types.h"
#include "common/util.h"
namespace Audio {
// Convert one float sample into a int16 sample
static inline int16 floatToInt16(float src) {
return (int16) CLIP<int>((int) floor(src + 0.5), -32768, 32767);
}
// Convert planar float samples into interleaved int16 samples
static inline void floatToInt16Interleave(int16 *dst, const float **src,
uint32 length, uint8 channels) {
if (channels == 2) {
for (uint32 i = 0; i < length; i++) {
dst[2 * i ] = floatToInt16(src[0][i]);
dst[2 * i + 1] = floatToInt16(src[1][i]);
}
} else {
for (uint8 c = 0; c < channels; c++)
for (uint32 i = 0, j = c; i < length; i++, j += channels)
dst[j] = floatToInt16(src[c][i]);
}
}
} // End of namespace Audio
#endif // AUDIO_DECODERS_UTIL_H

490
audio/decoders/voc.cpp Normal file
View File

@@ -0,0 +1,490 @@
/* 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 "audio/decoders/voc.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/util.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/list.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/voc.h"
namespace Audio {
bool checkVOCHeader(Common::ReadStream &stream) {
VocFileHeader fileHeader;
if (stream.read(&fileHeader, 8) != 8)
return false;
if (!memcmp(&fileHeader, "VTLK", 4)) {
if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader))
return false;
} else if (!memcmp(&fileHeader, "Creative", 8)) {
if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8)
return false;
} else {
return false;
}
if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0)
return false;
//if (fileHeader.desc[19] != 0x1A)
// debug(3, "checkVOCHeader: Partially invalid header");
int32 offset = FROM_LE_16(fileHeader.datablock_offset);
int16 version = FROM_LE_16(fileHeader.version);
int16 code = FROM_LE_16(fileHeader.id);
if (offset != sizeof(VocFileHeader))
return false;
// 0x100 is an invalid VOC version used by German version of DOTT (Disk) and
// French version of Simon the Sorcerer 2 (CD)
if (version != 0x010A && version != 0x0114 && version != 0x0100)
return false;
if (code != ~version + 0x1234)
return false;
return true;
}
VocStream::VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse)
: _stream(stream), _disposeAfterUse(disposeAfterUse), _isUnsigned(isUnsigned), _rate(0),
_length(), _blocks(), _curBlock(_blocks.end()), _blockLeft(0), _buffer() {
preProcess();
}
VocStream::~VocStream() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _stream;
}
int VocStream::readBuffer(int16 *buffer, const int numSamples) {
int samplesLeft = numSamples;
while (samplesLeft > 0) {
// Try to read up to "samplesLeft" samples.
int len = fillBuffer(samplesLeft);
// In case we were not able to read any samples
// we will stop reading here.
if (!len)
break;
// Adjust the samples left to read.
samplesLeft -= len;
// Copy the data to the caller's buffer.
const byte *src = _buffer;
while (len-- > 0)
*buffer++ = (*src++ << 8) ^ (_isUnsigned ? 0x8000 : 0);
}
return numSamples - samplesLeft;
}
void VocStream::updateBlockIfNeeded() {
// Have we now finished this block? If so, read the next block
if (_blockLeft == 0 && _curBlock != _blocks.end()) {
// Find the next sample block
while (true) {
// Next block
++_curBlock;
// Check whether we reached the end of the stream
// yet.
if (_curBlock == _blocks.end())
return;
// Skip all none sample blocks for now
if (_curBlock->code != 1 && _curBlock->code != 9)
continue;
_stream->seek(_curBlock->sampleBlock.offset, SEEK_SET);
// In case of an error we will stop
// stream playback.
if (_stream->err()) {
_blockLeft = 0;
_curBlock = _blocks.end();
} else {
_blockLeft = _curBlock->sampleBlock.samples;
}
return;
}
}
}
int VocStream::fillBuffer(int maxSamples) {
int bufferedSamples = 0;
byte *dst = _buffer;
// We can only read up to "kSampleBufferLength" samples
// so we take this into consideration, when trying to
// read up to maxSamples.
maxSamples = MIN<int>(kSampleBufferLength, maxSamples);
// We will only read up to maxSamples
while (maxSamples > 0 && !endOfData()) {
// Calculate how many samples we can safely read
// from the current block.
const int len = MIN<int>(maxSamples, _blockLeft);
// Try to read all the sample data and update the
// destination pointer.
const int bytesRead = _stream->read(dst, len);
dst += bytesRead;
// Calculate how many samples we actually read.
const int samplesRead = bytesRead;
// Update all status variables
bufferedSamples += samplesRead;
maxSamples -= samplesRead;
_blockLeft -= samplesRead;
// In case of an error or end of stream we will stop
// stream playback.
if (_stream->err() || _stream->eos()) {
_blockLeft = 0;
_curBlock = _blocks.end();
break;
}
// Advance to the next block in case the current
// one is already finished.
updateBlockIfNeeded();
}
return bufferedSamples;
}
bool VocStream::seek(const Timestamp &where) {
// Invalidate stream
_blockLeft = 0;
_curBlock = _blocks.end();
if (where > _length)
return false;
// Search for the block containing the requested sample
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
uint32 curSample = 0;
for (_curBlock = _blocks.begin(); _curBlock != _blocks.end(); ++_curBlock) {
// Skip all none sample blocks for now
if (_curBlock->code != 1 && _curBlock->code != 9)
continue;
uint32 nextBlockSample = curSample + _curBlock->sampleBlock.samples;
if (nextBlockSample > seekSample)
break;
curSample = nextBlockSample;
}
if (_curBlock == _blocks.end()) {
return ((seekSample - curSample) == 0);
} else {
const uint32 offset = seekSample - curSample;
_stream->seek(_curBlock->sampleBlock.offset + offset, SEEK_SET);
// In case of an error we will stop
// stream playback.
if (_stream->err()) {
_blockLeft = 0;
_curBlock = _blocks.end();
} else {
_blockLeft = _curBlock->sampleBlock.samples - offset;
}
return true;
}
}
void VocStream::preProcess() {
Block block;
// Scan through the file and collect all blocks
while (true) {
block.code = _stream->readByte();
block.length = 0;
// If we hit EOS here we found the end of the VOC file.
// According to https://wiki.multimedia.cx/index.php?title=Creative_Voice
// there is no need for an "Terminator" block to be present.
// In case we hit a "Terminator" block we also break here.
if (_stream->eos() || block.code == 0)
break;
// We will allow invalid block numbers as terminators. This is needed,
// since some games ship broken VOC files. The following occasions are
// known:
// - 128 is used as terminator in Simon 1 Amiga CD32
// - Full Throttle contains a VOC file with an incorrect block length
// resulting in a sample (127) to be read as block code.
if (block.code > 9) {
warning("VocStream::preProcess: Caught %d as terminator", block.code);
break;
}
block.length = _stream->readByte();
block.length |= _stream->readByte() << 8;
block.length |= _stream->readByte() << 16;
// Premature end of stream => error!
if (_stream->eos() || _stream->err()) {
warning("VocStream::preProcess: Reading failed");
return;
}
uint32 skip = 0;
switch (block.code) {
// Sound data
case 1:
// Sound data (New format)
case 9:
if (block.code == 1) {
if (block.length < 2) {
warning("Invalid sound data block length %d in VOC file", block.length);
return;
}
// Read header data
int freqDiv = _stream->readByte();
// Prevent division through 0
if (freqDiv == 256) {
warning("Invalid frequency divisor 256 in VOC file");
return;
}
block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv);
int codec = _stream->readByte();
// We only support 8bit PCM
if (codec != 0) {
warning("Unhandled codec %d in VOC file", codec);
return;
}
block.sampleBlock.samples = skip = block.length - 2;
block.sampleBlock.offset = _stream->pos();
// Check the last block if there is any
if (_blocks.size() > 0) {
BlockList::iterator lastBlock = _blocks.end();
--lastBlock;
// When we have found a block 8 as predecessor
// we need to use its settings
if (lastBlock->code == 8) {
block.sampleBlock.rate = lastBlock->sampleBlock.rate;
// Remove the block since we don't need it anymore
_blocks.erase(lastBlock);
}
}
} else {
if (block.length < 12) {
warning("Invalid sound data (wew format) block length %d in VOC file", block.length);
return;
}
block.sampleBlock.rate = _stream->readUint32LE();
int bitsPerSample = _stream->readByte();
// We only support 8bit PCM
if (bitsPerSample != 8) {
warning("Unhandled bits per sample %d in VOC file", bitsPerSample);
return;
}
int channels = _stream->readByte();
// We only support mono
if (channels != 1) {
warning("Unhandled channel count %d in VOC file", channels);
return;
}
int codec = _stream->readUint16LE();
// We only support 8bit PCM
if (codec != 0) {
warning("Unhandled codec %d in VOC file", codec);
return;
}
/*uint32 reserved = */_stream->readUint32LE();
block.sampleBlock.offset = _stream->pos();
block.sampleBlock.samples = skip = block.length - 12;
}
// Check whether we found a new highest rate
if (_rate < block.sampleBlock.rate)
_rate = block.sampleBlock.rate;
break;
// Silence
case 3: {
if (block.length != 3) {
warning("Invalid silence block length %d in VOC file", block.length);
return;
}
block.sampleBlock.offset = 0;
block.sampleBlock.samples = _stream->readUint16LE() + 1;
int freqDiv = _stream->readByte();
// Prevent division through 0
if (freqDiv == 256) {
warning("Invalid frequency divisor 256 in VOC file");
return;
}
block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv);
} break;
// Repeat start
case 6:
if (block.length != 2) {
warning("Invalid repeat start block length %d in VOC file", block.length);
return;
}
block.loopBlock.count = _stream->readUint16LE() + 1;
break;
// Repeat end
case 7:
break;
// Extra info
case 8: {
if (block.length != 4)
return;
int freqDiv = _stream->readUint16LE();
// Prevent division through 0
if (freqDiv == 65536) {
warning("Invalid frequency divisor 65536 in VOC file");
return;
}
int codec = _stream->readByte();
// We only support RAW 8bit PCM.
if (codec != 0) {
warning("Unhandled codec %d in VOC file", codec);
return;
}
int channels = _stream->readByte() + 1;
// We only support mono sound right now
if (channels != 1) {
warning("Unhandled channel count %d in VOC file", channels);
return;
}
block.sampleBlock.offset = 0;
block.sampleBlock.samples = 0;
block.sampleBlock.rate = 256000000L / (65536L - freqDiv);
} break;
default:
warning("Unhandled code %d in VOC file (len %d)", block.code, block.length);
// Skip the whole block and try to use the next one.
skip = block.length;
}
// Premature end of stream => error!
if (_stream->eos() || _stream->err()) {
warning("VocStream::preProcess: Reading failed");
return;
}
// Skip the rest of the block
if (skip)
_stream->skip(skip);
_blocks.push_back(block);
}
// Since we determined the sample rate we need for playback now, we will
// initialize the play length.
_length = Timestamp(0, _rate);
// Calculate the total play time and do some more sanity checks
for (const auto &curBlock : _blocks) {
// Check whether we found a block 8 which survived, this is not
// allowed to happen!
if (curBlock.code == 8) {
warning("VOC file contains unused block 8");
return;
}
// For now only use blocks with actual samples
if (curBlock.code != 1 && curBlock.code != 9)
continue;
// Check the sample rate
if (curBlock.sampleBlock.rate != _rate) {
warning("VOC file contains chunks with different sample rates (%d != %d)", _rate, curBlock.sampleBlock.rate);
return;
}
_length = _length.addFrames(curBlock.sampleBlock.samples);
}
// Set the current block to the first block in the stream
rewind();
}
int getSampleRateFromVOCRate(int vocSR) {
if (vocSR == 0xa5 || vocSR == 0xa6) {
return 11025;
} else if (vocSR == 0xd2 || vocSR == 0xd3) {
return 22050;
} else {
int sr = 1000000L / (256L - vocSR);
// inexact sampling rates occur e.g. in the kitchen in Monkey Island,
// very easy to reach right from the start of the game.
//warning("inexact sample rate used: %i (0x%x)", sr, vocSR);
return sr;
}
}
SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) {
if (!checkVOCHeader(*stream)) {
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return nullptr;
}
SeekableAudioStream *audioStream = new VocStream(stream, (flags & Audio::FLAG_UNSIGNED) != 0, disposeAfterUse);
if (audioStream->endOfData()) {
delete audioStream;
return nullptr;
} else {
return audioStream;
}
}
} // End of namespace Audio

172
audio/decoders/voc.h Normal file
View File

@@ -0,0 +1,172 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - agos
* - chewy (subclass)
* - kyra
* - saga
* - scumm
* - touche
*/
#ifndef AUDIO_VOC_H
#define AUDIO_VOC_H
#include "audio/audiostream.h"
#include "common/list.h"
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class ReadStream;
class SeekableReadStream;
}
namespace Audio {
class AudioStream;
class SeekableAudioStream;
#include "common/pack-start.h" // START STRUCT PACKING
struct VocFileHeader {
uint8 desc[20];
uint16 datablock_offset;
uint16 version;
uint16 id;
} PACKED_STRUCT;
struct VocBlockHeader {
uint8 blocktype;
uint8 size[3];
uint8 sr;
uint8 pack;
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
class VocStream : public SeekableAudioStream {
public:
VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse);
virtual ~VocStream();
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override { return false; }
int getRate() const override { return _rate; }
bool endOfData() const override { return (_curBlock == _blocks.end()) && (_blockLeft == 0); }
bool seek(const Timestamp &where) override;
Timestamp getLength() const override { return _length; }
protected:
void preProcess();
Common::SeekableReadStream *const _stream;
const DisposeAfterUse::Flag _disposeAfterUse;
const bool _isUnsigned;
int _rate;
Timestamp _length;
struct Block {
uint8 code;
uint32 length;
union {
struct {
uint32 offset;
int rate;
int samples;
} sampleBlock;
struct {
int count;
} loopBlock;
};
};
typedef Common::List<Block> BlockList;
BlockList _blocks;
BlockList::const_iterator _curBlock;
uint32 _blockLeft;
/**
* Advance one block in the stream in case
* the current one is empty.
*/
void updateBlockIfNeeded();
// Do some internal buffering for systems with really slow slow disk i/o
enum {
/**
* How many samples we can buffer at once.
*
* TODO: Check whether this size suffices
* for systems with slow disk I/O.
*/
kSampleBufferLength = 2048
};
byte _buffer[kSampleBufferLength];
/**
* Fill the temporary sample buffer used in readBuffer.
*
* @param maxSamples Maximum samples to read.
* @return actual count of samples read.
*/
int fillBuffer(int maxSamples);
};
/**
* Take a sample rate parameter as it occurs in a VOC sound header, and
* return the corresponding sample frequency.
*
* This method has special cases for the standard rates of 11025 and 22050 kHz,
* which due to limitations of the format, cannot be encoded exactly in a VOC
* file. As a consequence, many game files have sound data sampled with those
* rates, but the VOC marks them incorrectly as 11111 or 22222 kHz. This code
* works around that and "unrounds" the sampling rates.
*/
extern int getSampleRateFromVOCRate(int vocSR);
/**
* Try to load a VOC from the given seekable stream and create an AudioStream
* from that data. Currently this function only supports uncompressed raw PCM
* data.
*
* This does not use any of the looping features of VOC files!
*/
SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO);
} // End of namespace Audio
#endif

252
audio/decoders/vorbis.cpp Normal file
View File

@@ -0,0 +1,252 @@
/* 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/>.
*
*/
// Disable symbol overrides for FILE and fseek as those are used in the
// Vorbis headers.
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_fseek
#include "audio/decoders/vorbis.h"
#ifdef USE_VORBIS
#include "common/ptr.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "audio/audiostream.h"
#ifdef USE_TREMOR
#include <tremor/ivorbisfile.h>
#else
#define OV_EXCLUDE_STATIC_CALLBACKS
#include <vorbis/vorbisfile.h>
#endif
namespace Audio {
// These are wrapper functions to allow using a SeekableReadStream object to
// provide data to the OggVorbis_File object.
static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
uint32 result = stream->read(ptr, size * nmemb);
return result / size;
}
static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
stream->seek((int32)offset, whence);
return stream->pos();
}
static int close_stream_wrap(void *datasource) {
// Do nothing -- we leave it up to the VorbisStream to free memory as appropriate.
return 0;
}
static long tell_stream_wrap(void *datasource) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
return stream->pos();
}
static const ov_callbacks g_stream_wrap = {
read_stream_wrap, seek_stream_wrap, close_stream_wrap, tell_stream_wrap
};
#pragma mark -
#pragma mark --- Ogg Vorbis stream ---
#pragma mark -
class VorbisStream : public SeekableAudioStream {
protected:
Common::DisposablePtr<Common::SeekableReadStream> _inStream;
bool _isStereo;
int _rate;
Timestamp _length;
OggVorbis_File _ovFile;
int16 _buffer[4096];
const int16 *_bufferEnd;
const int16 *_pos;
public:
// startTime / duration are in milliseconds
VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
~VorbisStream();
int readBuffer(int16 *buffer, const int numSamples) override;
bool endOfData() const override { return _pos >= _bufferEnd; }
bool isStereo() const override { return _isStereo; }
int getRate() const override { return _rate; }
bool seek(const Timestamp &where) override;
Timestamp getLength() const override { return _length; }
protected:
bool refill();
};
VorbisStream::VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
_inStream(inStream, dispose),
_length(0, 1000),
_bufferEnd(ARRAYEND(_buffer)) {
int res = ov_open_callbacks(inStream, &_ovFile, nullptr, 0, g_stream_wrap);
if (res < 0) {
warning("Could not create Vorbis stream (%d)", res);
_pos = _bufferEnd;
return;
}
// Read in initial data
if (!refill())
return;
// Setup some header information
_isStereo = ov_info(&_ovFile, -1)->channels >= 2;
_rate = ov_info(&_ovFile, -1)->rate;
#ifdef USE_TREMOR
_length = Timestamp(ov_time_total(&_ovFile, -1), getRate());
#else
_length = Timestamp(uint32(ov_time_total(&_ovFile, -1) * 1000.0), getRate());
#endif
}
VorbisStream::~VorbisStream() {
ov_clear(&_ovFile);
}
int VorbisStream::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
while (samples < numSamples && _pos < _bufferEnd) {
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
memcpy(buffer, _pos, len * 2);
buffer += len;
_pos += len;
samples += len;
if (_pos >= _bufferEnd) {
if (!refill())
break;
}
}
return samples;
}
bool VorbisStream::seek(const Timestamp &where) {
// Vorbisfile uses the sample pair number, thus we always use "false" for the isStereo parameter
// of the convertTimeToStreamPos helper.
int res = ov_pcm_seek(&_ovFile, convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
if (res) {
warning("Error seeking in Vorbis stream (%d)", res);
_pos = _bufferEnd;
return false;
}
return refill();
}
bool VorbisStream::refill() {
// Read the samples
uint len_left = sizeof(_buffer);
char *read_pos = (char *)_buffer;
while (len_left > 0) {
long result;
#ifdef USE_TREMOR
// Tremor ov_read() always returns data as signed 16 bit interleaved PCM
// in host byte order. As such, it does not take arguments to request
// specific signedness, byte order or bit depth as in Vorbisfile.
result = ov_read(&_ovFile, read_pos, len_left,
NULL);
#else
#ifdef SCUMM_BIG_ENDIAN
result = ov_read(&_ovFile, read_pos, len_left,
1,
2, // 16 bit
1, // signed
NULL);
#else
result = ov_read(&_ovFile, read_pos, len_left,
0,
2, // 16 bit
1, // signed
nullptr);
#endif
#endif
if (result == OV_HOLE) {
// Possibly recoverable, just warn about it
warning("Corrupted data in Vorbis file");
} else if (result == 0) {
//warning("End of file while reading from Vorbis file");
//_pos = _bufferEnd;
//return false;
break;
} else if (result < 0) {
warning("Error reading from Vorbis stream (%d)", int(result));
_pos = _bufferEnd;
// Don't delete it yet, that causes problems in
// the CD player emulation code.
return false;
} else {
len_left -= result;
read_pos += result;
}
}
_pos = _buffer;
_bufferEnd = (int16 *)read_pos;
return true;
}
#pragma mark -
#pragma mark --- Ogg Vorbis factory functions ---
#pragma mark -
SeekableAudioStream *makeVorbisStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new VorbisStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return nullptr;
} else {
return s;
}
}
} // End of namespace Audio
#endif // #ifdef USE_VORBIS

73
audio/decoders/vorbis.h Normal file
View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - agos
* - draci
* - kyra
* - qdengine
* - queen
* - saga
* - sci
* - scumm
* - sword1
* - sword2
* - sword25
* - touche
* - tucker
* - vcruise
* - wintermute
*/
#ifndef AUDIO_VORBIS_H
#define AUDIO_VORBIS_H
#include "common/scummsys.h"
#include "common/types.h"
#ifdef USE_VORBIS
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
/**
* Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the Ogg Vorbis data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeVorbisStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif // #ifdef USE_VORBIS
#endif // #ifndef AUDIO_VORBIS_H

280
audio/decoders/wave.cpp Normal file
View File

@@ -0,0 +1,280 @@
/* 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/debug.h"
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/substream.h"
#include "audio/audiostream.h"
#include "audio/decoders/wave_types.h"
#include "audio/decoders/wave.h"
#include "audio/decoders/adpcm.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/g711.h"
#define EXT_CHUNKS 8
namespace Audio {
bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_, int *samplesPerBlock_) {
const int32 initialPos = stream.pos();
byte buf[4+1];
buf[4] = 0;
const char *extensionChunks[EXT_CHUNKS] = {
"JUNK",
"bext",
"iXML",
"qlty",
"mext",
"levl",
"link",
"axml"
};
stream.read(buf, 4);
if (memcmp(buf, "RIFF", 4) != 0) {
warning("getWavInfo: No 'RIFF' header");
return false;
}
int32 wavLength = stream.readUint32LE();
stream.read(buf, 4);
if (memcmp(buf, "WAVE", 4) != 0) {
warning("getWavInfo: No 'WAVE' header");
return false;
}
stream.read(buf, 4);
if (memcmp(buf, "fact", 4) == 0) {
// Initial fact chunk, so skip over it
uint32 factLen = stream.readUint32LE();
stream.skip(factLen);
stream.read(buf, 4);
}
while (1) { // skip junk/bext... chunks
int i;
for (i = 0; (i < EXT_CHUNKS) && (memcmp(buf, extensionChunks[i], 4) != 0); i++)
;
if (i != EXT_CHUNKS) { // found a known chunk
uint32 chunkSize = stream.readUint32LE();
// skip junk/ext chunk (add 1 byte if odd)
stream.skip(chunkSize + (chunkSize % 2));
stream.read(buf, 4);
debug(0, "Skipped %s chunk in wav file!", extensionChunks[i]);
} else // skipped all chunks, or found something unexpected
break;
}
if (memcmp(buf, "fmt ", 4) != 0) {
warning("getWavInfo: No 'fmt' header! Found %s", buf);
return false;
}
uint32 fmtLength = stream.readUint32LE();
if (fmtLength < 16) {
// A valid fmt chunk always contains at least 16 bytes
warning("getWavInfo: 'fmt' header is too short");
return false;
}
uint32 fmtRemaining = fmtLength;
// Next comes the "type" field of the fmt header. Some typical
// values for it:
// 1 -> uncompressed PCM
// 17 -> IMA ADPCM compressed WAVE
// See <https://mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html>
// for a more complete list of common WAVE compression formats...
uint16 type = stream.readUint16LE(); // == 1 for PCM data
uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo
uint32 samplesPerSec = stream.readUint32LE(); // in Hz
uint32 avgBytesPerSec = stream.readUint32LE(); // == SampleRate * NumChannels * BitsPerSample/8
uint16 blockAlign = stream.readUint16LE(); // == NumChannels * BitsPerSample/8
uint16 bitsPerSample = stream.readUint16LE(); // 8, 16 ...
// 8 bit data is unsigned, 16 bit data signed
fmtRemaining -= 16;
uint16 samplesPerBlock = 1;
if (type == kWaveFormatMSADPCM) {
// TODO: There is a samplesPerBlock in this header. It should be parsed and the below warning removed.
// (NB: The FMT header for MSADPCM has different information from the MSIMAADPCM)
warning("getWavInfo: 'fmt' header not parsed in entirety for MSADPCM");
} else if (type == kWaveFormatMSIMAADPCM) {
if (fmtRemaining != 4) {
// A valid IMA ADPCM fmt chunk is always 20 bytes long
warning("getWavInfo: 'fmt' header is wrong length for IMA ADPCM");
return false;
}
stream.readUint16LE(); // cbSize
samplesPerBlock = stream.readUint16LE();
fmtRemaining -= 4;
}
if (wavType != nullptr)
*wavType = type;
if (blockAlign_ != nullptr)
*blockAlign_ = blockAlign;
if (samplesPerBlock_ != nullptr)
*samplesPerBlock_ = samplesPerBlock;
#if 0
debug("WAVE information:");
debug(" total size: %d", wavLength);
debug(" fmt size: %d", fmtLength);
debug(" type: %d", type);
debug(" numChannels: %d", numChannels);
debug(" samplesPerSec: %d", samplesPerSec);
debug(" avgBytesPerSec: %d", avgBytesPerSec);
debug(" blockAlign: %d", blockAlign);
debug(" bitsPerSample: %d", bitsPerSample);
#endif
switch (type) {
case kWaveFormatPCM:
case kWaveFormatMSADPCM:
case kWaveFormatALawPCM:
case kWaveFormatMuLawPCM:
case kWaveFormatMSIMAADPCM:
#ifdef USE_MAD
case kWaveFormatMP3:
#endif
break;
default:
warning("getWavInfo: unsupported format (type %d)", type);
return false;
}
if (type == kWaveFormatMP3) {
bitsPerSample = 8;
} else if (type != kWaveFormatMSADPCM && type != kWaveFormatMSIMAADPCM) {
if (blockAlign != numChannels * bitsPerSample / 8) {
debug(0, "getWavInfo: blockAlign is invalid");
}
if (avgBytesPerSec != samplesPerSec * blockAlign) {
debug(0, "getWavInfo: avgBytesPerSec is invalid");
}
}
// Prepare the return values.
rate = samplesPerSec;
flags = 0;
if (bitsPerSample == 8) // 8 bit data is unsigned
flags |= Audio::FLAG_UNSIGNED;
else if (bitsPerSample == 16) // 16 bit data is signed little endian
flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
else if (bitsPerSample == 24) // 24 bit data is signed little endian
flags |= (Audio::FLAG_24BITS | Audio::FLAG_LITTLE_ENDIAN);
else if (bitsPerSample == 4 && (type == kWaveFormatMSADPCM || type == kWaveFormatMSIMAADPCM))
flags |= Audio::FLAG_16BITS;
else {
warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
return false;
}
if (numChannels == 2)
flags |= Audio::FLAG_STEREO;
else if (numChannels != 1) {
warning("getWavInfo: unsupported number of channels %d", numChannels);
return false;
}
// It's almost certainly a WAV file, but we still need to find its
// 'data' chunk.
// Skip over the rest of the fmt chunk.
int offset = fmtRemaining;
do {
stream.seek(offset, SEEK_CUR);
if (stream.pos() >= initialPos + wavLength + 8) {
warning("getWavInfo: Can't find 'data' chunk");
return false;
}
stream.read(buf, 4);
offset = stream.readUint32LE();
#if 0
debug(" found a '%s' tag of size %d", buf, offset);
#endif
} while (memcmp(buf, "data", 4) != 0);
// Stream now points at 'offset' bytes of sample data...
size = offset;
return true;
}
SeekableAudioStream *makeWAVStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
int size, rate;
byte flags;
uint16 type;
int blockAlign;
if (!loadWAVFromStream(*stream, size, rate, flags, &type, &blockAlign)) {
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return nullptr;
}
int channels = (flags & Audio::FLAG_STEREO) ? 2 : 1;
int bytesPerSample = (flags & Audio::FLAG_24BITS) ? 3 : ((flags & Audio::FLAG_16BITS) ? 2 : 1);
// Raw PCM, make sure the last packet is complete
if (type == kWaveFormatPCM) {
uint sampleSize = bytesPerSample * channels;
if (size % sampleSize != 0) {
warning("makeWAVStream: Trying to play a WAVE file with an incomplete PCM packet");
size &= ~(sampleSize - 1);
}
}
Common::SeekableReadStream *dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + size, disposeAfterUse);
switch (type) {
case kWaveFormatMSIMAADPCM:
return makeADPCMStream(dataStream, DisposeAfterUse::YES, 0, Audio::kADPCMMSIma, rate, channels, blockAlign);
case kWaveFormatMSADPCM:
return makeADPCMStream(dataStream, DisposeAfterUse::YES, 0, Audio::kADPCMMS, rate, channels, blockAlign);
#ifdef USE_MAD
case kWaveFormatMP3:
return makeMP3Stream(dataStream, DisposeAfterUse::YES);
#endif
case kWaveFormatALawPCM:
return makeALawStream(dataStream, DisposeAfterUse::YES, rate, channels);
case kWaveFormatMuLawPCM:
return makeMuLawStream(dataStream, DisposeAfterUse::YES, rate, channels);
case kWaveFormatPCM:
return makeRawStream(dataStream, rate, flags);
}
// If the format is unsupported, we already returned earlier, but just in case
delete dataStream;
return nullptr;
}
} // End of namespace Audio

98
audio/decoders/wave.h Normal file
View File

@@ -0,0 +1,98 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - access
* - agos
* - buried
* - cge
* - cge2
* - fullpipe
* - glk
* - gob
* - hopkins
* - mohawk
* - prince
* - qdengine
* - saga
* - sci
* - scumm
* - sherlock
* - sword1
* - sword2
* - titanic
* - tony
* - trecision
* - tucker
* - wintermute
* - zvision
*/
#ifndef AUDIO_WAVE_H
#define AUDIO_WAVE_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
/**
* Try to load a WAVE from the given seekable stream. Returns true if
* successful. In that case, the stream's seek position will be set to the
* start of the audio data, and size, rate and flags contain information
* necessary for playback. Currently this function supports uncompressed
* raw PCM data, MS IMA ADPCM and MS ADPCM (uses makeADPCMStream internally).
*/
extern bool loadWAVFromStream(
Common::SeekableReadStream &stream,
int &size,
int &rate,
byte &flags,
uint16 *wavType = 0,
int *blockAlign = 0,
int *samplesPerBlock = 0);
/**
* Try to load a WAVE from the given seekable stream and create an AudioStream
* from that data. Currently this function supports uncompressed
* raw PCM data, MS IMA ADPCM and MS ADPCM (uses makeADPCMStream internally).
*
* This function uses loadWAVFromStream() internally.
*
* @param stream the SeekableReadStream from which to read the WAVE data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeWAVStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif

View File

@@ -0,0 +1,44 @@
/* 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/>.
*
*/
#ifndef AUDIO_DECODERS_WAVE_TYPES_H
#define AUDIO_DECODERS_WAVE_TYPES_H
namespace Audio {
// Audio Codecs
enum WaveCompressionType {
kWaveFormatNone = 0x0000,
kWaveFormatPCM = 0x0001,
kWaveFormatMSADPCM = 0x0002,
kWaveFormatALawPCM = 0x0006,
kWaveFormatMuLawPCM = 0x0007,
kWaveFormatMSIMAADPCM = 0x0011,
kWaveFormatMP3 = 0x0055,
kWaveFormatDK3 = 0x0062, // rogue format number
kWaveFormatMSIMAADPCM2 = 0x0069,
kWaveFormatWMAv2 = 0x0161,
kWaveFormatXanDPCM = 0x594a // 'JY', Crusader: No Regret videos
};
} // End of namespace Audio
#endif

1512
audio/decoders/wma.cpp Normal file

File diff suppressed because it is too large Load Diff

225
audio/decoders/wma.h Normal file
View File

@@ -0,0 +1,225 @@
/* 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/>.
*
*/
// Based on xoreos' WMA code which is in turn
// Largely based on the WMA implementation found in FFmpeg.
#ifndef AUDIO_DECODERS_WMA_H
#define AUDIO_DECODERS_WMA_H
#include "common/array.h"
#include "common/bitstream.h"
#include "audio/decoders/codec.h"
namespace Common {
template <class BITSTREAM>
class Huffman;
}
namespace Math {
class MDCT;
}
namespace Audio {
struct WMACoefHuffmanParam;
class WMACodec : public Codec {
public:
WMACodec(int version, uint32 sampleRate, uint8 channels,
uint32 bitRate, uint32 blockAlign, Common::SeekableReadStream *extraData = 0);
~WMACodec();
AudioStream *decodeFrame(Common::SeekableReadStream &data);
private:
static const int kChannelsMax = 2; ///< Max number of channels we support.
static const int kBlockBitsMin = 7; ///< Min number of bits in a block.
static const int kBlockBitsMax = 11; ///< Max number of bits in a block.
/** Max number of bytes in a block. */
static const int kBlockSizeMax = (1 << kBlockBitsMax);
static const int kBlockNBSizes = (kBlockBitsMax - kBlockBitsMin + 1);
/** Max size of a superframe. */
static const int kSuperframeSizeMax = 16384;
/** Max size of a high band. */
static const int kHighBandSizeMax = 16;
/** Size of the noise table. */
static const int kNoiseTabSize = 8192;
/** Number of bits for the LSP power value. */
static const int kLSPPowBits = 7;
int _version; ///< WMA version.
uint32 _sampleRate; ///< Output sample rate.
uint8 _channels; ///< Output channel count.
uint32 _bitRate; ///< Input bit rate.
uint32 _blockAlign; ///< Input block align.
byte _audioFlags; ///< Output flags.
bool _useExpHuffman; ///< Exponents in Huffman code? Otherwise, in LSP.
bool _useBitReservoir; ///< Is each frame packet a "superframe"?
bool _useVariableBlockLen; ///< Are the block lengths variable?
bool _useNoiseCoding; ///< Should perceptual noise be added?
bool _resetBlockLengths; ///< Do we need new block lengths?
int _curFrame; ///< The number of the frame we're currently in.
int _frameLen; ///< The frame length.
int _frameLenBits; ///< log2 of the frame length.
int _blockSizeCount; ///< Number of block sizes.
int _framePos; ///< The position within the frame we're currently in.
int _curBlock; ///< The number of the block we're currently in.
int _blockLen; ///< Current block length.
int _blockLenBits; ///< log2 of current block length.
int _nextBlockLenBits; ///< log2 of next block length.
int _prevBlockLenBits; ///< log2 of previous block length.
int _byteOffsetBits;
// Coefficients
int _coefsStart; ///< First coded coef
int _coefsEnd[kBlockNBSizes]; ///< Max number of coded coefficients
int _exponentSizes[kBlockNBSizes];
uint16 _exponentBands[kBlockNBSizes][25];
int _highBandStart[kBlockNBSizes]; ///< Index of first coef in high band
int _exponentHighSizes[kBlockNBSizes];
int _exponentHighBands[kBlockNBSizes][kHighBandSizeMax];
typedef Common::Huffman<Common::BitStream8MSB> HuffmanDecoder;
HuffmanDecoder *_coefHuffman[2]; ///< Coefficients Huffman codes.
const WMACoefHuffmanParam *_coefHuffmanParam[2]; ///< Params for coef Huffman codes.
uint16 *_coefHuffmanRunTable[2]; ///< Run table for the coef Huffman.
float *_coefHuffmanLevelTable[2]; ///< Level table for the coef Huffman.
uint16 *_coefHuffmanIntTable[2]; ///< Int tablre for the coef Huffman.
// Noise
float _noiseMult; ///< Noise multiplier.
float _noiseTable[kNoiseTabSize]; ///< Noise table.
int _noiseIndex;
HuffmanDecoder *_hgainHuffman; ///< Perceptual noise huffman code.
// Exponents
int _exponentsBSize[kChannelsMax];
float _exponents[kChannelsMax][kBlockSizeMax];
float _maxExponent[kChannelsMax];
HuffmanDecoder *_expHuffman; ///< Exponents huffman code.
// Coded values in high bands
bool _highBandCoded [kChannelsMax][kHighBandSizeMax];
int _highBandValues[kChannelsMax][kHighBandSizeMax];
// Coefficients
float _coefs1[kChannelsMax][kBlockSizeMax];
float _coefs [kChannelsMax][kBlockSizeMax];
// Line spectral pairs
float _lspCosTable[kBlockSizeMax];
float _lspPowETable[256];
float _lspPowMTable1[(1 << kLSPPowBits)];
float _lspPowMTable2[(1 << kLSPPowBits)];
// MDCT
Common::Array<Math::MDCT *> _mdct; ///< MDCT contexts.
Common::Array<const float *> _mdctWindow; ///< MDCT window functions.
/** Overhang from the last superframe. */
byte _lastSuperframe[kSuperframeSizeMax + 4];
int _lastSuperframeLen; ///< Size of the overhang data. */
int _lastBitoffset; ///< Bit position within the overhang. */
// Output
float _output[kBlockSizeMax * 2];
float _frameOut[kChannelsMax][kBlockSizeMax * 2];
// Init helpers
void init(Common::SeekableReadStream *extraData);
uint16 getFlags(Common::SeekableReadStream *extraData);
void evalFlags(uint16 flags, Common::SeekableReadStream *extraData);
int getFrameBitLength();
int getBlockSizeCount(uint16 flags);
uint32 getNormalizedSampleRate();
bool useNoiseCoding(float &highFreq, float &bps);
void evalMDCTScales(float highFreq);
void initNoise();
void initCoefHuffman(float bps);
void initMDCT();
void initExponents();
HuffmanDecoder *initCoefHuffman(uint16 *&runTable, float *&levelTable,
uint16 *&intTable, const WMACoefHuffmanParam &params);
void initLSPToCurve();
// Decoding
Common::SeekableReadStream *decodeSuperFrame(Common::SeekableReadStream &data);
bool decodeFrame(Common::BitStream8MSB &bits, int16 *outputData);
int decodeBlock(Common::BitStream8MSB &bits);
// Decoding helpers
bool evalBlockLength(Common::BitStream8MSB &bits);
bool decodeChannels(Common::BitStream8MSB &bits, int bSize, bool msStereo, bool *hasChannel);
bool calculateIMDCT(int bSize, bool msStereo, bool *hasChannel);
void calculateCoefCount(int *coefCount, int bSize) const;
bool decodeNoise(Common::BitStream8MSB &bits, int bSize, bool *hasChannel, int *coefCount);
bool decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel);
bool decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel,
int *coefCount, int coefBitCount);
float getNormalizedMDCTLength() const;
void calculateMDCTCoefficients(int bSize, bool *hasChannel,
int *coefCount, int totalGain, float mdctNorm);
bool decodeExpHuffman(Common::BitStream8MSB &bits, int ch);
bool decodeExpLSP(Common::BitStream8MSB &bits, int ch);
bool decodeRunLevel(Common::BitStream8MSB &bits, const HuffmanDecoder &huffman,
const float *levelTable, const uint16 *runTable, int version, float *ptr,
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits);
void lspToCurve(float *out, float *val_max_ptr, int n, float *lsp);
void window(float *out) const;
float pow_m1_4(float x) const;
static int readTotalGain(Common::BitStream8MSB &bits);
static int totalGainToBits(int totalGain);
static uint32 getLargeVal(Common::BitStream8MSB &bits);
};
} // End of namespace Audio
#endif // AUDIO_DECODERS_WMA_H

1434
audio/decoders/wmadata.h Normal file

File diff suppressed because it is too large Load Diff

163
audio/decoders/xa.cpp Normal file
View File

@@ -0,0 +1,163 @@
/* 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 "audio/decoders/xa.h"
#include "audio/audiostream.h"
#include "common/stream.h"
namespace Audio {
class XAStream : public Audio::RewindableAudioStream {
public:
XAStream(Common::SeekableReadStream *stream, int rate, DisposeAfterUse::Flag disposeAfterUse);
~XAStream();
bool isStereo() const override { return false; }
bool endOfData() const override { return _endOfData && _samplesRemaining == 0; }
int getRate() const override { return _rate; }
int readBuffer(int16 *buffer, const int numSamples) override;
bool rewind() override;
private:
Common::SeekableReadStream *_stream;
DisposeAfterUse::Flag _disposeAfterUse;
void seekToPos(uint pos);
byte _predictor;
double _samples[28];
byte _samplesRemaining;
int _rate;
double _s1, _s2;
uint _loopPoint;
bool _endOfData;
};
XAStream::XAStream(Common::SeekableReadStream *stream, int rate, DisposeAfterUse::Flag disposeAfterUse)
: _stream(stream), _disposeAfterUse(disposeAfterUse) {
_samplesRemaining = 0;
_predictor = 0;
_s1 = _s2 = 0.0;
_rate = rate;
_loopPoint = 0;
_endOfData = false;
}
XAStream::~XAStream() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _stream;
}
static const double s_xaDataTable[5][2] = {
{ 0.0, 0.0 },
{ 60.0 / 64.0, 0.0 },
{ 115.0 / 64.0, -52.0 / 64.0 },
{ 98.0 / 64.0, -55.0 / 64.0 },
{ 122.0 / 64.0, -60.0 / 64.0 }
};
int XAStream::readBuffer(int16 *buffer, const int numSamples) {
int32 samplesDecoded = 0;
for (int i = 28 - _samplesRemaining; i < 28 && samplesDecoded < numSamples; i++) {
_samples[i] = _samples[i] + _s1 * s_xaDataTable[_predictor][0] + _s2 * s_xaDataTable[_predictor][1];
_s2 = _s1;
_s1 = _samples[i];
int16 d = (int)(_samples[i] + 0.5);
buffer[samplesDecoded] = d;
samplesDecoded++;
_samplesRemaining--;
}
if (endOfData())
return samplesDecoded;
while (samplesDecoded < numSamples) {
byte i = 0;
_predictor = _stream->readByte();
byte shift = _predictor & 0xf;
_predictor >>= 4;
byte flags = _stream->readByte();
if (flags == 3) {
// Loop
seekToPos(_loopPoint);
continue;
} else if (flags == 6) {
// Set loop point
_loopPoint = _stream->pos() - 2;
} else if (flags == 7) {
// End of stream
_endOfData = true;
return samplesDecoded;
}
for (i = 0; i < 28; i += 2) {
byte d = _stream->readByte();
int16 s = (d & 0xf) << 12;
if (s & 0x8000)
s |= 0xffff0000;
_samples[i] = (double)(s >> shift);
s = (d & 0xf0) << 8;
if (s & 0x8000)
s |= 0xffff0000;
_samples[i + 1] = (double)(s >> shift);
}
for (i = 0; i < 28 && samplesDecoded < numSamples; i++) {
_samples[i] = _samples[i] + _s1 * s_xaDataTable[_predictor][0] + _s2 * s_xaDataTable[_predictor][1];
_s2 = _s1;
_s1 = _samples[i];
int16 d = (int)(_samples[i] + 0.5);
buffer[samplesDecoded] = d;
samplesDecoded++;
}
if (i != 28)
_samplesRemaining = 28 - i;
if (_stream->pos() >= _stream->size())
_endOfData = true;
}
return samplesDecoded;
}
bool XAStream::rewind() {
seekToPos(0);
return true;
}
void XAStream::seekToPos(uint pos) {
_stream->seek(pos);
_samplesRemaining = 0;
_predictor = 0;
_s1 = _s2 = 0.0;
_endOfData = false;
}
RewindableAudioStream *makeXAStream(Common::SeekableReadStream *stream, int rate, DisposeAfterUse::Flag disposeAfterUse) {
return new XAStream(stream, rate, disposeAfterUse);
}
} // End of namespace Audio

59
audio/decoders/xa.h Normal file
View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - sword1 (PSX port of the game)
* - sword2 (PSX port of the game)
* - tinsel (PSX port of the game)
*/
#ifndef AUDIO_DECODERS_XA_H
#define AUDIO_DECODERS_XA_H
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class RewindableAudioStream;
/**
* Takes an input stream containing XA ADPCM sound data and creates
* a RewindableAudioStream from that.
*
* @param stream the SeekableReadStream from which to read the XA ADPCM data
* @param rate the sampling rate
* @param disposeAfterUse whether to delete the stream after use.
* @return a new RewindableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *makeXAStream(
Common::SeekableReadStream *stream,
int rate,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
} // End of namespace Audio
#endif

104
audio/decoders/xan_dpcm.cpp Normal file
View File

@@ -0,0 +1,104 @@
/* 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/stream.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
#include "audio/decoders/xan_dpcm.h"
namespace Audio {
/**
* Implements the Xan DPCM decoder used in Crusader: No Regret and Wing
* Commander IV movies. Implementation based on the description on the
* MultiMedia Wiki:
* https://wiki.multimedia.cx/index.php/Xan_DPCM
*/
class Xan_DPCMStream : public Audio::AudioStream {
public:
Xan_DPCMStream(int rate, int channels, Common::SeekableReadStream *data) : _rate(rate), _channels(channels), _data(data) {
assert(channels == 1 || channels == 2);
_pred[0] = data->readSint16LE();
if (channels == 2)
_pred[1] = data->readSint16LE();
_shift[0] = 4;
_shift[1] = 4;
};
int readBuffer(int16 *buffer, const int numSamples) override {
int i = 0;
for (; i < numSamples; i++) {
int32 *pshift = ((_channels == 2 && (i % 2)) ? _shift + 1 : _shift);
int32 *ppred = ((_channels == 2 && (i % 2)) ? _pred + 1 : _pred);
const uint8 b = _data->readByte();
const int diff = static_cast<int8>(b & 0xFC) * 256;
if ((b & 3) == 3)
*pshift += 1;
else
*pshift -= (2 * (b & 3));
if (*pshift < 0)
*pshift = 0;
if (*pshift > 15) {
warning("Xan DPCM shift should not go over 15, corrupt data?");
*pshift = 15;
}
*ppred += (diff >> *pshift);
*ppred = CLIP(*ppred, (int32)-32768, (int32)32767);
*buffer = *ppred;
buffer++;
if (_data->eos())
break;
}
return i;
}
bool isStereo() const override {
return _channels == 2;
}
int getRate() const override {
return _rate;
}
bool endOfData() const override {
return _data->eos();
}
private:
int _channels;
int _rate;
Common::SeekableReadStream *_data;
int32 _pred[2];
int32 _shift[2];
};
XanDPCMStream::XanDPCMStream(int rate, int channels) :
Audio::StatelessPacketizedAudioStream(rate, channels) {
}
AudioStream *XanDPCMStream::makeStream(Common::SeekableReadStream *data) {
return new Xan_DPCMStream(getRate(), getChannels(), data);
}
} // End of namespace Audio

65
audio/decoders/xan_dpcm.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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/>.
*
*/
/**
* @file
* Sound decoder used in engines:
* - ultima8 (Crusader: No Regret)
*/
#ifndef AUDIO_XAN_DPCM_H
#define AUDIO_XAN_DPCM_H
#include "common/scummsys.h"
#include "common/types.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class StatelessPacketizedAudioStream;
/**
* Implements the Xan DPCM decoder used in Crusader: No Regret and Wing
* Commander IV movies. Implementation based on the description on the
* MultiMedia Wiki:
* https://wiki.multimedia.cx/index.php/Xan_DPCM
*/
class XanDPCMStream : public StatelessPacketizedAudioStream {
public:
/**
* Create a Xan DPCM stream
* @param rate sampling rate (samples per second)
* @param channels number of channels to decode
* @return a new XanDPCMStream, or NULL, if an error occurred
*/
XanDPCMStream(int rate, int channels);
protected:
AudioStream *makeStream(Common::SeekableReadStream *data) override;
};
} // End of namespace Audio
#endif