[Scummvm-git-logs] scummvm master -> 0f045a4ce4dbfe4e6a22c28a75127ae8af8e1b4c
sev-
noreply at scummvm.org
Sun May 14 20:05:00 UTC 2023
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
5ca776639f AUDIO: Allow adjusting of rate in RateConverter
0f045a4ce4 AUDIO: Add rate adjustment functionality to Mixer
Commit: 5ca776639f0ed18cc250b14edd5bf5ee95840bb0
https://github.com/scummvm/scummvm/commit/5ca776639f0ed18cc250b14edd5bf5ee95840bb0
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-14T22:04:56+02:00
Commit Message:
AUDIO: Allow adjusting of rate in RateConverter
The RateConverter class has been modified to allow for
variable input and output rates. The optimized code paths
for copy/simple conversions are retained, but have been moved
inside separate functions instead of subclasses. The templatization
of the stereo parameters has been maintained, and is implemented
via the newly-added RateConverter_Impl template class.
Internal variables have been renamed to be more readable. The
flow() function has been renamed to convert(). The drain()
function has been removed, since it was never implemented
or used anywhere.
Changed paths:
audio/mixer.cpp
audio/rate.cpp
audio/rate.h
engines/sci/sound/audio32.cpp
engines/sword1/music.cpp
diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 7672988cd9b..4a663a2d8ca 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -652,7 +652,7 @@ int Channel::mix(int16 *data, uint len) {
_samplesConsumed = _samplesDecoded;
_mixerTimeStamp = g_system->getMillis(true);
_pauseTime = 0;
- res = _converter->flow(*_stream, data, len, _volL, _volR);
+ res = _converter->convert(*_stream, data, len, _volL, _volR);
_samplesDecoded += res;
}
diff --git a/audio/rate.cpp b/audio/rate.cpp
index d85c278d796..6c38fc74ab0 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -30,21 +30,10 @@
#include "audio/audiostream.h"
#include "audio/rate.h"
#include "audio/mixer.h"
-#include "common/frac.h"
-#include "common/textconsole.h"
#include "common/util.h"
namespace Audio {
-
-/**
- * The size of the intermediate input cache. Bigger values may increase
- * performance, but only until some point (depends largely on cache size,
- * target processor and various other factors), at which it will decrease
- * again.
- */
-#define INTERMEDIATE_BUFFER_SIZE 512
-
/**
* The default fractional type in frac.h (with 16 fractional bits) limits
* the rate conversion code to 65536Hz audio: we need to able to handle
@@ -56,352 +45,270 @@ enum {
FRAC_HALF_LOW = (1L << (FRAC_BITS_LOW-1))
};
-/**
- * Audio rate converter based on simple resampling. Used when no
- * interpolation is required.
- *
- * Limited to sampling frequency <= 65535 Hz.
- */
template<bool inStereo, bool outStereo, bool reverseStereo>
-class SimpleRateConverter : public RateConverter {
-protected:
- st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
- const st_sample_t *inPtr;
- int inLen;
+class RateConverter_Impl : public RateConverter {
+private:
+ /** Input and output rates */
+ st_rate_t _inRate, _outRate;
- /** position of how far output is ahead of input */
- /** Holds what would have been opos-ipos */
- long opos;
+ /**
+ * The intermediate input cache. Bigger values may increase performance,
+ * 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];
- /** fractional position increment in the output stream */
- long opos_inc;
+ /** Current position inside the buffer */
+ const st_sample_t *_bufferPos;
-public:
- SimpleRateConverter(st_rate_t inrate, st_rate_t outrate);
- int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) override;
- int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) override {
- return ST_SUCCESS;
- }
-};
+ /** Size of data currently loaded into the buffer */
+ int _bufferSize;
+ /** How far output is ahead of input when doing simple conversion */
+ frac_t _outPos;
-/*
- * Prepare processing.
- */
-template<bool inStereo, bool outStereo, bool reverseStereo>
-SimpleRateConverter<inStereo, outStereo, reverseStereo>::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");
- }
+ /** Fractional position of the output stream in input stream unit */
+ frac_t _outPosFrac;
- if (inrate >= 65536 || outrate >= 65536) {
- error("rate effect can only handle rates < 65536");
- }
+ /** Last sample(s) in the input stream (left/right channel) */
+ st_sample_t _inLastL, _inLastR;
+
+ /** Current sample(s) in the input stream (left/right channel) */
+ st_sample_t _inCurL, _inCurR;
- opos = 1;
+ 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);
+ int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
- /* increment */
- opos_inc = inrate / outrate;
+public:
+ RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate);
+ virtual ~RateConverter_Impl() {}
- inLen = 0;
-}
+ int convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
-/*
- * Processed signed long samples from ibuf to obuf.
- * Return number of sample pairs processed.
- */
-template<bool inStereo, bool outStereo, bool reverseStereo>
-int SimpleRateConverter<inStereo, outStereo, reverseStereo>::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;
+ void setInputRate(st_rate_t inputRate) override { _inRate = inputRate; }
+ void setOutputRate(st_rate_t outputRate) override { _outRate = outputRate; }
- ostart = obuf;
- oend = obuf + osamp * (outStereo ? 2 : 1);
+ st_rate_t getInputRate() const override { return _inRate; }
+ st_rate_t getOutputRate() const override { return _outRate; }
+};
- while (obuf < oend) {
+template<bool inStereo, bool outStereo, bool reverseStereo>
+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) {
+ st_sample_t *outStart, *outEnd;
- // read enough input samples so that opos >= 0
- do {
- // Check if we have to refill the buffer
- if (inLen == 0) {
- inPtr = inBuf;
- inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
- if (inLen <= 0)
- return (obuf - ostart) / (outStereo ? 2 : 1);
- }
- inLen -= (inStereo ? 2 : 1);
- opos--;
- if (opos >= 0) {
- inPtr += (inStereo ? 2 : 1);
- }
- } while (opos >= 0);
+ outStart = outBuffer;
+ outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
- st_sample_t in0, in1;
- in0 = *inPtr++;
- in1 = (inStereo ? *inPtr++ : in0);
+ while (outBuffer < outEnd) {
+ // Check if we have to refill the buffer
+ if (_bufferSize == 0) {
+ _bufferPos = _buffer;
+ _bufferSize = input.readBuffer(_buffer, ARRAYSIZE(_buffer));
- // Increment output position
- opos += opos_inc;
+ if (_bufferSize <= 0)
+ return (outBuffer - outStart) / (outStereo ? 2 : 1);
+ }
+
+ // Mix the data into the output buffer
+ st_sample_t inL, inR;
+ inL = *_bufferPos++;
+ inR = (inStereo ? *_bufferPos++ : inL);
+ _bufferSize -= (inStereo ? 2 : 1);
- st_sample_t out0, out1;
- out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume;
- out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume;
+ st_sample_t outL, outR;
+ outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
+ outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
if (outStereo) {
- // output left channel
- clampedAdd(obuf[reverseStereo ], out0);
+ // Output left channel
+ clampedAdd(outBuffer[reverseStereo ], outL);
- // output right channel
- clampedAdd(obuf[reverseStereo ^ 1], out1);
+ // Output right channel
+ clampedAdd(outBuffer[reverseStereo ^ 1], outR);
- obuf += 2;
+ outBuffer += 2;
} else {
- // output mono channel
- clampedAdd(obuf[0], (out0 + out1) / 2);
+ // Output mono channel
+ clampedAdd(outBuffer[0], (outL + outR) / 2);
- obuf += 1;
+ outBuffer += 1;
}
}
- return (obuf - ostart) / (outStereo ? 2 : 1);
-}
-/**
- * Audio rate converter based on simple linear Interpolation.
- *
- * The use of fractional increment allows us to use no buffer. It
- * avoid the problems at the end of the buffer we had with the old
- * method which stored a possibly big buffer of size
- * lcm(in_rate,out_rate).
- *
- * Limited to sampling frequency <= 65535 Hz.
- */
+ return (outBuffer - outStart) / (outStereo ? 2 : 1);
+}
template<bool inStereo, bool outStereo, bool reverseStereo>
-class LinearRateConverter : public RateConverter {
-protected:
- st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
- const st_sample_t *inPtr;
- int inLen;
+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) {
+ // How much to increment _outPos by
+ frac_t outPos_inc = _inRate / _outRate;
- /** fractional position of the output stream in input stream unit */
- frac_t opos;
+ st_sample_t *outStart, *outEnd;
- /** fractional position increment in the output stream */
- frac_t opos_inc;
+ outStart = outBuffer;
+ outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
- /** last sample(s) in the input stream (left/right channel) */
- st_sample_t ilast0, ilast1;
- /** current sample(s) in the input stream (left/right channel) */
- st_sample_t icur0, icur1;
+ while (outBuffer < outEnd) {
+ // Read enough input samples so that _outPos >= 0
+ do {
+ // Check if we have to refill the buffer
+ if (_bufferSize == 0) {
+ _bufferPos = _buffer;
+ _bufferSize = input.readBuffer(_buffer, ARRAYSIZE(_buffer));
-public:
- LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
- int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) override;
- int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) override {
- return ST_SUCCESS;
- }
-};
+ if (_bufferSize <= 0)
+ return (outBuffer - outStart) / (outStereo ? 2 : 1);
+ }
+ _bufferSize -= (inStereo ? 2 : 1);
+ _outPos--;
-/*
- * Prepare processing.
- */
-template<bool inStereo, bool outStereo, bool reverseStereo>
-LinearRateConverter<inStereo, outStereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
- if (inrate >= 131072 || outrate >= 131072) {
- error("rate effect can only handle rates < 131072");
- }
+ if (_outPos >= 0) {
+ _bufferPos += (inStereo ? 2 : 1);
+ }
+ } while (_outPos >= 0);
+
+ st_sample_t inL, inR;
+ inL = *_bufferPos++;
+ inR = (inStereo ? *_bufferPos++ : inL);
- opos = FRAC_ONE_LOW;
+ // Increment output position
+ _outPos += outPos_inc;
- // Compute the linear interpolation increment.
- // This will overflow if inrate >= 2^17, and underflow if outrate >= 2^17.
- // Also, if the quotient of the two rate becomes too small / too big, that
- // would cause problems, but since we rarely scale from 1 to 65536 Hz or vice
- // versa, I think we can live with that limitation ;-).
- opos_inc = (inrate << FRAC_BITS_LOW) / outrate;
+ st_sample_t outL, outR;
+ outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
+ outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
- ilast0 = ilast1 = 0;
- icur0 = icur1 = 0;
+ if (outStereo) {
+ // output left channel
+ clampedAdd(outBuffer[reverseStereo ], outL);
- inLen = 0;
+ // output right channel
+ clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+
+ outBuffer += 2;
+ } else {
+ // output mono channel
+ clampedAdd(outBuffer[0], (outL + outR) / 2);
+
+ outBuffer += 1;
+ }
+ }
+ return (outBuffer - outStart) / (outStereo ? 2 : 1);
}
-/*
- * Processed signed long samples from ibuf to obuf.
- * Return number of sample pairs processed.
- */
template<bool inStereo, bool outStereo, bool reverseStereo>
-int LinearRateConverter<inStereo, outStereo, reverseStereo>::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;
+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) {
+ // How much to increment _outPosFrac by
+ frac_t outPos_inc = (_inRate << FRAC_BITS_LOW) / _outRate;
- ostart = obuf;
- oend = obuf + osamp * (outStereo ? 2 : 1);
+ st_sample_t *outStart, *outEnd;
+ outStart = outBuffer;
+ outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
- while (obuf < oend) {
-
- // read enough input samples so that opos < 0
- while ((frac_t)FRAC_ONE_LOW <= opos) {
+ while (outBuffer < outEnd) {
+ // Read enough input samples so that _outPosFrac < 0
+ while ((frac_t)FRAC_ONE_LOW <= _outPosFrac) {
// Check if we have to refill the buffer
- if (inLen == 0) {
- inPtr = inBuf;
- inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
- if (inLen <= 0)
- return (obuf - ostart) / (outStereo ? 2 : 1);
+ if (_bufferSize == 0) {
+ _bufferPos = _buffer;
+ _bufferSize = input.readBuffer(_buffer, ARRAYSIZE(_buffer));
+
+ if (_bufferSize <= 0)
+ return (outBuffer - outStart) / (outStereo ? 2 : 1);
}
- inLen -= (inStereo ? 2 : 1);
- ilast0 = icur0;
- icur0 = *inPtr++;
+
+ _bufferSize -= (inStereo ? 2 : 1);
+ _inLastL = _inCurL;
+ _inCurL = *_bufferPos++;
+
if (inStereo) {
- ilast1 = icur1;
- icur1 = *inPtr++;
+ _inLastR = _inCurR;
+ _inCurR = *_bufferPos++;
}
- opos -= FRAC_ONE_LOW;
+
+ _outPosFrac -= FRAC_ONE_LOW;
}
- // Loop as long as the outpos trails behind, and as long as there is
+ // Loop as long as the _outPos trails behind, and as long as there is
// still space in the output buffer.
- while (opos < (frac_t)FRAC_ONE_LOW && obuf < oend) {
- // interpolate
- 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)) :
- in0);
-
- st_sample_t out0, out1;
- out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume;
- out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume;
+ 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));
+ inR = (inStereo ?
+ (st_sample_t)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
+ inL);
+
+ st_sample_t outL, outR;
+ outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
+ outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
if (outStereo) {
- // output left channel
- clampedAdd(obuf[reverseStereo ], out0);
+ // Output left channel
+ clampedAdd(outBuffer[reverseStereo ], outL);
- // output right channel
- clampedAdd(obuf[reverseStereo ^ 1], out1);
+ // Output right channel
+ clampedAdd(outBuffer[reverseStereo ^ 1], outR);
- obuf += 2;
+ outBuffer += 2;
} else {
- // output mono channel
- clampedAdd(obuf[0], (out0 + out1) / 2);
+ // Output mono channel
+ clampedAdd(outBuffer[0], (outL + outR) / 2);
- obuf += 1;
+ outBuffer += 1;
}
// Increment output position
- opos += opos_inc;
+ _outPosFrac += outPos_inc;
}
}
- return (obuf - ostart) / (outStereo ? 2 : 1);
+ return (outBuffer - outStart) / (outStereo ? 2 : 1);
}
-
-#pragma mark -
-
-
-/**
- * Simple audio rate converter for the case that the inrate equals the outrate.
- */
template<bool inStereo, bool outStereo, bool reverseStereo>
-class CopyRateConverter : public RateConverter {
- st_sample_t *_buffer;
- st_size_t _bufferSize;
-public:
- CopyRateConverter() : _buffer(nullptr), _bufferSize(0) {}
- ~CopyRateConverter() {
- free(_buffer);
- }
-
- 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() == inStereo);
-
- st_sample_t *ptr;
- st_size_t len;
-
- st_sample_t *ostart = obuf;
-
- if (inStereo)
- osamp *= 2;
-
- // Reallocate temp buffer, if necessary
- if (osamp > _bufferSize) {
- free(_buffer);
- _buffer = (st_sample_t *)malloc(osamp * sizeof(st_sample_t));
- _bufferSize = osamp;
- }
-
- if (!_buffer)
- error("[CopyRateConverter::flow] Cannot allocate memory for temp buffer");
-
- // Read up to 'osamp' samples into our temporary buffer
- len = input.readBuffer(_buffer, osamp);
-
- // Mix the data into the output buffer
- ptr = _buffer;
- for (; len > 0; len -= (inStereo ? 2 : 1)) {
- st_sample_t in0, in1;
- in0 = *ptr++;
- in1 = (inStereo ? *ptr++ : in0);
-
- st_sample_t out0, out1;
- out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume;
- out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume;
-
- if (outStereo) {
- // output left channel
- clampedAdd(obuf[reverseStereo ], out0);
-
- // 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) / (outStereo ? 2 : 1);
- }
-
- int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) override {
- return ST_SUCCESS;
- }
-};
-
-
-#pragma mark -
+RateConverter_Impl<inStereo, outStereo, reverseStereo>::RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate) :
+ _inRate(inputRate),
+ _outRate(outputRate),
+ _outPos(1),
+ _outPosFrac(FRAC_ONE_LOW),
+ _inLastL(0),
+ _inLastR(0),
+ _inCurL(0),
+ _inCurR(0),
+ _bufferSize(0),
+ _bufferPos(nullptr) {}
template<bool inStereo, bool outStereo, bool reverseStereo>
-RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate) {
- if (inrate != outrate) {
- if ((inrate % outrate) == 0 && (inrate < 65536)) {
- return new SimpleRateConverter<inStereo, outStereo, reverseStereo>(inrate, outrate);
+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) {
+ assert(input.isStereo() == inStereo);
+
+ if (_inRate == _outRate) {
+ return copyConvert(input, outBuffer, numSamples, volL, volR);
+ } else {
+ if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
+ return simpleConvert(input, outBuffer, numSamples, volL, volR);
} else {
- return new LinearRateConverter<inStereo, outStereo, reverseStereo>(inrate, outrate);
+ return interpolateConvert(input, outBuffer, numSamples, volL, volR);
}
- } else {
- return new CopyRateConverter<inStereo, outStereo, reverseStereo>();
}
}
-/**
- * Create and return a RateConverter object for the specified input and output rates.
- */
-RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool instereo, bool outstereo, bool reverseStereo) {
- if (instereo) {
- if (outstereo) {
+RateConverter *makeRateConverter(st_rate_t inRate, st_rate_t outRate, bool inStereo, bool outStereo, bool reverseStereo) {
+ if (inStereo) {
+ if (outStereo) {
if (reverseStereo)
- return makeRateConverter<true, true, true>(inrate, outrate);
+ return new RateConverter_Impl<true, true, true>(inRate, outRate);
else
- return makeRateConverter<true, true, false>(inrate, outrate);
+ return new RateConverter_Impl<true, true, false>(inRate, outRate);
} else
- return makeRateConverter<true, false, false>(inrate, outrate);
+ return new RateConverter_Impl<true, false, false>(inRate, outRate);
} else {
- if (outstereo) {
- return makeRateConverter<false, true, false>(inrate, outrate);
+ if (outStereo) {
+ return new RateConverter_Impl<false, true, false>(inRate, outRate);
} else
- return makeRateConverter<false, false, false>(inrate, outrate);
+ return new RateConverter_Impl<false, false, false>(inRate, outRate);
}
}
diff --git a/audio/rate.h b/audio/rate.h
index 67e34b8de1c..6a70da25cfd 100644
--- a/audio/rate.h
+++ b/audio/rate.h
@@ -22,7 +22,7 @@
#ifndef AUDIO_RATE_H
#define AUDIO_RATE_H
-#include "common/scummsys.h"
+#include "common/frac.h"
namespace Audio {
/**
@@ -46,11 +46,6 @@ enum {
ST_SAMPLE_MIN = (-ST_SAMPLE_MAX - 1L)
};
-enum {
- ST_EOF = -1,
- ST_SUCCESS = 0
-};
-
static inline void clampedAdd(int16& a, int b) {
int val;
#ifdef OUTPUT_UNSIGNED_AUDIO
@@ -71,20 +66,39 @@ static inline void clampedAdd(int16& a, int b) {
#endif
}
+/**
+ * Helper class that handles resampling an AudioStream between an input and output
+ * sample rate. Its regular use case is upsampling from the native stream rate
+ * to the one used by the sound mixer. However, the input/output rates can be
+ * manually adjusted to change playback speed and produce sound effects.
+*/
class RateConverter {
public:
- RateConverter() {}
+ RateConverter() {}
virtual ~RateConverter() {}
-
+
/**
+ * 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.
+ *
* @return Number of sample pairs written into the buffer.
*/
- virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) = 0;
+ 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 void setInputRate(st_rate_t inputRate) = 0;
+ virtual void setOutputRate(st_rate_t outputRate) = 0;
- virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) = 0;
+ virtual st_rate_t getInputRate() const = 0;
+ virtual st_rate_t getOutputRate() const = 0;
};
-RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool instereo, bool outstereo, bool reverseStereo);
+RateConverter *makeRateConverter(st_rate_t inRate, st_rate_t outRate, bool inStereo, bool outStereo, bool reverseStereo);
+
/** @} */
} // End of namespace Audio
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 69ffb9cfaae..b81df60a01f 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, Audio::st_sample_t *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.flow(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume);
+ const int samplePairsWritten = converter.convert(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume);
return samplePairsWritten << 1;
}
diff --git a/engines/sword1/music.cpp b/engines/sword1/music.cpp
index f8e56256fc8..f0867e2a585 100644
--- a/engines/sword1/music.cpp
+++ b/engines/sword1/music.cpp
@@ -246,7 +246,7 @@ void Music::mixer(int16 *buf, uint32 len) {
memset(buf, 0, 2 * len * sizeof(int16));
for (int i = 0; i < ARRAYSIZE(_handles); i++)
if (_handles[i].streaming() && _converter[i])
- _converter[i]->flow(_handles[i], buf, len, _volumeL, _volumeR);
+ _converter[i]->convert(_handles[i], buf, len, _volumeL, _volumeR);
}
void Music::setVolume(uint8 volL, uint8 volR) {
Commit: 0f045a4ce4dbfe4e6a22c28a75127ae8af8e1b4c
https://github.com/scummvm/scummvm/commit/0f045a4ce4dbfe4e6a22c28a75127ae8af8e1b4c
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-14T22:04:56+02:00
Commit Message:
AUDIO: Add rate adjustment functionality to Mixer
Extended the Mixer class to support manually setting the
sample rate of a channel.
Changed paths:
audio/mixer.cpp
audio/mixer.h
audio/mixer_intern.h
diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 4a663a2d8ca..87a778967c8 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -114,6 +114,26 @@ public:
*/
int8 getBalance();
+ /**
+ * Set the channel's sample rate.
+ *
+ * @param rate The new sample rate. Must be less than 131072
+ */
+ void setRate(uint32 rate);
+
+ /**
+ * Get the channel's sample rate.
+ *
+ * @return The current sample rate of the channel.
+ */
+ uint32 getRate();
+
+ /**
+ * Reset the sample rate of the channel back to its
+ * AudioStream's native rate.
+ */
+ void resetRate();
+
/**
* Notifies the channel that the global sound type
* volume settings changed.
@@ -401,6 +421,34 @@ int8 MixerImpl::getChannelBalance(SoundHandle handle) {
return _channels[index]->getBalance();
}
+void MixerImpl::setChannelRate(SoundHandle handle, uint32 rate) {
+ Common::StackLock lock(_mutex);
+
+ const int index = handle._val % NUM_CHANNELS;
+ if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
+ return;
+
+ _channels[index]->setRate(rate);
+}
+
+uint32 MixerImpl::getChannelRate(SoundHandle handle) {
+ const int index = handle._val % NUM_CHANNELS;
+ if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
+ return 0;
+
+ return _channels[index]->getRate();
+}
+
+void MixerImpl::resetChannelRate(SoundHandle handle) {
+ Common::StackLock lock(_mutex);
+
+ const int index = handle._val % NUM_CHANNELS;
+ if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
+ return;
+
+ _channels[index]->resetRate();
+}
+
uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) {
return getElapsedTime(handle).msecs();
}
@@ -559,6 +607,24 @@ int8 Channel::getBalance() {
return _balance;
}
+void Channel::setRate(uint32 rate) {
+ if (_converter)
+ _converter->setInputRate(rate);
+}
+
+uint32 Channel::getRate() {
+ if (_converter)
+ return _converter->getInputRate();
+
+ return 0;
+}
+
+void Channel::resetRate() {
+ if (_converter && _stream) {
+ _converter->setInputRate(_stream->getRate());
+ }
+}
+
void Channel::updateChannelVolumes() {
// From the channel balance/volume and the global volume, we compute
// the effective volume for the left and right channel. Note the
diff --git a/audio/mixer.h b/audio/mixer.h
index ff521799617..6cc3d473d1e 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -254,6 +254,31 @@ public:
*/
virtual int8 getChannelBalance(SoundHandle handle) = 0;
+ /**
+ * Set the sample rate for the given handle.
+ *
+ * @param handle The sound to affect.
+ * @param rate The new sample rate. Must be less than 131072
+ */
+ virtual void setChannelRate(SoundHandle handle, uint32 rate) = 0;
+
+ /**
+ * Get the sample rate for the given handle.
+ *
+ * @param handle The sound to affect.
+ *
+ * @return The current sample rate of the channel.
+ */
+ virtual uint32 getChannelRate(SoundHandle handle) = 0;
+
+ /**
+ * Reset the sample rate of the channel back to its
+ * AudioStream's native rate.
+ *
+ * @param handle The sound to affect.
+ */
+ virtual void resetChannelRate(SoundHandle handle) = 0;
+
/**
* Get an approximation of for how long the channel has been playing.
*/
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index c7d67771cbd..18a0d97f176 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -118,6 +118,9 @@ public:
virtual byte getChannelVolume(SoundHandle handle);
virtual void setChannelBalance(SoundHandle handle, int8 balance);
virtual int8 getChannelBalance(SoundHandle handle);
+ virtual void setChannelRate(SoundHandle handle, uint32 rate);
+ virtual uint32 getChannelRate(SoundHandle handle);
+ virtual void resetChannelRate(SoundHandle handle);
virtual uint32 getSoundElapsedTime(SoundHandle handle);
virtual Timestamp getElapsedTime(SoundHandle handle);
More information about the Scummvm-git-logs
mailing list