scummvm/sound/rate.cpp
Max Horn 1b28bd4644 cleanup
svn-id: r27500
2007-06-16 23:41:37 +00:00

382 lines
10 KiB
C++

/* 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.
*
* 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 2
* of the License, or (at your option) any later version.
* 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/*
* 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
* improvments over the original code were made.
*/
#include "common/stdafx.h"
#include "sound/audiostream.h"
#include "sound/rate.h"
#include "sound/mixer.h"
#include "common/util.h"
namespace Audio {
/**
* The precision of the fractional computations used by the rate converter.
* Normally you should never have to modify this value.
*/
#define FRAC_BITS 16
/**
* 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
/**
* Audio rate converter based on simple resampling. Used when no
* interpolation is required.
*
* Limited to sampling frequency <= 65535 Hz.
*/
template<bool stereo, bool reverseStereo>
class SimpleRateConverter : public RateConverter {
protected:
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
const st_sample_t *inPtr;
int inLen;
/** position of how far output is ahead of input */
/** Holds what would have been opos-ipos */
long opos;
/** fractional position increment in the output stream */
long opos_inc;
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);
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return ST_SUCCESS;
}
};
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) {
if (inrate == outrate) {
error("Input and Output rates must be different to use rate effect");
}
if ((inrate % outrate) != 0) {
error("Input rate must be a multiple of Output rate to use rate effect");
}
if (inrate >= 65536 || outrate >= 65536) {
error("rate effect can only handle rates < 65536");
}
opos = 1;
/* increment */
opos_inc = inrate / outrate;
inLen = 0;
}
/*
* Processed signed long samples from ibuf to obuf.
* Return number of samples 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) {
st_sample_t *ostart, *oend;
ostart = obuf;
oend = obuf + osamp * 2;
while (obuf < oend) {
// 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 ST_EOF;
}
inLen -= (stereo ? 2 : 1);
opos--;
if (opos >= 0) {
inPtr += (stereo ? 2 : 1);
}
} while (opos >= 0);
st_sample_t out0, out1;
out0 = *inPtr++;
out1 = (stereo ? *inPtr++ : out0);
// Increment output position
opos += opos_inc;
// output left channel
clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
obuf += 2;
}
return ST_SUCCESS;
}
/**
* 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.
*/
template<bool stereo, bool reverseStereo>
class LinearRateConverter : public RateConverter {
protected:
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
const st_sample_t *inPtr;
int inLen;
/** fractional position of the output stream in input stream unit */
long opos, opos_frac;
/** fractional position increment in the output stream */
long opos_inc, opos_inc_frac;
/** 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;
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);
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return ST_SUCCESS;
}
};
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
unsigned long incr;
if (inrate == outrate) {
error("Input and Output rates must be different to use rate effect");
}
if (inrate >= 65536 || outrate >= 65536) {
error("rate effect can only handle rates < 65536");
}
opos_frac = 0;
opos = 1;
/* increment */
incr = (inrate << FRAC_BITS) / outrate;
opos_inc_frac = incr & ((1UL << FRAC_BITS) - 1);
opos_inc = incr >> FRAC_BITS;
ilast0 = ilast1 = 0;
icur0 = icur1 = 0;
inLen = 0;
}
/*
* Processed signed long samples from ibuf to obuf.
* Return number of samples 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) {
st_sample_t *ostart, *oend;
ostart = obuf;
oend = obuf + osamp * 2;
while (obuf < oend) {
// read enough input samples so that opos <= 0
while (0 <= opos) {
// Check if we have to refill the buffer
if (inLen == 0) {
inPtr = inBuf;
inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
if (inLen <= 0)
return ST_EOF;
}
inLen -= (stereo ? 2 : 1);
ilast0 = icur0;
icur0 = *inPtr++;
if (stereo) {
ilast1 = icur1;
icur1 = *inPtr++;
}
opos--;
}
// Loop as long as the outpos trails behind, and as long as there is
// still space in the output buffer.
while (0 > opos && obuf < oend) {
// interpolate
st_sample_t out0, out1;
out0 = (st_sample_t)(ilast0 + (((icur0 - ilast0) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
out1 = (stereo ?
(st_sample_t)(ilast1 + (((icur1 - ilast1) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS)) :
out0);
// output left channel
clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
obuf += 2;
// Increment output position
long tmp = opos_frac + opos_inc_frac;
opos += opos_inc + (tmp >> FRAC_BITS);
opos_frac = tmp & ((1UL << FRAC_BITS) - 1);
}
}
return ST_SUCCESS;
}
#pragma mark -
/**
* Simple audio rate converter for the case that the inrate equals the outrate.
*/
template<bool stereo, bool reverseStereo>
class CopyRateConverter : public RateConverter {
st_sample_t *_buffer;
st_size_t _bufferSize;
public:
CopyRateConverter() : _buffer(0), _bufferSize(0) {}
~CopyRateConverter() {
free(_buffer);
}
virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
assert(input.isStereo() == stereo);
st_sample_t *ptr;
st_size_t len;
if (stereo)
osamp *= 2;
// Reallocate temp buffer, if necessary
if (osamp > _bufferSize) {
free(_buffer);
_buffer = (st_sample_t *)malloc(osamp * 2);
_bufferSize = osamp;
}
// 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 -= (stereo ? 2 : 1)) {
st_sample_t out0, out1;
out0 = *ptr++;
out1 = (stereo ? *ptr++ : out0);
// output left channel
clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume);
// output right channel
clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume);
obuf += 2;
}
return ST_SUCCESS;
}
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return ST_SUCCESS;
}
};
#pragma mark -
/**
* 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 (inrate != outrate) {
if ((inrate % outrate) == 0) {
if (stereo) {
if (reverseStereo)
return new SimpleRateConverter<true, true>(inrate, outrate);
else
return new SimpleRateConverter<true, false>(inrate, outrate);
} else
return new SimpleRateConverter<false, false>(inrate, outrate);
} else {
if (stereo) {
if (reverseStereo)
return new LinearRateConverter<true, true>(inrate, outrate);
else
return new LinearRateConverter<true, false>(inrate, outrate);
} else
return new LinearRateConverter<false, false>(inrate, outrate);
}
} else {
if (stereo) {
if (reverseStereo)
return new CopyRateConverter<true, true>();
else
return new CopyRateConverter<true, false>();
} else
return new CopyRateConverter<false, false>();
}
}
} // End of namespace Audio