[Scummvm-git-logs] scummvm master -> 85befa9c13c0cf953e63f79e67b605a00d5e8fa4

sev- noreply at scummvm.org
Sun Jun 21 13:17:43 UTC 2026


This automated email contains information about 8 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
ccbcb756b0 AUDIO: Fix constness
acab1a9175 AUDIO: Input stream always works with int16
d440280f0e SCI32: Don't use Audio::st_sample_t for AudioStream instances
a6d828135f AUDIO: Add support for 32-bit sample size
b9cca6360e AUDIO: RateConverter now supports unclamped mixing
424681dca2 AUDIO: Make sample clamping optional
995c360cfc AUDIO: Don't clamp muted channels
85befa9c13 AUDIO: Template-optimize convert methods


Commit: ccbcb756b0e98088d456843186175015704f28f7
    https://github.com/scummvm/scummvm/commit/ccbcb756b0e98088d456843186175015704f28f7
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Fix constness

Changed paths:
    audio/mixer.cpp
    audio/mixer.h
    audio/mixer_intern.h


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 4791628bbac..51ea508d56f 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -98,7 +98,7 @@ public:
 	 *
 	 * @return volume
 	 */
-	byte getVolume();
+	byte getVolume() const;
 
 	/**
 	 * Sets the channel's balance setting.
@@ -112,7 +112,7 @@ public:
 	 *
 	 * @return balance
 	 */
-	int8 getBalance();
+	int8 getBalance() const;
 
 	/**
 	 * Sets the channel's left fader level.
@@ -126,7 +126,7 @@ public:
 	 *
 	 * @return The channel's left fader level.
 	 */
-	uint8 getFaderL();
+	uint8 getFaderL() const;
 
 	/**
 	 * Sets the channel's right fader level.
@@ -140,7 +140,7 @@ public:
 	 *
 	 * @return The channel's right fader level.
 	 */
-	uint8 getFaderR();
+	uint8 getFaderR() const;
 
 	/**
 	 * Set the channel's sample rate.
@@ -154,7 +154,7 @@ public:
 	 *
 	 * @return The current sample rate of the channel.
 	 */
-	uint32 getRate();
+	uint32 getRate() const;
 
 	/**
 	 * Reset the sample rate of the channel back to its
@@ -171,7 +171,7 @@ public:
 	/**
 	 * Queries how long the channel has been playing.
 	 */
-	Timestamp getElapsedTime();
+	Timestamp getElapsedTime() const;
 
 	/**
 	 * Replaces the channel's stream with a version that loops indefinitely.
@@ -425,7 +425,7 @@ void MixerImpl::setChannelVolume(SoundHandle handle, byte volume) {
 	_channels[index]->setVolume(volume);
 }
 
-byte MixerImpl::getChannelVolume(SoundHandle handle) {
+byte MixerImpl::getChannelVolume(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -443,7 +443,7 @@ void MixerImpl::setChannelBalance(SoundHandle handle, int8 balance) {
 	_channels[index]->setBalance(balance);
 }
 
-int8 MixerImpl::getChannelBalance(SoundHandle handle) {
+int8 MixerImpl::getChannelBalance(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -461,7 +461,7 @@ void MixerImpl::setChannelFaderL(SoundHandle handle, uint8 faderL) {
 	_channels[index]->setFaderL(faderL);
 }
 
-uint8 MixerImpl::getChannelFaderL(SoundHandle handle) {
+uint8 MixerImpl::getChannelFaderL(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -479,7 +479,7 @@ void MixerImpl::setChannelFaderR(SoundHandle handle, uint8 faderR) {
 	_channels[index]->setFaderR(faderR);
 }
 
-uint8 MixerImpl::getChannelFaderR(SoundHandle handle) {
+uint8 MixerImpl::getChannelFaderR(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -497,7 +497,7 @@ void MixerImpl::setChannelRate(SoundHandle handle, uint32 rate) {
 	_channels[index]->setRate(rate);
 }
 
-uint32 MixerImpl::getChannelRate(SoundHandle handle) {
+uint32 MixerImpl::getChannelRate(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -515,11 +515,11 @@ void MixerImpl::resetChannelRate(SoundHandle handle) {
 	_channels[index]->resetRate();
 }
 
-uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) {
+uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) const {
 	return getElapsedTime(handle).msecs();
 }
 
-Timestamp MixerImpl::getElapsedTime(SoundHandle handle) {
+Timestamp MixerImpl::getElapsedTime(SoundHandle handle) const {
 	Common::StackLock lock(_mutex);
 
 	const int index = handle._val % NUM_CHANNELS;
@@ -569,7 +569,7 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) {
 	_channels[index]->pause(paused);
 }
 
-bool MixerImpl::isSoundIDActive(int id) {
+bool MixerImpl::isSoundIDActive(int id) const {
 	Common::StackLock lock(_mutex);
 
 #ifdef ENABLE_EVENTRECORDER
@@ -582,7 +582,7 @@ bool MixerImpl::isSoundIDActive(int id) {
 	return false;
 }
 
-int MixerImpl::getSoundID(SoundHandle handle) {
+int MixerImpl::getSoundID(SoundHandle handle) const {
 	Common::StackLock lock(_mutex);
 	const int index = handle._val % NUM_CHANNELS;
 	if (_channels[index] && _channels[index]->getHandle()._val == handle._val)
@@ -590,7 +590,7 @@ int MixerImpl::getSoundID(SoundHandle handle) {
 	return 0;
 }
 
-bool MixerImpl::isSoundHandleActive(SoundHandle handle) {
+bool MixerImpl::isSoundHandleActive(SoundHandle handle) const {
 	Common::StackLock lock(_mutex);
 
 #ifdef ENABLE_EVENTRECORDER
@@ -601,7 +601,7 @@ bool MixerImpl::isSoundHandleActive(SoundHandle handle) {
 	return _channels[index] && _channels[index]->getHandle()._val == handle._val;
 }
 
-bool MixerImpl::hasActiveChannelOfType(SoundType type) {
+bool MixerImpl::hasActiveChannelOfType(SoundType type) const {
 	Common::StackLock lock(_mutex);
 	for (int i = 0; i != NUM_CHANNELS; i++)
 		if (_channels[i] && _channels[i]->getType() == type)
@@ -660,7 +660,7 @@ void Channel::setVolume(const byte volume) {
 	updateChannelVolumes();
 }
 
-byte Channel::getVolume() {
+byte Channel::getVolume() const {
 	return _volume;
 }
 
@@ -669,7 +669,7 @@ void Channel::setBalance(const int8 balance) {
 	updateChannelVolumes();
 }
 
-int8 Channel::getBalance() {
+int8 Channel::getBalance() const {
 	return _balance;
 }
 
@@ -678,7 +678,7 @@ void Channel::setFaderL(uint8 faderL) {
 	updateChannelVolumes();
 }
 
-uint8 Channel::getFaderL() {
+uint8 Channel::getFaderL() const {
 	return _faderL;
 }
 
@@ -687,7 +687,7 @@ void Channel::setFaderR(uint8 faderR) {
 	updateChannelVolumes();
 }
 
-uint8 Channel::getFaderR() {
+uint8 Channel::getFaderR() const {
 	return _faderR;
 }
 
@@ -696,7 +696,7 @@ void Channel::setRate(uint32 rate) {
 		_converter->setInputRate(rate);
 }
 
-uint32 Channel::getRate() {
+uint32 Channel::getRate() const {
 	if (_converter)
 		return _converter->getInputRate();
 
@@ -756,7 +756,7 @@ void Channel::pause(bool paused) {
 	}
 }
 
-Timestamp Channel::getElapsedTime() {
+Timestamp Channel::getElapsedTime() const {
 	const uint32 rate = _mixer->getOutputRate();
 	uint32 delta = 0;
 
diff --git a/audio/mixer.h b/audio/mixer.h
index 4e34879c23a..2c6c35f07b2 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -192,7 +192,7 @@ public:
 	 *
 	 * @return True if the sound is active.
 	 */
-	virtual bool isSoundIDActive(int id) = 0;
+	virtual bool isSoundIDActive(int id) const = 0;
 
 	/**
 	 * Get the sound ID for the given handle.
@@ -201,7 +201,7 @@ public:
 	 *
 	 * @return Sound ID if the sound is active.
 	 */
-	virtual int getSoundID(SoundHandle handle) = 0;
+	virtual int getSoundID(SoundHandle handle) const = 0;
 
 	/**
 	 * Check whether a sound with the given handle is active.
@@ -210,7 +210,7 @@ public:
 	 *
 	 * @return True if the sound is active.
 	 */
-	virtual bool isSoundHandleActive(SoundHandle handle) = 0;
+	virtual bool isSoundHandleActive(SoundHandle handle) const = 0;
 
 
 	/**
@@ -243,7 +243,7 @@ public:
 	 *
 	 * @return The channel volume.
 	 */
-	virtual byte getChannelVolume(SoundHandle handle) = 0;
+	virtual byte getChannelVolume(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the channel balance for the given handle.
@@ -261,7 +261,7 @@ public:
 	 *
 	 * @return The channel balance.
 	 */
-	virtual int8 getChannelBalance(SoundHandle handle) = 0;
+	virtual int8 getChannelBalance(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the channel's left fader level for the given handle.
@@ -278,7 +278,7 @@ public:
 	 *
 	 * @return The channel's left fader level.
 	 */
-	virtual uint8 getChannelFaderL(SoundHandle handle) = 0;
+	virtual uint8 getChannelFaderL(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the channel's right fader level for the given handle.
@@ -295,7 +295,7 @@ public:
 	 *
 	 * @return The channel's right fader level.
 	 */
-	virtual uint8 getChannelFaderR(SoundHandle handle) = 0;
+	virtual uint8 getChannelFaderR(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the sample rate for the given handle.
@@ -312,7 +312,7 @@ public:
 	 *
 	 * @return The current sample rate of the channel.
 	*/
-	virtual uint32 getChannelRate(SoundHandle handle) = 0;
+	virtual uint32 getChannelRate(SoundHandle handle) const = 0;
 
 	/**
 	 * Reset the sample rate of the channel back to its
@@ -325,12 +325,12 @@ public:
 	/**
 	 * Get an approximation of for how long the channel has been playing.
 	 */
-	virtual uint32 getSoundElapsedTime(SoundHandle handle) = 0;
+	virtual uint32 getSoundElapsedTime(SoundHandle handle) const = 0;
 
 	/**
 	 * Get an approximation of for how long the channel has been playing.
 	 */
-	virtual Timestamp getElapsedTime(SoundHandle handle) = 0;
+	virtual Timestamp getElapsedTime(SoundHandle handle) const = 0;
 
 	/**
 	 * Replace the channel's stream with a version that loops indefinitely.
@@ -347,7 +347,7 @@ public:
 	 *
 	 * @return True if any channels of the specified type are active.
 	 */
-	virtual bool hasActiveChannelOfType(SoundType type) = 0;
+	virtual bool hasActiveChannelOfType(SoundType type) const = 0;
 
 	/**
 	 * Set the volume for the given sound type.
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index 409315c3c6f..c46a43825ac 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -106,32 +106,32 @@ public:
 	void pauseID(int id, bool paused) override;
 	void pauseHandle(SoundHandle handle, bool paused) override;
 
-	bool isSoundIDActive(int id) override;
-	int getSoundID(SoundHandle handle) override;
+	bool isSoundIDActive(int id) const override;
+	int getSoundID(SoundHandle handle) const override;
 
-	bool isSoundHandleActive(SoundHandle handle) override;
+	bool isSoundHandleActive(SoundHandle handle) const override;
 
 	void muteSoundType(SoundType type, bool mute) override;
 	bool isSoundTypeMuted(SoundType type) const override;
 
 	void setChannelVolume(SoundHandle handle, byte volume) override;
-	byte getChannelVolume(SoundHandle handle) override;
+	byte getChannelVolume(SoundHandle handle) const override;
 	void setChannelBalance(SoundHandle handle, int8 balance) override;
-	int8 getChannelBalance(SoundHandle handle) override;
+	int8 getChannelBalance(SoundHandle handle) const override;
 	void setChannelFaderL(SoundHandle handle, uint8 scaleL) override;
-	uint8 getChannelFaderL(SoundHandle handle) override;
+	uint8 getChannelFaderL(SoundHandle handle) const override;
 	void setChannelFaderR(SoundHandle handle, uint8 scaleR) override;
-	uint8 getChannelFaderR(SoundHandle handle) override;
+	uint8 getChannelFaderR(SoundHandle handle) const override;
 	void setChannelRate(SoundHandle handle, uint32 rate) override;
-	uint32 getChannelRate(SoundHandle handle) override;
+	uint32 getChannelRate(SoundHandle handle) const override;
 	void resetChannelRate(SoundHandle handle) override;
 
-	uint32 getSoundElapsedTime(SoundHandle handle) override;
-	Timestamp getElapsedTime(SoundHandle handle) override;
+	uint32 getSoundElapsedTime(SoundHandle handle) const override;
+	Timestamp getElapsedTime(SoundHandle handle) const override;
 
 	void loopChannel(SoundHandle handle) override;
 
-	bool hasActiveChannelOfType(SoundType type) override;
+	bool hasActiveChannelOfType(SoundType type) const override;
 
 	void setVolumeForSoundType(SoundType type, int volume) override;
 	int getVolumeForSoundType(SoundType type) const override;


Commit: acab1a917526161c6bb2780ea62a9289f7c32be7
    https://github.com/scummvm/scummvm/commit/acab1a917526161c6bb2780ea62a9289f7c32be7
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Input stream always works with int16

So make it explicit in code.

Changed paths:
    audio/rate.cpp


diff --git a/audio/rate.cpp b/audio/rate.cpp
index 4638f827270..9ae4d3eb3cf 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -60,10 +60,10 @@ private:
 	 * but only until some point (depends largely on cache size, target
 	 * processor and various other factors), at which it will decrease again.
 	 */
-	st_sample_t _buffer[512];
+	int16 _buffer[512];
 
 	/** Current position inside the buffer */
-	const st_sample_t *_bufferPos;
+	const int16 *_bufferPos;
 
 	/** Size of data currently loaded into the buffer */
 	int _bufferSize;
@@ -75,10 +75,10 @@ private:
 	frac_t _outPosFrac;
 
 	/** Last sample(s) in the input stream (left/right channel) */
-	st_sample_t _inLastL, _inLastR;
-	
+	int16 _inLastL, _inLastR;
+
 	/** Current sample(s) in the input stream (left/right channel) */
-	st_sample_t _inCurL, _inCurR;
+	int16 _inCurL, _inCurR;
 
 	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
 	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
@@ -141,7 +141,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 		}
 
 		// Mix the data into the output buffer
-		st_sample_t inL, inR;
+		int16 inL, inR;
 		inL = *_bufferPos++;
 		inR = (inStereo ? *_bufferPos++ : inL);
 		_bufferSize -= (inStereo ? 2 : 1);
@@ -201,7 +201,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 			}
 		} while (_outPos >= 0);
 
-		st_sample_t inL, inR;
+		int16 inL, inR;
 		inL = *_bufferPos++;
 		inR = (inStereo ? *_bufferPos++ : inL);
 
@@ -269,10 +269,10 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 		// still space in the output buffer.
 		while (_outPosFrac < (frac_t)FRAC_ONE_LOW && outBuffer < outEnd) {
 			// Interpolate
-			st_sample_t inL, inR;
-			inL = (st_sample_t)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
+			int16 inL, inR;
+			inL = (int16)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
 			inR = (inStereo ?
-						(st_sample_t)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
+						(int16)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
 						inL);
 
 			st_sample_t outL, outR;


Commit: d440280f0ef0c5ae13d5cf6d0654ab7601b138e9
    https://github.com/scummvm/scummvm/commit/d440280f0ef0c5ae13d5cf6d0654ab7601b138e9
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
SCI32: Don't use Audio::st_sample_t for AudioStream instances

AudioStream always works with int16 so make it explicit. The only
exception here is Audio32::writeAudioInternal() where the targetBuffer
actually is expected to be of type Audio::st_sample_t but that is just
Audio32 class taking advantage of an existing converter. It could easily
take byte or void pointer as input.

Changed paths:
    engines/sci/sound/audio32.cpp
    engines/sci/sound/audio32.h
    engines/sci/video/robot_decoder.cpp
    engines/sci/video/robot_decoder.h


diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index b81df60a01f..0b21070ec7c 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -232,7 +232,7 @@ Audio32::~Audio32() {
 #pragma mark -
 #pragma mark AudioStream implementation
 
-int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
+int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
 	const int samplePairsToRead = numSamples >> 1;
 	const int samplePairsWritten = converter.convert(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume);
 	return samplePairsWritten << 1;
@@ -289,7 +289,7 @@ bool Audio32::channelShouldMix(const AudioChannel &channel) const {
 // do all audio processing in real time, and we have streams, and we do not need
 // to completely fill the audio buffer, the functionality of all these original
 // functions is combined here and simplified.
-int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) {
+int Audio32::readBuffer(int16 *const buffer, const int numSamples) {
 	Common::StackLock lock(_mutex);
 
 	if (_pausedAtTick != 0 || _numActiveChannels == 0) {
@@ -307,7 +307,7 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples)
 	// The caller of `readBuffer` is a rate converter, which reuses (without
 	// clearing) an intermediate buffer, so we need to zero the intermediate
 	// buffer to prevent mixing into audio data from the last callback.
-	memset(buffer, 0, numSamples * sizeof(Audio::st_sample_t));
+	memset(buffer, 0, numSamples * sizeof(int16));
 
 	// This emulates the attenuated mixing mode of SSCI engine, which reduces
 	// the volume of the target buffer when each new channel is mixed in.
@@ -413,12 +413,12 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples)
 			if (numSamples > (int)_monitoredBuffer.size()) {
 				_monitoredBuffer.resize(numSamples);
 			}
-			memset(_monitoredBuffer.data(), 0, _monitoredBuffer.size() * sizeof(Audio::st_sample_t));
+			memset(_monitoredBuffer.data(), 0, _monitoredBuffer.size() * sizeof(int16));
 			_numMonitoredSamples = writeAudioInternal(*channel.stream, *channel.converter, _monitoredBuffer.data(), numSamples, leftVolume, rightVolume);
 
-			Audio::st_sample_t *sourceBuffer = _monitoredBuffer.data();
-			Audio::st_sample_t *targetBuffer = buffer;
-			const Audio::st_sample_t *const end = _monitoredBuffer.data() + _numMonitoredSamples;
+			int16 *sourceBuffer = _monitoredBuffer.data();
+			int16 *targetBuffer = buffer;
+			const int16 *const end = _monitoredBuffer.data() + _numMonitoredSamples;
 			while (sourceBuffer != end) {
 				Audio::clampedAdd(*targetBuffer++, *sourceBuffer++);
 			}
@@ -1203,11 +1203,11 @@ bool Audio32::hasSignal() const {
 		return false;
 	}
 
-	const Audio::st_sample_t *buffer = _monitoredBuffer.data();
-	const Audio::st_sample_t *const end = _monitoredBuffer.data() + _numMonitoredSamples;
+	const int16 *buffer = _monitoredBuffer.data();
+	const int16 *const end = _monitoredBuffer.data() + _numMonitoredSamples;
 
 	while (buffer != end) {
-		const Audio::st_sample_t sample = *buffer++;
+		const int16 sample = *buffer++;
 		if (sample > 1280 || sample < -1280) {
 			return true;
 		}
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index cfa8691d027..ee095294dea 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -196,7 +196,7 @@ private:
 #pragma mark -
 #pragma mark AudioStream implementation
 public:
-	int readBuffer(Audio::st_sample_t *buffer, const int numSamples) override;
+	int readBuffer(int16 *buffer, const int numSamples) override;
 	bool isStereo() const override { return true; }
 	int getRate() const override { return _mixer->getOutputRate(); }
 	bool endOfData() const override { return _numActiveChannels == 0; }
@@ -219,7 +219,7 @@ private:
 	 * Mixes audio from the given source stream into the target buffer using the
 	 * given rate converter.
 	 */
-	int writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume);
+	int writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume);
 
 #pragma mark -
 #pragma mark Channel management
@@ -627,7 +627,7 @@ private:
 	 * The data buffer holding decompressed audio data for the channel that will
 	 * be monitored for an audio signal.
 	 */
-	Common::Array<Audio::st_sample_t> _monitoredBuffer;
+	Common::Array<int16> _monitoredBuffer;
 
 	/**
 	 * The number of valid audio samples in the signal monitoring buffer.
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index 7db361bd8fa..0da109dc2fa 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -299,7 +299,7 @@ RobotAudioStream::StreamState RobotAudioStream::getStatus() const {
 	return status;
 }
 
-int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples) {
+int RobotAudioStream::readBuffer(int16 *outBuffer, int numSamples) {
 	Common::StackLock lock(_mutex);
 
 	if (_waiting) {
@@ -307,7 +307,7 @@ int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples)
 	}
 
 	assert(!((_writeHeadAbs - _readHeadAbs) & 1));
-	const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(Audio::st_sample_t);
+	const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(int16);
 	numSamples = MIN(numSamples, maxNumSamples);
 
 	if (!numSamples) {
@@ -316,22 +316,22 @@ int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples)
 
 	interpolateMissingSamples(numSamples);
 
-	Audio::st_sample_t *inBuffer = (Audio::st_sample_t *)(_loopBuffer + _readHead);
+	int16 *inBuffer = (int16 *)(_loopBuffer + _readHead);
 
 	assert(!((_loopBufferSize - _readHead) & 1));
-	const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(Audio::st_sample_t);
+	const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(int16);
 
 	int numSamplesToRead = MIN(numSamples, numSamplesToEnd);
 	Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
 
 	if (numSamplesToRead < numSamples) {
-		inBuffer = (Audio::st_sample_t *)_loopBuffer;
+		inBuffer = (int16 *)_loopBuffer;
 		outBuffer += numSamplesToRead;
 		numSamplesToRead = numSamples - numSamplesToRead;
 		Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
 	}
 
-	const int32 numBytes = numSamples * sizeof(Audio::st_sample_t);
+	const int32 numBytes = numSamples * sizeof(int16);
 
 	_readHead += numBytes;
 	if (_readHead > _loopBufferSize) {
diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h
index 226854c54f4..2df4985546c 100644
--- a/engines/sci/video/robot_decoder.h
+++ b/engines/sci/video/robot_decoder.h
@@ -433,7 +433,7 @@ private:
 #pragma mark -
 #pragma mark RobotAudioStream - AudioStream implementation
 public:
-	int readBuffer(Audio::st_sample_t *outBuffer, int numSamples) override;
+	int readBuffer(int16 *outBuffer, int numSamples) override;
 	bool isStereo() const override { return false; };
 	int getRate() const override { return 22050; };
 	bool endOfData() const override {


Commit: a6d828135f33c467e9823d790a5af4148f2c0549
    https://github.com/scummvm/scummvm/commit/a6d828135f33c467e9823d790a5af4148f2c0549
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Add support for 32-bit sample size

- RateConverter now accepts 32-bit input (although still with max 24-bit
  values)

- make clampedAdd suited for 8-24 bit samples

Changed paths:
    audio/mixer.cpp
    audio/mixer.h
    audio/mixer_intern.h
    audio/rate.cpp
    audio/rate.h
    engines/sci/sound/audio32.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 51ea508d56f..174fd8f515d 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -49,12 +49,13 @@ public:
 	 * Mixes the channel's samples into the given buffer.
 	 *
 	 * @param data buffer where to mix the data
-	 * @param len  number of sample *pairs*. So a value of
-	 *             10 means that the buffer contains twice 10 sample, each
-	 *             16 bits, for a total of 40 bytes.
+	 * @param len  number of sample *pairs*. So a value of 10
+	 *             in stereo and 16-bit samples means that the
+	 *             buffer contains twice 10 sample, each 16 bits,
+	 *             for a total of 40 bytes.
 	 * @return number of sample pairs processed (which can still be silence!)
 	 */
-	int mix(int16 *data, uint len);
+	int mix(byte *data, uint len);
 
 	/**
 	 * Queries whether the channel is still playing or not.
@@ -226,8 +227,9 @@ private:
 #pragma mark --- Mixer ---
 #pragma mark -
 
-MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize)
-	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
+MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize, uint outBytesPerSample)
+	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _outBytesPerSample(outBytesPerSample)
+	, _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
 
 	assert(sampleRate > 0);
 
@@ -258,6 +260,10 @@ uint MixerImpl::getOutputBufSize() const {
 	return _outBufSize;
 }
 
+uint MixerImpl::getOutputBytesPerSample() const {
+	return _outBytesPerSample;
+}
+
 void MixerImpl::insertChannel(SoundHandle *handle, Channel *chan) {
 	int index = -1;
 	for (int i = 0; i != NUM_CHANNELS; i++) {
@@ -333,22 +339,16 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 
 	Common::StackLock lock(_mutex);
 
-	int16 *buf = (int16 *)samples;
-
 	// Since the mixer callback has been called, the mixer must be ready...
 	_mixerReady = true;
 
-	//  zero the buf
-	memset(buf, 0, len);
+	// zero the sample buffer
+	memset(samples, 0, len);
 
-	// we store 16-bit samples
-	if (_stereo) {
-		assert(len % 4 == 0);
-		len >>= 2;
-	} else {
-		assert(len % 2 == 0);
-		len >>= 1;
-	}
+	// we store samples of size defined by the backend
+	const uint bytesPerFrame = _outBytesPerSample * (_stereo ? 2 : 1);
+	assert(len % bytesPerFrame == 0);
+	len /= bytesPerFrame;
 
 	// mix all channels
 	int res = 0, tmp;
@@ -358,7 +358,7 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 				delete _channels[i];
 				_channels[i] = nullptr;
 			} else if (!_channels[i]->isPaused()) {
-				tmp = _channels[i]->mix(buf, len);
+				tmp = _channels[i]->mix(samples, len);
 
 				if (tmp > res)
 					res = tmp;
@@ -793,7 +793,7 @@ void Channel::loop() {
 	}
 }
 
-int Channel::mix(int16 *data, uint len) {
+int Channel::mix(byte *data, uint len) {
 	assert(_stream);
 	assert(_converter);
 
@@ -802,7 +802,7 @@ int Channel::mix(int16 *data, uint len) {
 		_samplesConsumed = _samplesDecoded;
 		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
-		res = _converter->convert(*_stream, data, len, _volL, _volR);
+		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR);
 		_samplesDecoded += res;
 	}
 
diff --git a/audio/mixer.h b/audio/mixer.h
index 2c6c35f07b2..7ec1745683f 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -390,6 +390,15 @@ public:
 	 * @return The number of samples processed at each audio callback.
 	 */
 	virtual uint getOutputBufSize() const = 0;
+
+	/**
+	 * Return the output sample size of the system.
+	 *
+	 * The return value is measured in bytes.
+	 *
+	 * @return The output sample size is bytes.
+	 */
+	virtual uint getOutputBytesPerSample() const = 0;
 };
 
 /** @} */
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index c46a43825ac..8201736692a 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -66,6 +66,7 @@ private:
 	const uint _sampleRate;
 	const bool _stereo;
 	uint _outBufSize;
+	const uint _outBytesPerSample;
 	bool _mixerReady;
 	uint32 _handleSeed;
 
@@ -82,7 +83,7 @@ private:
 
 public:
 
-	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0);
+	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0, uint outBytesPerSample = 2);
 	~MixerImpl();
 
 	bool isReady() const override { Common::StackLock lock(_mutex); return _mixerReady; }
@@ -139,6 +140,7 @@ public:
 	uint getOutputRate() const override;
 	bool getOutputStereo() const override;
 	uint getOutputBufSize() const override;
+	uint getOutputBytesPerSample() const override;
 
 protected:
 	void insertChannel(SoundHandle *handle, Channel *chan);
diff --git a/audio/rate.cpp b/audio/rate.cpp
index 9ae4d3eb3cf..80d876cf5d4 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -80,10 +80,16 @@ private:
 	/** Current sample(s) in the input stream (left/right channel) */
 	int16 _inCurL, _inCurR;
 
+	template<typename st_sample_t>
 	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
+	template<typename st_sample_t>
 	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
+	template<typename st_sample_t>
 	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
 
+	template<typename st_sample_t>
+	int convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR);
+
 	// keep a single printConvertType shared across all RateConverter_Impl specializations.
 	// PrintContext must be trivially destructible: it lives in a function-scope static and
 	// is torn down after the OSystem (and its memory pool that backs Common::String) is gone.
@@ -110,7 +116,7 @@ public:
 	RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate);
 	virtual ~RateConverter_Impl() {}
 
-	int convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
+	int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
 
 	void setInputRate(st_rate_t inputRate) override { _inRate = inputRate; }
 	void setOutputRate(st_rate_t outputRate) override { _outRate = outputRate; }
@@ -122,6 +128,7 @@ public:
 };
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
+template<typename st_sample_t>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -170,6 +177,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
+template<typename st_sample_t>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -231,6 +239,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
+template<typename st_sample_t>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -315,20 +324,30 @@ RateConverter_Impl<inStereo, outStereo, reverseStereo>::RateConverter_Impl(st_ra
 	_bufferPos(nullptr) {}
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+template<typename st_sample_t>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	assert(input.isStereo() == inStereo);
 
 	if (_inRate == _outRate) {
-		return copyConvert(input, outBuffer, numSamples, volL, volR);
+		return copyConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	} else {
 		if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
-			return simpleConvert(input, outBuffer, numSamples, volL, volR);
+			return simpleConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		} else {
-			return interpolateConvert(input, outBuffer, numSamples, volL, volR);
+			return interpolateConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		}
 	}
 }
 
+template<bool inStereo, bool outStereo, bool reverseStereo>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+	if (outBytesPerSample == sizeof(int32)) {
+		return convertForType<int32>(input, outBuffer, numSamples, volL, volR);
+	} else {
+		return convertForType<int16>(input, outBuffer, numSamples, volL, volR);
+	}
+}
+
 RateConverter *makeRateConverter(st_rate_t inRate, st_rate_t outRate, bool inStereo, bool outStereo, bool reverseStereo) {
 	if (inStereo) {
 		if (outStereo) {
diff --git a/audio/rate.h b/audio/rate.h
index 4fc0213038d..1a61d07c854 100644
--- a/audio/rate.h
+++ b/audio/rate.h
@@ -35,34 +35,34 @@ namespace Audio {
 
 class AudioStream;
 
-typedef int16 st_sample_t;
 typedef uint16 st_volume_t;
 typedef uint32 st_size_t;
 typedef uint32 st_rate_t;
 
-/* Minimum and maximum values a sample can hold. */
-enum {
-	ST_SAMPLE_MAX = 0x7fffL,
-	ST_SAMPLE_MIN = (-ST_SAMPLE_MAX - 1L)
-};
+// This assumes that 'a' and 'b' are 24-bit samples at most
+template <typename T>
+static inline void clampedAdd(T& a, int b) {
+	constexpr unsigned long long highestBit = 1ULL << (sizeof(T) * 8 - 1);
+	constexpr int maxVal = (int)(highestBit - 1);
+	constexpr int minVal = ~maxVal;
 
-static inline void clampedAdd(int16& a, int b) {
 	int val;
 #ifdef OUTPUT_UNSIGNED_AUDIO
-	val = (a ^ 0x8000) + b;
+	constexpr int signMask = (int)highestBit;
+	val = ((int)a ^ signMask) + b;
 #else
-	val = a + b;
+	val = (int)a + b;
 #endif
 
-	if (val > ST_SAMPLE_MAX)
-		val = ST_SAMPLE_MAX;
-	else if (val < ST_SAMPLE_MIN)
-		val = ST_SAMPLE_MIN;
+	if (val > maxVal)
+		val = maxVal;
+	else if (val < minVal)
+		val = minVal;
 
 #ifdef OUTPUT_UNSIGNED_AUDIO
-	a = ((int16)val) ^ 0x8000;
+	a = (T)(val ^ signMask);
 #else
-	a = val;
+	a = (T)val;
 #endif
 }
 
@@ -79,16 +79,17 @@ public:
 
 	/**
 	 * Convert the provided AudioStream to the target sample rate.
-	 * 
-	 * @param input			The AudioStream to read data from.
-	 * @param outBuffer		The buffer that the resampled audio will be written to. Must have size of at least @p numSamples.
-	 * @param numSamples	The desired number of samples to be written into the buffer.
-	 * @param vol_l			Volume for left channel.
-	 * @param vol_r			Volume for right channel.
-	 * 
+	 *
+	 * @param input				The AudioStream to read data from.
+	 * @param outBuffer			The buffer that the resampled audio will be written to. Must have size of at least @p numSamples.
+	 * @param outBytesPerSample	The size of each output sample in bytes (e.g. sizeof(int16) or sizeof(int32)).
+	 * @param numSamples		The desired number of samples to be written into the buffer.
+	 * @param vol_l				Volume for left channel.
+	 * @param vol_r				Volume for right channel.
+	 *
 	 * @return Number of sample pairs written into the buffer.
 	 */
-	virtual int convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) = 0;
+	virtual int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) = 0;
 
 	virtual void setInputRate(st_rate_t inputRate) = 0;
 	virtual void setOutputRate(st_rate_t outputRate) = 0;
@@ -98,7 +99,7 @@ public:
 
 	/**
 	 * Does the internal buffer still have some leftover data?
-	 * 
+	 *
 	 * @return True if we need to drain, false otherwise
 	 */
 	virtual bool needsDraining() const = 0;
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 0b21070ec7c..ce160284650 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -234,7 +234,7 @@ Audio32::~Audio32() {
 
 int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
 	const int samplePairsToRead = numSamples >> 1;
-	const int samplePairsWritten = converter.convert(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume);
+	const int samplePairsWritten = converter.convert(sourceStream, (byte *)targetBuffer, sizeof(int16), samplePairsToRead, leftVolume, rightVolume);
 	return samplePairsWritten << 1;
 }
 


Commit: b9cca6360ead999587371787f87276247dc583fb
    https://github.com/scummvm/scummvm/commit/b9cca6360ead999587371787f87276247dc583fb
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: RateConverter now supports unclamped mixing

Changed paths:
    audio/mixer.cpp
    audio/rate.cpp
    audio/rate.h
    engines/sci/sound/audio32.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 174fd8f515d..015812d90ce 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -802,7 +802,7 @@ int Channel::mix(byte *data, uint len) {
 		_samplesConsumed = _samplesDecoded;
 		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
-		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR);
+		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR, MIX_CLAMPED_ADD);
 		_samplesDecoded += res;
 	}
 
diff --git a/audio/rate.cpp b/audio/rate.cpp
index 80d876cf5d4..b115f63c1e9 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -80,14 +80,14 @@ private:
 	/** Current sample(s) in the input stream (left/right channel) */
 	int16 _inCurL, _inCurR;
 
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
 
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR);
 
 	// keep a single printConvertType shared across all RateConverter_Impl specializations.
@@ -116,7 +116,7 @@ public:
 	RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate);
 	virtual ~RateConverter_Impl() {}
 
-	int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
+	int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r, MixMode mixMode) override;
 
 	void setInputRate(st_rate_t inputRate) override { _inRate = inputRate; }
 	void setOutputRate(st_rate_t outputRate) override { _outRate = outputRate; }
@@ -128,7 +128,7 @@ public:
 };
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -159,15 +159,15 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 
 		if (outStereo) {
 			// Output left channel
-			clampedAdd(outBuffer[reverseStereo    ], outL);
+			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
 
 			// Output right channel
-			clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
 
 			outBuffer += 2;
 		} else {
 			// Output mono channel
-			clampedAdd(outBuffer[0], (outL + outR) / 2);
+			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
 
 			outBuffer += 1;
 		}
@@ -177,7 +177,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -222,15 +222,15 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 
 		if (outStereo) {
 			// output left channel
-			clampedAdd(outBuffer[reverseStereo    ], outL);
+			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
 
 			// output right channel
-			clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
 
 			outBuffer += 2;
 		} else {
 			// output mono channel
-			clampedAdd(outBuffer[0], (outL + outR) / 2);
+			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
 
 			outBuffer += 1;
 		}
@@ -239,7 +239,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -290,15 +290,15 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 
 			if (outStereo) {
 				// Output left channel
-				clampedAdd(outBuffer[reverseStereo    ], outL);
+				processSample<mixMode>(outBuffer[reverseStereo    ], outL);
 
 				// Output right channel
-				clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+				processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
 
 				outBuffer += 2;
 			} else {
 				// Output mono channel
-				clampedAdd(outBuffer[0], (outL + outR) / 2);
+				processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
 
 				outBuffer += 1;
 			}
@@ -324,27 +324,33 @@ RateConverter_Impl<inStereo, outStereo, reverseStereo>::RateConverter_Impl(st_ra
 	_bufferPos(nullptr) {}
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	assert(input.isStereo() == inStereo);
 
 	if (_inRate == _outRate) {
-		return copyConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		return copyConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	} else {
 		if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
-			return simpleConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+			return simpleConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		} else {
-			return interpolateConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+			return interpolateConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		}
 	}
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t volL, st_volume_t volR, MixMode mixMode) {
 	if (outBytesPerSample == sizeof(int32)) {
-		return convertForType<int32>(input, outBuffer, numSamples, volL, volR);
+		if (mixMode == MIX_ADD)
+			return convertForType<int32, MIX_ADD>(input, outBuffer, numSamples, volL, volR);
+		else
+			return convertForType<int32, MIX_CLAMPED_ADD>(input, outBuffer, numSamples, volL, volR);
 	} else {
-		return convertForType<int16>(input, outBuffer, numSamples, volL, volR);
+		if (mixMode == MIX_ADD)
+			return convertForType<int16, MIX_ADD>(input, outBuffer, numSamples, volL, volR);
+		else
+			return convertForType<int16, MIX_CLAMPED_ADD>(input, outBuffer, numSamples, volL, volR);
 	}
 }
 
diff --git a/audio/rate.h b/audio/rate.h
index 1a61d07c854..cf59a8f5f0c 100644
--- a/audio/rate.h
+++ b/audio/rate.h
@@ -39,31 +39,49 @@ typedef uint16 st_volume_t;
 typedef uint32 st_size_t;
 typedef uint32 st_rate_t;
 
+enum MixMode {
+	MIX_ADD,
+	MIX_CLAMPED_ADD
+};
+
 // This assumes that 'a' and 'b' are 24-bit samples at most
-template <typename T>
-static inline void clampedAdd(T& a, int b) {
+template <MixMode Mode, typename T>
+static inline void processSample(T& a, int b) {
 	constexpr unsigned long long highestBit = 1ULL << (sizeof(T) * 8 - 1);
 	constexpr int maxVal = (int)(highestBit - 1);
 	constexpr int minVal = ~maxVal;
 
-	int val;
 #ifdef OUTPUT_UNSIGNED_AUDIO
 	constexpr int signMask = (int)highestBit;
-	val = ((int)a ^ signMask) + b;
+#endif
+
+	if (Mode == MIX_ADD) {
+		a += b;
+	} else if (Mode == MIX_CLAMPED_ADD) {
+		int val;
+#ifdef OUTPUT_UNSIGNED_AUDIO
+		val = ((int)a ^ signMask) + b;
 #else
-	val = (int)a + b;
+		val = (int)a + b;
 #endif
 
-	if (val > maxVal)
-		val = maxVal;
-	else if (val < minVal)
-		val = minVal;
+		if (val > maxVal)
+			val = maxVal;
+		else if (val < minVal)
+			val = minVal;
 
 #ifdef OUTPUT_UNSIGNED_AUDIO
-	a = (T)(val ^ signMask);
+		a = (T)(val ^ signMask);
 #else
-	a = (T)val;
+		a = (T)val;
 #endif
+	}
+}
+
+// Fallback for engine code
+template <typename T>
+static inline void clampedAdd(T& a, int b) {
+	processSample<MIX_CLAMPED_ADD>(a, b);
 }
 
 /**
@@ -86,10 +104,11 @@ public:
 	 * @param numSamples		The desired number of samples to be written into the buffer.
 	 * @param vol_l				Volume for left channel.
 	 * @param vol_r				Volume for right channel.
+	 * @param mixMode			Sample mix mode for outBuffer.
 	 *
 	 * @return Number of sample pairs written into the buffer.
 	 */
-	virtual int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) = 0;
+	virtual int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r, MixMode mixMode) = 0;
 
 	virtual void setInputRate(st_rate_t inputRate) = 0;
 	virtual void setOutputRate(st_rate_t outputRate) = 0;
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index ce160284650..995d1dbf777 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -234,7 +234,7 @@ Audio32::~Audio32() {
 
 int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
 	const int samplePairsToRead = numSamples >> 1;
-	const int samplePairsWritten = converter.convert(sourceStream, (byte *)targetBuffer, sizeof(int16), samplePairsToRead, leftVolume, rightVolume);
+	const int samplePairsWritten = converter.convert(sourceStream, (byte *)targetBuffer, sizeof(int16), samplePairsToRead, leftVolume, rightVolume, Audio::MIX_CLAMPED_ADD);
 	return samplePairsWritten << 1;
 }
 


Commit: 424681dca2ac20e89bb19bfa2c86818311e248ed
    https://github.com/scummvm/scummvm/commit/424681dca2ac20e89bb19bfa2c86818311e248ed
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Make sample clamping optional

Changed paths:
    audio/mixer.cpp
    audio/mixer.h
    audio/mixer_intern.h


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 015812d90ce..0f6ab5298e9 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -227,8 +227,8 @@ private:
 #pragma mark --- Mixer ---
 #pragma mark -
 
-MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize, uint outBytesPerSample)
-	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _outBytesPerSample(outBytesPerSample)
+MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize, uint outBytesPerSample, bool clamp)
+	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _outBytesPerSample(outBytesPerSample), _clamp(clamp)
 	, _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
 
 	assert(sampleRate > 0);
@@ -264,6 +264,10 @@ uint MixerImpl::getOutputBytesPerSample() const {
 	return _outBytesPerSample;
 }
 
+bool MixerImpl::getClamping() const {
+	return _clamp;
+}
+
 void MixerImpl::insertChannel(SoundHandle *handle, Channel *chan) {
 	int index = -1;
 	for (int i = 0; i != NUM_CHANNELS; i++) {
@@ -802,7 +806,14 @@ int Channel::mix(byte *data, uint len) {
 		_samplesConsumed = _samplesDecoded;
 		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
-		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR, MIX_CLAMPED_ADD);
+		res = _converter->convert(
+			*_stream,
+			data,
+			_mixer->getOutputBytesPerSample(),
+			len,
+			_volL,
+			_volR,
+			_mixer->getClamping() ? MIX_CLAMPED_ADD : MIX_ADD);
 		_samplesDecoded += res;
 	}
 
diff --git a/audio/mixer.h b/audio/mixer.h
index 7ec1745683f..9c15a99f831 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -399,6 +399,13 @@ public:
 	 * @return The output sample size is bytes.
 	 */
 	virtual uint getOutputBytesPerSample() const = 0;
+
+	/**
+	 * Return whether the mixer is expected to clamp outgoing samples.
+	 *
+	 * @return true if output is clamped, false if not.
+	 */
+	virtual bool getClamping() const = 0;
 };
 
 /** @} */
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index 8201736692a..d6ff2de96c0 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -67,6 +67,7 @@ private:
 	const bool _stereo;
 	uint _outBufSize;
 	const uint _outBytesPerSample;
+	const bool _clamp;
 	bool _mixerReady;
 	uint32 _handleSeed;
 
@@ -83,7 +84,7 @@ private:
 
 public:
 
-	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0, uint outBytesPerSample = 2);
+	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0, uint outBytesPerSample = 2, bool clamp = true);
 	~MixerImpl();
 
 	bool isReady() const override { Common::StackLock lock(_mutex); return _mixerReady; }
@@ -141,6 +142,7 @@ public:
 	bool getOutputStereo() const override;
 	uint getOutputBufSize() const override;
 	uint getOutputBytesPerSample() const override;
+	bool getClamping() const override;
 
 protected:
 	void insertChannel(SoundHandle *handle, Channel *chan);


Commit: 995c360cfc03ce037959ddaaab00c5b5b224f630
    https://github.com/scummvm/scummvm/commit/995c360cfc03ce037959ddaaab00c5b5b224f630
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Don't clamp muted channels

Changed paths:
    audio/mixer.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 0f6ab5298e9..cbb47186ae6 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -143,6 +143,11 @@ public:
 	 */
 	uint8 getFaderR() const;
 
+	/**
+	 * Queries whether the channel is silent, i.e. will produce no audio output.
+	 */
+	bool isSilent() const { return _volL == 0 && _volR == 0; }
+
 	/**
 	 * Set the channel's sample rate.
 	 *
@@ -346,15 +351,13 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 	// Since the mixer callback has been called, the mixer must be ready...
 	_mixerReady = true;
 
-	// zero the sample buffer
-	memset(samples, 0, len);
-
 	// we store samples of size defined by the backend
 	const uint bytesPerFrame = _outBytesPerSample * (_stereo ? 2 : 1);
 	assert(len % bytesPerFrame == 0);
-	len /= bytesPerFrame;
+	const uint numFrames = len / bytesPerFrame;
 
-	// mix all channels
+	// mix all channels, zeroing the buffer lazily on first non-silent channel
+	bool zeroed = false;
 	int res = 0, tmp;
 	for (int i = 0; i != NUM_CHANNELS; i++)
 		if (_channels[i]) {
@@ -362,14 +365,19 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 				delete _channels[i];
 				_channels[i] = nullptr;
 			} else if (!_channels[i]->isPaused()) {
-				tmp = _channels[i]->mix(samples, len);
+				if (!_channels[i]->isSilent() && !zeroed) {
+					memset(samples, 0, len);
+					zeroed = true;
+				}
+				tmp = _channels[i]->mix(samples, numFrames);
 
 				if (tmp > res)
 					res = tmp;
 			}
 		}
 
-	return res;
+	// optimisation: let the caller know that there's nothing to clamp
+	return (!_clamp && !zeroed) ? 0 : res;
 }
 
 void MixerImpl::stopAll() {


Commit: 85befa9c13c0cf953e63f79e67b605a00d5e8fa4
    https://github.com/scummvm/scummvm/commit/85befa9c13c0cf953e63f79e67b605a00d5e8fa4
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Template-optimize convert methods

Also, introduce upscaleConvert for e.g. 11025, 22050 -> 44100
conversions.

Changed paths:
    audio/rate.cpp


diff --git a/audio/rate.cpp b/audio/rate.cpp
index b115f63c1e9..7ebf1e88af9 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -80,12 +80,17 @@ private:
 	/** Current sample(s) in the input stream (left/right channel) */
 	int16 _inCurL, _inCurR;
 
-	template<typename st_sample_t, MixMode mixMode>
-	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t, MixMode mixMode>
-	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t, MixMode mixMode>
-	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
+	template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+	int commonConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val, int outputSamples);
+
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int downsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int upsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
 
 	template<typename st_sample_t, MixMode mixMode>
 	int convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR);
@@ -128,14 +133,10 @@ public:
 };
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t, MixMode mixMode>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
-	PRINT_OUTPUT_RATE;
-
-	st_sample_t *outStart, *outEnd;
-
-	outStart = outBuffer;
-	outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::commonConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val, int outputSamples) {
+	const st_sample_t *outStart = outBuffer;
+	const st_sample_t *outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
 
 	while (outBuffer < outEnd) {
 		// Check if we have to refill the buffer
@@ -147,51 +148,112 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 				return (outBuffer - outStart) / (outStereo ? 2 : 1);
 		}
 
-		// Mix the data into the output buffer
-		int16 inL, inR;
-		inL = *_bufferPos++;
-		inR = (inStereo ? *_bufferPos++ : inL);
-		_bufferSize -= (inStereo ? 2 : 1);
-
-		st_sample_t outL, outR;
-		outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
-		outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
-
-		if (outStereo) {
-			// Output left channel
-			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
-
-			// Output right channel
-			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
-
-			outBuffer += 2;
+		// Process as many samples as we can from the current buffer
+		const int count = MIN(
+			_bufferSize / (inStereo ? 2 : 1),
+			(int)(outEnd - outBuffer) / (outStereo ? 2 : 1) / outputSamples);
+		_bufferSize -= count * (inStereo ? 2 : 1);
+
+		if (volL | volR) {
+			// Mix the data into the output buffer
+			for (int i = 0; i < count; ++i) {
+				int16 inL, inR;
+
+				if (inStereo) {
+					if (volL != 0)
+						inL = *_bufferPos++;
+					else
+						_bufferPos++;
+
+					if (volR != 0)
+						inR = *_bufferPos++;
+					else
+						_bufferPos++;
+				} else {
+					if (volL != 0) {
+						inL = *_bufferPos++;
+						if (volR != 0)
+							inR = inL;
+					} else {
+						inR = *_bufferPos++;
+					}
+				}
+
+				st_sample_t outL, outR;
+
+				if (volL != 0) {
+					if (volL != Audio::Mixer::kMaxMixerVolume)
+						outL = (inL * (int)volL_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outL = inL;
+				}
+
+				if (volR != 0) {
+					if (volR != Audio::Mixer::kMaxMixerVolume)
+						outR = (inR * (int)volR_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outR = inR;
+				}
+
+				// TODO: could be unrolled
+				for (int j = 0; j < outputSamples; ++j) {
+					if (outStereo) {
+						// Output left channel
+						if (volL != 0)
+							processSample<mixMode>(outBuffer[reverseStereo    ], outL);
+
+						// Output right channel
+						if (volR != 0)
+							processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
+					} else {
+						// Output mono channel
+						st_sample_t monoOut;
+						if (volL != 0 && volR != 0)
+							monoOut = (outL + outR) / 2;
+						else if (volL != 0)
+							monoOut = outL / 2;
+						else if (volR != 0)
+							monoOut = outR / 2;
+						processSample<mixMode>(outBuffer[0], monoOut);
+					}
+					outBuffer += (outStereo ? 2 : 1);
+				}
+			}
 		} else {
-			// Output mono channel
-			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
-
-			outBuffer += 1;
+			_bufferPos += count * (inStereo ? 2 : 1);
+			outBuffer += count * outputSamples * (outStereo ? 2 : 1);
 		}
 	}
-
 	return (outBuffer - outStart) / (outStereo ? 2 : 1);
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t, MixMode mixMode>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
 	PRINT_OUTPUT_RATE;
 
-	// How much to increment _outPos by
-	frac_t outPos_inc = _inRate / _outRate;
+	return commonConvert<volL, volR, st_sample_t, mixMode>(input, outBuffer, numSamples, volL_val, volR_val, 1);
+}
+
+template<bool inStereo, bool outStereo, bool reverseStereo>
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::downsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
+	PRINT_OUTPUT_RATE;
 
-	st_sample_t *outStart, *outEnd;
+	// How much to increment _outPos by
+	const frac_t outPos_inc = _inRate / _outRate;
 
-	outStart = outBuffer;
-	outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
+	const st_sample_t *outStart = outBuffer;
+	const st_sample_t *outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
 
 	while (outBuffer < outEnd) {
 		// Read enough input samples so that _outPos >= 0
-		do {
+		while (_outPos >= 0) {
+			const int skip = MIN((int)_outPos + 1, _bufferSize / (inStereo ? 2 : 1));
+			_bufferPos += skip * (inStereo ? 2 : 1);
+			_bufferSize -= skip * (inStereo ? 2 : 1);
+			_outPos -= skip;
+
 			// Check if we have to refill the buffer
 			if (_bufferSize == 0) {
 				_bufferPos = _buffer;
@@ -200,55 +262,106 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 				if (_bufferSize <= 0)
 					return (outBuffer - outStart) / (outStereo ? 2 : 1);
 			}
+		}
 
-			_bufferSize -= (inStereo ? 2 : 1);
-			_outPos--;
-
-			if (_outPos >= 0) {
-				_bufferPos += (inStereo ? 2 : 1);
+		// Process as many samples as we can from the current buffer
+		const int count = MIN(
+			_bufferSize / (inStereo ? 2 : 1) / outPos_inc,
+			(int)(outEnd - outBuffer) / (outStereo ? 2 : 1));
+		_bufferSize -= count * outPos_inc * (inStereo ? 2 : 1);
+		_outPos = outPos_inc - 1;
+
+		// Frame stride remaining after reading one frame
+		const int stride = (outPos_inc - 1) * (inStereo ? 2 : 1);
+
+		if (volL | volR) {
+			for (int i = 0; i < count; ++i) {
+				int16 inL, inR;
+
+				if (inStereo) {
+					if (volL != 0)
+						inL = *_bufferPos++;
+					else
+						_bufferPos++;
+
+					if (volR != 0)
+						inR = *_bufferPos++;
+					else
+						_bufferPos++;
+				} else {
+					if (volL != 0) {
+						inL = *_bufferPos++;
+						if (volR != 0)
+							inR = inL;
+					} else {
+						inR = *_bufferPos++;
+					}
+				}
+
+				_bufferPos += stride;
+
+				st_sample_t outL, outR;
+				if (volL != 0) {
+					if (volL != Audio::Mixer::kMaxMixerVolume)
+						outL = (inL * (int)volL_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outL = inL;
+				}
+				if (volR != 0) {
+					if (volR != Audio::Mixer::kMaxMixerVolume)
+						outR = (inR * (int)volR_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outR = inR;
+				}
+
+				if (outStereo) {
+					// Output left channel
+					if (volL != 0)
+						processSample<mixMode>(outBuffer[reverseStereo    ], outL);
+
+					// Output right channel
+					if (volR != 0)
+						processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
+				} else {
+					// Output mono channel
+					st_sample_t monoOut;
+					if (volL != 0 && volR != 0)
+						monoOut = (outL + outR) / 2;
+					else if (volL != 0)
+						monoOut = outL / 2;
+					else if (volR != 0)
+						monoOut = outR / 2;
+					processSample<mixMode>(outBuffer[0], monoOut);
+				}
+
+				outBuffer += (outStereo ? 2 : 1);
 			}
-		} while (_outPos >= 0);
-
-		int16 inL, inR;
-		inL = *_bufferPos++;
-		inR = (inStereo ? *_bufferPos++ : inL);
-
-		// Increment output position
-		_outPos += outPos_inc;
-
-		st_sample_t outL, outR;
-		outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
-		outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
-
-		if (outStereo) {
-			// output left channel
-			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
-
-			// output right channel
-			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
-
-			outBuffer += 2;
 		} else {
-			// output mono channel
-			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
-
-			outBuffer += 1;
+			_bufferPos += count * outPos_inc * (inStereo ? 2 : 1);
+			outBuffer += count * (outStereo ? 2 : 1);
 		}
 	}
 	return (outBuffer - outStart) / (outStereo ? 2 : 1);
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t, MixMode mixMode>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::upsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
+	PRINT_OUTPUT_RATE;
+
+	return commonConvert<volL, volR, st_sample_t, mixMode>(input, outBuffer, numSamples, volL_val, volR_val, _outRate / _inRate);
+}
+
+template<bool inStereo, bool outStereo, bool reverseStereo>
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
 	PRINT_OUTPUT_RATE;
 
 	// How much to increment _outPosFrac by
-	frac_t outPos_inc = (_inRate << FRAC_BITS_LOW) / _outRate;
+	const frac_t outPos_inc = (_inRate << FRAC_BITS_LOW) / _outRate;
 
-	st_sample_t *outStart, *outEnd;
-	outStart = outBuffer;
-	outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
+	const st_sample_t *outStart = outBuffer;
+	const st_sample_t *outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
 
 	while (outBuffer < outEnd) {
 		// Read enough input samples so that _outPosFrac < 0
@@ -263,12 +376,21 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 			}
 
 			_bufferSize -= (inStereo ? 2 : 1);
-			_inLastL = _inCurL;
-			_inCurL = *_bufferPos++;
+
+			if (volL != 0 || (!inStereo && volR != 0)) {
+				_inLastL = _inCurL;
+				_inCurL = *_bufferPos++;
+			} else {
+				_bufferPos++;
+			}
 
 			if (inStereo) {
-				_inLastR = _inCurR;
-				_inCurR = *_bufferPos++;
+				if (volR != 0) {
+					_inLastR = _inCurR;
+					_inCurR = *_bufferPos++;
+				} else {
+					_bufferPos++;
+				}
 			}
 
 			_outPosFrac -= FRAC_ONE_LOW;
@@ -277,32 +399,58 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 		// Loop as long as the _outPos trails behind, and as long as there is
 		// still space in the output buffer.
 		while (_outPosFrac < (frac_t)FRAC_ONE_LOW && outBuffer < outEnd) {
-			// Interpolate
-			int16 inL, inR;
-			inL = (int16)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
-			inR = (inStereo ?
-						(int16)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
-						inL);
+			if (volL | volR) {
+				// Interpolate
+				int16 inL, inR;
 
-			st_sample_t outL, outR;
-			outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
-			outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
+				if (volL != 0 || (!inStereo && volR != 0)) {
+					inL = (int16)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
+				}
 
-			if (outStereo) {
-				// Output left channel
-				processSample<mixMode>(outBuffer[reverseStereo    ], outL);
-
-				// Output right channel
-				processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
-
-				outBuffer += 2;
-			} else {
-				// Output mono channel
-				processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
-
-				outBuffer += 1;
+				if (volR != 0) {
+					inR = (inStereo ?
+						(int16)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
+						inL);
+				}
+
+				st_sample_t outL, outR;
+				if (volL != 0) {
+					if (volL != Audio::Mixer::kMaxMixerVolume)
+						outL = (inL * (int)volL_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outL = inL;
+				}
+
+				if (volR != 0) {
+					if (volR != Audio::Mixer::kMaxMixerVolume)
+						outR = (inR * (int)volR_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outR = inR;
+				}
+
+				if (outStereo) {
+					// Output left channel
+					if (volL != 0)
+						processSample<mixMode>(outBuffer[reverseStereo    ], outL);
+
+					// Output right channel
+					if (volR != 0)
+						processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
+				} else {
+					// Output mono channel
+					st_sample_t monoOut;
+					if (volL != 0 && volR != 0)
+						monoOut = (outL + outR) / 2;
+					else if (volL != 0)
+						monoOut = outL / 2;
+					else
+						monoOut = outR / 2;
+					processSample<mixMode>(outBuffer[0], monoOut);
+				}
 			}
 
+			outBuffer += (outStereo ? 2 : 1);
+
 			// Increment output position
 			_outPosFrac += outPos_inc;
 		}
@@ -328,14 +476,52 @@ template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	assert(input.isStereo() == inStereo);
 
+	constexpr auto kMax = Audio::Mixer::kMaxMixerVolume;
+
 	if (_inRate == _outRate) {
-		return copyConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		if (volL == 0 && volR == 0)
+			return copyConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return copyConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return copyConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return copyConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return copyConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+	} else if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
+		if (volL == 0 && volR == 0)
+			return downsampleConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return downsampleConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return downsampleConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return downsampleConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return downsampleConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+	} else if ((_outRate % _inRate) == 0) {
+		if (volL == 0 && volR == 0)
+			return upsampleConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return upsampleConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return upsampleConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return upsampleConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return upsampleConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	} else {
-		if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
-			return simpleConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
-		} else {
-			return interpolateConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
-		}
+		if (volL == 0 && volR == 0)
+			return interpolateConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return interpolateConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return interpolateConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return interpolateConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return interpolateConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	}
 }
 




More information about the Scummvm-git-logs mailing list