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