From caa3db465955ea9fee8fc78c908b7050e587487f Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 5 Jan 2010 21:10:34 +0000 Subject: - Initial implementation of looping of SeekableAudioStreams in Mixer. - Adapted AudioCD code to use this for audio CD emulation. svn-id: r47045 --- sound/audiocd.cpp | 7 ++-- sound/mixer.cpp | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++- sound/mixer.h | 36 ++++++++++++++++- sound/mixer_intern.h | 11 ++++- 4 files changed, 159 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/audiocd.cpp b/sound/audiocd.cpp index b1bd474a4c..d60ae2fab2 100644 --- a/sound/audiocd.cpp +++ b/sound/audiocd.cpp @@ -60,7 +60,7 @@ void AudioCDManager::play(int track, int numLoops, int startFrame, int duration, char trackName[2][16]; sprintf(trackName[0], "track%d", track); sprintf(trackName[1], "track%02d", track); - Audio::AudioStream *stream = 0; + Audio::SeekableAudioStream *stream = 0; for (int i = 0; !stream && i < 2; ++i) { /* @@ -69,7 +69,7 @@ void AudioCDManager::play(int track, int numLoops, int startFrame, int duration, repetitions. Finally, -1 means infinitely many */ // We multiply by 40 / 3 = 1000 / 75 to convert frames to milliseconds - stream = AudioStream::openStreamFile(trackName[i], startFrame * 40 / 3, duration * 40 / 3, (numLoops < 1) ? numLoops + 1 : numLoops); + stream = AudioStream::openStreamFile(trackName[i]); } // Stop any currently playing emulated track @@ -77,7 +77,8 @@ void AudioCDManager::play(int track, int numLoops, int startFrame, int duration, if (stream != 0) { _emulating = true; - _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_handle, stream); + _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)); } else { _emulating = false; if (!only_emulate) diff --git a/sound/mixer.cpp b/sound/mixer.cpp index 5ad8924fe1..7bfb11fdbb 100644 --- a/sound/mixer.cpp +++ b/sound/mixer.cpp @@ -170,7 +170,7 @@ private: AudioStream *_input; public: - SimpleChannel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, bool autofreeStream, bool reverseStereo = false, int id = -1, bool permanent = false); + SimpleChannel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, bool autofreeStream, bool reverseStereo, int id, bool permanent); ~SimpleChannel(); void mix(int16 *data, uint len); @@ -180,6 +180,23 @@ public: } }; +class LoopingChannel : public Channel { +public: + LoopingChannel(Mixer *mixer, Mixer::SoundType type, SeekableAudioStream *input, uint loopCount, Timestamp loopStart, Timestamp loopEnd, bool autofreeStream, bool reverseStereo, int id, bool permanent); + ~LoopingChannel(); + + void mix(int16 *data, uint len); + bool isFinished() const; +private: + uint _loopCount; + Timestamp _loopStart; + Timestamp _loopEnd; + + bool _autofreeStream; + RateConverter *_converter; + SeekableAudioStream *_input; + Timestamp _pos; +}; #pragma mark - #pragma mark --- Mixer --- @@ -283,7 +300,47 @@ void MixerImpl::playInputStream( } // Create the channel - SimpleChannel *chan = new SimpleChannel(this, type, input, autofreeStream, reverseStereo, id, permanent); + Channel *chan = new SimpleChannel(this, type, input, autofreeStream, reverseStereo, id, permanent); + chan->setVolume(volume); + chan->setBalance(balance); + insertChannel(handle, chan); +} + +void MixerImpl::playInputStreamLooping( + SoundType type, + SoundHandle *handle, + SeekableAudioStream *input, + uint loopCount, + Timestamp loopStart, Timestamp loopEnd, + int id, byte volume, int8 balance, + bool autofreeStream, + bool permanent, + bool reverseStereo) { + Common::StackLock lock(_mutex); + + if (input == 0) { + warning("input stream is 0"); + return; + } + + // Prevent duplicate sounds + if (id != -1) { + for (int i = 0; i != NUM_CHANNELS; i++) + if (_channels[i] != 0 && _channels[i]->getId() == id) { + if (autofreeStream) + delete input; + return; + } + } + + if (loopEnd.msecs() == 0) + loopEnd = input->getLength(); + + // Create the channel + Channel *chan = new LoopingChannel(this, type, input, loopCount, + loopStart.convertToFramerate(getOutputRate()), + loopEnd.convertToFramerate(getOutputRate()), + autofreeStream, reverseStereo, id, permanent); chan->setVolume(volume); chan->setBalance(balance); insertChannel(handle, chan); @@ -586,5 +643,56 @@ void SimpleChannel::mix(int16 *data, uint len) { } } +LoopingChannel::LoopingChannel(Mixer *mixer, Mixer::SoundType type, SeekableAudioStream *input, uint loopCount, + Timestamp loopStart, Timestamp loopEnd, bool autofreeStream, bool reverseStereo, + int id, bool permanent) + : Channel(mixer, type, id, permanent), _loopCount(loopCount), _loopStart(loopStart), _loopEnd(loopEnd), + _autofreeStream(autofreeStream), _converter(0), _input(input), _pos(0, mixer->getOutputRate()) { + _input->seek(loopStart); + // Get a rate converter instance + _converter = makeRateConverter(_input->getRate(), mixer->getOutputRate(), _input->isStereo(), reverseStereo); +} + +LoopingChannel::~LoopingChannel() { + delete _converter; + if (_autofreeStream) + delete _input; +} + +void LoopingChannel::mix(int16 *data, uint len) { + Timestamp newPos = _pos.addFrames(len); + int frameDiff = newPos.frameDiff(_loopEnd); + bool needLoop = false; + + assert(frameDiff <= (int)len); + + if (frameDiff >= 0) { + len -= frameDiff; + needLoop = true; + } + + _samplesConsumed = _samplesDecoded; + _mixerTimeStamp = g_system->getMillis(); + _pauseTime = 0; + uint samplesRead = _converter->flow(*_input, data, len, getLeftVolume(), getRightVolume()); + _samplesDecoded += samplesRead; + _pos = _pos.addFrames(samplesRead); + + if (needLoop) { + if (!_loopCount || _loopCount > 1) { + if (_loopCount > 1) + --_loopCount; + + _input->seek(_loopStart); + samplesRead = _converter->flow(*_input, data + len * 2, frameDiff, getLeftVolume(), getRightVolume()); + _samplesDecoded += samplesRead; + _pos = _loopStart.addFrames(samplesRead); + } + } +} + +bool LoopingChannel::isFinished() const { + return (_loopCount == 1) && (_pos == _loopEnd); +} } // End of namespace Audio diff --git a/sound/mixer.h b/sound/mixer.h index dbb947cc7e..528a17c6c3 100644 --- a/sound/mixer.h +++ b/sound/mixer.h @@ -29,6 +29,7 @@ #include "common/scummsys.h" #include "common/mutex.h" +#include "sound/timestamp.h" class OSystem; @@ -36,6 +37,7 @@ class OSystem; namespace Audio { class AudioStream; +class SeekableAudioStream; class Channel; class Mixer; class MixerImpl; @@ -167,7 +169,39 @@ public: bool permanent = false, bool reverseStereo = false) = 0; - + /** + * Start playing the given audio input stream with looping. + * + * Note that the sound id assigned below is unique. At most one stream + * with a given id can play at any given time. Trying to play a sound + * with an id that is already in use causes the new sound to be not played. + * + * @param type the type (voice/sfx/music) of the stream + * @param handle a SoundHandle which can be used to reference and control + * the stream via suitable mixer methods + * @param input the actual SeekableAudioStream to be played + * @param loopCount how often the data shall be looped (0 = infinite) + * @param loopStart the (optional) time offset from which to start playback + * @param loopEnd the (optional) time offset where the loop should end + * @param id a unique id assigned to this stream + * @param volume the volume with which to play the sound, ranging from 0 to 255 + * @param balance the balance with which to play the sound, ranging from -128 to 127 + * @param autofreeStream a flag indicating whether the stream should be + * freed after playback finished + * @param permanent a flag indicating whether a plain stopAll call should + * not stop this particular stream + * @param reverseStereo a flag indicating whether left and right channels shall be swapped + */ + virtual void playInputStreamLooping( + SoundType type, + SoundHandle *handle, + SeekableAudioStream *input, + uint loopCount, + Timestamp loopStart = Timestamp(0, 1000), Timestamp loopEnd = Timestamp(0, 1000), + int id = -1, byte volume = kMaxChannelVolume, int8 balance = 0, + bool autofreeStream = true, + bool permanent = false, + bool reverseStereo = false) = 0; /** * Stop all currently playing sounds. diff --git a/sound/mixer_intern.h b/sound/mixer_intern.h index d74fc70a1c..0cd8793ff3 100644 --- a/sound/mixer_intern.h +++ b/sound/mixer_intern.h @@ -90,7 +90,16 @@ public: bool permanent = false, bool reverseStereo = false); - + virtual void playInputStreamLooping( + SoundType type, + SoundHandle *handle, + SeekableAudioStream *input, + uint loopCount, + Timestamp loopStart = Timestamp(0, 1000), Timestamp loopEnd = Timestamp(0, 1000), + int id = -1, byte volume = kMaxChannelVolume, int8 balance = 0, + bool autofreeStream = true, + bool permanent = false, + bool reverseStereo = false); virtual void stopAll(); virtual void stopID(int id); -- cgit v1.2.3