aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schickel2010-01-07 14:20:36 +0000
committerJohannes Schickel2010-01-07 14:20:36 +0000
commitaa92e004f58470685e49b180ef19d92dc66c6436 (patch)
tree1dab512e2223521db09292f6bf38f5ec249bd2c6
parent4701d0799c997bb5479275ba0a3ff0ffd4113197 (diff)
downloadscummvm-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.cpp25
-rw-r--r--sound/audiostream.cpp74
-rw-r--r--sound/audiostream.h93
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