AUDIO: Support mono audio output in the mixer

This commit is contained in:
Cameron Cawley 2022-11-13 14:03:38 +00:00 committed by Eugene Sandulenko
parent 5b3eab9be6
commit 9f4f22b3bf
12 changed files with 145 additions and 82 deletions

View File

@ -176,8 +176,8 @@ private:
#pragma mark --- Mixer ---
#pragma mark -
MixerImpl::MixerImpl(uint sampleRate, uint outBufSize)
: _mutex(), _sampleRate(sampleRate), _outBufSize(outBufSize), _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize)
: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
assert(sampleRate > 0);
@ -200,6 +200,10 @@ uint MixerImpl::getOutputRate() const {
return _sampleRate;
}
bool MixerImpl::getOutputStereo() const {
return _stereo;
}
uint MixerImpl::getOutputBufSize() const {
return _outBufSize;
}
@ -280,15 +284,21 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
Common::StackLock lock(_mutex);
int16 *buf = (int16 *)samples;
// we store stereo, 16-bit samples
assert(len % 4 == 0);
len >>= 2;
// Since the mixer callback has been called, the mixer must be ready...
_mixerReady = true;
// zero the buf
memset(buf, 0, 2 * len * sizeof(int16));
memset(buf, 0, len);
// we store 16-bit samples
if (_stereo) {
assert(len % 4 == 0);
len >>= 2;
} else {
assert(len % 2 == 0);
len >>= 1;
}
// mix all channels
int res = 0, tmp;
@ -524,7 +534,7 @@ Channel::Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *stream,
assert(stream);
// Get a rate converter instance
_converter = makeRateConverter(_stream->getRate(), mixer->getOutputRate(), _stream->isStereo(), reverseStereo);
_converter = makeRateConverter(_stream->getRate(), mixer->getOutputRate(), _stream->isStereo(), mixer->getOutputStereo(), reverseStereo);
}
Channel::~Channel() {

View File

@ -305,6 +305,13 @@ public:
*/
virtual uint getOutputRate() const = 0;
/**
* Check whether the output is stereo.
*
* @return true if output is stereo, false if not.
*/
virtual bool getOutputStereo() const = 0;
/**
* Return the output sample buffer size of the system.
*

View File

@ -64,6 +64,7 @@ private:
Common::Mutex _mutex;
const uint _sampleRate;
const bool _stereo;
const uint _outBufSize;
bool _mixerReady;
uint32 _handleSeed;
@ -81,7 +82,7 @@ private:
public:
MixerImpl(uint sampleRate, uint outBufSize = 0);
MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0);
~MixerImpl();
virtual bool isReady() const { Common::StackLock lock(_mutex); return _mixerReady; }
@ -129,6 +130,7 @@ public:
virtual int getVolumeForSoundType(SoundType type) const;
virtual uint getOutputRate() const;
virtual bool getOutputStereo() const;
virtual uint getOutputBufSize() const;
protected:

View File

@ -62,7 +62,7 @@ enum {
*
* Limited to sampling frequency <= 65535 Hz.
*/
template<bool stereo, bool reverseStereo>
template<bool inStereo, bool outStereo, bool reverseStereo>
class SimpleRateConverter : public RateConverter {
protected:
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
@ -88,8 +88,8 @@ public:
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) {
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");
}
@ -110,12 +110,12 @@ SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate
* Processed signed long samples from ibuf to obuf.
* Return number of sample pairs processed.
*/
template<bool stereo, bool reverseStereo>
int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
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;
ostart = obuf;
oend = obuf + osamp * 2;
oend = obuf + osamp * (outStereo ? 2 : 1);
while (obuf < oend) {
@ -126,31 +126,42 @@ int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp
inPtr = inBuf;
inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
if (inLen <= 0)
return (obuf - ostart) / 2;
return (obuf - ostart) / (outStereo ? 2 : 1);
}
inLen -= (stereo ? 2 : 1);
inLen -= (inStereo ? 2 : 1);
opos--;
if (opos >= 0) {
inPtr += (stereo ? 2 : 1);
inPtr += (inStereo ? 2 : 1);
}
} while (opos >= 0);
st_sample_t out0, out1;
out0 = *inPtr++;
out1 = (stereo ? *inPtr++ : out0);
st_sample_t in0, in1;
in0 = *inPtr++;
in1 = (inStereo ? *inPtr++ : in0);
// Increment output position
opos += opos_inc;
// output left channel
clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
st_sample_t out0, out1;
out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume;
out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume;
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
if (outStereo) {
// output left channel
clampedAdd(obuf[reverseStereo ], out0);
obuf += 2;
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], out1);
obuf += 2;
} else {
// output mono channel
clampedAdd(obuf[0], (out0 + out1) / 2);
obuf += 1;
}
}
return (obuf - ostart) / 2;
return (obuf - ostart) / (outStereo ? 2 : 1);
}
/**
@ -164,7 +175,7 @@ int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp
* Limited to sampling frequency <= 65535 Hz.
*/
template<bool stereo, bool reverseStereo>
template<bool inStereo, bool outStereo, bool reverseStereo>
class LinearRateConverter : public RateConverter {
protected:
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
@ -194,8 +205,8 @@ public:
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
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");
}
@ -219,12 +230,12 @@ LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate
* Processed signed long samples from ibuf to obuf.
* Return number of sample pairs processed.
*/
template<bool stereo, bool reverseStereo>
int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
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;
ostart = obuf;
oend = obuf + osamp * 2;
oend = obuf + osamp * (outStereo ? 2 : 1);
while (obuf < oend) {
@ -235,12 +246,12 @@ int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp
inPtr = inBuf;
inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
if (inLen <= 0)
return (obuf - ostart) / 2;
return (obuf - ostart) / (outStereo ? 2 : 1);
}
inLen -= (stereo ? 2 : 1);
inLen -= (inStereo ? 2 : 1);
ilast0 = icur0;
icur0 = *inPtr++;
if (stereo) {
if (inStereo) {
ilast1 = icur1;
icur1 = *inPtr++;
}
@ -251,25 +262,36 @@ int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp
// still space in the output buffer.
while (opos < (frac_t)FRAC_ONE_LOW && obuf < oend) {
// interpolate
st_sample_t out0, out1;
out0 = (st_sample_t)(ilast0 + (((icur0 - ilast0) * opos + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
out1 = (stereo ?
st_sample_t in0, in1;
in0 = (st_sample_t)(ilast0 + (((icur0 - ilast0) * opos + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
in1 = (inStereo ?
(st_sample_t)(ilast1 + (((icur1 - ilast1) * opos + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
out0);
in0);
// output left channel
clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
st_sample_t out0, out1;
out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume;
out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume;
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
if (outStereo) {
// output left channel
clampedAdd(obuf[reverseStereo ], out0);
obuf += 2;
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], out1);
obuf += 2;
} else {
// output mono channel
clampedAdd(obuf[0], (out0 + out1) / 2);
obuf += 1;
}
// Increment output position
opos += opos_inc;
}
}
return (obuf - ostart) / 2;
return (obuf - ostart) / (outStereo ? 2 : 1);
}
@ -279,7 +301,7 @@ int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_samp
/**
* Simple audio rate converter for the case that the inrate equals the outrate.
*/
template<bool stereo, bool reverseStereo>
template<bool inStereo, bool outStereo, bool reverseStereo>
class CopyRateConverter : public RateConverter {
st_sample_t *_buffer;
st_size_t _bufferSize;
@ -290,20 +312,20 @@ public:
}
int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) override {
assert(input.isStereo() == stereo);
assert(input.isStereo() == inStereo);
st_sample_t *ptr;
st_size_t len;
st_sample_t *ostart = obuf;
if (stereo)
if (inStereo)
osamp *= 2;
// Reallocate temp buffer, if necessary
if (osamp > _bufferSize) {
free(_buffer);
_buffer = (st_sample_t *)malloc(osamp * 2);
_buffer = (st_sample_t *)malloc(osamp * sizeof(st_sample_t));
_bufferSize = osamp;
}
@ -315,20 +337,31 @@ public:
// Mix the data into the output buffer
ptr = _buffer;
for (; len > 0; len -= (stereo ? 2 : 1)) {
for (; len > 0; len -= (inStereo ? 2 : 1)) {
st_sample_t in0, in1;
in0 = *ptr++;
in1 = (inStereo ? *ptr++ : in0);
st_sample_t out0, out1;
out0 = *ptr++;
out1 = (stereo ? *ptr++ : out0);
out0 = (in0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume;
out1 = (in1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume;
// output left channel
clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
if (outStereo) {
// output left channel
clampedAdd(obuf[reverseStereo ], out0);
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], out1);
obuf += 2;
obuf += 2;
} else {
// output mono channel
clampedAdd(obuf[0], (out0 + out1) / 2);
obuf += 1;
}
}
return (obuf - ostart) / 2;
return (obuf - ostart) / (outStereo ? 2 : 1);
}
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) override {
@ -339,30 +372,37 @@ public:
#pragma mark -
template<bool stereo, bool reverseStereo>
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<stereo, reverseStereo>(inrate, outrate);
return new SimpleRateConverter<inStereo, outStereo, reverseStereo>(inrate, outrate);
} else {
return new LinearRateConverter<stereo, reverseStereo>(inrate, outrate);
return new LinearRateConverter<inStereo, outStereo, reverseStereo>(inrate, outrate);
}
} else {
return new CopyRateConverter<stereo, reverseStereo>();
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 stereo, bool reverseStereo) {
if (stereo) {
if (reverseStereo)
return makeRateConverter<true, true>(inrate, outrate);
else
return makeRateConverter<true, false>(inrate, outrate);
} else
return makeRateConverter<false, false>(inrate, outrate);
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool instereo, bool outstereo, bool reverseStereo) {
if (instereo) {
if (outstereo) {
if (reverseStereo)
return makeRateConverter<true, true, true>(inrate, outrate);
else
return makeRateConverter<true, true, false>(inrate, outrate);
} else
return makeRateConverter<true, false, false>(inrate, outrate);
} else {
if (outstereo) {
return makeRateConverter<false, true, false>(inrate, outrate);
} else
return makeRateConverter<false, false, false>(inrate, outrate);
}
}
} // End of namespace Audio

View File

@ -84,7 +84,7 @@ public:
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) = 0;
};
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo = false);
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool instereo, bool outstereo, bool reverseStereo);
/** @} */
} // End of namespace Audio

View File

@ -36,7 +36,7 @@ NullMixerManager::~NullMixerManager() {
}
void NullMixerManager::init() {
_mixer = new Audio::MixerImpl(_outputRate, _samples);
_mixer = new Audio::MixerImpl(_outputRate, true, _samples);
assert(_mixer);
_mixer->setReady(true);
}

View File

@ -73,7 +73,7 @@ void SdlMixerManager::init() {
warning("Could not open audio device: %s", SDL_GetError());
// The mixer is not marked as ready
_mixer = new Audio::MixerImpl(desired.freq, desired.samples);
_mixer = new Audio::MixerImpl(desired.freq, true, desired.samples);
return;
}
@ -88,7 +88,7 @@ void SdlMixerManager::init() {
warning("Could not open audio device: %s", SDL_GetError());
// The mixer is not marked as ready
_mixer = new Audio::MixerImpl(desired.freq, desired.samples);
_mixer = new Audio::MixerImpl(desired.freq, true, desired.samples);
return;
}
@ -106,7 +106,7 @@ void SdlMixerManager::init() {
if (_obtained.channels != 2)
error("SDL mixer output requires stereo output device");
_mixer = new Audio::MixerImpl(_obtained.freq, desired.samples);
_mixer = new Audio::MixerImpl(_obtained.freq, true, desired.samples);
assert(_mixer);
_mixer->setReady(true);

View File

@ -470,7 +470,7 @@ void OSystem_Android::initBackend() {
// The division by four happens because the Mixer stores the size in frame units
// instead of bytes; this means that, since we have audio in stereo (2 channels)
// with a word size of 16 bit (2 bytes), we have to divide the effective size by 4.
_mixer = new Audio::MixerImpl(_audio_sample_rate, _audio_buffer_size / 4);
_mixer = new Audio::MixerImpl(_audio_sample_rate, true, _audio_buffer_size / 4);
_mixer->setReady(true);
_timer_thread_exit = false;

View File

@ -45,7 +45,7 @@ void OSystem_iOS7::mixCallback(void *sys, byte *samples, int len) {
}
void OSystem_iOS7::setupMixer() {
_mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, WAVE_BUFFER_SIZE);
_mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, true, WAVE_BUFFER_SIZE);
s_soundCallback = mixCallback;
s_soundParam = this;

View File

@ -45,7 +45,7 @@ void OSystem_IPHONE::mixCallback(void *sys, byte *samples, int len) {
}
void OSystem_IPHONE::setupMixer() {
_mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, WAVE_BUFFER_SIZE);
_mixer = new Audio::MixerImpl(AUDIO_SAMPLE_RATE, true, WAVE_BUFFER_SIZE);
s_soundCallback = mixCallback;
s_soundParam = this;

View File

@ -694,7 +694,8 @@ bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) {
channel.soundNode = NULL_REG;
channel.volume = kMaxVolume;
channel.pan = -1;
channel.converter.reset(Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false));
// TODO: Avoid unnecessary channel conversion
channel.converter.reset(Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false, true, false));
// The RobotAudioStream buffer size is
// ((bytesPerSample * channels * sampleRate * 2000ms) / 1000ms) & ~3
// where bytesPerSample = 2, channels = 1, and sampleRate = 22050
@ -860,7 +861,8 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
}
channel.stream.reset(new MutableLoopAudioStream(audioStream, loop));
channel.converter.reset(Audio::makeRateConverter(channel.stream->getRate(), getRate(), channel.stream->isStereo(), false));
// TODO: Avoid unnecessary channel conversion
channel.converter.reset(Audio::makeRateConverter(channel.stream->getRate(), getRate(), channel.stream->isStereo(), true, false));
// SSCI sets up a decompression buffer here for the audio stream, plus
// writes information about the sample to the channel to convert to the

View File

@ -303,12 +303,14 @@ void Music::startMusic(int32 tuneId, int32 loopFlag) {
if (SwordEngine::isPsx()) {
if (_handles[newStream].playPSX(tuneId, loopFlag != 0)) {
_mutex.lock();
_converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false);
// TODO: Avoid unnecessary channel conversion
_converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), true, false);
_mutex.unlock();
}
} else if (_handles[newStream].play(_tuneList[tuneId], loopFlag != 0)) {
_mutex.lock();
_converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false);
// TODO: Avoid unnecessary channel conversion
_converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), true, false);
_mutex.unlock();
} else {
if (tuneId != 81) // file 81 was apparently removed from BS.