1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * The code in this file, together with the rate_arm_asm.s file offers
25  * an ARM optimised version of the code in rate.cpp. The operation of this
26  * code should be identical to that of rate.cpp, but faster. The heavy
27  * lifting is done in the assembler file.
28  *
29  * To be as portable as possible we implement the core routines with C
30  * linkage in assembly, and implement the C++ routines that call into
31  * the C here. The C++ symbol mangling varies wildly between compilers,
32  * so this is the simplest way to ensure that the C/C++ combination should
33  * work on as many ARM based platforms as possible.
34  *
35  * Essentially the algorithm herein is the same as that in rate.cpp, so
36  * anyone seeking to understand this should attempt to understand that
37  * first. That code was based in turn on code with Copyright 1998 Fabrice
38  * Bellard - part of SoX (http://sox.sourceforge.net).
39  * Max Horn adapted that code to the needs of ScummVM and partially rewrote
40  * it, in the process removing any use of floating point arithmetic. Various
41  * other improvments over the original code were made.
42  */
43 
44 #include "audio/audiostream.h"
45 #include "audio/rate.h"
46 #include "audio/mixer.h"
47 #include "common/util.h"
48 #include "common/textconsole.h"
49 
50 //#define DEBUG_RATECONV
51 
52 namespace Audio {
53 
54 /**
55  * The precision of the fractional computations used by the rate converter.
56  * Normally you should never have to modify this value.
57  * This stuff is defined in common/frac.h, but we redefine it here as the
58  * ARM routine we call doesn't respect those definitions.
59  */
60 #define FRAC_BITS 16
61 #define FRAC_ONE  (1 << FRAC_BITS)
62 
63 /**
64  * The size of the intermediate input cache. Bigger values may increase
65  * performance, but only until some point (depends largely on cache size,
66  * target processor and various other factors), at which it will decrease
67  * again.
68  */
69 #define INTERMEDIATE_BUFFER_SIZE 512
70 
71 /**
72  * The default fractional type in frac.h (with 16 fractional bits) limits
73  * the rate conversion code to 65536Hz audio: we need to able to handle
74  * 96kHz audio, so we use fewer fractional bits in this code.
75  */
76 enum {
77 	FRAC_BITS_LOW = 15,
78 	FRAC_ONE_LOW = (1L << FRAC_BITS_LOW),
79 	FRAC_HALF_LOW = (1L << (FRAC_BITS_LOW-1))
80 };
81 
82 /**
83  * Audio rate converter based on simple resampling. Used when no
84  * interpolation is required.
85  *
86  * Limited to sampling frequency <= 65535 Hz.
87  */
88 typedef struct {
89 	const st_sample_t *inPtr;
90 	int inLen;
91 
92 	/** position of how far output is ahead of input */
93 	/** Holds what would have been opos-ipos */
94 	long opos;
95 
96 	/** fractional position increment in the output stream */
97 	long opos_inc;
98 
99 	st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
100 } SimpleRateDetails;
101 
102 template<bool stereo, bool reverseStereo>
103 class SimpleRateConverter : public RateConverter {
104 protected:
105 	SimpleRateDetails  sr;
106 public:
107 	SimpleRateConverter(st_rate_t inrate, st_rate_t outrate);
108 	int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
drain(st_sample_t * obuf,st_size_t osamp,st_volume_t vol)109 	int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
110 		return (ST_SUCCESS);
111 	}
112 };
113 
114 
115 /*
116  * Prepare processing.
117  */
118 template<bool stereo, bool reverseStereo>
SimpleRateConverter(st_rate_t inrate,st_rate_t outrate)119 SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) {
120 	if (inrate == outrate) {
121 		error("Input and Output rates must be different to use rate effect");
122 	}
123 
124 	if ((inrate % outrate) != 0) {
125 		error("Input rate must be a multiple of Output rate to use rate effect");
126 	}
127 
128 	if (inrate >= 65536 || outrate >= 65536) {
129 		error("rate effect can only handle rates < 65536");
130 	}
131 
132 	sr.opos = 1;
133 
134 	/* increment */
135 	sr.opos_inc = inrate / outrate;
136 
137 	sr.inLen = 0;
138 }
139 
140 #ifndef IPHONE
141 #define ARM_SimpleRate_M _ARM_SimpleRate_M
142 #define ARM_SimpleRate_S _ARM_SimpleRate_S
143 #define ARM_SimpleRate_R _ARM_SimpleRate_R
144 #endif
145 
146 extern "C" st_sample_t *ARM_SimpleRate_M(
147 								AudioStream &input,
148 								int (*fn)(Audio::AudioStream&,int16*,int),
149 								SimpleRateDetails *sr,
150 								st_sample_t *obuf,
151 								st_size_t osamp,
152 								st_volume_t vol_l,
153 								st_volume_t vol_r);
154 
155 extern "C" st_sample_t *ARM_SimpleRate_S(
156 								AudioStream &input,
157 								int (*fn)(Audio::AudioStream&,int16*,int),
158 								SimpleRateDetails *sr,
159 								st_sample_t *obuf,
160 								st_size_t osamp,
161 								st_volume_t vol_l,
162 								st_volume_t vol_r);
163 
164 extern "C" st_sample_t *ARM_SimpleRate_R(
165 								AudioStream &input,
166 								int (*fn)(Audio::AudioStream&,int16*,int),
167 								SimpleRateDetails *sr,
168 								st_sample_t *obuf,
169 								st_size_t osamp,
170 								st_volume_t vol_l,
171 								st_volume_t vol_r);
172 
SimpleRate_readFudge(Audio::AudioStream & input,int16 * a,int b)173 extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, int16 *a, int b)
174 {
175 #ifdef DEBUG_RATECONV
176 	debug("Reading ptr=%x n%d", a, b);
177 #endif
178 	return input.readBuffer(a, b);
179 }
180 
181 template<bool stereo, bool reverseStereo>
flow(AudioStream & input,st_sample_t * obuf,st_size_t osamp,st_volume_t vol_l,st_volume_t vol_r)182 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) {
183 
184 #ifdef DEBUG_RATECONV
185 	debug("Simple st=%d rev=%d", stereo, reverseStereo);
186 #endif
187 	st_sample_t *ostart = obuf;
188 
189 	if (!stereo) {
190 		obuf = ARM_SimpleRate_M(input,
191 								&SimpleRate_readFudge,
192 								&sr,
193 								obuf, osamp, vol_l, vol_r);
194 	} else if (reverseStereo) {
195 		obuf = ARM_SimpleRate_R(input,
196 								&SimpleRate_readFudge,
197 								&sr,
198 								obuf, osamp, vol_l, vol_r);
199 	} else {
200 		obuf = ARM_SimpleRate_S(input,
201 								&SimpleRate_readFudge,
202 								&sr,
203 								obuf, osamp, vol_l, vol_r);
204 	}
205 
206 	return (obuf - ostart) / 2;
207 }
208 
209 /**
210  * Audio rate converter based on simple linear Interpolation.
211  *
212  * The use of fractional increment allows us to use no buffer. It
213  * avoid the problems at the end of the buffer we had with the old
214  * method which stored a possibly big buffer of size
215  * lcm(in_rate,out_rate).
216  *
217  * Limited to sampling frequency <= 65535 Hz.
218  */
219 
220 typedef struct {
221 	const st_sample_t *inPtr;
222 	int inLen;
223 
224 	/** position of how far output is ahead of input */
225 	/** Holds what would have been opos-ipos<<16 + opos_frac */
226 	long opos;
227 
228 	/** integer position increment in the output stream */
229 	long opos_inc;
230 
231 	/** current sample(s) in the input stream (left/right channel) */
232 	st_sample_t icur[2];
233 	/** last sample(s) in the input stream (left/right channel) */
234 	/** Note, these are deliberately ints, not st_sample_t's */
235 	int32 ilast[2];
236 
237 	st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
238 } LinearRateDetails;
239 
240 extern "C" {
241 #ifndef IPHONE
242 #define ARM_LinearRate_M _ARM_LinearRate_M
243 #define ARM_LinearRate_S _ARM_LinearRate_S
244 #define ARM_LinearRate_R _ARM_LinearRate_R
245 #endif
246 }
247 
248 extern "C" st_sample_t *ARM_LinearRate_M(
249 								AudioStream &input,
250 								int (*fn)(Audio::AudioStream&,int16*,int),
251 								LinearRateDetails *lr,
252 								st_sample_t *obuf,
253 								st_size_t osamp,
254 								st_volume_t vol_l,
255 								st_volume_t vol_r);
256 
257 extern "C" st_sample_t *ARM_LinearRate_S(
258 								AudioStream &input,
259 								int (*fn)(Audio::AudioStream&,int16*,int),
260 								LinearRateDetails *lr,
261 								st_sample_t *obuf,
262 								st_size_t osamp,
263 								st_volume_t vol_l,
264 								st_volume_t vol_r);
265 
266 extern "C" st_sample_t *ARM_LinearRate_R(
267 								AudioStream &input,
268 								int (*fn)(Audio::AudioStream&,int16*,int),
269 								LinearRateDetails *lr,
270 								st_sample_t *obuf,
271 								st_size_t osamp,
272 								st_volume_t vol_l,
273 								st_volume_t vol_r);
274 
275 template<bool stereo, bool reverseStereo>
276 class LinearRateConverter : public RateConverter {
277 protected:
278 	LinearRateDetails lr;
279 
280 public:
281 	LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
282 	int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
drain(st_sample_t * obuf,st_size_t osamp,st_volume_t vol)283 	int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
284 		return (ST_SUCCESS);
285 	}
286 };
287 
288 
289 /*
290  * Prepare processing.
291  */
292 template<bool stereo, bool reverseStereo>
LinearRateConverter(st_rate_t inrate,st_rate_t outrate)293 LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
294 	unsigned long incr;
295 
296 	if (inrate == outrate) {
297 		error("Input and Output rates must be different to use rate effect");
298 	}
299 
300 	if (inrate >= 131072 || outrate >= 131072) {
301 		error("rate effect can only handle rates < 131072");
302 	}
303 
304 	lr.opos = FRAC_ONE_LOW;
305 
306 	/* increment */
307 	incr = (inrate << FRAC_BITS_LOW) / outrate;
308 
309 	lr.opos_inc = incr;
310 
311 	// FIXME: Does 32768 here need changing to 65536 or 0? Compare to rate.cpp code...
312 	lr.ilast[0] = lr.ilast[1] = 32768;
313 	lr.icur[0] = lr.icur[1] = 0;
314 
315 	lr.inLen = 0;
316 }
317 
318 /*
319  * Processed signed long samples from ibuf to obuf.
320  * Return number of sample pairs processed.
321  */
322 template<bool stereo, bool reverseStereo>
flow(AudioStream & input,st_sample_t * obuf,st_size_t osamp,st_volume_t vol_l,st_volume_t vol_r)323 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) {
324 
325 #ifdef DEBUG_RATECONV
326 	debug("Linear st=%d rev=%d", stereo, reverseStereo);
327 #endif
328 	st_sample_t *ostart = obuf;
329 
330 	if (vol_l > 0xff)
331 		vol_l = 0xff;
332 
333 	if (vol_r > 0xff)
334 		vol_r = 0xff;
335 
336 	if (!stereo) {
337 		obuf = ARM_LinearRate_M(input,
338 								&SimpleRate_readFudge,
339 								&lr,
340 								obuf, osamp, vol_l, vol_r);
341 	} else if (reverseStereo) {
342 		obuf = ARM_LinearRate_R(input,
343 								&SimpleRate_readFudge,
344 								&lr,
345 								obuf, osamp, vol_l, vol_r);
346 	} else {
347 		obuf = ARM_LinearRate_S(input,
348 								&SimpleRate_readFudge,
349 								&lr,
350 								obuf, osamp, vol_l, vol_r);
351 	}
352 	return (obuf - ostart) / 2;
353 }
354 
355 
356 #pragma mark -
357 
358 
359 /**
360  * Simple audio rate converter for the case that the inrate equals the outrate.
361  */
362 extern "C" {
363 #ifndef IPHONE
364 #define ARM_CopyRate_M _ARM_CopyRate_M
365 #define ARM_CopyRate_S _ARM_CopyRate_S
366 #define ARM_CopyRate_R _ARM_CopyRate_R
367 #endif
368 }
369 
370 extern "C" st_sample_t *ARM_CopyRate_M(
371 								st_size_t len,
372 								st_sample_t *obuf,
373 								st_volume_t vol_l,
374 								st_volume_t vol_r,
375 								st_sample_t *_buffer);
376 
377 extern "C" st_sample_t *ARM_CopyRate_S(
378 								st_size_t len,
379 								st_sample_t *obuf,
380 								st_volume_t vol_l,
381 								st_volume_t vol_r,
382 								st_sample_t *_buffer);
383 
384 extern "C" st_sample_t *ARM_CopyRate_R(
385 								st_size_t len,
386 								st_sample_t *obuf,
387 								st_volume_t vol_l,
388 								st_volume_t vol_r,
389 								st_sample_t *_buffer);
390 
391 
392 template<bool stereo, bool reverseStereo>
393 class CopyRateConverter : public RateConverter {
394 	st_sample_t *_buffer;
395 	st_size_t _bufferSize;
396 
397 public:
CopyRateConverter()398 	CopyRateConverter() : _buffer(0), _bufferSize(0) {}
~CopyRateConverter()399 	~CopyRateConverter() {
400 		free(_buffer);
401 	}
402 
flow(AudioStream & input,st_sample_t * obuf,st_size_t osamp,st_volume_t vol_l,st_volume_t vol_r)403 	virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
404 		assert(input.isStereo() == stereo);
405 
406 #ifdef DEBUG_RATECONV
407 		debug("Copy st=%d rev=%d", stereo, reverseStereo);
408 #endif
409 		st_size_t len;
410 		st_sample_t *ostart = obuf;
411 
412 		if (stereo)
413 			osamp *= 2;
414 
415 		// Reallocate temp buffer, if necessary
416 		if (osamp > _bufferSize) {
417 			free(_buffer);
418 			_buffer = (st_sample_t *)malloc(osamp * 2);
419 			_bufferSize = osamp;
420 		}
421 
422 		// Read up to 'osamp' samples into our temporary buffer
423 		len = input.readBuffer(_buffer, osamp);
424 		if (len <= 0)
425 			return 0;
426 
427 		// Mix the data into the output buffer
428 		if (stereo && reverseStereo)
429 			obuf = ARM_CopyRate_R(len, obuf, vol_l, vol_r, _buffer);
430 		else if (stereo)
431 			obuf = ARM_CopyRate_S(len, obuf, vol_l, vol_r, _buffer);
432 		else
433 			obuf = ARM_CopyRate_M(len, obuf, vol_l, vol_r, _buffer);
434 
435 		return (obuf - ostart) / 2;
436 	}
437 
drain(st_sample_t * obuf,st_size_t osamp,st_volume_t vol)438 	virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
439 		return (ST_SUCCESS);
440 	}
441 };
442 
443 
444 #pragma mark -
445 
446 
447 /**
448  * Create and return a RateConverter object for the specified input and output rates.
449  */
makeRateConverter(st_rate_t inrate,st_rate_t outrate,bool stereo,bool reverseStereo)450 RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) {
451 	if (inrate != outrate) {
452 		if ((inrate % outrate) == 0 && (inrate < 65536)) {
453 			if (stereo) {
454 				if (reverseStereo)
455 					return new SimpleRateConverter<true, true>(inrate, outrate);
456 				else
457 					return new SimpleRateConverter<true, false>(inrate, outrate);
458 			} else
459 				return new SimpleRateConverter<false, false>(inrate, outrate);
460 		} else {
461 			if (stereo) {
462 				if (reverseStereo)
463 					return new LinearRateConverter<true, true>(inrate, outrate);
464 				else
465 					return new LinearRateConverter<true, false>(inrate, outrate);
466 			} else
467 				return new LinearRateConverter<false, false>(inrate, outrate);
468 		 }
469 	} else {
470 		if (stereo) {
471 			if (reverseStereo)
472 				return new CopyRateConverter<true, true>();
473 			else
474 				return new CopyRateConverter<true, false>();
475 		} else
476 			return new CopyRateConverter<false, false>();
477 	}
478 }
479 
480 } // End of namespace Audio
481