aboutsummaryrefslogtreecommitdiff
path: root/sound/rate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/rate.cpp')
-rw-r--r--sound/rate.cpp222
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)