/*---------------------------------------------------------------------------- * * File: * eas_wtengine.c * * Contents and purpose: * This file contains the critical synthesizer components that need to * be optimized for best performance. * * Copyright Sonic Network Inc. 2004-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: 844 $ * $Date: 2007-08-23 14:33:32 -0700 (Thu, 23 Aug 2007) $ *---------------------------------------------------------------------------- */ /*------------------------------------ * includes *------------------------------------ */ #include "eas_types.h" #include "eas_math.h" #include "eas_audioconst.h" #include "eas_sndlib.h" #include "eas_wtengine.h" #include "eas_mixer.h" /*---------------------------------------------------------------------------- * prototypes *---------------------------------------------------------------------------- */ extern void WT_NoiseGenerator (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame); extern void WT_VoiceGain (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame); #if defined(_OPTIMIZED_MONO) extern void WT_InterpolateMono (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame); #else extern void WT_InterpolateNoLoop (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame); extern void WT_Interpolate (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame); #endif #if defined(_FILTER_ENABLED) extern void WT_VoiceFilter (S_FILTER_CONTROL*pFilter, S_WT_INT_FRAME *pWTIntFrame); #endif #if defined(_OPTIMIZED_MONO) || !defined(NATIVE_EAS_KERNEL) /*---------------------------------------------------------------------------- * WT_VoiceGain *---------------------------------------------------------------------------- * Purpose: * Output gain for individual voice * * Inputs: * * Outputs: * *---------------------------------------------------------------------------- */ /*lint -esym(715, pWTVoice) reserved for future use */ void WT_VoiceGain (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { EAS_I32 *pMixBuffer; EAS_PCM *pInputBuffer; EAS_I32 gain; EAS_I32 gainIncrement; EAS_I32 tmp0; EAS_I32 tmp1; EAS_I32 tmp2; EAS_I32 numSamples; #if (NUM_OUTPUT_CHANNELS == 2) EAS_I32 gainLeft, gainRight; #endif /* initialize some local variables */ numSamples = pWTIntFrame->numSamples; pMixBuffer = pWTIntFrame->pMixBuffer; pInputBuffer = pWTIntFrame->pAudioBuffer; /*lint -e{703} <avoid multiply for performance>*/ gainIncrement = (pWTIntFrame->frame.gainTarget - pWTIntFrame->prevGain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS); if (gainIncrement < 0) gainIncrement++; /*lint -e{703} <avoid multiply for performance>*/ gain = pWTIntFrame->prevGain << 16; #if (NUM_OUTPUT_CHANNELS == 2) gainLeft = pWTVoice->gainLeft; gainRight = pWTVoice->gainRight; #endif while (numSamples--) { /* incremental gain step to prevent zipper noise */ tmp0 = *pInputBuffer++; gain += gainIncrement; /*lint -e{704} <avoid divide>*/ tmp2 = gain >> 16; /* scale sample by gain */ tmp2 *= tmp0; /* stereo output */ #if (NUM_OUTPUT_CHANNELS == 2) /*lint -e{704} <avoid divide>*/ tmp2 = tmp2 >> 14; /* get the current sample in the final mix buffer */ tmp1 = *pMixBuffer; /* left channel */ tmp0 = tmp2 * gainLeft; /*lint -e{704} <avoid divide>*/ tmp0 = tmp0 >> NUM_MIXER_GUARD_BITS; tmp1 += tmp0; *pMixBuffer++ = tmp1; /* get the current sample in the final mix buffer */ tmp1 = *pMixBuffer; /* right channel */ tmp0 = tmp2 * gainRight; /*lint -e{704} <avoid divide>*/ tmp0 = tmp0 >> NUM_MIXER_GUARD_BITS; tmp1 += tmp0; *pMixBuffer++ = tmp1; /* mono output */ #else /* get the current sample in the final mix buffer */ tmp1 = *pMixBuffer; /*lint -e{704} <avoid divide>*/ tmp2 = tmp2 >> (NUM_MIXER_GUARD_BITS - 1); tmp1 += tmp2; *pMixBuffer++ = tmp1; #endif } } #endif #ifndef NATIVE_EAS_KERNEL /*---------------------------------------------------------------------------- * WT_Interpolate *---------------------------------------------------------------------------- * Purpose: * Interpolation engine for wavetable synth * * Inputs: * * Outputs: * *---------------------------------------------------------------------------- */ void WT_Interpolate (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { EAS_PCM *pOutputBuffer; EAS_I32 phaseInc; EAS_I32 phaseFrac; EAS_I32 acc0; const EAS_SAMPLE *pSamples; const EAS_SAMPLE *loopEnd; EAS_I32 samp1; EAS_I32 samp2; EAS_I32 numSamples; /* initialize some local variables */ numSamples = pWTIntFrame->numSamples; pOutputBuffer = pWTIntFrame->pAudioBuffer; loopEnd = (const EAS_SAMPLE*) pWTVoice->loopEnd + 1; pSamples = (const EAS_SAMPLE*) pWTVoice->phaseAccum; /*lint -e{713} truncation is OK */ phaseFrac = pWTVoice->phaseFrac; phaseInc = pWTIntFrame->frame.phaseIncrement; /* fetch adjacent samples */ #if defined(_8_BIT_SAMPLES) /*lint -e{701} <avoid multiply for performance>*/ samp1 = pSamples[0] << 8; /*lint -e{701} <avoid multiply for performance>*/ samp2 = pSamples[1] << 8; #else samp1 = pSamples[0]; samp2 = pSamples[1]; #endif while (numSamples--) { /* linear interpolation */ acc0 = samp2 - samp1; acc0 = acc0 * phaseFrac; /*lint -e{704} <avoid divide>*/ acc0 = samp1 + (acc0 >> NUM_PHASE_FRAC_BITS); /* save new output sample in buffer */ /*lint -e{704} <avoid divide>*/ *pOutputBuffer++ = (EAS_I16)(acc0 >> 2); /* increment phase */ phaseFrac += phaseInc; /*lint -e{704} <avoid divide>*/ acc0 = phaseFrac >> NUM_PHASE_FRAC_BITS; /* next sample */ if (acc0 > 0) { /* advance sample pointer */ pSamples += acc0; phaseFrac = (EAS_I32)((EAS_U32)phaseFrac & PHASE_FRAC_MASK); /* check for loop end */ acc0 = (EAS_I32) (pSamples - loopEnd); if (acc0 >= 0) pSamples = (const EAS_SAMPLE*) pWTVoice->loopStart + acc0; /* fetch new samples */ #if defined(_8_BIT_SAMPLES) /*lint -e{701} <avoid multiply for performance>*/ samp1 = pSamples[0] << 8; /*lint -e{701} <avoid multiply for performance>*/ samp2 = pSamples[1] << 8; #else samp1 = pSamples[0]; samp2 = pSamples[1]; #endif } } /* save pointer and phase */ pWTVoice->phaseAccum = (EAS_U32) pSamples; pWTVoice->phaseFrac = (EAS_U32) phaseFrac; } #endif #ifndef NATIVE_EAS_KERNEL /*---------------------------------------------------------------------------- * WT_InterpolateNoLoop *---------------------------------------------------------------------------- * Purpose: * Interpolation engine for wavetable synth * * Inputs: * * Outputs: * *---------------------------------------------------------------------------- */ void WT_InterpolateNoLoop (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { EAS_PCM *pOutputBuffer; EAS_I32 phaseInc; EAS_I32 phaseFrac; EAS_I32 acc0; const EAS_SAMPLE *pSamples; EAS_I32 samp1; EAS_I32 samp2; EAS_I32 numSamples; /* initialize some local variables */ numSamples = pWTIntFrame->numSamples; pOutputBuffer = pWTIntFrame->pAudioBuffer; phaseInc = pWTIntFrame->frame.phaseIncrement; pSamples = (const EAS_SAMPLE*) pWTVoice->phaseAccum; phaseFrac = (EAS_I32)pWTVoice->phaseFrac; /* fetch adjacent samples */ #if defined(_8_BIT_SAMPLES) /*lint -e{701} <avoid multiply for performance>*/ samp1 = pSamples[0] << 8; /*lint -e{701} <avoid multiply for performance>*/ samp2 = pSamples[1] << 8; #else samp1 = pSamples[0]; samp2 = pSamples[1]; #endif while (numSamples--) { /* linear interpolation */ acc0 = samp2 - samp1; acc0 = acc0 * phaseFrac; /*lint -e{704} <avoid divide>*/ acc0 = samp1 + (acc0 >> NUM_PHASE_FRAC_BITS); /* save new output sample in buffer */ /*lint -e{704} <avoid divide>*/ *pOutputBuffer++ = (EAS_I16)(acc0 >> 2); /* increment phase */ phaseFrac += phaseInc; /*lint -e{704} <avoid divide>*/ acc0 = phaseFrac >> NUM_PHASE_FRAC_BITS; /* next sample */ if (acc0 > 0) { /* advance sample pointer */ pSamples += acc0; phaseFrac = (EAS_I32)((EAS_U32)phaseFrac & PHASE_FRAC_MASK); /* fetch new samples */ #if defined(_8_BIT_SAMPLES) /*lint -e{701} <avoid multiply for performance>*/ samp1 = pSamples[0] << 8; /*lint -e{701} <avoid multiply for performance>*/ samp2 = pSamples[1] << 8; #else samp1 = pSamples[0]; samp2 = pSamples[1]; #endif } } /* save pointer and phase */ pWTVoice->phaseAccum = (EAS_U32) pSamples; pWTVoice->phaseFrac = (EAS_U32) phaseFrac; } #endif #if defined(_FILTER_ENABLED) && !defined(NATIVE_EAS_KERNEL) /*---------------------------------------------------------------------------- * WT_VoiceFilter *---------------------------------------------------------------------------- * Purpose: * Implements a 2-pole filter * * Inputs: * * Outputs: * *---------------------------------------------------------------------------- */ void WT_VoiceFilter (S_FILTER_CONTROL *pFilter, S_WT_INT_FRAME *pWTIntFrame) { EAS_PCM *pAudioBuffer; EAS_I32 k; EAS_I32 b1; EAS_I32 b2; EAS_I32 z1; EAS_I32 z2; EAS_I32 acc0; EAS_I32 acc1; EAS_I32 numSamples; /* initialize some local variables */ numSamples = pWTIntFrame->numSamples; pAudioBuffer = pWTIntFrame->pAudioBuffer; z1 = pFilter->z1; z2 = pFilter->z2; b1 = -pWTIntFrame->frame.b1; /*lint -e{702} <avoid divide> */ b2 = -pWTIntFrame->frame.b2 >> 1; /*lint -e{702} <avoid divide> */ k = pWTIntFrame->frame.k >> 1; while (numSamples--) { /* do filter calculations */ acc0 = *pAudioBuffer; acc1 = z1 * b1; acc1 += z2 * b2; acc0 = acc1 + k * acc0; z2 = z1; /*lint -e{702} <avoid divide> */ z1 = acc0 >> 14; *pAudioBuffer++ = (EAS_I16) z1; } /* save delay values */ pFilter->z1 = (EAS_I16) z1; pFilter->z2 = (EAS_I16) z2; } #endif /*---------------------------------------------------------------------------- * WT_NoiseGenerator *---------------------------------------------------------------------------- * Purpose: * Generate pseudo-white noise using PRNG and interpolation engine * * Inputs: * * Outputs: * * Notes: * This output is scaled -12dB to prevent saturation in the filter. For a * high quality synthesizer, the output can be set to full scale, however * if the filter is used, it can overflow with certain coefficients. In this * case, either a saturation operation should take in the filter before * scaling back to 16 bits or the signal path should be increased to 18 bits * or more. *---------------------------------------------------------------------------- */ void WT_NoiseGenerator (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { EAS_PCM *pOutputBuffer; EAS_I32 phaseInc; EAS_I32 tmp0; EAS_I32 tmp1; EAS_I32 nInterpolatedSample; EAS_I32 numSamples; /* initialize some local variables */ numSamples = pWTIntFrame->numSamples; pOutputBuffer = pWTIntFrame->pAudioBuffer; phaseInc = pWTIntFrame->frame.phaseIncrement; /* get last two samples generated */ /*lint -e{704} <avoid divide for performance>*/ tmp0 = (EAS_I32) (pWTVoice->phaseAccum) >> 18; /*lint -e{704} <avoid divide for performance>*/ tmp1 = (EAS_I32) (pWTVoice->loopEnd) >> 18; /* generate a buffer of noise */ while (numSamples--) { nInterpolatedSample = MULT_AUDIO_COEF( tmp0, (PHASE_ONE - pWTVoice->phaseFrac)); nInterpolatedSample += MULT_AUDIO_COEF( tmp1, pWTVoice->phaseFrac); *pOutputBuffer++ = (EAS_PCM) nInterpolatedSample; /* update PRNG */ pWTVoice->phaseFrac += (EAS_U32) phaseInc; if (GET_PHASE_INT_PART(pWTVoice->phaseFrac)) { tmp0 = tmp1; pWTVoice->phaseAccum = pWTVoice->loopEnd; pWTVoice->loopEnd = (5 * pWTVoice->loopEnd + 1); tmp1 = (EAS_I32) (pWTVoice->loopEnd) >> 18; pWTVoice->phaseFrac = GET_PHASE_FRAC_PART(pWTVoice->phaseFrac); } } } #ifndef _OPTIMIZED_MONO /*---------------------------------------------------------------------------- * WT_ProcessVoice *---------------------------------------------------------------------------- * Purpose: * This routine does the block processing for one voice. It is isolated * from the main synth code to allow for various implementation-specific * optimizations. It calls the interpolator, filter, and gain routines * appropriate for a particular configuration. * * Inputs: * * Outputs: * * Notes: *---------------------------------------------------------------------------- */ void WT_ProcessVoice (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { /* use noise generator */ if (pWTVoice->loopStart == WT_NOISE_GENERATOR) WT_NoiseGenerator(pWTVoice, pWTIntFrame); /* generate interpolated samples for looped waves */ else if (pWTVoice->loopStart != pWTVoice->loopEnd) WT_Interpolate(pWTVoice, pWTIntFrame); /* generate interpolated samples for unlooped waves */ else { WT_InterpolateNoLoop(pWTVoice, pWTIntFrame); } #ifdef _FILTER_ENABLED if (pWTIntFrame->frame.k != 0) WT_VoiceFilter(&pWTVoice->filter, pWTIntFrame); #endif //2 TEST NEW MIXER FUNCTION #ifdef UNIFIED_MIXER { EAS_I32 gainLeft, gainIncLeft; #if (NUM_OUTPUT_CHANNELS == 2) EAS_I32 gainRight, gainIncRight; #endif gainLeft = (pWTIntFrame->prevGain * pWTVoice->gainLeft) << 1; gainIncLeft = (((pWTIntFrame->frame.gainTarget * pWTVoice->gainLeft) << 1) - gainLeft) >> SYNTH_UPDATE_PERIOD_IN_BITS; #if (NUM_OUTPUT_CHANNELS == 2) gainRight = (pWTIntFrame->prevGain * pWTVoice->gainRight) << 1; gainIncRight = (((pWTIntFrame->frame.gainTarget * pWTVoice->gainRight) << 1) - gainRight) >> SYNTH_UPDATE_PERIOD_IN_BITS; EAS_MixStream( pWTIntFrame->pAudioBuffer, pWTIntFrame->pMixBuffer, pWTIntFrame->numSamples, gainLeft, gainRight, gainIncLeft, gainIncRight, MIX_FLAGS_STEREO_OUTPUT); #else EAS_MixStream( pWTIntFrame->pAudioBuffer, pWTIntFrame->pMixBuffer, pWTIntFrame->numSamples, gainLeft, 0, gainIncLeft, 0, 0); #endif } #else /* apply gain, and left and right gain */ WT_VoiceGain(pWTVoice, pWTIntFrame); #endif } #endif #if defined(_OPTIMIZED_MONO) && !defined(NATIVE_EAS_KERNEL) /*---------------------------------------------------------------------------- * WT_InterpolateMono *---------------------------------------------------------------------------- * Purpose: * A C version of the sample interpolation + gain routine, optimized for mono. * It's not pretty, but it matches the assembly code exactly. * * Inputs: * * Outputs: * * Notes: *---------------------------------------------------------------------------- */ void WT_InterpolateMono (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { EAS_I32 *pMixBuffer; const EAS_I8 *pLoopEnd; const EAS_I8 *pCurrentPhaseInt; EAS_I32 numSamples; EAS_I32 gain; EAS_I32 gainIncrement; EAS_I32 currentPhaseFrac; EAS_I32 phaseInc; EAS_I32 tmp0; EAS_I32 tmp1; EAS_I32 tmp2; EAS_I8 *pLoopStart; numSamples = pWTIntFrame->numSamples; pMixBuffer = pWTIntFrame->pMixBuffer; /* calculate gain increment */ gainIncrement = (pWTIntFrame->gainTarget - pWTIntFrame->prevGain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS); if (gainIncrement < 0) gainIncrement++; gain = pWTIntFrame->prevGain << 16; pCurrentPhaseInt = pWTVoice->pPhaseAccum; currentPhaseFrac = pWTVoice->phaseFrac; phaseInc = pWTIntFrame->phaseIncrement; pLoopStart = pWTVoice->pLoopStart; pLoopEnd = pWTVoice->pLoopEnd + 1; InterpolationLoop: tmp0 = (EAS_I32)(pCurrentPhaseInt - pLoopEnd); if (tmp0 >= 0) pCurrentPhaseInt = pLoopStart + tmp0; tmp0 = *pCurrentPhaseInt; tmp1 = *(pCurrentPhaseInt + 1); tmp2 = phaseInc + currentPhaseFrac; tmp1 = tmp1 - tmp0; tmp1 = tmp1 * currentPhaseFrac; tmp1 = tmp0 + (tmp1 >> NUM_EG1_FRAC_BITS); pCurrentPhaseInt += (tmp2 >> NUM_PHASE_FRAC_BITS); currentPhaseFrac = tmp2 & PHASE_FRAC_MASK; gain += gainIncrement; tmp2 = (gain >> SYNTH_UPDATE_PERIOD_IN_BITS); tmp0 = *pMixBuffer; tmp2 = tmp1 * tmp2; tmp2 = (tmp2 >> 9); tmp0 = tmp2 + tmp0; *pMixBuffer++ = tmp0; numSamples--; if (numSamples) goto InterpolationLoop; pWTVoice->pPhaseAccum = pCurrentPhaseInt; pWTVoice->phaseFrac = currentPhaseFrac; /*lint -e{702} <avoid divide>*/ pWTVoice->gain = (EAS_I16)(gain >> SYNTH_UPDATE_PERIOD_IN_BITS); } #endif #ifdef _OPTIMIZED_MONO /*---------------------------------------------------------------------------- * WT_ProcessVoice *---------------------------------------------------------------------------- * Purpose: * This routine does the block processing for one voice. It is isolated * from the main synth code to allow for various implementation-specific * optimizations. It calls the interpolator, filter, and gain routines * appropriate for a particular configuration. * * Inputs: * * Outputs: * * Notes: * This special version works handles an optimized mono-only signal * without filters *---------------------------------------------------------------------------- */ void WT_ProcessVoice (S_WT_VOICE *pWTVoice, S_WT_INT_FRAME *pWTIntFrame) { /* use noise generator */ if (pWTVoice->loopStart== WT_NOISE_GENERATOR) { WT_NoiseGenerator(pWTVoice, pWTIntFrame); WT_VoiceGain(pWTVoice, pWTIntFrame); } /* or generate interpolated samples */ else { WT_InterpolateMono(pWTVoice, pWTIntFrame); } } #endif