diff --git a/audio/mixer.cpp b/audio/mixer.cpp index c118b18c40c..3e348402827 100644 --- a/audio/mixer.cpp +++ b/audio/mixer.cpp @@ -176,8 +176,8 @@ private: #pragma mark --- Mixer --- #pragma mark - -MixerImpl::MixerImpl(uint sampleRate, uint outBufSize) - : _mutex(), _sampleRate(sampleRate), _outBufSize(outBufSize), _mixerReady(false), _handleSeed(0), _soundTypeSettings() { +MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize) + : _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _mixerReady(false), _handleSeed(0), _soundTypeSettings() { assert(sampleRate > 0); @@ -200,6 +200,10 @@ uint MixerImpl::getOutputRate() const { return _sampleRate; } +bool MixerImpl::getOutputStereo() const { + return _stereo; +} + uint MixerImpl::getOutputBufSize() const { return _outBufSize; } @@ -280,15 +284,21 @@ int MixerImpl::mixCallback(byte *samples, uint len) { Common::StackLock lock(_mutex); int16 *buf = (int16 *)samples; - // we store stereo, 16-bit samples - assert(len % 4 == 0); - len >>= 2; // Since the mixer callback has been called, the mixer must be ready... _mixerReady = true; // zero the buf - memset(buf, 0, 2 * len * sizeof(int16)); + memset(buf, 0, len); + + // we store 16-bit samples + if (_stereo) { + assert(len % 4 == 0); + len >>= 2; + } else { + assert(len % 2 == 0); + len >>= 1; + } // mix all channels int res = 0, tmp; @@ -524,7 +534,7 @@ Channel::Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *stream, assert(stream); // Get a rate converter instance - _converter = makeRateConverter(_stream->getRate(), mixer->getOutputRate(), _stream->isStereo(), reverseStereo); + _converter = makeRateConverter(_stream->getRate(), mixer->getOutputRate(), _stream->isStereo(), mixer->getOutputStereo(), reverseStereo); } Channel::~Channel() { diff --git a/audio/mixer.h b/audio/mixer.h index 804022ecf00..ff521799617 100644 --- a/audio/mixer.h +++ b/audio/mixer.h @@ -305,6 +305,13 @@ public: */ virtual uint getOutputRate() const = 0; + /** + * Check whether the output is stereo. + * + * @return true if output is stereo, false if not. + */ + virtual bool getOutputStereo() const = 0; + /** * Return the output sample buffer size of the system. * diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h index 424a0d4853f..c7d67771cbd 100644 --- a/audio/mixer_intern.h +++ b/audio/mixer_intern.h @@ -64,6 +64,7 @@ private: Common::Mutex _mutex; const uint _sampleRate; + const bool _stereo; const uint _outBufSize; bool _mixerReady; uint32 _handleSeed; @@ -81,7 +82,7 @@ private: public: - MixerImpl(uint sampleRate, uint outBufSize = 0); + MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0); ~MixerImpl(); virtual bool isReady() const { Common::StackLock lock(_mutex); return _mixerReady; } @@ -129,6 +130,7 @@ public: virtual int getVolumeForSoundType(SoundType type) const; virtual uint getOutputRate() const; + virtual bool getOutputStereo() const; virtual uint getOutputBufSize() const; protected: diff --git a/audio/rate.cpp b/audio/rate.cpp index 1bd67c6d181..d85c278d796 100644 --- a/audio/rate.cpp +++ b/audio/rate.cpp @@ -62,7 +62,7 @@ enum { * * Limited to sampling frequency <= 65535 Hz. */ -template +template class SimpleRateConverter : public RateConverter { protected: st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE]; @@ -88,8 +88,8 @@ public: /* * Prepare processing. */ -template -SimpleRateConverter::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) { +template +SimpleRateConverter::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) { if ((inrate % outrate) != 0) { error("Input rate must be a multiple of output rate to use rate effect"); } @@ -110,12 +110,12 @@ SimpleRateConverter::SimpleRateConverter(st_rate_t inrate * Processed signed long samples from ibuf to obuf. * Return number of sample pairs processed. */ -template -int SimpleRateConverter::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) { +template +int SimpleRateConverter::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) { st_sample_t *ostart, *oend; ostart = obuf; - oend = obuf + osamp * 2; + oend = obuf + osamp * (outStereo ? 2 : 1); while (obuf < oend) { @@ -126,31 +126,42 @@ int SimpleRateConverter::flow(AudioStream &input, st_samp inPtr = inBuf; inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf)); if (inLen <= 0) - return (obuf - ostart) / 2; + return (obuf - ostart) / (outStereo ? 2 : 1); } - inLen -= (stereo ? 2 : 1); + inLen -= (inStereo ? 2 : 1); opos--; if (opos >= 0) { - inPtr += (stereo ? 2 : 1); + inPtr += (inStereo ? 2 : 1); } } while (opos >= 0); - st_sample_t out0, out1; - out0 = *inPtr++; - out1 = (stereo ? *inPtr++ : out0); + st_sample_t in0, in1; + in0 = *inPtr++; + in1 = (inStereo ? *inPtr++ : in0); // Increment output position opos += opos_inc; - // output left channel - clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); + st_sample_t out0, out1; + out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume; + out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume; - // output right channel - clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + if (outStereo) { + // output left channel + clampedAdd(obuf[reverseStereo ], out0); - obuf += 2; + // output right channel + clampedAdd(obuf[reverseStereo ^ 1], out1); + + obuf += 2; + } else { + // output mono channel + clampedAdd(obuf[0], (out0 + out1) / 2); + + obuf += 1; + } } - return (obuf - ostart) / 2; + return (obuf - ostart) / (outStereo ? 2 : 1); } /** @@ -164,7 +175,7 @@ int SimpleRateConverter::flow(AudioStream &input, st_samp * Limited to sampling frequency <= 65535 Hz. */ -template +template class LinearRateConverter : public RateConverter { protected: st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE]; @@ -194,8 +205,8 @@ public: /* * Prepare processing. */ -template -LinearRateConverter::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) { +template +LinearRateConverter::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) { if (inrate >= 131072 || outrate >= 131072) { error("rate effect can only handle rates < 131072"); } @@ -219,12 +230,12 @@ LinearRateConverter::LinearRateConverter(st_rate_t inrate * Processed signed long samples from ibuf to obuf. * Return number of sample pairs processed. */ -template -int LinearRateConverter::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) { +template +int LinearRateConverter::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) { st_sample_t *ostart, *oend; ostart = obuf; - oend = obuf + osamp * 2; + oend = obuf + osamp * (outStereo ? 2 : 1); while (obuf < oend) { @@ -235,12 +246,12 @@ int LinearRateConverter::flow(AudioStream &input, st_samp inPtr = inBuf; inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf)); if (inLen <= 0) - return (obuf - ostart) / 2; + return (obuf - ostart) / (outStereo ? 2 : 1); } - inLen -= (stereo ? 2 : 1); + inLen -= (inStereo ? 2 : 1); ilast0 = icur0; icur0 = *inPtr++; - if (stereo) { + if (inStereo) { ilast1 = icur1; icur1 = *inPtr++; } @@ -251,25 +262,36 @@ int LinearRateConverter::flow(AudioStream &input, st_samp // still space in the output buffer. while (opos < (frac_t)FRAC_ONE_LOW && obuf < oend) { // interpolate - st_sample_t out0, out1; - out0 = (st_sample_t)(ilast0 + (((icur0 - ilast0) * opos + FRAC_HALF_LOW) >> FRAC_BITS_LOW)); - out1 = (stereo ? + st_sample_t in0, in1; + in0 = (st_sample_t)(ilast0 + (((icur0 - ilast0) * opos + FRAC_HALF_LOW) >> FRAC_BITS_LOW)); + in1 = (inStereo ? (st_sample_t)(ilast1 + (((icur1 - ilast1) * opos + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) : - out0); + in0); - // output left channel - clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); + st_sample_t out0, out1; + out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume; + out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume; - // output right channel - clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + if (outStereo) { + // output left channel + clampedAdd(obuf[reverseStereo ], out0); - obuf += 2; + // output right channel + clampedAdd(obuf[reverseStereo ^ 1], out1); + + obuf += 2; + } else { + // output mono channel + clampedAdd(obuf[0], (out0 + out1) / 2); + + obuf += 1; + } // Increment output position opos += opos_inc; } } - return (obuf - ostart) / 2; + return (obuf - ostart) / (outStereo ? 2 : 1); } @@ -279,7 +301,7 @@ int LinearRateConverter::flow(AudioStream &input, st_samp /** * Simple audio rate converter for the case that the inrate equals the outrate. */ -template +template class CopyRateConverter : public RateConverter { st_sample_t *_buffer; st_size_t _bufferSize; @@ -290,20 +312,20 @@ public: } int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) override { - assert(input.isStereo() == stereo); + assert(input.isStereo() == inStereo); st_sample_t *ptr; st_size_t len; st_sample_t *ostart = obuf; - if (stereo) + if (inStereo) osamp *= 2; // Reallocate temp buffer, if necessary if (osamp > _bufferSize) { free(_buffer); - _buffer = (st_sample_t *)malloc(osamp * 2); + _buffer = (st_sample_t *)malloc(osamp * sizeof(st_sample_t)); _bufferSize = osamp; } @@ -315,20 +337,31 @@ public: // Mix the data into the output buffer ptr = _buffer; - for (; len > 0; len -= (stereo ? 2 : 1)) { + for (; len > 0; len -= (inStereo ? 2 : 1)) { + st_sample_t in0, in1; + in0 = *ptr++; + in1 = (inStereo ? *ptr++ : in0); + st_sample_t out0, out1; - out0 = *ptr++; - out1 = (stereo ? *ptr++ : out0); + out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume; + out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume; - // output left channel - clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); + if (outStereo) { + // output left channel + clampedAdd(obuf[reverseStereo ], out0); - // output right channel - clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + // output right channel + clampedAdd(obuf[reverseStereo ^ 1], out1); - obuf += 2; + obuf += 2; + } else { + // output mono channel + clampedAdd(obuf[0], (out0 + out1) / 2); + + obuf += 1; + } } - return (obuf - ostart) / 2; + return (obuf - ostart) / (outStereo ? 2 : 1); } int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) override { @@ -339,30 +372,37 @@ public: #pragma mark - -template +template RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate) { if (inrate != outrate) { if ((inrate % outrate) == 0 && (inrate < 65536)) { - return new SimpleRateConverter(inrate, outrate); + return new SimpleRateConverter(inrate, outrate); } else { - return new LinearRateConverter(inrate, outrate); + return new LinearRateConverter(inrate, outrate); } } else { - return new CopyRateConverter(); + return new CopyRateConverter(); } } /** * Create and return a RateConverter object for the specified input and output rates. */ -RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) { - if (stereo) { - if (reverseStereo) - return makeRateConverter(inrate, outrate); - else - return makeRateConverter(inrate, outrate); - } else - return makeRateConverter(inrate, outrate); +RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool instereo, bool outstereo, bool reverseStereo) { + if (instereo) { + if (outstereo) { + if (reverseStereo) + return makeRateConverter(inrate, outrate); + else + return makeRateConverter(inrate, outrate); + } else + return makeRateConverter(inrate, outrate); + } else { + if (outstereo) { + return makeRateConverter(inrate, outrate); + } else + return makeRateConverter(inrate, outrate); + } } } // End of namespace Audio diff --git a/audio/rate.h b/audio/rate.h index 93d9041ab06..67e34b8de1c 100644 --- a/audio/rate.h +++ b/audio/rate.h @@ -84,7 +84,7 @@ public: virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) = 0; }; -RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo = false); +RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool instereo, bool outstereo, bool reverseStereo); /** @} */ } // End of namespace Audio diff --git a/backends/mixer/null/null-mixer.cpp b/backends/mixer/null/null-mixer.cpp index 45f41223424..e7f3f79455a 100644 --- a/backends/mixer/null/null-mixer.cpp +++ b/backends/mixer/null/null-mixer.cpp @@ -36,7 +36,7 @@ NullMixerManager::~NullMixerManager() { } void NullMixerManager::init() { - _mixer = new Audio::MixerImpl(_outputRate, _samples); + _mixer = new Audio::MixerImpl(_outputRate, true, _samples); assert(_mixer); _mixer->setReady(true); } diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index fc9c095970e..f3b9d93e3df 100644 --- a/backends/mixer/sdl/sdl-mixer.cpp +++ b/backends/mixer/sdl/sdl-mixer.cpp @@ -73,7 +73,7 @@ void SdlMixerManager::init() { warning("Could not open audio device: %s", SDL_GetError()); // The mixer is not marked as ready - _mixer = new Audio::MixerImpl(desired.freq, desired.samples); + _mixer = new Audio::MixerImpl(desired.freq, true, desired.samples); return; } @@ -88,7 +88,7 @@ void SdlMixerManager::init() { warning("Could not open audio device: %s", SDL_GetError()); // The mixer is not marked as ready - _mixer = new Audio::MixerImpl(desired.freq, desired.samples); + _mixer = new Audio::MixerImpl(desired.freq, true, desired.samples); return; } @@ -106,7 +106,7 @@ void SdlMixerManager::init() { if (_obtained.channels != 2) error("SDL mixer output requires stereo output device"); - _mixer = new Audio::MixerImpl(_obtained.freq, desired.samples); + _mixer = new Audio::MixerImpl(_obtained.freq, true, desired.samples); assert(_mixer); _mixer->setReady(true); diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index e947441ebd7..e014520f824 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -470,7 +470,7 @@ void OSystem_Android::initBackend() { // The division by four happens because the Mixer stores the size in frame units // instead of bytes; this means that, since we have audio in stereo (2 channels) // with a word size of 16 bit (2 bytes), we have to divide the effective size by 4. - _mixer = new Audio::MixerImpl(_audio_sample_rate, _audio_buffer_size / 4); + _mixer = new Audio::MixerImpl(_audio_sample_rate, true, _audio_buffer_size / 4); _mixer->setReady(true); _timer_thread_exit = false; diff --git a/backends/platform/ios7/ios7_osys_sound.cpp b/backends/platform/ios7/ios7_osys_sound.cpp index 648bceac4b4..65008b26f30 100644 --- a/backends/platform/ios7/ios7_osys_sound.cpp +++ b/backends/platform/ios7/ios7_osys_sound.cpp @@ -45,7 +45,7 @@ void OSystem_iOS7::mixCallback(void *sys, byte *samples, int len) { } void OSystem_iOS7::setupMixer() { - _mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, WAVE_BUFFER_SIZE); + _mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, true, WAVE_BUFFER_SIZE); s_soundCallback = mixCallback; s_soundParam = this; diff --git a/backends/platform/iphone/osys_sound.cpp b/backends/platform/iphone/osys_sound.cpp index ae6117b9a9f..bc71f3b760d 100644 --- a/backends/platform/iphone/osys_sound.cpp +++ b/backends/platform/iphone/osys_sound.cpp @@ -45,7 +45,7 @@ void OSystem_IPHONE::mixCallback(void *sys, byte *samples, int len) { } void OSystem_IPHONE::setupMixer() { - _mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, WAVE_BUFFER_SIZE); + _mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, true, WAVE_BUFFER_SIZE); s_soundCallback = mixCallback; s_soundParam = this; diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp index fd855331dab..69ffb9cfaae 100644 --- a/engines/sci/sound/audio32.cpp +++ b/engines/sci/sound/audio32.cpp @@ -694,7 +694,8 @@ bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) { channel.soundNode = NULL_REG; channel.volume = kMaxVolume; channel.pan = -1; - channel.converter.reset(Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false)); + // TODO: Avoid unnecessary channel conversion + channel.converter.reset(Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false, true, false)); // The RobotAudioStream buffer size is // ((bytesPerSample * channels * sampleRate * 2000ms) / 1000ms) & ~3 // where bytesPerSample = 2, channels = 1, and sampleRate = 22050 @@ -860,7 +861,8 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool } channel.stream.reset(new MutableLoopAudioStream(audioStream, loop)); - channel.converter.reset(Audio::makeRateConverter(channel.stream->getRate(), getRate(), channel.stream->isStereo(), false)); + // TODO: Avoid unnecessary channel conversion + channel.converter.reset(Audio::makeRateConverter(channel.stream->getRate(), getRate(), channel.stream->isStereo(), true, false)); // SSCI sets up a decompression buffer here for the audio stream, plus // writes information about the sample to the channel to convert to the diff --git a/engines/sword1/music.cpp b/engines/sword1/music.cpp index 33a70991268..f8e56256fc8 100644 --- a/engines/sword1/music.cpp +++ b/engines/sword1/music.cpp @@ -303,12 +303,14 @@ void Music::startMusic(int32 tuneId, int32 loopFlag) { if (SwordEngine::isPsx()) { if (_handles[newStream].playPSX(tuneId, loopFlag != 0)) { _mutex.lock(); - _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false); + // TODO: Avoid unnecessary channel conversion + _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), true, false); _mutex.unlock(); } } else if (_handles[newStream].play(_tuneList[tuneId], loopFlag != 0)) { _mutex.lock(); - _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false); + // TODO: Avoid unnecessary channel conversion + _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), true, false); _mutex.unlock(); } else { if (tuneId != 81) // file 81 was apparently removed from BS.