summaryrefslogtreecommitdiffstats
path: root/arm-fm-22k/lib_src/eas_mixer.c
blob: c4a2f9f9ba807f814ef6f842337bb14b4396fd94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
/*----------------------------------------------------------------------------
 *
 * File: 
 * eas_mixer.c
 *
 * Contents and purpose:
 * This file contains the critical components of the mix engine that
 * must be optimized for best performance.
 *			
 * Copyright Sonic Network Inc. 2005

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *----------------------------------------------------------------------------
 * Revision Control:
 *   $Revision: 706 $
 *   $Date: 2007-05-31 17:22:51 -0700 (Thu, 31 May 2007) $
 *----------------------------------------------------------------------------
*/

//3 dls: This module is in the midst of being converted from a synth
//3 specific module to a general purpose mix engine

/*------------------------------------
 * includes
 *------------------------------------
*/
#include "eas_data.h"
#include "eas_host.h"
#include "eas_math.h"
#include "eas_mixer.h"
#include "eas_config.h"
#include "eas_report.h"

#ifdef _MAXIMIZER_ENABLED
EAS_I32 MaximizerProcess (EAS_VOID_PTR pInstData, EAS_I32 *pSrc, EAS_I32 *pDst, EAS_I32 numSamples);
#endif

/*------------------------------------
 * defines
 *------------------------------------
*/

/* need to boost stereo by ~3dB to compensate for the panner */
#define STEREO_3DB_GAIN_BOOST		512

/*----------------------------------------------------------------------------
 * EAS_MixEngineInit()
 *----------------------------------------------------------------------------
 * Purpose:
 * Prepares the mix engine for work, allocates buffers, locates effects modules, etc.
 *
 * Inputs:
 * pEASData		 	- instance data
 * pInstData		- pointer to variable to receive instance data handle
 *
 * Outputs:
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_MixEngineInit (S_EAS_DATA *pEASData)
{
	
	/* check Configuration Module for mix buffer allocation */
	if (pEASData->staticMemoryModel)
		pEASData->pMixBuffer = EAS_CMEnumData(EAS_CM_MIX_BUFFER);
	else
		pEASData->pMixBuffer = EAS_HWMalloc(pEASData->hwInstData, BUFFER_SIZE_IN_MONO_SAMPLES * NUM_OUTPUT_CHANNELS * sizeof(EAS_I32));
	if (pEASData->pMixBuffer == NULL)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL, "Failed to allocate mix buffer memory\n"); */ }
		return EAS_ERROR_MALLOC_FAILED;
	}
	EAS_HWMemSet((void *)(pEASData->pMixBuffer), 0, BUFFER_SIZE_IN_MONO_SAMPLES * NUM_OUTPUT_CHANNELS * sizeof(EAS_I32));

	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_MixEnginePrep()
 *----------------------------------------------------------------------------
 * Purpose:
 * Performs prep before synthesize a buffer of audio, such as clearing
 * audio buffers, etc.
 *
 * Inputs:
 * psEASData - pointer to overall EAS data structure
 *
 * Outputs:
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
void EAS_MixEnginePrep (S_EAS_DATA *pEASData, EAS_I32 numSamples)
{

	/* clear the mix buffer */
#if (NUM_OUTPUT_CHANNELS == 2)
	EAS_HWMemSet(pEASData->pMixBuffer, 0, numSamples * (EAS_I32) sizeof(long) * 2);
#else
	EAS_HWMemSet(pEASData->pMixBuffer, 0, (EAS_I32) numSamples * (EAS_I32) sizeof(long));
#endif

	/* need to clear other side-chain effect buffers (chorus & reverb) */
}

/*----------------------------------------------------------------------------
 * EAS_MixEnginePost
 *----------------------------------------------------------------------------
 * Purpose: 
 * This routine does the post-processing after all voices have been
 * synthesized. It calls any sweeteners and does the final mixdown to
 * the output buffer.
 * 
 * Inputs: 
 *			
 * Outputs:
 *
 * Notes:
 *----------------------------------------------------------------------------
*/
void EAS_MixEnginePost (S_EAS_DATA *pEASData, EAS_I32 numSamples)
{
	EAS_U16 gain;

//3 dls: Need to restore the mix engine metrics

	/* calculate the gain multiplier */
#ifdef _MAXIMIZER_ENABLED
	if (pEASData->effectsModules[EAS_MODULE_MAXIMIZER].effect)
	{
		EAS_I32 temp;
		temp = MaximizerProcess(pEASData->effectsModules[EAS_MODULE_MAXIMIZER].effectData, pEASData->pMixBuffer, pEASData->pMixBuffer, numSamples);
		temp = (temp * pEASData->masterGain) >> 15;
		if (temp > 32767)
			gain = 32767;
		else
			gain = (EAS_U16) temp;
	}
	else
		gain = (EAS_U16) pEASData->masterGain;
#else
	gain = (EAS_U16) pEASData->masterGain;
#endif

	/* Not using all the gain bits for now
	 * Reduce the input to the compressor by 6dB to prevent saturation
	 */
#ifdef _COMPRESSOR_ENABLED
	if (pEASData->effectsModules[EAS_MODULE_COMPRESSOR].effectData)
		gain = gain >> 5;
	else
		gain = gain >> 4;
#else
	gain = gain >> 4;
#endif

	/* convert 32-bit mix buffer to 16-bit output format */
#if (NUM_OUTPUT_CHANNELS == 2)	
	SynthMasterGain(pEASData->pMixBuffer, pEASData->pOutputAudioBuffer, gain, (EAS_U16) ((EAS_U16) numSamples * 2));
#else	
	SynthMasterGain(pEASData->pMixBuffer, pEASData->pOutputAudioBuffer, gain, (EAS_U16) numSamples);
#endif

#ifdef _ENHANCER_ENABLED
	/* enhancer effect */
	if (pEASData->effectsModules[EAS_MODULE_ENHANCER].effectData)
		(*pEASData->effectsModules[EAS_MODULE_ENHANCER].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_ENHANCER].effectData,
			pEASData->pOutputAudioBuffer,
			pEASData->pOutputAudioBuffer,
			numSamples);
#endif	
	
#ifdef _GRAPHIC_EQ_ENABLED	
	/* graphic EQ effect */
	if (pEASData->effectsModules[EAS_MODULE_GRAPHIC_EQ].effectData)
		(*pEASData->effectsModules[EAS_MODULE_GRAPHIC_EQ].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_GRAPHIC_EQ].effectData,
			pEASData->pOutputAudioBuffer,
			pEASData->pOutputAudioBuffer,
			numSamples);
#endif	
	
#ifdef _COMPRESSOR_ENABLED	
	/* compressor effect */
	if (pEASData->effectsModules[EAS_MODULE_COMPRESSOR].effectData)
		(*pEASData->effectsModules[EAS_MODULE_COMPRESSOR].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_COMPRESSOR].effectData,
			pEASData->pOutputAudioBuffer,
			pEASData->pOutputAudioBuffer,
			numSamples);
#endif	

#ifdef _WOW_ENABLED	
	/* WOW requires a 32-bit buffer, borrow the mix buffer and
	 * pass it as the destination buffer
	 */
	/*lint -e{740} temporarily passing a parameter through an existing I/F */
	if (pEASData->effectsModules[EAS_MODULE_WOW].effectData)
		(*pEASData->effectsModules[EAS_MODULE_WOW].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_WOW].effectData,
			pEASData->pOutputAudioBuffer,
			(EAS_PCM*) pEASData->pMixBuffer,
			numSamples);
#endif	

#ifdef _TONECONTROLEQ_ENABLED
	/* ToneControlEQ effect */
	if (pEASData->effectsModules[EAS_MODULE_TONECONTROLEQ].effectData)
		(*pEASData->effectsModules[EAS_MODULE_TONECONTROLEQ].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_TONECONTROLEQ].effectData,
			pEASData->pOutputAudioBuffer,
			pEASData->pOutputAudioBuffer,
			numSamples);
#endif	

#ifdef _REVERB_ENABLED
	/* Reverb effect */
	if (pEASData->effectsModules[EAS_MODULE_REVERB].effectData)
		(*pEASData->effectsModules[EAS_MODULE_REVERB].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_REVERB].effectData,
			pEASData->pOutputAudioBuffer,
			pEASData->pOutputAudioBuffer,
			numSamples);
#endif	

#ifdef _CHORUS_ENABLED
	/* Chorus effect */
	if (pEASData->effectsModules[EAS_MODULE_CHORUS].effectData)
		(*pEASData->effectsModules[EAS_MODULE_CHORUS].effect->pfProcess)
			(pEASData->effectsModules[EAS_MODULE_CHORUS].effectData,
			pEASData->pOutputAudioBuffer,
			pEASData->pOutputAudioBuffer,
			numSamples);
#endif	

}

#ifndef NATIVE_EAS_KERNEL
/*----------------------------------------------------------------------------
 * SynthMasterGain
 *----------------------------------------------------------------------------
 * Purpose:
 * Mixes down audio from 32-bit to 16-bit target buffer
 *
 * Inputs: 
 *			
 * Outputs:
 *
 *----------------------------------------------------------------------------
*/
void SynthMasterGain (long *pInputBuffer, EAS_PCM *pOutputBuffer, EAS_U16 nGain, EAS_U16 numSamples) {

	/* loop through the buffer */
	while (numSamples--) {
		long s;
		
		/* read a sample from the input buffer and add some guard bits */
		s = *pInputBuffer++;
		
		/* add some guard bits */
		/*lint -e{704} <avoid divide for performance>*/
		s = s >> 7;
		
		/* apply master gain */
		s *= (long) nGain;

		/* shift to lower 16-bits */
		/*lint -e{704} <avoid divide for performance>*/
		s = s >> 9;
		
		/* saturate */
		s = SATURATE(s);

		*pOutputBuffer++ = (EAS_PCM)s;
	}
}
#endif

/*----------------------------------------------------------------------------
 * EAS_MixEngineShutdown()
 *----------------------------------------------------------------------------
 * Purpose:
 * Shuts down effects modules and deallocates memory
 *
 * Inputs:
 * pEASData		 	- instance data
 * pInstData		- instance data handle
 *
 * Outputs:
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_MixEngineShutdown (S_EAS_DATA *pEASData)
{

	/* check Configuration Module for static memory allocation */
	if (!pEASData->staticMemoryModel && (pEASData->pMixBuffer != NULL))
		EAS_HWFree(pEASData->hwInstData, pEASData->pMixBuffer);

	return EAS_SUCCESS;
}

#ifdef UNIFIED_MIXER
#ifndef NATIVE_MIX_STREAM
/*----------------------------------------------------------------------------
 * EAS_MixStream
 *----------------------------------------------------------------------------
 * Mix a 16-bit stream into a 32-bit buffer
 *
 * pInputBuffer	16-bit input buffer
 * pMixBuffer	32-bit mix buffer
 * numSamples	number of samples to mix
 * gainLeft		initial gain left or mono
 * gainRight	initial gain right
 * gainLeft		left gain increment per sample
 * gainRight	right gain increment per sample
 * flags		bit 0 = stereo source
 * 				bit 1 = stereo output
 *----------------------------------------------------------------------------
*/
void EAS_MixStream (EAS_PCM *pInputBuffer, EAS_I32 *pMixBuffer, EAS_I32 numSamples, EAS_I32 gainLeft, EAS_I32 gainRight, EAS_I32 gainIncLeft, EAS_I32 gainIncRight, EAS_I32 flags)
{
	EAS_I32 temp;
	EAS_INT src, dest;

	/* NOTE: There are a lot of optimizations that can be done 
	 * in the native implementations based on register
	 * availability, etc. For example, it may make sense to
	 * break this down into 8 separate routines:
	 *
	 * 1. Mono source to mono output
	 * 2. Mono source to stereo output
	 * 3. Stereo source to mono output
	 * 4. Stereo source to stereo output
	 * 5. Mono source to mono output - no gain change
	 * 6. Mono source to stereo output - no gain change
	 * 7. Stereo source to mono output - no gain change
	 * 8. Stereo source to stereo output - no gain change
	 *
	 * Other possibilities include loop unrolling, skipping
	 * a gain calculation every 2 or 4 samples, etc.
	 */

	/* no gain change, use fast loops */	
	if ((gainIncLeft == 0) && (gainIncRight == 0))
	{
		switch (flags & (MIX_FLAGS_STEREO_SOURCE | MIX_FLAGS_STEREO_OUTPUT))
		{
			/* mono to mono */
			case 0:
				gainLeft >>= 15;
				for (src = dest = 0; src < numSamples; src++, dest++)
				{
					
					pMixBuffer[dest] += (pInputBuffer[src] * gainLeft) >> NUM_MIXER_GUARD_BITS;
				}
				break;
				
			/* mono to stereo */
			case MIX_FLAGS_STEREO_OUTPUT:
				gainLeft >>= 15;
				gainRight >>= 15;
				for (src = dest = 0; src < numSamples; src++, dest+=2)
				{
					pMixBuffer[dest] += (pInputBuffer[src] * gainLeft) >> NUM_MIXER_GUARD_BITS;
					pMixBuffer[dest+1] += (pInputBuffer[src] * gainRight) >> NUM_MIXER_GUARD_BITS;
				}
				break;

			/* stereo to mono */
			case MIX_FLAGS_STEREO_SOURCE:
				gainLeft >>= 15;
				gainRight >>= 15;
				for (src = dest = 0; src < numSamples; src+=2, dest++)
				{
					temp = (pInputBuffer[src] * gainLeft) >> NUM_MIXER_GUARD_BITS;
					temp += ((pInputBuffer[src+1] * gainRight) >> NUM_MIXER_GUARD_BITS);
					pMixBuffer[dest] += temp;
				}
				break;
				
			/* stereo to stereo */
			case MIX_FLAGS_STEREO_SOURCE | MIX_FLAGS_STEREO_OUTPUT:
				gainLeft >>= 15;
				gainRight >>= 15;
				for (src = dest = 0; src < numSamples; src+=2, dest+=2)
				{
					pMixBuffer[dest] += (pInputBuffer[src] * gainLeft) >> NUM_MIXER_GUARD_BITS;
					pMixBuffer[dest+1] += (pInputBuffer[src+1] * gainRight) >> NUM_MIXER_GUARD_BITS;
				}
				break;
		}
	}

	/* gain change - do gain increment */
	else
	{
		switch (flags & (MIX_FLAGS_STEREO_SOURCE | MIX_FLAGS_STEREO_OUTPUT))
		{
			/* mono to mono */
			case 0:
				for (src = dest = 0; src < numSamples; src++, dest++)
				{
					gainLeft += gainIncLeft;
					pMixBuffer[dest] += (pInputBuffer[src] * (gainLeft >> 15)) >> NUM_MIXER_GUARD_BITS;
				}
				break;
				
			/* mono to stereo */
			case MIX_FLAGS_STEREO_OUTPUT:
				for (src = dest = 0; src < numSamples; src++, dest+=2)
				{
					gainLeft += gainIncLeft;
					gainRight += gainIncRight;
					pMixBuffer[dest] += (pInputBuffer[src] * (gainLeft >> 15)) >> NUM_MIXER_GUARD_BITS;
					pMixBuffer[dest+1] += (pInputBuffer[src] * (gainRight >> 15)) >> NUM_MIXER_GUARD_BITS;
				}
				break;

			/* stereo to mono */
			case MIX_FLAGS_STEREO_SOURCE:
				for (src = dest = 0; src < numSamples; src+=2, dest++)
				{
					gainLeft += gainIncLeft;
					gainRight += gainIncRight;
					temp = (pInputBuffer[src] * (gainLeft >> 15)) >> NUM_MIXER_GUARD_BITS;
					temp += ((pInputBuffer[src+1] * (gainRight >> 15)) >> NUM_MIXER_GUARD_BITS);
					pMixBuffer[dest] += temp;
				}
				break;
				
			/* stereo to stereo */
			case MIX_FLAGS_STEREO_SOURCE | MIX_FLAGS_STEREO_OUTPUT:
				for (src = dest = 0; src < numSamples; src+=2, dest+=2)
				{
					gainLeft += gainIncLeft;
					gainRight += gainIncRight;
					pMixBuffer[dest] += (pInputBuffer[src] * (gainLeft >> 15)) >> NUM_MIXER_GUARD_BITS;
					pMixBuffer[dest+1] += (pInputBuffer[src+1] * (gainRight >> 15)) >> NUM_MIXER_GUARD_BITS;
				}
				break;
		}
	}
}
#endif
#endif