/* ScummVM - Scumm Interpreter * Copyright (C) 2001-2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ /* * 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 "stdafx.h" #include "sound/rate.h" #define FRAC_BITS 16 /** * 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 class LinearRateConverter : public RateConverter { protected: bool _reverseStereo; /** fractional position of the output stream in input stream unit */ unsigned 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; /** last sample(s) in the input stream (left/right channel) */ st_sample_t ilast[2]; /** current sample(s) in the input stream (left/right channel) */ st_sample_t icur[2]; public: LinearRateConverter(st_rate_t inrate, st_rate_t outrate); int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol); int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { return (ST_SUCCESS); } }; /* * Prepare processing. */ template LinearRateConverter::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; ipos = 0; ilast[0] = ilast[1] = 0; icur[0] = icur[1] = 0; } /* * Processed signed long samples from ibuf to obuf. * Return number of samples processed. */ template int LinearRateConverter::flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { st_sample_t *ostart, *oend; st_sample_t out[2], tmpOut; ostart = obuf; oend = obuf + osamp * 2; while (obuf < oend) { // read enough input samples so that ipos > opos while (ipos <= opos) { // Abort if we reached the end of the input buffer if (input.eof()) goto the_end; ilast[0] = icur[0]; icur[0] = input.read(); if (stereo) { ilast[1] = icur[1]; icur[1] = input.read(); } ipos++; } // Loop as long as the outpos trails behind, and as long as there is // still space in the output buffer. while (ipos > opos) { // interpolate tmpOut = (st_sample_t)(ilast[0] + (((icur[0] - ilast[0]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS)); // adjust volume out[0] = out[1] = (st_sample_t)((tmpOut * vol) >> 8); if (stereo) { // interpolate tmpOut = (st_sample_t)(ilast[1] + (((icur[1] - ilast[1]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS)); // adjust volume out[reverseStereo ? 0 : 1] = (st_sample_t)((tmpOut * vol) >> 8); } // output left channel clampedAdd(*obuf++, out[0]); // output right channel clampedAdd(*obuf++, out[1]); // Increment output position unsigned 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); } #pragma mark - /** * Simple audio rate converter for the case that the inrate equals the outrate. */ template class CopyRateConverter : public RateConverter { public: virtual int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { int16 tmp[2]; st_size_t len = osamp; assert(input.isStereo() == stereo); while (!input.eof() && len--) { tmp[0] = tmp[1] = (input.read() * vol) >> 8; if (stereo) tmp[reverseStereo ? 0 : 1] = (input.read() * vol) >> 8; clampedAdd(*obuf++, tmp[0]); clampedAdd(*obuf++, tmp[1]); } 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 (stereo) { if (reverseStereo) return new LinearRateConverter(inrate, outrate); else return new LinearRateConverter(inrate, outrate); } else return new LinearRateConverter(inrate, outrate); //return new ResampleRateConverter(inrate, outrate, 1); } else { if (stereo) { if (reverseStereo) return new CopyRateConverter(); else return new CopyRateConverter(); } else return new CopyRateConverter(); } }