1/*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions, and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32// audio_buffer_tools.h
33// eamoon@meadgroup.com
34//
35// Some straightforward audio buffer-handling routines
36
37#ifndef __AUDIO_BUFFER_TOOLS_H__
38#define __AUDIO_BUFFER_TOOLS_H__
39
40#include <ByteOrder.h>
41#include <Debug.h>
42
43// ---------------------------------------------------------------- //
44// sample conversion +++++
45// 31mar99: providing conversion to and from float, and defining
46// other conversions based on that.
47// ---------------------------------------------------------------- //
48
49/*
50template<class from_sample_t, class to_sample_t>
51void convert_sample(from_sample_t in, to_sample_t& out) {
52	out = (to_sample_t)in; // +++++ arbitrary conversion stub
53}
54*/
55
56inline void convert_sample(float& in, float& out) {
57	out = in;
58}
59
60inline void convert_sample(uchar& in, float& out) {
61	out = (float)(in - 128) / 127.0;
62}
63
64inline void convert_sample(short& in, float& out) {
65	out = (float)in / 32767.0;
66}
67
68inline void convert_sample(int32& in, float& out) {
69	out = (float)in / (float)0x7fffffff;
70}
71
72inline void convert_sample(float& in, uchar& out) {
73	out = (uchar)(in * 127.0);
74}
75
76inline void convert_sample(float& in, short& out) {
77	out = (short)(in * 32767.0);
78}
79
80inline void convert_sample(float& in, int32& out) {
81	out = (int32)(in * 0x7fffffff);
82}
83
84inline void swap_convert_sample(float& in, float& out) {
85	out = B_SWAP_FLOAT(in);
86}
87
88inline void swap_convert_sample(uchar& in, float& out) {
89	// no swap needed for char
90	out = (float)(in - 128) / 127.0;
91}
92
93inline void swap_convert_sample(short& in, float& out) {
94	out = (float)(int16)(B_SWAP_INT16(in)) / 32767.0;
95}
96
97inline void swap_convert_sample(int32& in, float& out) {
98	out = (float)(int32)(B_SWAP_INT32(in)) / (float)0x7fffffff;
99}
100
101inline void swap_convert_sample(float& in, uchar& out) {
102	out = (uchar)((B_SWAP_FLOAT(in)) * 127.0);
103}
104
105inline void swap_convert_sample(float& in, short& out) {
106	out = (short)((B_SWAP_FLOAT(in)) * 32767.0);
107}
108
109inline void swap_convert_sample(float& in, int32& out) {
110	out = (int32)((B_SWAP_FLOAT(in)) * 0x7fffffff);
111}
112
113
114template<class to_sample_t>
115inline void convert_sample(void* pIn, to_sample_t& out, int32 in_audio_format) {
116	switch(in_audio_format) {
117		case media_raw_audio_format::B_AUDIO_UCHAR:
118			convert_sample(*(uchar*)pIn, out);
119			break;
120		case media_raw_audio_format::B_AUDIO_SHORT:
121			convert_sample(*(short*)pIn, out);
122			break;
123		case media_raw_audio_format::B_AUDIO_FLOAT:
124			convert_sample(*(float*)pIn, out);
125			break;
126		case media_raw_audio_format::B_AUDIO_INT:
127			convert_sample(*(int32*)pIn, out);
128			break;
129		default:
130			ASSERT(!"convert_sample(): bad raw_audio_format value");
131	}
132}
133
134template<class from_sample_t>
135inline void convert_sample(from_sample_t in, void* pOut, int32 out_audio_format) {
136	switch(out_audio_format) {
137		case media_raw_audio_format::B_AUDIO_UCHAR:
138			convert_sample(in, *(uchar*)pOut);
139			break;
140		case media_raw_audio_format::B_AUDIO_SHORT:
141			convert_sample(in, *(short*)pOut);
142			break;
143		case media_raw_audio_format::B_AUDIO_FLOAT:
144			convert_sample(in, *(float*)pOut);
145			break;
146		case media_raw_audio_format::B_AUDIO_INT:
147			convert_sample(in, *(int32*)pOut);
148			break;
149		default:
150			ASSERT(!"convert_sample(): bad raw_audio_format value");
151	}
152}
153
154inline void convert_sample(void* pIn, void* pOut,
155	int32 in_audio_format, int32 out_audio_format) {
156
157	// simplest case
158	if(in_audio_format == out_audio_format)
159		return;
160
161	// one-step cases
162	if(in_audio_format == media_raw_audio_format::B_AUDIO_FLOAT)
163		convert_sample(*(float*)pIn, pOut, out_audio_format);
164
165	else if(out_audio_format == media_raw_audio_format::B_AUDIO_FLOAT)
166		convert_sample(pOut, *(float*)pIn, in_audio_format);
167
168	else {
169		// two-step cases
170		float fTemp = 0;
171		convert_sample(pIn, fTemp, in_audio_format);
172		convert_sample(fTemp, pOut, out_audio_format);
173	}
174}
175
176// ---------------------------------------------------------------- //
177// data-copying helper templates
178// ---------------------------------------------------------------- //
179
180// copy from linear buffer to circular buffer; no rescaling
181// returns new offset into destination buffer
182
183template<class from_sample_t, class to_sample_t>
184inline size_t copy_linear_to_circular(
185	from_sample_t* pFrom,
186	to_sample_t* pTo,
187	size_t samples, size_t toOffset, size_t toLength) {
188
189	ASSERT(pFrom != 0);
190	ASSERT(pTo != 0);
191	ASSERT(samples > 0);
192	ASSERT(toLength > 0);
193	ASSERT(toOffset < toLength);
194
195	size_t n = toOffset;
196	for(; samples; samples--) {
197		pTo[n] = (to_sample_t)*pFrom++;
198		if(++n >= toLength)
199			n = 0;
200	}
201
202	return n;
203}
204
205// copy from a linear buffer in one sample format to a circular buffer
206// in another, delegating rescaling duties to convert_sample().
207// returns new offset into destination buffer
208
209template<class from_sample_t, class to_sample_t>
210inline size_t copy_linear_to_circular_convert(
211	from_sample_t* pFrom,
212	to_sample_t* pTo,
213	size_t samples, size_t toOffset, size_t toLength) {
214
215	ASSERT(pFrom != 0);
216	ASSERT(pTo != 0);
217	ASSERT(samples > 0);
218	ASSERT(toLength > 0);
219	ASSERT(toOffset < toLength);
220
221	size_t n = toOffset;
222	for(; samples; samples--) {
223		convert_sample(*pFrom++, pTo[n]);
224		if(++n >= toLength)
225			n = 0;
226	}
227
228	return n;
229}
230
231// copy from linear buffer to circular buffer; no rescaling
232// returns new offset into source buffer
233
234template<class from_sample_t, class to_sample_t>
235inline size_t copy_circular_to_linear(
236	from_sample_t* pFrom,
237	to_sample_t* pTo,
238	size_t samples, size_t fromOffset, size_t fromLength) {
239
240	ASSERT(pFrom != 0);
241	ASSERT(pTo != 0);
242	ASSERT(samples > 0);
243	ASSERT(fromLength > 0);
244	ASSERT(fromOffset < fromLength);
245
246	size_t n = fromOffset;
247	for(; samples; samples--) {
248		*pTo++ = (to_sample_t)pFrom[n];
249		if(++n >= fromLength)
250			n = 0;
251	}
252
253	return n;
254}
255
256// copy from a circular buffer in one sample format to a linear buffer
257// in another, delegating rescaling duties to convert_sample().
258// returns new offset into source buffer
259
260template<class from_sample_t, class to_sample_t>
261inline size_t copy_circular_to_linear_convert(
262	from_sample_t* pFrom,
263	to_sample_t* pTo,
264	size_t samples, size_t fromOffset, size_t fromLength) {
265
266	ASSERT(pFrom != 0);
267	ASSERT(pTo != 0);
268	ASSERT(samples > 0);
269	ASSERT(fromLength > 0);
270	ASSERT(fromOffset < fromLength);
271
272	size_t n = fromOffset;
273	for(; samples; samples--) {
274		convert_sample(pFrom[n], *pTo++);
275		if(++n >= fromLength)
276			n = 0;
277	}
278
279	return n;
280}
281
282//// copy between circular buffers in the same format
283//// +++++ re-templatize
284//
285///*template<class samp_t>
286//inline*/ void copy_circular_to_circular(
287//	sample_t* pFrom,
288//	sample_t* pTo,
289//	size_t samples,
290//	size_t& ioFromOffset, size_t fromLength,
291//	size_t& ioToOffset, size_t toLength);
292
293// mix from a linear buffer to a circular buffer (no rescaling)
294// input samples are multiplied by fSourceGain; destination samples
295// are multiplied by fFeedback
296// returns new offset within destination buffer
297
298template<class from_sample_t, class to_sample_t>
299inline size_t mix_linear_to_circular(
300	from_sample_t* pFrom,
301	to_sample_t* pTo,
302	uint32 samples,
303	uint32 toOffset,
304	uint32 toLength,
305	float fSourceGain,
306	float fFeedback) {
307
308	// feedback
309	size_t n, nLength;
310	if(fFeedback == 0.0) // +++++ memset?
311		for(n = toOffset, nLength = samples;
312			nLength;
313			n = (n+1 == toLength) ? 0 : n+1, nLength--) { pTo[n] = 0.0; }
314	else if(fFeedback != 1.0)
315		for(n = toOffset, nLength = samples;
316			nLength;
317			n = (n+1 == toLength) ? 0 : n+1, nLength--) { pTo[n] *= fFeedback; }
318	// else nothing to do
319
320	// mix source, unless muted or not provided
321	if(pFrom && fSourceGain != 0.0) {
322		if(fSourceGain == 1.0)
323			for(n = toOffset, nLength = samples;
324				nLength;
325				n = (n+1 == toLength) ? 0 : n+1, nLength--) {
326				pTo[n] += (to_sample_t)*pFrom++;
327			}
328		else
329			for(n = toOffset, nLength = samples;
330				nLength;
331				n = (n+1 == toLength) ? 0 : n+1, nLength--) {
332				pTo[n] += (to_sample_t)*pFrom++ * fSourceGain; // +++++ re-cast to dest format?
333			}
334	}
335
336	// increment loop position w/ rollover
337	toOffset += samples;
338	if(toOffset >= toLength)
339		toOffset -= toLength;
340
341	return toOffset;
342}
343
344// mix from a linear buffer in one sample format to a
345// circular buffer in another, delegating to convert_sample() for rescaling
346// input samples are multiplied by fSourceGain; destination samples
347// are multiplied by fFeedback
348// returns new offset within destination buffer
349
350template<class from_sample_t, class to_sample_t>
351inline size_t mix_linear_to_circular_convert(
352	from_sample_t* pFrom,
353	to_sample_t* pTo,
354	size_t samples,
355	size_t toOffset,
356	size_t toLength,
357	float fSourceGain,
358	float fFeedback) {
359
360	// feedback
361	size_t n, nLength;
362	if(fFeedback == 0.0) // +++++ memset?
363		for(n = toOffset, nLength = samples;
364			nLength;
365			n = (n+1 == toLength) ? 0 : n+1, nLength--) { pTo[n] = 0.0; }
366	else if(fFeedback != 1.0)
367		for(n = toOffset, nLength = samples;
368			nLength;
369			n = (n+1 == toLength) ? 0 : n+1, nLength--) { pTo[n] *= fFeedback; }
370	// else nothing to do
371
372	// mix source, unless muted or not provided
373	if(pFrom && fSourceGain != 0.0) {
374		if(fSourceGain == 1.0)
375			for(n = toOffset, nLength = samples;
376				nLength;
377				n = (n+1 == toLength) ? 0 : n+1, nLength--) {
378				to_sample_t from;
379				convert_sample(*pFrom++, from);
380				pTo[n] += from;
381			}
382		else
383			for(n = toOffset, nLength = samples;
384				nLength;
385				n = (n+1 == toLength) ? 0 : n+1, nLength--) {
386				to_sample_t from;
387				convert_sample(*pFrom++, from);
388				pTo[n] += from * fSourceGain; // +++++ re-cast to dest format?
389			}
390	}
391
392	// increment loop position w/ rollover
393	toOffset += samples;
394	if(toOffset >= toLength)
395		toOffset -= toLength;
396
397	return toOffset;
398}
399
400#endif /* __AUDIO_BUFFER_TOOLS_H__ */
401