mirror of
https://github.com/id-Software/DOOM-IOS2.git
synced 2026-03-20 08:59:35 +01:00
786 lines
21 KiB
C
Executable File
786 lines
21 KiB
C
Executable File
/*----------------------------------------------------------------------------
|
|
*
|
|
* File:
|
|
* eas_fmengine.c
|
|
*
|
|
* Contents and purpose:
|
|
* Implements the low-level FM synthesizer functions.
|
|
*
|
|
* 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: 795 $
|
|
* $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* includes */
|
|
#include "eas_types.h"
|
|
#include "eas_math.h"
|
|
#include "eas_audioconst.h"
|
|
#include "eas_fmengine.h"
|
|
|
|
#if defined(EAS_FM_SYNTH) || defined(EAS_HYBRID_SYNTH) || defined(EAS_SPLIT_HYBRID_SYNTH) || defined(EAS_SPLIT_FM_SYNTH)
|
|
#include "eas_data.h"
|
|
#endif
|
|
|
|
/* externals */
|
|
extern const EAS_I16 sineTable[];
|
|
extern const EAS_U8 fmScaleTable[16];
|
|
|
|
// saturation constants for 32-bit to 16-bit conversion
|
|
#define _EAS_MAX_OUTPUT 32767
|
|
#define _EAS_MIN_OUTPUT -32767
|
|
|
|
static S_FM_ENG_VOICE voices[NUM_FM_VOICES];
|
|
|
|
/* local prototypes */
|
|
void FM_SynthMixVoice (S_FM_ENG_VOICE *p, EAS_U16 gainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer);
|
|
|
|
/* used in development environment */
|
|
#if defined(_SATURATION_MONITOR)
|
|
static EAS_BOOL bSaturated = EAS_FALSE;
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_CheckSaturation()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Allows the sound development tool to check for saturation at the voice
|
|
* level. Useful for tuning the level controls.
|
|
*
|
|
* Inputs:
|
|
*
|
|
* Outputs:
|
|
* Returns true if saturation has occurred since the last time the function
|
|
* was called.
|
|
*
|
|
* Side Effects:
|
|
* Resets the saturation flag
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
EAS_BOOL FM_CheckSaturation ()
|
|
{
|
|
EAS_BOOL bTemp;
|
|
bTemp = bSaturated;
|
|
bSaturated = EAS_FALSE;
|
|
return bTemp;
|
|
}
|
|
#endif
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_Saturate()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* This inline function saturates a 32-bit number to 16-bits
|
|
*
|
|
* Inputs:
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
* Returns a 16-bit integer
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
EAS_INLINE EAS_I16 FM_Saturate (EAS_I32 nValue)
|
|
{
|
|
if (nValue > _EAS_MAX_OUTPUT)
|
|
{
|
|
#if defined(_SATURATION_MONITOR)
|
|
bSaturated = EAS_TRUE;
|
|
#endif
|
|
return _EAS_MAX_OUTPUT;
|
|
}
|
|
if (nValue < _EAS_MIN_OUTPUT)
|
|
{
|
|
#if defined(_SATURATION_MONITOR)
|
|
bSaturated = EAS_TRUE;
|
|
#endif
|
|
return _EAS_MIN_OUTPUT;
|
|
}
|
|
return (EAS_I16) nValue;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_Noise()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* A 31-bit low-cost linear congruential PRNG algorithm used to
|
|
* generate noise.
|
|
*
|
|
* Inputs:
|
|
* pnSeed - pointer to 32-bit PRNG seed
|
|
*
|
|
* Outputs:
|
|
* Returns a 16-bit integer
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
EAS_INLINE EAS_I16 FM_Noise (EAS_U32 *pnSeed)
|
|
{
|
|
*pnSeed = *pnSeed * 214013L + 2531011L;
|
|
return (EAS_I16) ((*pnSeed >> 15) & 0xffff);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_PhaseInc()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Transform pitch cents to linear phase increment
|
|
*
|
|
* Inputs:
|
|
* nCents - measured in cents
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
* nResult - int.frac result (where frac has NUM_DENTS_FRAC_BITS)
|
|
*
|
|
* Side Effects:
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
static EAS_I32 FM_PhaseInc (EAS_I32 nCents)
|
|
{
|
|
EAS_I32 nDents;
|
|
EAS_I32 nExponentInt, nExponentFrac;
|
|
EAS_I32 nTemp1, nTemp2;
|
|
EAS_I32 nResult;
|
|
|
|
/* convert cents to dents */
|
|
nDents = FMUL_15x15(nCents, CENTS_TO_DENTS);
|
|
nExponentInt = GET_DENTS_INT_PART(nDents) + (32 - SINE_TABLE_SIZE_IN_BITS - NUM_EG1_FRAC_BITS);
|
|
nExponentFrac = GET_DENTS_FRAC_PART(nDents);
|
|
|
|
/* implement 2^(fracPart) as a power series */
|
|
nTemp1 = GN2_TO_X2 + MULT_DENTS_COEF(nExponentFrac, GN2_TO_X3);
|
|
nTemp2 = GN2_TO_X1 + MULT_DENTS_COEF(nExponentFrac, nTemp1);
|
|
nTemp1 = GN2_TO_X0 + MULT_DENTS_COEF(nExponentFrac, nTemp2);
|
|
|
|
/*
|
|
implement 2^(intPart) as
|
|
a left shift for intPart >= 0 or
|
|
a left shift for intPart < 0
|
|
*/
|
|
if (nExponentInt >= 0)
|
|
{
|
|
/* left shift for positive exponents */
|
|
/*lint -e{703} <avoid multiply for performance>*/
|
|
nResult = nTemp1 << nExponentInt;
|
|
}
|
|
else
|
|
{
|
|
/* right shift for negative exponents */
|
|
nExponentInt = -nExponentInt;
|
|
nResult = nTemp1 >> nExponentInt;
|
|
}
|
|
|
|
return nResult;
|
|
}
|
|
|
|
#if (NUM_OUTPUT_CHANNELS == 2)
|
|
/*----------------------------------------------------------------------------
|
|
* FM_CalculatePan()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Assign the left and right gain values corresponding to the given pan value.
|
|
*
|
|
* Inputs:
|
|
* psVoice - ptr to the voice we have assigned for this channel
|
|
* psArticulation - ptr to this voice's articulation
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
*
|
|
* Side Effects:
|
|
* the given voice's m_nGainLeft and m_nGainRight are assigned
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
static void FM_CalculatePan (EAS_I16 pan, EAS_U16 *pGainLeft, EAS_U16 *pGainRight)
|
|
{
|
|
EAS_I32 nTemp;
|
|
EAS_INT nNetAngle;
|
|
|
|
/*
|
|
Implement the following
|
|
sin(x) = (2-4*c)*x^2 + c + x
|
|
cos(x) = (2-4*c)*x^2 + c - x
|
|
|
|
where c = 1/sqrt(2)
|
|
using the a0 + x*(a1 + x*a2) approach
|
|
*/
|
|
|
|
/*
|
|
Get the Midi CC10 pan value for this voice's channel
|
|
convert the pan value to an "angle" representation suitable for
|
|
our sin, cos calculator. This representation is NOT necessarily the same
|
|
as the transform in the GM manuals because of our sin, cos calculator.
|
|
"angle" = (CC10 - 64)/128
|
|
*/
|
|
/*lint -e{703} <avoid multiply for performance reasons>*/
|
|
nNetAngle = ((EAS_I32) pan) << (NUM_EG1_FRAC_BITS -7);
|
|
|
|
/* calculate sin */
|
|
nTemp = EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
|
|
nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
|
|
|
|
if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
|
|
nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
|
|
else if (nTemp < 0)
|
|
nTemp = 0;
|
|
|
|
*pGainRight = (EAS_U16) nTemp;
|
|
|
|
/* calculate cos */
|
|
nTemp = -EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
|
|
nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
|
|
|
|
if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
|
|
nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
|
|
else if (nTemp < 0)
|
|
nTemp = 0;
|
|
|
|
*pGainLeft = (EAS_U16) nTemp;
|
|
}
|
|
#endif /* #if (NUM_OUTPUT_CHANNELS == 2) */
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_Operator()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Synthesizes a buffer of samples based on passed parameters.
|
|
*
|
|
* Inputs:
|
|
* nNumSamplesToAdd - number of samples to synthesize
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
*
|
|
* Side Effects:
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
void FM_Operator (
|
|
S_FM_ENG_OPER *p,
|
|
EAS_I32 numSamplesToAdd,
|
|
EAS_PCM *pBuffer,
|
|
EAS_PCM *pModBuffer,
|
|
EAS_BOOL mix,
|
|
EAS_U16 gainTarget,
|
|
EAS_I16 pitch,
|
|
EAS_U8 feedback,
|
|
EAS_I16 *pLastOutput)
|
|
{
|
|
EAS_I32 gain;
|
|
EAS_I32 gainInc;
|
|
EAS_U32 phase;
|
|
EAS_U32 phaseInc;
|
|
EAS_U32 phaseTemp;
|
|
EAS_I32 temp;
|
|
EAS_I32 temp2;
|
|
|
|
/* establish local gain variable */
|
|
gain = (EAS_I32) p->gain << 16;
|
|
|
|
/* calculate gain increment */
|
|
/*lint -e{703} use shift for performance */
|
|
gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
|
|
|
|
/* establish local phase variables */
|
|
phase = p->phase;
|
|
|
|
/* calculate the new phase increment */
|
|
phaseInc = (EAS_U32) FM_PhaseInc(pitch);
|
|
|
|
/* restore final output from previous frame for feedback loop */
|
|
if (pLastOutput)
|
|
temp = *pLastOutput;
|
|
else
|
|
temp = 0;
|
|
|
|
/* generate a buffer of samples */
|
|
while (numSamplesToAdd--)
|
|
{
|
|
|
|
/* incorporate modulation */
|
|
if (pModBuffer)
|
|
{
|
|
/*lint -e{701} use shift for performance */
|
|
temp = *pModBuffer++ << FM_MODULATOR_INPUT_SHIFT;
|
|
}
|
|
|
|
/* incorporate feedback */
|
|
else
|
|
{
|
|
/*lint -e{703} use shift for performance */
|
|
temp = (temp * (EAS_I32) feedback) << FM_FEEDBACK_SHIFT;
|
|
}
|
|
|
|
/*lint -e{737} <use this behavior to avoid extra mask step> */
|
|
phaseTemp = phase + temp;
|
|
|
|
/* fetch sample from wavetable */
|
|
temp = sineTable[phaseTemp >> (32 - SINE_TABLE_SIZE_IN_BITS)];
|
|
|
|
/* increment operator phase */
|
|
phase += phaseInc;
|
|
|
|
/* internal gain for modulation effects */
|
|
temp = FMUL_15x15(temp, (gain >> 16));
|
|
|
|
/* output gain calculation */
|
|
temp2 = FMUL_15x15(temp, p->outputGain);
|
|
|
|
/* saturating add to buffer */
|
|
if (mix)
|
|
{
|
|
temp2 += *pBuffer;
|
|
*pBuffer++ = FM_Saturate(temp2);
|
|
}
|
|
|
|
/* output to buffer */
|
|
else
|
|
*pBuffer++ = (EAS_I16) temp2;
|
|
|
|
/* increment gain */
|
|
gain += gainInc;
|
|
|
|
}
|
|
|
|
/* save phase and gain */
|
|
p->phase = phase;
|
|
p->gain = gainTarget;
|
|
|
|
/* save last output for feedback in next frame */
|
|
if (pLastOutput)
|
|
*pLastOutput = (EAS_I16) temp;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_NoiseOperator()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Synthesizes a buffer of samples based on passed parameters.
|
|
*
|
|
* Inputs:
|
|
* nNumSamplesToAdd - number of samples to synthesize
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
*
|
|
* Side Effects:
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
void FM_NoiseOperator (
|
|
S_FM_ENG_OPER *p,
|
|
EAS_I32 numSamplesToAdd,
|
|
EAS_PCM *pBuffer,
|
|
EAS_BOOL mix,
|
|
EAS_U16 gainTarget,
|
|
EAS_U8 feedback,
|
|
EAS_I16 *pLastOutput)
|
|
{
|
|
EAS_I32 gain;
|
|
EAS_I32 gainInc;
|
|
EAS_U32 phase;
|
|
EAS_I32 temp;
|
|
EAS_I32 temp2;
|
|
|
|
/* establish local gain variable */
|
|
gain = (EAS_I32) p->gain << 16;
|
|
|
|
/* calculate gain increment */
|
|
/*lint -e{703} use shift for performance */
|
|
gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
|
|
|
|
/* establish local phase variables */
|
|
phase = p->phase;
|
|
|
|
/* establish local phase variables */
|
|
phase = p->phase;
|
|
|
|
/* recall last sample for filter Z-1 term */
|
|
temp = 0;
|
|
if (pLastOutput)
|
|
temp = *pLastOutput;
|
|
|
|
/* generate a buffer of samples */
|
|
while (numSamplesToAdd--)
|
|
{
|
|
|
|
/* if using filter */
|
|
if (pLastOutput)
|
|
{
|
|
/* use PRNG for noise */
|
|
temp2 = FM_Noise(&phase);
|
|
|
|
/*lint -e{704} use shift for performance */
|
|
temp += ((temp2 -temp) * feedback) >> 8;
|
|
}
|
|
else
|
|
{
|
|
temp = FM_Noise(&phase);
|
|
}
|
|
|
|
/* internal gain for modulation effects */
|
|
temp2 = FMUL_15x15(temp, (gain >> 16));
|
|
|
|
/* output gain calculation */
|
|
temp2 = FMUL_15x15(temp2, p->outputGain);
|
|
|
|
/* saturating add to buffer */
|
|
if (mix)
|
|
{
|
|
temp2 += *pBuffer;
|
|
*pBuffer++ = FM_Saturate(temp2);
|
|
}
|
|
|
|
/* output to buffer */
|
|
else
|
|
*pBuffer++ = (EAS_I16) temp2;
|
|
|
|
/* increment gain */
|
|
gain += gainInc;
|
|
|
|
}
|
|
|
|
/* save phase and gain */
|
|
p->phase = phase;
|
|
p->gain = gainTarget;
|
|
|
|
/* save last output for feedback in next frame */
|
|
if (pLastOutput)
|
|
*pLastOutput = (EAS_I16) temp;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_ConfigVoice()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Receives parameters to start a new voice.
|
|
*
|
|
* Inputs:
|
|
* voiceNum - voice number to start
|
|
* vCfg - configuration data
|
|
* pMixBuffer - pointer to host supplied buffer
|
|
*
|
|
* Outputs:
|
|
*
|
|
* Side Effects:
|
|
*
|
|
* Notes:
|
|
* pFrameBuffer is not used in the test version, but is passed as a
|
|
* courtesy to split architecture implementations. It can be used as
|
|
* as pointer to the interprocessor communications buffer when the
|
|
* synthesis parameters are passed off to a DSP for synthesis.
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
/*lint -esym(715, pFrameBuffer) pFrameBuffer not used in test version - see above */
|
|
void FM_ConfigVoice (EAS_I32 voiceNum, S_FM_VOICE_CONFIG *vCfg, EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
|
|
{
|
|
S_FM_ENG_VOICE *pVoice;
|
|
EAS_INT i;
|
|
|
|
/* establish pointer to voice data */
|
|
pVoice = &voices[voiceNum];
|
|
|
|
/* save data */
|
|
pVoice->feedback = vCfg->feedback;
|
|
pVoice->flags = vCfg->flags;
|
|
pVoice->voiceGain = vCfg->voiceGain;
|
|
|
|
/* initialize Z-1 terms */
|
|
pVoice->op1Out = 0;
|
|
pVoice->op3Out = 0;
|
|
|
|
/* initialize operators */
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
/* save operator data */
|
|
pVoice->oper[i].gain = vCfg->gain[i];
|
|
pVoice->oper[i].outputGain = vCfg->outputGain[i];
|
|
pVoice->oper[i].outputGain = vCfg->outputGain[i];
|
|
|
|
/* initalize operator */
|
|
pVoice->oper[i].phase = 0;
|
|
}
|
|
|
|
/* calculate pan */
|
|
#if NUM_OUTPUT_CHANNELS == 2
|
|
FM_CalculatePan(vCfg->pan, &pVoice->gainLeft, &pVoice->gainRight);
|
|
#endif
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_ProcessVoice()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Synthesizes a buffer of samples based on calculated parameters.
|
|
*
|
|
* Inputs:
|
|
* nNumSamplesToAdd - number of samples to synthesize
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
*
|
|
* Side Effects:
|
|
*
|
|
* Notes:
|
|
* pOut is not used in the test version, but is passed as a
|
|
* courtesy to split architecture implementations. It can be used as
|
|
* as pointer to the interprocessor communications buffer when the
|
|
* synthesis parameters are passed off to a DSP for synthesis.
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
/*lint -esym(715, pOut) pOut not used in test version - see above */
|
|
void FM_ProcessVoice (
|
|
EAS_I32 voiceNum,
|
|
S_FM_VOICE_FRAME *pFrame,
|
|
EAS_I32 numSamplesToAdd,
|
|
EAS_PCM *pTempBuffer,
|
|
EAS_PCM *pBuffer,
|
|
EAS_I32 *pMixBuffer,
|
|
EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
|
|
{
|
|
S_FM_ENG_VOICE *p;
|
|
EAS_PCM *pOutBuf;
|
|
EAS_PCM *pMod;
|
|
EAS_BOOL mix;
|
|
EAS_U8 feedback1;
|
|
EAS_U8 feedback3;
|
|
EAS_U8 mode;
|
|
|
|
/* establish pointer to voice data */
|
|
p = &voices[voiceNum];
|
|
mode = p->flags & 0x07;
|
|
|
|
/* lookup feedback values */
|
|
feedback1 = fmScaleTable[p->feedback >> 4];
|
|
feedback3 = fmScaleTable[p->feedback & 0x0f];
|
|
|
|
/* operator 3 is on output bus in modes 0, 1, and 3 */
|
|
if ((mode == 0) || (mode == 1) || (mode == 3))
|
|
pOutBuf = pBuffer;
|
|
else
|
|
pOutBuf = pTempBuffer;
|
|
|
|
if (p->flags & FLAG_FM_ENG_VOICE_OP3_NOISE)
|
|
{
|
|
FM_NoiseOperator(
|
|
p->oper + 2,
|
|
numSamplesToAdd,
|
|
pOutBuf,
|
|
EAS_FALSE,
|
|
pFrame->gain[2],
|
|
feedback3,
|
|
&p->op3Out);
|
|
}
|
|
else
|
|
{
|
|
FM_Operator(
|
|
p->oper + 2,
|
|
numSamplesToAdd,
|
|
pOutBuf,
|
|
0,
|
|
EAS_FALSE,
|
|
pFrame->gain[2],
|
|
pFrame->pitch[2],
|
|
feedback3,
|
|
&p->op3Out);
|
|
}
|
|
|
|
/* operator 4 is on output bus in modes 0, 1, and 2 */
|
|
if (mode < 3)
|
|
pOutBuf = pBuffer;
|
|
else
|
|
pOutBuf = pTempBuffer;
|
|
|
|
/* operator 4 is modulated in modes 2, 4, and 5 */
|
|
if ((mode == 2) || (mode == 4) || (mode == 5))
|
|
pMod = pTempBuffer;
|
|
else
|
|
pMod = 0;
|
|
|
|
/* operator 4 is in mix mode in modes 0 and 1 */
|
|
mix = (mode < 2);
|
|
|
|
if (p->flags & FLAG_FM_ENG_VOICE_OP4_NOISE)
|
|
{
|
|
FM_NoiseOperator(
|
|
p->oper + 3,
|
|
numSamplesToAdd,
|
|
pOutBuf,
|
|
mix,
|
|
pFrame->gain[3],
|
|
0,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
FM_Operator(
|
|
p->oper + 3,
|
|
numSamplesToAdd,
|
|
pOutBuf,
|
|
pMod,
|
|
mix,
|
|
pFrame->gain[3],
|
|
pFrame->pitch[3],
|
|
0,
|
|
0);
|
|
}
|
|
|
|
/* operator 1 is on output bus in mode 0 */
|
|
if (mode == 0)
|
|
pOutBuf = pBuffer;
|
|
else
|
|
pOutBuf = pTempBuffer;
|
|
|
|
/* operator 1 is modulated in modes 3 and 4 */
|
|
if ((mode == 3) || (mode == 4))
|
|
pMod = pTempBuffer;
|
|
else
|
|
pMod = 0;
|
|
|
|
/* operator 1 is in mix mode in modes 0 and 5 */
|
|
mix = ((mode == 0) || (mode == 5));
|
|
|
|
if (p->flags & FLAG_FM_ENG_VOICE_OP1_NOISE)
|
|
{
|
|
FM_NoiseOperator(
|
|
p->oper,
|
|
numSamplesToAdd,
|
|
pOutBuf,
|
|
mix,
|
|
pFrame->gain[0],
|
|
feedback1,
|
|
&p->op1Out);
|
|
}
|
|
else
|
|
{
|
|
FM_Operator(
|
|
p->oper,
|
|
numSamplesToAdd,
|
|
pOutBuf,
|
|
pMod,
|
|
mix,
|
|
pFrame->gain[0],
|
|
pFrame->pitch[0],
|
|
feedback1,
|
|
&p->op1Out);
|
|
}
|
|
|
|
/* operator 2 is modulated in all modes except 0 */
|
|
if (mode != 0)
|
|
pMod = pTempBuffer;
|
|
else
|
|
pMod = 0;
|
|
|
|
/* operator 1 is in mix mode in modes 0 -3 */
|
|
mix = (mode < 4);
|
|
|
|
if (p->flags & FLAG_FM_ENG_VOICE_OP2_NOISE)
|
|
{
|
|
FM_NoiseOperator(
|
|
p->oper + 1,
|
|
numSamplesToAdd,
|
|
pBuffer,
|
|
mix,
|
|
pFrame->gain[1],
|
|
0,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
FM_Operator(
|
|
p->oper + 1,
|
|
numSamplesToAdd,
|
|
pBuffer,
|
|
pMod,
|
|
mix,
|
|
pFrame->gain[1],
|
|
pFrame->pitch[1],
|
|
0,
|
|
0);
|
|
}
|
|
|
|
/* mix voice output to synthesizer output buffer */
|
|
FM_SynthMixVoice(p, pFrame->voiceGain, numSamplesToAdd, pBuffer, pMixBuffer);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* FM_SynthMixVoice()
|
|
*----------------------------------------------------------------------------
|
|
* Purpose:
|
|
* Mixes the voice output buffer into the final mix using an anti-zipper
|
|
* filter.
|
|
*
|
|
* Inputs:
|
|
* nNumSamplesToAdd - number of samples to synthesize
|
|
* psEASData - pointer to overall EAS data structure
|
|
*
|
|
* Outputs:
|
|
*
|
|
* Side Effects:
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
void FM_SynthMixVoice(S_FM_ENG_VOICE *p, EAS_U16 nGainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer)
|
|
{
|
|
EAS_I32 nGain;
|
|
EAS_I32 nGainInc;
|
|
EAS_I32 nTemp;
|
|
|
|
/* restore previous gain */
|
|
/*lint -e{703} <use shift for performance> */
|
|
nGain = (EAS_I32) p->voiceGain << 16;
|
|
|
|
/* calculate gain increment */
|
|
/*lint -e{703} <use shift for performance> */
|
|
nGainInc = ((EAS_I32) nGainTarget - (EAS_I32) p->voiceGain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
|
|
|
|
/* mix the output buffer */
|
|
while (numSamplesToAdd--)
|
|
{
|
|
/* output gain calculation */
|
|
nTemp = *pInputBuffer++;
|
|
|
|
/* sum to output buffer */
|
|
#if (NUM_OUTPUT_CHANNELS == 2)
|
|
|
|
/*lint -e{704} <use shift for performance> */
|
|
nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_GAIN_SHIFT;
|
|
|
|
/*lint -e{704} <use shift for performance> */
|
|
{
|
|
EAS_I32 nTemp2;
|
|
nTemp = nTemp >> FM_STEREO_PRE_GAIN_SHIFT;
|
|
nTemp2 = (nTemp * p->gainLeft) >> FM_STEREO_POST_GAIN_SHIFT;
|
|
*pBuffer++ += nTemp2;
|
|
nTemp2 = (nTemp * p->gainRight) >> FM_STEREO_POST_GAIN_SHIFT;
|
|
*pBuffer++ += nTemp2;
|
|
}
|
|
#else
|
|
/*lint -e{704} <use shift for performance> */
|
|
nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_MONO_GAIN_SHIFT;
|
|
*pBuffer++ += nTemp;
|
|
#endif
|
|
|
|
/* increment gain for anti-zipper filter */
|
|
nGain += nGainInc;
|
|
}
|
|
|
|
/* save gain */
|
|
p->voiceGain = nGainTarget;
|
|
}
|
|
|