mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-10 04:43:26 +00:00
Merge pull request #336 from clone2727/emi-ps2-audio
GRIM: Add support for EMI PS2 audio
This commit is contained in:
commit
1c32722e59
@ -20,79 +20,78 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/vag.h"
|
||||
#include "audio/decoders/xa.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class VagStream : public Audio::RewindableAudioStream {
|
||||
class XAStream : public Audio::RewindableAudioStream {
|
||||
public:
|
||||
VagStream(Common::SeekableReadStream *stream, int rate);
|
||||
~VagStream();
|
||||
XAStream(Common::SeekableReadStream *stream, int rate, DisposeAfterUse::Flag disposeAfterUse);
|
||||
~XAStream();
|
||||
|
||||
bool isStereo() const { return false; }
|
||||
bool endOfData() const { return _stream->pos() == _stream->size(); }
|
||||
bool endOfData() const { return _endOfData && _samplesRemaining == 0; }
|
||||
int getRate() const { return _rate; }
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool rewind();
|
||||
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;
|
||||
};
|
||||
|
||||
VagStream::VagStream(Common::SeekableReadStream *stream, int rate) : _stream(stream) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
VagStream::~VagStream() {
|
||||
delete _stream;
|
||||
XAStream::~XAStream() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
static const double s_vagDataTable[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 }
|
||||
};
|
||||
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 VagStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int XAStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int32 samplesDecoded = 0;
|
||||
|
||||
if (_samplesRemaining) {
|
||||
byte i = 0;
|
||||
|
||||
for (i = 28 - _samplesRemaining; i < 28 && samplesDecoded < numSamples; i++) {
|
||||
_samples[i] = _samples[i] + _s1 * s_vagDataTable[_predictor][0] + _s2 * s_vagDataTable[_predictor][1];
|
||||
_s2 = _s1;
|
||||
_s1 = _samples[i];
|
||||
int16 d = (int) (_samples[i] + 0.5);
|
||||
buffer[samplesDecoded] = d;
|
||||
samplesDecoded++;
|
||||
}
|
||||
|
||||
#if 0
|
||||
assert(i == 28); // We're screwed if this fails :P
|
||||
#endif
|
||||
// This might mean the file is corrupted, or that the stream has
|
||||
// been closed.
|
||||
if (i != 28) return 0;
|
||||
|
||||
_samplesRemaining = 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;
|
||||
|
||||
@ -100,8 +99,19 @@ int VagStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
byte shift = _predictor & 0xf;
|
||||
_predictor >>= 4;
|
||||
|
||||
if (_stream->readByte() == 7)
|
||||
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();
|
||||
@ -116,7 +126,7 @@ int VagStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
}
|
||||
|
||||
for (i = 0; i < 28 && samplesDecoded < numSamples; i++) {
|
||||
_samples[i] = _samples[i] + _s1 * s_vagDataTable[_predictor][0] + _s2 * s_vagDataTable[_predictor][1];
|
||||
_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);
|
||||
@ -124,24 +134,31 @@ int VagStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
samplesDecoded++;
|
||||
}
|
||||
|
||||
if (i != 27)
|
||||
if (i != 28)
|
||||
_samplesRemaining = 28 - i;
|
||||
|
||||
if (_stream->pos() >= _stream->size())
|
||||
_endOfData = true;
|
||||
}
|
||||
|
||||
return samplesDecoded;
|
||||
}
|
||||
|
||||
bool VagStream::rewind() {
|
||||
_stream->seek(0);
|
||||
_samplesRemaining = 0;
|
||||
_predictor = 0;
|
||||
_s1 = _s2 = 0.0;
|
||||
|
||||
bool XAStream::rewind() {
|
||||
seekToPos(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
RewindableAudioStream *makeVagStream(Common::SeekableReadStream *stream, int rate) {
|
||||
return new VagStream(stream, rate);
|
||||
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
|
@ -28,8 +28,10 @@
|
||||
* - tinsel (PSX port of the game)
|
||||
*/
|
||||
|
||||
#ifndef SOUND_VAG_H
|
||||
#define SOUND_VAG_H
|
||||
#ifndef AUDIO_DECODERS_XA_H
|
||||
#define AUDIO_DECODERS_XA_H
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
@ -40,17 +42,19 @@ namespace Audio {
|
||||
class RewindableAudioStream;
|
||||
|
||||
/**
|
||||
* Takes an input stream containing Vag sound data and creates
|
||||
* Takes an input stream containing XA ADPCM sound data and creates
|
||||
* an RewindableAudioStream from that.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the ADPCM data
|
||||
* @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 *makeVagStream(
|
||||
RewindableAudioStream *makeXAStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
int rate = 11025);
|
||||
int rate,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
} // End of namespace Sword1
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
@ -13,8 +13,8 @@ MODULE_OBJS := \
|
||||
decoders/flac.o \
|
||||
decoders/mp3.o \
|
||||
decoders/raw.o \
|
||||
decoders/vag.o \
|
||||
decoders/vorbis.o \
|
||||
decoders/xa.o \
|
||||
softsynth/adlib.o \
|
||||
softsynth/cms.o \
|
||||
softsynth/opl/dbopl.o \
|
||||
|
@ -71,6 +71,7 @@ MODULE_OBJS := \
|
||||
resource.o \
|
||||
savegame.o \
|
||||
scene.o \
|
||||
scx.o \
|
||||
sector.o \
|
||||
textobject.o \
|
||||
textsplit.o \
|
||||
|
200
engines/grim/scx.cpp
Normal file
200
engines/grim/scx.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
/* Residual - A 3D game interpreter
|
||||
*
|
||||
* Residual 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/xa.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
#include "grim/scx.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
// I've only ever seen up to two
|
||||
#define MAX_CHANNELS 2
|
||||
|
||||
class SCXStream : public Audio::RewindableAudioStream {
|
||||
public:
|
||||
SCXStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
~SCXStream();
|
||||
|
||||
bool isStereo() const { return _channels == 2; }
|
||||
bool endOfData() const { return _xaStreams[0]->endOfData(); }
|
||||
int getRate() const { return _rate; }
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool rewind();
|
||||
|
||||
private:
|
||||
int _channels;
|
||||
int _rate;
|
||||
uint16 _blockSize;
|
||||
|
||||
Audio::RewindableAudioStream *_xaStreams[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
SCXStream::SCXStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
static const uint32 stereoChannelNames[MAX_CHANNELS] = { MKTAG('L', 'E', 'F', 'T'), MKTAG('R', 'G', 'H', 'T') };
|
||||
|
||||
stream->readUint32BE(); // 'SCRX'
|
||||
stream->readUint32LE();
|
||||
|
||||
_blockSize = stream->readUint16LE();
|
||||
/* totalBlockSize = */ stream->readUint16LE();
|
||||
|
||||
if (_blockSize & 0xf)
|
||||
error("Bad SCX block size %04x", _blockSize);
|
||||
|
||||
// Base our channel count based off the block size
|
||||
_channels = (_blockSize == 0) ? 1 : 2;
|
||||
|
||||
stream->skip(12);
|
||||
|
||||
uint32 channelSize[MAX_CHANNELS];
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
uint32 tag = stream->readUint32BE();
|
||||
|
||||
if (isStereo()) {
|
||||
if (tag != stereoChannelNames[i])
|
||||
error("Bad stereo channel tag found '%s'", tag2str(tag));
|
||||
} else if (tag != MKTAG('M', 'O', 'N', 'O'))
|
||||
error("Bad mono channel tag found '%s'", tag2str(tag));
|
||||
|
||||
channelSize[i] = stream->readUint32LE();
|
||||
}
|
||||
|
||||
stream->seek(0x80);
|
||||
|
||||
uint32 leftRate = 0, rightRate = 0;
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
if (stream->readUint32BE() != MKTAG('V', 'A', 'G', 'p'))
|
||||
error("Bad VAG header");
|
||||
|
||||
/* uint32 version = */ stream->readUint32BE();
|
||||
stream->readUint32BE();
|
||||
stream->readUint32BE();
|
||||
|
||||
if (i == 0)
|
||||
leftRate = stream->readUint32BE();
|
||||
else
|
||||
rightRate = stream->readUint32BE();
|
||||
|
||||
stream->skip(12); // skip useless info
|
||||
stream->skip(16); // skip name
|
||||
stream->skip(16); // skip zeroes
|
||||
}
|
||||
|
||||
if (isStereo() && leftRate != rightRate)
|
||||
error("Mismatching SCX rates");
|
||||
|
||||
_rate = leftRate;
|
||||
|
||||
if (isStereo()) {
|
||||
// TODO: Make XAStream allow for appending data (similar to how ScummVM
|
||||
// handles AAC/QDM2. For now, we de-interleave the XA ADPCM data and then
|
||||
// re-interleave in readBuffer().
|
||||
// Of course, in doing something that does better streaming, it would
|
||||
// screw up the XA loop points. So, I'm not really sure what is best atm.
|
||||
byte *leftOut = new byte[channelSize[0]];
|
||||
byte *rightOut = new byte[channelSize[1]];
|
||||
Common::MemoryWriteStream *leftStream = new Common::MemoryWriteStream(leftOut, channelSize[0]);
|
||||
Common::MemoryWriteStream *rightStream = new Common::MemoryWriteStream(rightOut, channelSize[1]);
|
||||
byte *buf = new byte[_blockSize];
|
||||
|
||||
while (stream->pos() < stream->size()) {
|
||||
stream->read(buf, _blockSize);
|
||||
leftStream->write(buf, _blockSize);
|
||||
stream->read(buf, _blockSize);
|
||||
rightStream->write(buf, _blockSize);
|
||||
}
|
||||
|
||||
_xaStreams[0] = Audio::makeXAStream(new Common::MemoryReadStream(leftOut, channelSize[0], DisposeAfterUse::YES), _rate);
|
||||
_xaStreams[1] = Audio::makeXAStream(new Common::MemoryReadStream(rightOut, channelSize[1], DisposeAfterUse::YES), _rate);
|
||||
|
||||
delete[] buf;
|
||||
delete leftStream;
|
||||
delete rightStream;
|
||||
} else {
|
||||
_xaStreams[0] = Audio::makeXAStream(stream->readStream(channelSize[0]), _rate);
|
||||
_xaStreams[1] = 0;
|
||||
}
|
||||
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
}
|
||||
|
||||
SCXStream::~SCXStream() {
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
delete _xaStreams[i];
|
||||
}
|
||||
|
||||
int SCXStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
if (isStereo()) {
|
||||
// Needs to be divisible by the channel count
|
||||
assert((numSamples % 2) == 0);
|
||||
|
||||
// TODO: As per above, this probably should do more actual streaming
|
||||
|
||||
// Decode enough data from each channel
|
||||
int samplesPerChannel = numSamples / 2;
|
||||
int16 *leftSamples = new int16[samplesPerChannel];
|
||||
int16 *rightSamples = new int16[samplesPerChannel];
|
||||
|
||||
int samplesDecodedLeft = _xaStreams[0]->readBuffer(leftSamples, samplesPerChannel);
|
||||
int samplesDecodedRight = _xaStreams[1]->readBuffer(rightSamples, samplesPerChannel);
|
||||
assert(samplesDecodedLeft == samplesDecodedRight);
|
||||
|
||||
// Now re-interleave the data
|
||||
int samplesDecoded = 0;
|
||||
int16 *leftSrc = leftSamples, *rightSrc = rightSamples;
|
||||
for (; samplesDecoded < numSamples; samplesDecoded += 2) {
|
||||
*buffer++ = *leftSrc++;
|
||||
*buffer++ = *rightSrc++;
|
||||
}
|
||||
|
||||
delete[] leftSamples;
|
||||
delete[] rightSamples;
|
||||
return samplesDecoded;
|
||||
}
|
||||
|
||||
// Just read from the stream directly for mono
|
||||
return _xaStreams[0]->readBuffer(buffer, numSamples);
|
||||
}
|
||||
|
||||
bool SCXStream::rewind() {
|
||||
if (!_xaStreams[0]->rewind())
|
||||
return false;
|
||||
|
||||
return !isStereo() || _xaStreams[1]->rewind();
|
||||
}
|
||||
|
||||
Audio::RewindableAudioStream *makeSCXStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
if (stream->readUint32BE() != MKTAG('S', 'C', 'R', 'X')) {
|
||||
delete stream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->seek(0);
|
||||
return new SCXStream(stream, disposeAfterUse);
|
||||
}
|
||||
|
||||
} // End of namespace Grim
|
48
engines/grim/scx.h
Normal file
48
engines/grim/scx.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* Residual - A 3D game interpreter
|
||||
*
|
||||
* Residual 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_SCX_H
|
||||
#define GRIM_SCX_H
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Audio::RewindableAudioStream;
|
||||
|
||||
/**
|
||||
* Takes an input stream containing SCX sound data and creates
|
||||
* an RewindableAudioStream from that.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the SCX data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new RewindableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
Audio::RewindableAudioStream *makeSCXStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
} // End of namespace Grim
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user