EMI: Implement seeking to start position for SCX tracks.

This commit is contained in:
Joni Vähämäki 2014-07-29 14:09:21 +03:00
parent da2518bdb3
commit 5e8d3dad0c
3 changed files with 69 additions and 40 deletions

View File

@ -29,31 +29,8 @@
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 override { return _channels == 2; }
bool endOfData() const override { return _xaStreams[0]->endOfData(); }
int getRate() const override { return _rate; }
int readBuffer(int16 *buffer, const int numSamples) override;
bool rewind() override;
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') };
SCXStream::SCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse) {
static const uint32 stereoChannelNames[SCX_MAX_CHANNELS] = { MKTAG('L', 'E', 'F', 'T'), MKTAG('R', 'G', 'H', 'T') };
stream->readUint32BE(); // 'SCRX'
stream->readUint32LE();
@ -69,7 +46,7 @@ SCXStream::SCXStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag d
stream->skip(12);
uint32 channelSize[MAX_CHANNELS];
uint32 channelSize[SCX_MAX_CHANNELS];
for (int i = 0; i < _channels; i++) {
uint32 tag = stream->readUint32BE();
@ -127,23 +104,41 @@ SCXStream::SCXStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag d
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);
_fileStreams[0] = new Common::MemoryReadStream(leftOut, channelSize[0], DisposeAfterUse::YES);
_fileStreams[1] = new Common::MemoryReadStream(rightOut, channelSize[1], DisposeAfterUse::YES);
_xaStreams[0] = Audio::makeXAStream(_fileStreams[0], _rate);
_xaStreams[1] = Audio::makeXAStream(_fileStreams[1], _rate);
delete[] buf;
delete leftStream;
delete rightStream;
} else {
_xaStreams[0] = Audio::makeXAStream(stream->readStream(channelSize[0]), _rate);
_fileStreams[0] = stream->readStream(channelSize[0]);
_fileStreams[1] = nullptr;
_xaStreams[0] = Audio::makeXAStream(_fileStreams[0], _rate);
_xaStreams[1] = nullptr;
}
if (start) {
// Read data from the sound stream until we hit the desired start position.
// We do this instead of seeking so the loop point gets set up properly.
int samples = (int)((int64)start->msecs() * _rate / 1000);
int16 temp[1024];
while (samples > 0) {
samples -= _xaStreams[0]->readBuffer(temp, samples < 1024 ? samples : 1024);
if (_xaStreams[1]) {
_xaStreams[1]->readBuffer(temp, samples < 1024 ? samples : 1024);
}
}
}
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
}
SCXStream::~SCXStream() {
for (int i = 0; i < MAX_CHANNELS; i++)
for (int i = 0; i < SCX_MAX_CHANNELS; i++)
delete _xaStreams[i];
}
@ -187,14 +182,24 @@ bool SCXStream::rewind() {
return !isStereo() || _xaStreams[1]->rewind();
}
Audio::RewindableAudioStream *makeSCXStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
Audio::Timestamp SCXStream::getPos() const {
int32 pos = _fileStreams[0]->pos();
// Each XA ADPCM block of 16 bytes decompresses to 28 samples.
int32 samples = pos * 28 / 16;
uint32 msecs = (uint32)((int64)samples * 1000 / _rate);
return Audio::Timestamp(msecs);
}
SCXStream *makeSCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse) {
if (stream->readUint32BE() != MKTAG('S', 'C', 'R', 'X')) {
delete stream;
return nullptr;
}
stream->seek(0);
return new SCXStream(stream, disposeAfterUse);
return new SCXStream(stream, start, disposeAfterUse);
}
} // End of namespace Grim

View File

@ -29,6 +29,31 @@ namespace Common {
namespace Grim {
// I've only ever seen up to two
#define SCX_MAX_CHANNELS 2
class SCXStream : public Audio::RewindableAudioStream {
public:
SCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse);
~SCXStream();
bool isStereo() const override { return _channels == 2; }
bool endOfData() const override { return _xaStreams[0]->endOfData(); }
int getRate() const override { return _rate; }
int readBuffer(int16 *buffer, const int numSamples) override;
bool rewind() override;
Audio::Timestamp getPos() const;
private:
int _channels;
int _rate;
uint16 _blockSize;
Common::SeekableReadStream *_fileStreams[SCX_MAX_CHANNELS];
Audio::RewindableAudioStream *_xaStreams[SCX_MAX_CHANNELS];
};
/**
* Takes an input stream containing SCX sound data and creates
* an RewindableAudioStream from that.
@ -37,8 +62,9 @@ namespace Grim {
* @param disposeAfterUse whether to delete the stream after use
* @return a new RewindableAudioStream, or NULL, if an error occurred
*/
Audio::RewindableAudioStream *makeSCXStream(
SCXStream *makeSCXStream(
Common::SeekableReadStream *stream,
const Audio::Timestamp *start,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
} // End of namespace Grim

View File

@ -50,11 +50,8 @@ bool SCXTrack::openSound(const Common::String &filename, const Common::String &s
return false;
}
_soundName = soundName;
Audio::RewindableAudioStream *scxStream = makeSCXStream(file, DisposeAfterUse::YES);
if (_soundType == Audio::Mixer::kMusicSoundType)
_stream = Audio::makeLoopingAudioStream(scxStream, 0);
else
_stream = scxStream;
Audio::RewindableAudioStream *scxStream = makeSCXStream(file, start, DisposeAfterUse::YES);
_stream = scxStream;
_handle = new Audio::SoundHandle();
return true;
}
@ -67,8 +64,9 @@ bool SCXTrack::isPlaying() {
}
Audio::Timestamp SCXTrack::getPos() {
// FIXME: Return actual stream position.
return g_system->getMixer()->getSoundElapsedTime(*_handle);
if (!_stream)
return Audio::Timestamp(0);
return static_cast<SCXStream*>(_stream)->getPos();
}
} // end of namespace Grim