diff options
author | Johannes Schickel | 2010-01-07 14:20:36 +0000 |
---|---|---|
committer | Johannes Schickel | 2010-01-07 14:20:36 +0000 |
commit | aa92e004f58470685e49b180ef19d92dc66c6436 (patch) | |
tree | 1dab512e2223521db09292f6bf38f5ec249bd2c6 | |
parent | 4701d0799c997bb5479275ba0a3ff0ffd4113197 (diff) | |
download | scummvm-rg350-aa92e004f58470685e49b180ef19d92dc66c6436.tar.gz scummvm-rg350-aa92e004f58470685e49b180ef19d92dc66c6436.tar.bz2 scummvm-rg350-aa92e004f58470685e49b180ef19d92dc66c6436.zip |
First step of a slight revision of the new AudioStream looping API:
- Create a RewinadableAudioStream, for streams which can only be reset to the start
- Create a LoopableAudioStream, which loops a whole RewindableAudioStream
- Make SeekableAudioStream a subclass of RewindableAudioStream
- Create a SubSeekableAudioStream, which allows of limiting the range of an SeekableAudioStream to be played.
- Adapt AudioCD code.
svn-id: r47109
-rw-r--r-- | sound/audiocd.cpp | 25 | ||||
-rw-r--r-- | sound/audiostream.cpp | 74 | ||||
-rw-r--r-- | sound/audiostream.h | 93 |
3 files changed, 181 insertions, 11 deletions
diff --git a/sound/audiocd.cpp b/sound/audiocd.cpp index d2b205ab6c..f0e63dd1ca 100644 --- a/sound/audiocd.cpp +++ b/sound/audiocd.cpp @@ -62,23 +62,28 @@ void AudioCDManager::play(int track, int numLoops, int startFrame, int duration, sprintf(trackName[1], "track%02d", track); Audio::SeekableAudioStream *stream = 0; - for (int i = 0; !stream && i < 2; ++i) { - /* - FIXME: Seems numLoops == 0 and numLoops == 1 both indicate a single repetition, - while all other positive numbers indicate precisely the number of desired - repetitions. Finally, -1 means infinitely many - */ - // We multiply by 40 / 3 = 1000 / 75 to convert frames to milliseconds + for (int i = 0; !stream && i < 2; ++i) stream = SeekableAudioStream::openStreamFile(trackName[i]); - } // Stop any currently playing emulated track _mixer->stopHandle(_handle); if (stream != 0) { + if (startFrame != 0 || duration != 0) { + stream = new SubSeekableAudioStream(stream, Timestamp(0, startFrame, 75), Timestamp(0, startFrame + duration, 75)); + assert(stream); + } + + /* + FIXME: Seems numLoops == 0 and numLoops == 1 both indicate a single repetition, + while all other positive numbers indicate precisely the number of desired + repetitions. Finally, -1 means infinitely many + */ + AudioStream *output = new LoopingAudioStream(stream, (numLoops < 1) ? numLoops + 1 : numLoops); + assert(output); + _emulating = true; - _mixer->playInputStreamLooping(Audio::Mixer::kMusicSoundType, &_handle, stream, (numLoops < 1) ? numLoops + 1 : numLoops, - Timestamp(startFrame * 40 / 3, 1000), Timestamp(startFrame * 40 / 3 + duration * 40 / 3, 1000)); + _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_handle, output); } else { _emulating = false; if (!only_emulate) diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index 748e480c61..79e8308115 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -99,6 +99,80 @@ SeekableAudioStream *SeekableAudioStream::openStreamFile(const Common::String &b } #pragma mark - +#pragma mark --- LoopingAudioStream --- +#pragma mark - + +LoopingAudioStream::LoopingAudioStream(RewindableAudioStream *stream, uint loops, bool disposeAfterUse) + : _parent(stream), _disposeAfterUse(disposeAfterUse), _loops(loops), _completeIterations(0) { +} + +LoopingAudioStream::~LoopingAudioStream() { + if (_disposeAfterUse) + delete _parent; +} + +int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) { + int samplesRead = _parent->readBuffer(buffer, numSamples); + + if (_parent->endOfStream()) { + ++_completeIterations; + if (_completeIterations == _loops) + return samplesRead; + + int remainingSamples = numSamples - samplesRead; + + if (!_parent->rewind()) { + // TODO: Properly indicate error + _loops = _completeIterations = 1; + return samplesRead; + } + + samplesRead += _parent->readBuffer(buffer, remainingSamples); + } + + return samplesRead; +} + +bool LoopingAudioStream::endOfData() const { + return (_loops != 0 && (_completeIterations == _loops)); +} + +#pragma mark - +#pragma mark --- SubSeekableAudioStream --- +#pragma mark - + +SubSeekableAudioStream::SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, bool disposeAfterUse) + : _parent(parent), _disposeAfterUse(disposeAfterUse), _isStereo(parent->isStereo()), + _start(start.convertToFramerate(getRate())), _pos(0, getRate()), _length(end.convertToFramerate(getRate())) { + // TODO: This really looks like Timestamp::operator- + _length = Timestamp(_length.secs() - _start.secs(), _length.numberOfFrames() - _start.numberOfFrames(), getRate()); + _parent->seek(_start); +} + +SubSeekableAudioStream::~SubSeekableAudioStream() { + if (_disposeAfterUse) + delete _parent; +} + +int SubSeekableAudioStream::readBuffer(int16 *buffer, const int numSamples) { + int framesLeft = MIN(_length.frameDiff(_pos) * (_isStereo ? 2 : 1), numSamples); + int framesRead = _parent->readBuffer(buffer, framesLeft); + _pos = _pos.addFrames(framesRead / (_isStereo ? 2 : 1)); + return framesRead; +} + +bool SubSeekableAudioStream::seek(const Timestamp &where) { + _pos = where.convertToFramerate(getRate()); + // TODO: This really looks like Timestamp::operator+ + if (_parent->seek(Timestamp(_pos.secs() + _start.secs(), _pos.numberOfFrames() + _start.numberOfFrames(), getRate()))) { + return true; + } else { + _pos = _length; + return false; + } +} + +#pragma mark - #pragma mark --- LinearMemoryStream --- #pragma mark - diff --git a/sound/audiostream.h b/sound/audiostream.h index 3889cffbb4..8f861299e9 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -102,11 +102,61 @@ public: }; /** + * A rewindable audio stream. This allows for restting the AudioStream + * to its initial state. Note that rewinding itself is not required to + * be working when the stream is being played by Mixer! + */ +class RewindableAudioStream : public AudioStream { +public: + /** + * Rewinds the stream to its start. + * + * @return true on success, false otherwise. + */ + virtual bool rewind() = 0; +}; + +/** + * A looping audio stream. This object does nothing beides using + * a RewindableAudioStream to play a stream in a loop. + */ +class LoopingAudioStream : public AudioStream { +public: + /** + * Creates a looping audio stream object. + * + * @param stream Stream to loop + * @param loops How often to loop (0 = infinite) + * @param disposeAfteruse Destroy the stream after the LoopingAudioStream has finished playback. + */ + LoopingAudioStream(RewindableAudioStream *stream, uint loops, bool disposeAfterUse = true); + ~LoopingAudioStream(); + + int readBuffer(int16 *buffer, const int numSamples); + bool endOfData() const; + + bool isStereo() const { return _parent->isStereo(); } + int getRate() const { return _parent->getRate(); } + + /** + * Returns number of loops the stream has played. + * @param numLoops number of loops to play, 0 - infinite + */ + uint getCompleteIterations() const { return _completeIterations; } +private: + RewindableAudioStream *_parent; + bool _disposeAfterUse; + + uint _loops; + uint _completeIterations; +}; + +/** * A seekable audio stream. Subclasses of this class implement a * working seeking. The seeking itself is not required to be * working when the stream is being played by Mixer! */ -class SeekableAudioStream : public AudioStream { +class SeekableAudioStream : public RewindableAudioStream { public: /** * Tries to load a file by trying all available formats. @@ -142,8 +192,49 @@ public: * @return length as Timestamp. */ virtual Timestamp getLength() const = 0; + + virtual bool rewind() { return seek(0); } }; +/** + * A SubSeekableAudioStream provides access to a SeekableAudioStream + * just in the range [start, end). + * The same caveats apply to SubSeekableAudioStream as do to SeekableAudioStream. + * + * Manipulating the parent stream directly /will/ mess up a substream. + */ +class SubSeekableAudioStream : public SeekableAudioStream { +public: + /** + * Creates a new SubSeekableAudioStream. + * + * @param parent parent stream object. + * @param start Start time. + * @param end End time. + * @param disposeAfterUse Whether the parent stream object should be destroied on desctruction of the SubSeekableAudioStream. + */ + SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, bool disposeAfterUse = true); + ~SubSeekableAudioStream(); + + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return _isStereo; } + + int getRate() const { return _parent->getRate(); } + + bool endOfData() const { return (_pos >= _length) || _parent->endOfStream(); } + + bool seek(const Timestamp &where); + + Timestamp getLength() const { return _length; } +private: + SeekableAudioStream *_parent; + bool _disposeAfterUse; + const bool _isStereo; + + const Timestamp _start; + Timestamp _pos, _length; +}; /** * Factory function for a raw linear AudioStream, which will simply treat all |