/*----------------------------------------------------------------------------
*
* 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