aboutsummaryrefslogtreecommitdiff
path: root/sound/rate_arm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/rate_arm.cpp')
-rw-r--r--sound/rate_arm.cpp428
1 files changed, 428 insertions, 0 deletions
diff --git a/sound/rate_arm.cpp b/sound/rate_arm.cpp
new file mode 100644
index 0000000000..c4758b2d15
--- /dev/null
+++ b/sound/rate_arm.cpp
@@ -0,0 +1,428 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * 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, together with the rate_arm_asm.s file offers
+ * an ARM optimised version of the code in rate.cpp. The operation of this
+ * code should be identical to that of rate.cpp, but faster. The heavy
+ * lifting is done in the assembler file.
+ *
+ * To be as portable as possible we implement the core routines with C
+ * linkage in assembly, and implement the C++ routines that call into
+ * the C here. The C++ symbol mangling varies wildly between compilers,
+ * so this is the simplest way to ensure that the C/C++ combination should
+ * work on as many ARM based platforms as possible.
+ *
+ * Essentially the algorithm herein is the same as that in rate.cpp, so
+ * anyone seeking to understand this should attempt to understand that
+ * first. That code was based in turn on code with Copyright 1998 Fabrice
+ * Bellard - part of SoX (http://sox.sourceforge.net).
+ * Max Horn adapted that code to the needs of ScummVM and partially rewrote
+ * it, 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.
+ */
+typedef struct {
+ 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;
+
+ st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
+} SimpleRateDetails;
+
+template<bool stereo, bool reverseStereo>
+class SimpleRateConverter : public RateConverter {
+protected:
+ SimpleRateDetails sr;
+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");
+ }
+
+ sr.opos = 1;
+
+ /* increment */
+ sr.opos_inc = inrate / outrate;
+
+ sr.inLen = 0;
+}
+
+extern "C" void ARM_SimpleRate_M(AudioStream &input,
+ int (*fn)(Audio::AudioStream&,int16*,int),
+ SimpleRateDetails *sr,
+ st_sample_t *obuf,
+ st_size_t osamp,
+ st_volume_t vol_l,
+ st_volume_t vol_r);
+
+extern "C" void ARM_SimpleRate_S(AudioStream &input,
+ int (*fn)(Audio::AudioStream&,int16*,int),
+ SimpleRateDetails *sr,
+ st_sample_t *obuf,
+ st_size_t osamp,
+ st_volume_t vol_l,
+ st_volume_t vol_r);
+
+extern "C" void ARM_SimpleRate_R(AudioStream &input,
+ int (*fn)(Audio::AudioStream&,int16*,int),
+ SimpleRateDetails *sr,
+ st_sample_t *obuf,
+ st_size_t osamp,
+ st_volume_t vol_l,
+ st_volume_t vol_r);
+
+extern "C" int SimpleRate_readFudge(Audio::AudioStream &input,
+ int16 *a, int b)
+{
+ return input.readBuffer(a, b);
+}
+
+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) {
+
+#ifdef DEBUG_RATECONV
+fprintf(stderr, "Simple st=%d rev=%d\n", stereo, reverseStereo);
+fflush(stderr);
+#endif
+ if (!stereo) {
+ ARM_SimpleRate_M(input,
+ &SimpleRate_readFudge,
+ &sr,
+ obuf, osamp, vol_l, vol_r);
+ } else if (reverseStereo) {
+ ARM_SimpleRate_R(input,
+ &SimpleRate_readFudge,
+ &sr,
+ obuf, osamp, vol_l, vol_r);
+ } else {
+ ARM_SimpleRate_S(input,
+ &SimpleRate_readFudge,
+ &sr,
+ obuf, osamp, vol_l, vol_r);
+ }
+ 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.
+ */
+
+typedef struct {
+ 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;
+
+ /** integer position increment in the output stream */
+ long opos_inc;
+
+ /** current sample(s) in the input stream (left/right channel) */
+ st_sample_t icur[2];
+ /** last sample(s) in the input stream (left/right channel) */
+ st_sample_t ilast[2];
+
+ /** fractional position in the output stream */
+ long opos_frac;
+
+ /** fractional position increment in the output stream */
+ long opos_inc_frac;
+
+ st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
+} LinearRateDetails;
+
+extern "C" void ARM_LinearRate_M(AudioStream &input,
+ int (*fn)(Audio::AudioStream&,int16*,int),
+ LinearRateDetails *lr,
+ st_sample_t *obuf,
+ st_size_t osamp,
+ st_volume_t vol_l,
+ st_volume_t vol_r);
+
+extern "C" void ARM_LinearRate_S(AudioStream &input,
+ int (*fn)(Audio::AudioStream&,int16*,int),
+ LinearRateDetails *lr,
+ st_sample_t *obuf,
+ st_size_t osamp,
+ st_volume_t vol_l,
+ st_volume_t vol_r);
+
+extern "C" void ARM_LinearRate_R(AudioStream &input,
+ int (*fn)(Audio::AudioStream&,int16*,int),
+ LinearRateDetails *lr,
+ st_sample_t *obuf,
+ st_size_t osamp,
+ st_volume_t vol_l,
+ st_volume_t vol_r);
+
+template<bool stereo, bool reverseStereo>
+class LinearRateConverter : public RateConverter {
+protected:
+ LinearRateDetails lr;
+
+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");
+ }
+
+ lr.opos_frac = 0;
+ lr.opos = 1;
+
+ /* increment */
+ incr = (inrate << FRAC_BITS) / outrate;
+
+ lr.opos_inc_frac = incr & ((1UL << FRAC_BITS) - 1);
+ lr.opos_inc = incr >> FRAC_BITS;
+
+ lr.ilast[0] = lr.ilast[1] = 0;
+ lr.icur[0] = lr.icur[1] = 0;
+
+ lr.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) {
+
+#ifdef DEBUG_RATECONV
+fprintf(stderr, "Linear st=%d rev=%d\n", stereo, reverseStereo);
+fflush(stderr);
+#endif
+ if (!stereo) {
+ ARM_LinearRate_M(input,
+ &SimpleRate_readFudge,
+ &lr,
+ obuf, osamp, vol_l, vol_r);
+ } else if (reverseStereo) {
+ ARM_LinearRate_R(input,
+ &SimpleRate_readFudge,
+ &lr,
+ obuf, osamp, vol_l, vol_r);
+ } else {
+ ARM_LinearRate_S(input,
+ &SimpleRate_readFudge,
+ &lr,
+ obuf, osamp, vol_l, vol_r);
+ }
+ return (ST_SUCCESS);
+}
+
+
+#pragma mark -
+
+
+/**
+ * Simple audio rate converter for the case that the inrate equals the outrate.
+ */
+extern "C" void ARM_CopyRate_M(st_size_t len,
+ st_sample_t *obuf,
+ st_volume_t vol_l,
+ st_volume_t vol_r,
+ st_sample_t *_buffer);
+
+extern "C" void ARM_CopyRate_S(st_size_t len,
+ st_sample_t *obuf,
+ st_volume_t vol_l,
+ st_volume_t vol_r,
+ st_sample_t *_buffer);
+
+extern "C" void ARM_CopyRate_R(st_size_t len,
+ st_sample_t *obuf,
+ st_volume_t vol_l,
+ st_volume_t vol_r,
+ st_sample_t *_buffer);
+
+
+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);
+
+#ifdef DEBUG_RATECONV
+fprintf(stderr, "Copy st=%d rev=%d\n", stereo, reverseStereo);
+fflush(stderr);
+#endif
+ 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);
+ if (len <= 0)
+ return (ST_SUCCESS);
+
+ // Mix the data into the output buffer
+ if (stereo && reverseStereo)
+ ARM_CopyRate_R(len, obuf, vol_l, vol_r, _buffer);
+ else if (stereo)
+ ARM_CopyRate_S(len, obuf, vol_l, vol_r, _buffer);
+ else
+ ARM_CopyRate_M(len, obuf, vol_l, vol_r, _buffer);
+
+ 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