mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-23 12:44:02 +00:00
EMI: Implement seeking to start position for SCX tracks.
This commit is contained in:
parent
da2518bdb3
commit
5e8d3dad0c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user