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