/*----------------------------------------------------------------------------
 *
 * File:
 * eas_math.c
 *
 * Contents and purpose:
 * Contains common math routines for the various audio engines.
 *
 *
 * 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: 586 $
 *   $Date: 2007-03-08 20:33:04 -0800 (Thu, 08 Mar 2007) $
 *----------------------------------------------------------------------------
*/

#include "eas.h"
#include "eas_math.h"

/* anything less than this converts to a fraction too small to represent in 32-bits */
#define MIN_CENTS   -18000
/* anything greater than this converts to a fraction too large to represent in 32-bits */
#define MAX_CENTS    19200

/*----------------------------------------------------------------------------
 * EAS_Calculate2toX()
 *----------------------------------------------------------------------------
 * Purpose:
 * Calculate 2^x
 *
 * 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:
 *
 *----------------------------------------------------------------------------
*/

EAS_I32 EAS_Calculate2toX (EAS_I32 nCents)
{
    EAS_I32 nDents;
    EAS_I32 nExponentInt, nExponentFrac;
    EAS_I32 nTemp1, nTemp2;
    EAS_I32 nResult;

    /* check for minimum value */
    if (nCents < MIN_CENTS)
        return 0;

    if (nCents > MAX_CENTS) {
        nCents = MAX_CENTS;
    }

    /* for the time being, convert cents to dents */
    nDents = FMUL_15x15(nCents, CENTS_TO_DENTS);

    nExponentInt = GET_DENTS_INT_PART(nDents);
    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;
}

/*----------------------------------------------------------------------------
 * EAS_LogToLinear16()
 *----------------------------------------------------------------------------
 * Purpose:
 * Transform log value to linear gain multiplier using piece-wise linear
 * approximation
 *
 * Inputs:
 * nGain - log scale value in 20.10 format. Even though gain is normally
 * stored in 6.10 (16-bit) format we use 32-bit numbers here to eliminate
 * the need for saturation checking when combining gain values.
 *
 * Outputs:
 * Returns a 16-bit linear value approximately equal to 2^(nGain/1024)
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_U16 EAS_LogToLinear16 (EAS_I32 nGain)
{
    EAS_INT nExp;
    EAS_U16 nTemp;

    /* bias to positive */
    nGain += 32767;

    /* check for infinite attenuation */
    if (nGain < 0)
        return 0;

    /* extract the exponent */
    nExp = 31 - (nGain >> 10);

    /* check for maximum output */
    if (nExp < 0)
        return 0x7fff;

    /* extract mantissa and restore implied 1 bit */
    nTemp = (EAS_U16)((((nGain & 0x3ff) << 4) | 0x4000) >> nExp);

    /* use shift to approximate power-of-2 operation */
    return nTemp;
}

/*----------------------------------------------------------------------------
 * EAS_VolumeToGain()
 *----------------------------------------------------------------------------
 * Purpose:
 * Transform volume control in 1dB increments to gain multiplier
 *
 * Inputs:
 * volume - 128 = +28dB, 100 = 0dB, 99 = -1dB, 0 = -inf
 *
 * Outputs:
 * Linear gain in EG1 frac, i.e. 1.0 for +0dB
 *----------------------------------------------------------------------------
*/
EAS_I32 EAS_VolumeToGain (EAS_INT volume)
{
    /* check for limits */
    if (volume <= 0)
        return 0;

    // 108852 >> 15 is log2(10)
    // 1200 is 1 cent, which is unit of EAS_Calculate2toX
    // dB = 20*log10(linear ratio)
    // placing `/ 20` before `* 108852` avoids overflow
    return EAS_Calculate2toX((volume - EAS_REF_VOLUME) * 1200 / 20 * 108852 / (1 << 15));
}

