2007-05-30 21:56:52 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
2003-08-02 16:31:31 +00:00
|
|
|
*
|
2021-12-26 18:47:58 +01:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2014-02-18 02:34:17 +01:00
|
|
|
*
|
2003-08-02 16:31:31 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2014-02-18 02:34:17 +01:00
|
|
|
*
|
2003-08-02 16:31:31 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-12-26 18:47:58 +01:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2003-08-02 16:31:31 +00:00
|
|
|
*
|
|
|
|
*/
|
2003-07-24 17:46:38 +00:00
|
|
|
|
|
|
|
/*
|
2003-08-02 16:31:31 +00:00
|
|
|
* The code in this file is based on code with Copyright 1998 Fabrice Bellard
|
|
|
|
* Fabrice original code is part of SoX (http://sox.sourceforge.net).
|
|
|
|
* Max Horn adapted that code to the needs of ScummVM and rewrote it partial,
|
|
|
|
* in the process removing any use of floating point arithmetic. Various other
|
2010-03-12 00:37:25 +00:00
|
|
|
* improvements over the original code were made.
|
2003-07-24 17:46:38 +00:00
|
|
|
*/
|
|
|
|
|
2011-02-09 01:09:01 +00:00
|
|
|
#include "audio/audiostream.h"
|
|
|
|
#include "audio/rate.h"
|
|
|
|
#include "audio/mixer.h"
|
2005-01-10 22:51:42 +00:00
|
|
|
#include "common/util.h"
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2005-05-11 00:01:44 +00:00
|
|
|
namespace Audio {
|
|
|
|
|
2013-07-02 23:04:17 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* 96kHz audio, so we use fewer fractional bits in this code.
|
|
|
|
*/
|
|
|
|
enum {
|
|
|
|
FRAC_BITS_LOW = 15,
|
|
|
|
FRAC_ONE_LOW = (1L << FRAC_BITS_LOW),
|
|
|
|
FRAC_HALF_LOW = (1L << (FRAC_BITS_LOW-1))
|
|
|
|
};
|
2003-08-02 16:31:31 +00:00
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
template<bool inStereo, bool outStereo, bool reverseStereo>
|
2023-05-01 23:51:55 +03:00
|
|
|
class RateConverter_Impl : public RateConverter {
|
|
|
|
private:
|
|
|
|
/** Input and output rates */
|
|
|
|
st_rate_t _inRate, _outRate;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
/**
|
|
|
|
* 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];
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
/** Current position inside the buffer */
|
|
|
|
const st_sample_t *_bufferPos;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
/** Size of data currently loaded into the buffer */
|
|
|
|
int _bufferSize;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
/** How far output is ahead of input when doing simple conversion */
|
|
|
|
frac_t _outPos;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
/** Fractional position of the output stream in input stream unit */
|
|
|
|
frac_t _outPosFrac;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
/** 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;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
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);
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
public:
|
|
|
|
RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate);
|
|
|
|
virtual ~RateConverter_Impl() {}
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
int convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
void setInputRate(st_rate_t inputRate) override { _inRate = inputRate; }
|
|
|
|
void setOutputRate(st_rate_t outputRate) override { _outRate = outputRate; }
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
st_rate_t getInputRate() const override { return _inRate; }
|
|
|
|
st_rate_t getOutputRate() const override { return _outRate; }
|
2023-08-16 20:41:28 +03:00
|
|
|
|
|
|
|
bool needsDraining() const override { return _bufferSize != 0; }
|
2023-05-01 23:51:55 +03:00
|
|
|
};
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
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;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
outStart = outBuffer;
|
|
|
|
outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
|
2007-06-16 17:12:08 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
while (outBuffer < outEnd) {
|
|
|
|
// Check if we have to refill the buffer
|
|
|
|
if (_bufferSize == 0) {
|
|
|
|
_bufferPos = _buffer;
|
|
|
|
_bufferSize = input.readBuffer(_buffer, ARRAYSIZE(_buffer));
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
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);
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
st_sample_t outL, outR;
|
|
|
|
outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
|
|
|
|
outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
|
2007-06-16 17:09:54 +00:00
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
if (outStereo) {
|
2023-05-01 23:51:55 +03:00
|
|
|
// Output left channel
|
|
|
|
clampedAdd(outBuffer[reverseStereo ], outL);
|
2007-09-19 08:40:12 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
// Output right channel
|
|
|
|
clampedAdd(outBuffer[reverseStereo ^ 1], outR);
|
2022-11-13 14:03:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
outBuffer += 2;
|
2022-11-13 14:03:38 +00:00
|
|
|
} else {
|
2023-05-01 23:51:55 +03:00
|
|
|
// Output mono channel
|
|
|
|
clampedAdd(outBuffer[0], (outL + outR) / 2);
|
2022-11-13 14:03:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
outBuffer += 1;
|
2022-11-13 14:03:38 +00:00
|
|
|
}
|
2007-06-16 17:09:54 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
return (outBuffer - outStart) / (outStereo ? 2 : 1);
|
|
|
|
}
|
2003-08-02 17:22:52 +00:00
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
template<bool inStereo, bool outStereo, bool reverseStereo>
|
2023-05-01 23:51:55 +03:00
|
|
|
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;
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
st_sample_t *outStart, *outEnd;
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
outStart = outBuffer;
|
|
|
|
outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
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));
|
2003-08-02 16:31:31 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
if (_bufferSize <= 0)
|
|
|
|
return (outBuffer - outStart) / (outStereo ? 2 : 1);
|
|
|
|
}
|
2003-08-02 16:31:31 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
_bufferSize -= (inStereo ? 2 : 1);
|
|
|
|
_outPos--;
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
if (_outPos >= 0) {
|
|
|
|
_bufferPos += (inStereo ? 2 : 1);
|
|
|
|
}
|
|
|
|
} while (_outPos >= 0);
|
|
|
|
|
|
|
|
st_sample_t inL, inR;
|
|
|
|
inL = *_bufferPos++;
|
|
|
|
inR = (inStereo ? *_bufferPos++ : inL);
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
// Increment output position
|
|
|
|
_outPos += outPos_inc;
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
st_sample_t outL, outR;
|
|
|
|
outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
|
|
|
|
outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
if (outStereo) {
|
|
|
|
// output left channel
|
|
|
|
clampedAdd(outBuffer[reverseStereo ], outL);
|
2003-11-08 23:05:04 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
// 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);
|
2003-07-24 17:46:38 +00:00
|
|
|
}
|
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
template<bool inStereo, bool outStereo, bool reverseStereo>
|
2023-05-01 23:51:55 +03:00
|
|
|
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;
|
2003-11-08 23:05:04 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
st_sample_t *outStart, *outEnd;
|
|
|
|
outStart = outBuffer;
|
|
|
|
outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
|
2003-07-24 17:46:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
while (outBuffer < outEnd) {
|
|
|
|
// Read enough input samples so that _outPosFrac < 0
|
|
|
|
while ((frac_t)FRAC_ONE_LOW <= _outPosFrac) {
|
2003-08-04 22:15:16 +00:00
|
|
|
// Check if we have to refill the buffer
|
2023-05-01 23:51:55 +03:00
|
|
|
if (_bufferSize == 0) {
|
|
|
|
_bufferPos = _buffer;
|
|
|
|
_bufferSize = input.readBuffer(_buffer, ARRAYSIZE(_buffer));
|
|
|
|
|
|
|
|
if (_bufferSize <= 0)
|
|
|
|
return (outBuffer - outStart) / (outStereo ? 2 : 1);
|
2003-08-04 22:15:16 +00:00
|
|
|
}
|
2023-05-01 23:51:55 +03:00
|
|
|
|
|
|
|
_bufferSize -= (inStereo ? 2 : 1);
|
|
|
|
_inLastL = _inCurL;
|
|
|
|
_inCurL = *_bufferPos++;
|
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
if (inStereo) {
|
2023-05-01 23:51:55 +03:00
|
|
|
_inLastR = _inCurR;
|
|
|
|
_inCurR = *_bufferPos++;
|
2003-08-02 14:48:11 +00:00
|
|
|
}
|
2023-05-01 23:51:55 +03:00
|
|
|
|
|
|
|
_outPosFrac -= FRAC_ONE_LOW;
|
2003-07-24 17:46:38 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
// Loop as long as the _outPos trails behind, and as long as there is
|
2003-07-28 11:13:01 +00:00
|
|
|
// still space in the output buffer.
|
2023-05-01 23:51:55 +03:00
|
|
|
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;
|
2003-09-05 23:27:11 +00:00
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
if (outStereo) {
|
2023-05-01 23:51:55 +03:00
|
|
|
// Output left channel
|
|
|
|
clampedAdd(outBuffer[reverseStereo ], outL);
|
2007-09-19 08:40:12 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
// Output right channel
|
|
|
|
clampedAdd(outBuffer[reverseStereo ^ 1], outR);
|
2022-11-13 14:03:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
outBuffer += 2;
|
2022-11-13 14:03:38 +00:00
|
|
|
} else {
|
2023-05-01 23:51:55 +03:00
|
|
|
// Output mono channel
|
|
|
|
clampedAdd(outBuffer[0], (outL + outR) / 2);
|
2022-11-13 14:03:38 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
outBuffer += 1;
|
2022-11-13 14:03:38 +00:00
|
|
|
}
|
2003-09-05 23:27:11 +00:00
|
|
|
|
2003-07-28 01:13:31 +00:00
|
|
|
// Increment output position
|
2023-05-01 23:51:55 +03:00
|
|
|
_outPosFrac += outPos_inc;
|
2003-07-28 01:13:31 +00:00
|
|
|
}
|
2003-07-24 17:46:38 +00:00
|
|
|
}
|
2023-05-01 23:51:55 +03:00
|
|
|
return (outBuffer - outStart) / (outStereo ? 2 : 1);
|
2003-07-28 01:13:31 +00:00
|
|
|
}
|
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
template<bool inStereo, bool outStereo, bool reverseStereo>
|
2023-05-01 23:51:55 +03:00
|
|
|
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) {}
|
2003-08-02 16:31:31 +00:00
|
|
|
|
2022-11-13 14:03:38 +00:00
|
|
|
template<bool inStereo, bool outStereo, bool reverseStereo>
|
2023-05-01 23:51:55 +03:00
|
|
|
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);
|
2007-06-16 17:09:54 +00:00
|
|
|
} else {
|
2023-05-01 23:51:55 +03:00
|
|
|
return interpolateConvert(input, outBuffer, numSamples, volL, volR);
|
2007-06-28 19:35:48 +00:00
|
|
|
}
|
2003-08-02 16:31:31 +00:00
|
|
|
}
|
2003-07-24 17:46:38 +00:00
|
|
|
}
|
2005-05-11 00:01:44 +00:00
|
|
|
|
2023-05-01 23:51:55 +03:00
|
|
|
RateConverter *makeRateConverter(st_rate_t inRate, st_rate_t outRate, bool inStereo, bool outStereo, bool reverseStereo) {
|
|
|
|
if (inStereo) {
|
|
|
|
if (outStereo) {
|
2022-11-13 14:03:38 +00:00
|
|
|
if (reverseStereo)
|
2023-05-01 23:51:55 +03:00
|
|
|
return new RateConverter_Impl<true, true, true>(inRate, outRate);
|
2022-11-13 14:03:38 +00:00
|
|
|
else
|
2023-05-01 23:51:55 +03:00
|
|
|
return new RateConverter_Impl<true, true, false>(inRate, outRate);
|
2022-11-13 14:03:38 +00:00
|
|
|
} else
|
2023-05-01 23:51:55 +03:00
|
|
|
return new RateConverter_Impl<true, false, false>(inRate, outRate);
|
2022-11-13 14:03:38 +00:00
|
|
|
} else {
|
2023-05-01 23:51:55 +03:00
|
|
|
if (outStereo) {
|
|
|
|
return new RateConverter_Impl<false, true, false>(inRate, outRate);
|
2022-11-13 14:03:38 +00:00
|
|
|
} else
|
2023-05-01 23:51:55 +03:00
|
|
|
return new RateConverter_Impl<false, false, false>(inRate, outRate);
|
2022-11-13 14:03:38 +00:00
|
|
|
}
|
2007-06-28 19:35:48 +00:00
|
|
|
}
|
|
|
|
|
2005-05-11 00:01:44 +00:00
|
|
|
} // End of namespace Audio
|