diff options
Diffstat (limited to 'sound/rate.cpp')
-rw-r--r-- | sound/rate.cpp | 222 |
1 files changed, 158 insertions, 64 deletions
diff --git a/sound/rate.cpp b/sound/rate.cpp index 7d1a7b51cf..6a51f3d5d5 100644 --- a/sound/rate.cpp +++ b/sound/rate.cpp @@ -55,6 +55,107 @@ namespace Audio { /** + * 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 @@ -73,24 +174,21 @@ protected: int inLen; /** fractional position of the output stream in input stream unit */ - unsigned long opos, opos_frac; + long opos, opos_frac; /** fractional position increment in the output stream */ - unsigned long opos_inc, opos_inc_frac; - - /** position in the input stream (integer) */ - unsigned long ipos; + long opos_inc, opos_inc_frac; /** last sample(s) in the input stream (left/right channel) */ - st_sample_t ilast[2]; + st_sample_t ilast0, ilast1; /** current sample(s) in the input stream (left/right channel) */ - st_sample_t icur[2]; + 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); + return ST_SUCCESS; } }; @@ -119,10 +217,8 @@ LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate opos_inc_frac = incr & ((1UL << FRAC_BITS) - 1); opos_inc = incr >> FRAC_BITS; - ipos = 0; - - ilast[0] = ilast[1] = 0; - icur[0] = icur[1] = 0; + ilast0 = ilast1 = 0; + icur0 = icur1 = 0; inLen = 0; } @@ -134,64 +230,56 @@ LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate 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; - st_sample_t out[2]; - - const int numChannels = stereo ? 2 : 1; - int i; ostart = obuf; oend = obuf + osamp * 2; while (obuf < oend) { - // read enough input samples so that ipos > opos - while (ipos <= opos) { + // 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) - goto the_end; + return ST_EOF; } - for (i = 0; i < numChannels; i++) { - ilast[i] = icur[i]; - icur[i] = *inPtr++; - inLen--; + inLen -= (stereo ? 2 : 1); + ilast0 = icur0; + icur0 = *inPtr++; + if (stereo) { + ilast1 = icur1; + icur1 = *inPtr++; } - ipos++; + opos--; } // Loop as long as the outpos trails behind, and as long as there is // still space in the output buffer. - while (ipos > opos) { - + while (0 > opos && obuf < oend) { // interpolate - out[0] = out[1] = (st_sample_t)(ilast[0] + (((icur[0] - ilast[0]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS)); - - if (stereo) { - // interpolate - out[reverseStereo ? 0 : 1] = (st_sample_t)(ilast[1] + (((icur[1] - ilast[1]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS)); - } + 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++, (out[0] * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); + clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); // output right channel - clampedAdd(*obuf++, (out[1] * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + + obuf += 2; // Increment output position - unsigned long tmp = opos_frac + opos_inc_frac; + long tmp = opos_frac + opos_inc_frac; opos += opos_inc + (tmp >> FRAC_BITS); opos_frac = tmp & ((1UL << FRAC_BITS) - 1); - - // Abort if we reached the end of the output buffer - if (obuf >= oend) - goto the_end; } } - -the_end: - return (ST_SUCCESS); + return ST_SUCCESS; } @@ -232,27 +320,24 @@ public: // Mix the data into the output buffer ptr = _buffer; - while (len-- > 0) { - st_sample_t tmp0, tmp1; - tmp0 = tmp1 = *ptr++; - if (stereo) { - if (reverseStereo) - tmp0 = *ptr++; - else - tmp1 = *ptr++; - len--; - } + for (; len > 0; len -= (stereo ? 2 : 1)) { + st_sample_t out0, out1; + out0 = *ptr++; + out1 = (stereo ? *ptr++ : out0); // output left channel - clampedAdd(*obuf++, (tmp0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); + clampedAdd(obuf[reverseStereo ], (out0 * (int)vol_l) / Audio::Mixer::kMaxMixerVolume); // output right channel - clampedAdd(*obuf++, (tmp1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + clampedAdd(obuf[reverseStereo ^ 1], (out1 * (int)vol_r) / Audio::Mixer::kMaxMixerVolume); + + obuf += 2; } - return (ST_SUCCESS); + return ST_SUCCESS; } + virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { - return (ST_SUCCESS); + return ST_SUCCESS; } }; @@ -265,14 +350,23 @@ public: */ RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) { if (inrate != outrate) { - 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); - //return new ResampleRateConverter(inrate, outrate, 1); + 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) |