#include "fmod_settings.h"

#include "fmod_dspi.h"
#include "fmod_dsp_connectioni.h"
#include "fmod_globals.h"
#include "fmod_localcriticalsection.h"

#ifdef PLATFORM_PS3_SPU
    #include "fmod_systemi_spu.h"
#else
    #include "fmod_systemi.h"
#endif

namespace FMOD
{

#ifdef FMOD_SUPPORT_SIMD
extern "C"
{
    void FMOD_DSP_Connection_MixMonoToStereo_SIMD  (float *inbuffer, float *outbuffer, unsigned int length, float lvolume, float rvolume);
    void FMOD_DSP_Connection_MixStereoToStereo_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float lvolume, float rvolume);
    void FMOD_DSP_Connection_MixMonoTo5_1_SIMD     (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to1, float *volume2to5);
    void FMOD_DSP_Connection_Mix5_1To5_1_SIMD      (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to1, float *volume2to5);
    void FMOD_DSP_Connection_MixMonoTo7_1_SIMD     (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to7);
    void FMOD_DSP_Connection_Mix7_1To7_1_SIMD      (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to7);
    void FMOD_DSP_Connection_MixStereoTo7_1_SIMD   (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume01to31, float *volume4to7, float *volume41to71);
}
#endif


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::validate(DSPConnection *dspconnection, DSPConnectionI **dspconnectioni)
{
    if (!dspconnectioni)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    if (!dspconnection)
    {
        return FMOD_ERR_INVALID_HANDLE;
    }

    *dspconnectioni = (DSPConnectionI *)dspconnection;

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::init(DSP_LEVEL_TYPE * &levelmemory, int maxoutputlevels, int maxinputlevels)
{  
#ifdef FMOD_SUPPORT_SOFTWARE
    mMaxOutputLevels = maxoutputlevels;
    mMaxInputLevels  = maxinputlevels;

    if (maxoutputlevels < 2)
    {
        maxoutputlevels = 2;
    }

    if (mMaxInputLevels < mMaxOutputLevels)
    {
        mMaxInputLevels = mMaxOutputLevels;
    }

    #ifdef PLATFORM_PS3_PPU
    mMramAddress        = (unsigned int)this;
    mMramAddressLevels  = (unsigned int)levelmemory;
    #endif

    int count;
    for (count = 0; count < DSP_MAXLEVELS_OUT; count++)
    {
        if (count < mMaxOutputLevels)
        {
            mLevel[count]        = levelmemory;
            levelmemory += mMaxInputLevels;
            mLevelCurrent[count] = levelmemory;
            levelmemory += mMaxInputLevels;
            mLevelDelta[count]   = levelmemory;
            levelmemory += mMaxInputLevels;
        }
        else
        {
            mLevel[count]        = 0;
            mLevelCurrent[count] = 0;
            mLevelDelta[count]   = 0;
        }
    }

    return FMOD_OK;
#else
    return FMOD_ERR_NEEDSSOFTWARE;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getInput(DSPI **input)
{
#ifdef FMOD_SUPPORT_SOFTWARE
    if (!input)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    if (!mInputUnit)
    {
        *input = 0;
        return FMOD_ERR_NOTREADY;
    }

    *input = mInputUnit;

    return FMOD_OK;
#else
    return FMOD_ERR_NEEDSSOFTWARE;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getOutput(DSPI **output)
{
    if (!output)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    if (!mOutputUnit)
    {
        *output = 0;
        return FMOD_ERR_NOTREADY;
    }

    *output = mOutputUnit;

    return FMOD_OK;
}

#ifdef FMOD_SUPPORT_SOFTWARE

/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::reset()
{
    int count, count2;

    mVolume = 1.0f;
    mUserData = 0;
    mRampCount = 0;

    for (count = 0; count < mMaxOutputLevels; count++)
    {
        for (count2 = 0; count2 < mMaxInputLevels; count2++)
        {
            mLevel       [count][count2] = 0;
            mLevelCurrent[count][count2] = 0;
            mLevelDelta  [count][count2] = 0;
        }
    }
    
    mSetLevelsUsed = false;

    return FMOD_OK;
}



/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::setUnity()
{
    int count, count2;

    for (count = 0; count < mMaxOutputLevels; count++)
    {
        for (count2 = 0; count2 < mMaxInputLevels; count2++)
        {
            if (count == count2)
            {
                mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(1.0f);
                mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f);
            }
            else
            {
                mLevel[count][count2] = 0;
                mLevelCurrent[count][count2] = 0;
            }
        }
    }
    mVolume = 1.0f;
    mRampCount = 0;

    return FMOD_OK;
}

/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::mix(float * FMOD_RESTRICT outbuffer, float * FMOD_RESTRICT inbuffer, int outchannels, int inchannels, unsigned int length)
{
    unsigned int count;
    float        l00, l10;
    float        l01, l11;
#if (DSP_MAXLEVELS_OUT > 2)
    float        l20, l30, l40, l50;
    float        l21, l31, l41, l51;
    float        l22, l33, l44, l55;
#endif    
#if (DSP_MAXLEVELS_OUT > 6)
    float        l60, l70;
    float        l61, l71;
    float        l66, l77;
#endif
    bool         supportssimd;
    float        levelcurrent[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN] = { 0 };

    if (!inbuffer)
    {
        return FMOD_OK;
    }

    supportssimd = FMOD_OS_SupportsSIMD();

    if (mRampCount)
    {
        unsigned int len = mRampCount;

        if (len > length)
        {
            len = length;
        }

        mixAndRamp(outbuffer, inbuffer, outchannels, inchannels, len);

        length -= len;
        outbuffer += (len * outchannels);
        inbuffer += (len * inchannels);
    }

    if (!length)
    {
        return FMOD_OK;
    }

    if (FMOD_FABS(mVolume) < DSP_LEVEL_SMALLVAL)
    {
        return FMOD_OK;
    }
    
    for (count = 0; count < (unsigned int)mMaxOutputLevels; count++)
    {
        int count2;

        if (mLevelCurrent[count])
        {
            for (count2 = 0; count2 < mMaxInputLevels; count2++)
            {
                levelcurrent[count][count2] = DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][count2]);
            }
        }
    }

    /*
        Because mLevelCurrent could be misaligned this could hopefully put the values into registers.  (confirmed on ps2).
    */

    l00 = levelcurrent[0][0];
    l10 = levelcurrent[1][0];

#if (DSP_MAXLEVELS_OUT > 2)
    l20 = levelcurrent[2][0];
    l30 = levelcurrent[3][0];
    l40 = levelcurrent[4][0];
    l50 = levelcurrent[5][0];
#endif
#if (DSP_MAXLEVELS_OUT > 6)
    l60 = levelcurrent[6][0];
    l70 = levelcurrent[7][0];
#endif

    l01 = levelcurrent[0][1];
    l11 = levelcurrent[1][1];

#if (DSP_MAXLEVELS_OUT > 2)
    l21 = levelcurrent[2][1];
    l31 = levelcurrent[3][1];
    l41 = levelcurrent[4][1];
    l51 = levelcurrent[5][1];

    l22 = levelcurrent[2][2];
    l33 = levelcurrent[3][3];
    l44 = levelcurrent[4][4];
    l55 = levelcurrent[5][5];
#endif
#if (DSP_MAXLEVELS_OUT > 6)
    l61 = levelcurrent[6][1];
    l71 = levelcurrent[7][1];

    l66 = levelcurrent[6][6];
    l77 = levelcurrent[7][7];
#endif

    /*
        The most common configuration is mono or stereo coming in, and stereo going out, so we special case these for speed.
        Other combinations go through generic for loop code.
    */
    if (0)
    {
    }
#if !defined(PLATFORM_PS3_SPU)
    else if (outchannels == 2 && (inchannels == 1 || inchannels == 2))
    {
        if (inchannels == 1)
        {
            #ifdef FMOD_SUPPORT_SIMD
            if (supportssimd)
            {
                /*
                    First 16byte align destination for simd.
                */
                while ((((unsigned int)outbuffer & 0xF) || ((unsigned int)inbuffer & 0xF)) && length)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[1] += (inbuffer[0] * l10);
                    inbuffer  ++;
                    outbuffer += 2;
                    length    --;
                }
                FMOD_DSP_Connection_MixMonoToStereo_SIMD(inbuffer, outbuffer, length, l00, l10);
            }
            else
            #endif
            {
                unsigned int len;

                len = length >> 2;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[2] += (inbuffer[1] * l00);
                    outbuffer[3] += (inbuffer[1] * l10);
                    outbuffer[4] += (inbuffer[2] * l00);
                    outbuffer[5] += (inbuffer[2] * l10);
                    outbuffer[6] += (inbuffer[3] * l00);
                    outbuffer[7] += (inbuffer[3] * l10);
                    inbuffer  += 4;
                    outbuffer += 8;
                    len       --;
                }
                len = length & 3;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[1] += (inbuffer[0] * l10);
                    inbuffer  ++;
                    outbuffer += 2;
                    len       --;
                }
            }
        }
        else if (inchannels == 2)
        {
            unsigned int len;

            if (FMOD_FABS(l01) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l10) < DSP_LEVEL_SMALLVAL)     /* Pan matrix looks like [l 0] which is the normal way. */
            {                                       /*                       [0 r]                          */
                #ifdef FMOD_SUPPORT_SIMD
                if (supportssimd)
                {
                    /*
                        First 16byte align destination for simd.
                    */
                    while ((unsigned int)outbuffer & 0xF && length)
                    {
                        outbuffer[0] += (inbuffer[0] * l00);
                        outbuffer[1] += (inbuffer[1] * l11);
                        inbuffer  += 2;
                        outbuffer += 2;
                        length    --;
                    }
                    FMOD_DSP_Connection_MixStereoToStereo_SIMD(inbuffer, outbuffer, length, l00, l11);
                }
                else
                #endif
                {
                    len = length >> 2;
                    while (len)
                    {
                        outbuffer[0] += (inbuffer[0] * l00);
                        outbuffer[1] += (inbuffer[1] * l11);

                        outbuffer[2] += (inbuffer[2] * l00);
                        outbuffer[3] += (inbuffer[3] * l11);

                        outbuffer[4] += (inbuffer[4] * l00);
                        outbuffer[5] += (inbuffer[5] * l11);

                        outbuffer[6] += (inbuffer[6] * l00);
                        outbuffer[7] += (inbuffer[7] * l11);
                        inbuffer  += 8;
                        outbuffer += 8;
                        len       --;
                    }

                    len = length & 3;
                    while (len)
                    {
                        outbuffer[0] += (inbuffer[0] * l00);
                        outbuffer[1] += (inbuffer[1] * l11);
                        inbuffer  += 2;
                        outbuffer += 2;
                        len       --;
                    }
                }
            }
            else                                /* Pan matrix looks like [l ?] must have used setSpeakerMix. */
            {                                   /*                       [? r]                               */
                len = length >> 2;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[0] += (inbuffer[1] * l01);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[1] += (inbuffer[1] * l11);

                    outbuffer[2] += (inbuffer[2] * l00);
                    outbuffer[2] += (inbuffer[3] * l01);
                    outbuffer[3] += (inbuffer[2] * l10);
                    outbuffer[3] += (inbuffer[3] * l11);

                    outbuffer[4] += (inbuffer[4] * l00);
                    outbuffer[4] += (inbuffer[5] * l01);
                    outbuffer[5] += (inbuffer[4] * l10);
                    outbuffer[5] += (inbuffer[5] * l11);

                    outbuffer[6] += (inbuffer[6] * l00);
                    outbuffer[6] += (inbuffer[7] * l01);
                    outbuffer[7] += (inbuffer[6] * l10);
                    outbuffer[7] += (inbuffer[7] * l11);
                    inbuffer  += 8;
                    outbuffer += 8;
                    len       --;
                }

                len = length & 3;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[0] += (inbuffer[1] * l01);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[1] += (inbuffer[1] * l11);
                    inbuffer  += 2;
                    outbuffer += 2;
                    len       --;
                }

            }
        }
    }
#if (DSP_MAXLEVELS_OUT > 2) && !defined(PLATFORM_PS3_PPU)
    else if (outchannels == 6 && (inchannels == 1 || inchannels == 2 || inchannels == 6))//(inchannels == 6 && checkUnity(outchannels, inchannels) == FMOD_OK)))
    {
        if (inchannels == 1)
        {
            #ifdef FMOD_SUPPORT_SIMD   
            if (supportssimd)
            {
                float volume0to3[4] = { l00, l10, l20, l30 };
                float volume4to1[4] = { l40, l50, l00, l10 };
                float volume2to5[4] = { l20, l30, l40, l50 };

                /*
                    First 16byte align destination for simd.
                */
                while ((unsigned int)outbuffer & 0xF && length)
                {
                    outbuffer[0]  += (inbuffer[0] * l00);
                    outbuffer[1]  += (inbuffer[0] * l10);
                    outbuffer[2]  += (inbuffer[0] * l20);
                    outbuffer[3]  += (inbuffer[0] * l30);
                    outbuffer[4]  += (inbuffer[0] * l40);
                    outbuffer[5]  += (inbuffer[0] * l50);

                    inbuffer  ++;
                    outbuffer +=6;
                    length    --;
                }

                FMOD_DSP_Connection_MixMonoTo5_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to1, volume2to5);
            }
            else
            #endif
            {
                unsigned int len;

                len = length >> 2;
                while (len)
                {               
                    outbuffer[0]  += (inbuffer[0] * l00);
                    outbuffer[1]  += (inbuffer[0] * l10);
                    outbuffer[2]  += (inbuffer[0] * l20);
                    outbuffer[3]  += (inbuffer[0] * l30);
                    
                    outbuffer[4]  += (inbuffer[0] * l40);
                    outbuffer[5]  += (inbuffer[0] * l50);
                    outbuffer[6]  += (inbuffer[1] * l00);
                    outbuffer[7]  += (inbuffer[1] * l10);
                    
                    outbuffer[8]  += (inbuffer[1] * l20);
                    outbuffer[9]  += (inbuffer[1] * l30);
                    outbuffer[10] += (inbuffer[1] * l40);
                    outbuffer[11] += (inbuffer[1] * l50);

                    outbuffer[12]  += (inbuffer[2] * l00);
                    outbuffer[13]  += (inbuffer[2] * l10);
                    outbuffer[14]  += (inbuffer[2] * l20);
                    outbuffer[15]  += (inbuffer[2] * l30);
                    
                    outbuffer[16]  += (inbuffer[2] * l40);
                    outbuffer[17]  += (inbuffer[2] * l50);
                    outbuffer[18]  += (inbuffer[3] * l00);
                    outbuffer[19]  += (inbuffer[3] * l10);
                    
                    outbuffer[20]  += (inbuffer[3] * l20);
                    outbuffer[21]  += (inbuffer[3] * l30);
                    outbuffer[22]  += (inbuffer[3] * l40);
                    outbuffer[23]  += (inbuffer[3] * l50);
                    
                    outbuffer += 24;
                    inbuffer  += 4;
                    
                    len--;
                }
                
                len = length & 3;
                if (len)
                {
                    while (len)
                    {
                        outbuffer[0]  += (inbuffer[0] * l00);
                        outbuffer[1]  += (inbuffer[0] * l10);
                        outbuffer[2]  += (inbuffer[0] * l20);
                        outbuffer[3]  += (inbuffer[0] * l30);
                        outbuffer[4]  += (inbuffer[0] * l40);
                        outbuffer[5]  += (inbuffer[0] * l50);

                        inbuffer  ++;
                        outbuffer +=6;
                        len       --;
                    }
                }
            }
        }
        else if (inchannels == 2)
        {
            unsigned int len;
   
            /* Pan matrix looks like [l 0] which is the normal way. */
            /*                       [0 r]                          */
            /*                       [0 0]                          */
            /*                       [0 0]                          */
            /*                       [0 0]                          */
            /*                       [0 0]                          */
            if (FMOD_FABS(l01) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l10) < DSP_LEVEL_SMALLVAL && 
                FMOD_FABS(l20) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l21) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l30) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l31) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l40) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l41) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l50) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l51) < DSP_LEVEL_SMALLVAL)
            {                                       
                len = length >> 2;
                while (len)
                {
                    outbuffer[0]   += (inbuffer[0] * l00);
                    outbuffer[1]   += (inbuffer[1] * l11);
//                    outbuffer[2   += 0;
//                    outbuffer[3]  += 0;
//                    outbuffer[4]  += 0;
//                    outbuffer[5]  += 0;

                    outbuffer[6]   += (inbuffer[2] * l00);
                    outbuffer[7]   += (inbuffer[3] * l11);
//                    outbuffer[8]  += 0;
//                    outbuffer[9]  += 0;
//                    outbuffer[10] += 0;
//                    outbuffer[11] += 0;

                    outbuffer[12]  += (inbuffer[4] * l00);
                    outbuffer[13]  += (inbuffer[5] * l11);
//                    outbuffer[14] += 0;
//                    outbuffer[15] += 0;
//                    outbuffer[16] += 0;
//                    outbuffer[17] += 0;

                    outbuffer[18]  += (inbuffer[6] * l00);
                    outbuffer[19]  += (inbuffer[7] * l11);
//                   outbuffer[20] += 0;
//                   outbuffer[21] += 0;
//                   outbuffer[22] += 0;
//                   outbuffer[23] += 0;
                    
                    outbuffer += 24;
                    inbuffer  += 8;
                    len--;
                }

                len = length & 3;
                if (len)
                {               
                    while (len)
                    {
                        outbuffer[0]  += (inbuffer[0] * l00);
                        outbuffer[1]  += (inbuffer[1] * l11);
                        //outbuffer[2]  += 0;
                        //outbuffer[3]  += 0;
                        //outbuffer[4]  += 0;
                        //outbuffer[5]  += 0;

                        inbuffer  += 2;
                        outbuffer += 6;
                        len       --;
                    }
                }
            }
            else                                /* Pan matrix looks like [l ?] must have used setSpeakerMix. */
            {                                   /*                       [? r]                               */
                len = length >> 1;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[0] += (inbuffer[1] * l01);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[1] += (inbuffer[1] * l11);
                    outbuffer[2] += (inbuffer[0] * l20);
                    outbuffer[2] += (inbuffer[1] * l21);
                    outbuffer[3] += (inbuffer[0] * l30);
                    outbuffer[3] += (inbuffer[1] * l31);
                    outbuffer[4] += (inbuffer[0] * l40);
                    outbuffer[4] += (inbuffer[1] * l41);
                    outbuffer[5] += (inbuffer[0] * l50);
                    outbuffer[5] += (inbuffer[1] * l51);

                    outbuffer[6] += (inbuffer[2] * l00);
                    outbuffer[6] += (inbuffer[3] * l01);
                    outbuffer[7] += (inbuffer[2] * l10);
                    outbuffer[7] += (inbuffer[3] * l11);
                    outbuffer[8] += (inbuffer[2] * l20);
                    outbuffer[8] += (inbuffer[3] * l21);
                    outbuffer[9] += (inbuffer[2] * l30);
                    outbuffer[9] += (inbuffer[3] * l31);
                    outbuffer[10] += (inbuffer[2] * l40);
                    outbuffer[10] += (inbuffer[3] * l41);
                    outbuffer[11] += (inbuffer[2] * l50);
                    outbuffer[11] += (inbuffer[3] * l51);

                    inbuffer  += 4;
                    outbuffer += 12;
                    len       --;
                }

                len = length & 1;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[0] += (inbuffer[1] * l01);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[1] += (inbuffer[1] * l11);
                    outbuffer[2] += (inbuffer[0] * l20);
                    outbuffer[2] += (inbuffer[1] * l21);
                    outbuffer[3] += (inbuffer[0] * l30);
                    outbuffer[3] += (inbuffer[1] * l31);
                    outbuffer[4] += (inbuffer[0] * l40);
                    outbuffer[4] += (inbuffer[1] * l41);
                    outbuffer[5] += (inbuffer[0] * l50);
                    outbuffer[5] += (inbuffer[1] * l51);

                    inbuffer  += 2;
                    outbuffer += 6;
                    len       --;
                }
            }
        }
        else if (inchannels == 6)
        {
            FMOD_RESULT unity = checkUnity(outchannels, inchannels);
            FMOD_RESULT mono  = FMOD_ERR_TOOMANYCHANNELS;

            if (unity != FMOD_OK)
            {
                mono = checkMono(outchannels, inchannels);
            }

            if (unity == FMOD_OK || mono == FMOD_OK)
            {
                float l[6];

                if (unity == FMOD_OK)
                {
                    l[0] = l00;
                    l[1] = l11;
                    l[2] = l22;
                    l[3] = l33;
                    l[4] = l44;
                    l[5] = l55;
                }
                else
                {
                    l[0] = l00;
                    l[1] = l10;
                    l[2] = l20;
                    l[3] = l30;
                    l[4] = l40;
                    l[5] = l50;
                }

                #ifdef FMOD_SUPPORT_SIMD
                if (supportssimd)
                {
                    float volume0to3[4] = { l[0], l[1], l[2], l[3] };
                    float volume4to1[4] = { l[4], l[5], l[0], l[1] };
                    float volume2to5[4] = { l[2], l[3], l[4], l[5] };

                    /*
                        First 16byte align destination for simd.
                    */
                    while ((unsigned int)outbuffer & 0xF && length)
                    {
                        outbuffer[0]  += (inbuffer[0] * l[0]);
                        outbuffer[1]  += (inbuffer[1] * l[1]);
                        outbuffer[2]  += (inbuffer[2] * l[2]);
                        outbuffer[3]  += (inbuffer[3] * l[3]);
                        outbuffer[4]  += (inbuffer[4] * l[4]);
                        outbuffer[5]  += (inbuffer[5] * l[5]);

                        inbuffer  +=6;
                        outbuffer +=6;
                        length    --;
                    }

                    FMOD_DSP_Connection_Mix5_1To5_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to1, volume2to5);
                }
                else
                #endif
                {
                    unsigned int len;
                         
                    len = length >> 2;
                    while (len)
                    {                   
                        outbuffer[0]  += (inbuffer[0]  * l[0]);
                        outbuffer[1]  += (inbuffer[1]  * l[1]);
                        outbuffer[2]  += (inbuffer[2]  * l[2]);
                        outbuffer[3]  += (inbuffer[3]  * l[3]);
                        outbuffer[4]  += (inbuffer[4]  * l[4]);
                        outbuffer[5]  += (inbuffer[5]  * l[5]);
                        outbuffer[6]  += (inbuffer[6]  * l[0]);
                        outbuffer[7]  += (inbuffer[7]  * l[1]);
                        outbuffer[8]  += (inbuffer[8]  * l[2]);
                        outbuffer[9]  += (inbuffer[9]  * l[3]);
                        outbuffer[10] += (inbuffer[10] * l[4]);
                        outbuffer[11] += (inbuffer[11] * l[5]);

                        outbuffer[12]  += (inbuffer[12] * l[0]);
                        outbuffer[13]  += (inbuffer[13] * l[1]);
                        outbuffer[14]  += (inbuffer[14] * l[2]);
                        outbuffer[15]  += (inbuffer[15] * l[3]);
                        outbuffer[16]  += (inbuffer[16] * l[4]);
                        outbuffer[17]  += (inbuffer[17] * l[5]);
                        outbuffer[18]  += (inbuffer[18] * l[0]);
                        outbuffer[19]  += (inbuffer[19] * l[1]);
                        outbuffer[20]  += (inbuffer[20] * l[2]);
                        outbuffer[21]  += (inbuffer[21] * l[3]);
                        outbuffer[22]  += (inbuffer[22] * l[4]);
                        outbuffer[23]  += (inbuffer[23] * l[5]);
                    
                        outbuffer += 24;
                        inbuffer += 24;
                        len--;
                    }
                
                    len = length & 3;
                    if (len)
                    {
                        while (len)
                        {
                            outbuffer[0]  += (inbuffer[0] * l[0]);
                            outbuffer[1]  += (inbuffer[1] * l[1]);
                            outbuffer[2]  += (inbuffer[2] * l[2]);
                            outbuffer[3]  += (inbuffer[3] * l[3]);
                            outbuffer[4]  += (inbuffer[4] * l[4]);
                            outbuffer[5]  += (inbuffer[5] * l[5]);

                            inbuffer  +=6;
                            outbuffer +=6;
                            len       --;
                        }
                    }
                }
            }
            else
            {
                #if 0
                for (count = 0; count < length; count++)
                {
                    int count2;
            
                    for (count2 = 0; count2 < outchannels; count2++)
                    {
                        float srcval  = 0;
                        float destval = *outbuffer;
                        int count3;
 
                        for (count3 = 0; count3 < inchannels; count3++)
                        {
                            srcval += (inbuffer[count3] * levelcurrent[count2][count3]);
                        }
 
                        destval += srcval;
                        *outbuffer++ = destval;
                    }
                    
                    inbuffer += inchannels;
                }
                #else
                for (count = 0; count < length; count++)
                {
                    float srcval;
                    
                    srcval  = (inbuffer[0] * levelcurrent[0][0]);
                    srcval += (inbuffer[1] * levelcurrent[0][1]);
                    srcval += (inbuffer[2] * levelcurrent[0][2]);
                    srcval += (inbuffer[3] * levelcurrent[0][3]);
                    srcval += (inbuffer[4] * levelcurrent[0][4]);
                    srcval += (inbuffer[5] * levelcurrent[0][5]);
                    outbuffer[0] += srcval;

                    srcval  = (inbuffer[0] * levelcurrent[1][0]);
                    srcval += (inbuffer[1] * levelcurrent[1][1]);
                    srcval += (inbuffer[2] * levelcurrent[1][2]);
                    srcval += (inbuffer[3] * levelcurrent[1][3]);
                    srcval += (inbuffer[4] * levelcurrent[1][4]);
                    srcval += (inbuffer[5] * levelcurrent[1][5]);
                    outbuffer[1] += srcval;

                    srcval  = (inbuffer[0] * levelcurrent[2][0]);
                    srcval += (inbuffer[1] * levelcurrent[2][1]);
                    srcval += (inbuffer[2] * levelcurrent[2][2]);
                    srcval += (inbuffer[3] * levelcurrent[2][3]);
                    srcval += (inbuffer[4] * levelcurrent[2][4]);
                    srcval += (inbuffer[5] * levelcurrent[2][5]);
                    outbuffer[2] += srcval;

                    srcval  = (inbuffer[0] * levelcurrent[3][0]);
                    srcval += (inbuffer[1] * levelcurrent[3][1]);
                    srcval += (inbuffer[2] * levelcurrent[3][2]);
                    srcval += (inbuffer[3] * levelcurrent[3][3]);
                    srcval += (inbuffer[4] * levelcurrent[3][4]);
                    srcval += (inbuffer[5] * levelcurrent[3][5]);
                    outbuffer[3] += srcval;

                    srcval  = (inbuffer[0] * levelcurrent[4][0]);
                    srcval += (inbuffer[1] * levelcurrent[4][1]);
                    srcval += (inbuffer[2] * levelcurrent[4][2]);
                    srcval += (inbuffer[3] * levelcurrent[4][3]);
                    srcval += (inbuffer[4] * levelcurrent[4][4]);
                    srcval += (inbuffer[5] * levelcurrent[4][5]);
                    outbuffer[4] += srcval;

                    srcval  = (inbuffer[0] * levelcurrent[5][0]);
                    srcval += (inbuffer[1] * levelcurrent[5][1]);
                    srcval += (inbuffer[2] * levelcurrent[5][2]);
                    srcval += (inbuffer[3] * levelcurrent[5][3]);
                    srcval += (inbuffer[4] * levelcurrent[5][4]);
                    srcval += (inbuffer[5] * levelcurrent[5][5]);
                    outbuffer[5] += srcval;

                    outbuffer += 6;
                    inbuffer += 6;
                }
                #endif
            }
        }
    }
#endif    
#endif  /* #if (DSP_MAXLEVELS_OUT != 8) */

#if (DSP_MAXLEVELS_OUT > 6) && !defined(PLATFORM_PS3_PPU)
    else if (outchannels == 8 && (inchannels == 1 || inchannels == 2 || inchannels == 8)) // && checkUnity(outchannels, inchannels) == FMOD_OK)))
    {
        if (inchannels == 1)
        {
            #ifdef FMOD_SUPPORT_SIMD   
            if (supportssimd)
            {
                float volume0to3[4] = { l00, l10, l20, l30 };
                float volume4to7[4] = { l40, l50, l60, l70 };

                FMOD_DSP_Connection_MixMonoTo7_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to7);
            }
            else
            #endif
            {
                unsigned int len;

                len = length >> 1;
                while (len)
                {               
                    outbuffer[0]  += (inbuffer[0] * l00);
                    outbuffer[1]  += (inbuffer[0] * l10);
                    outbuffer[2]  += (inbuffer[0] * l20);
                    outbuffer[3]  += (inbuffer[0] * l30);
                    outbuffer[4]  += (inbuffer[0] * l40);
                    outbuffer[5]  += (inbuffer[0] * l50);
                    outbuffer[6]  += (inbuffer[0] * l60);
                    outbuffer[7]  += (inbuffer[0] * l70);
                    
                    outbuffer[8]  += (inbuffer[1] * l00);
                    outbuffer[9]  += (inbuffer[1] * l10);
                    outbuffer[10] += (inbuffer[1] * l20);
                    outbuffer[11] += (inbuffer[1] * l30);
                    outbuffer[12] += (inbuffer[1] * l40);
                    outbuffer[13] += (inbuffer[1] * l50);
                    outbuffer[14] += (inbuffer[1] * l60);
                    outbuffer[15] += (inbuffer[1] * l70);
                                       
                    outbuffer += 16;
                    inbuffer  += 2;
                    
                    len--;
                }
                
                len = length & 1;
                if (len)
                {
                    while (len)
                    {
                        outbuffer[0]  += (inbuffer[0] * l00);
                        outbuffer[1]  += (inbuffer[0] * l10);
                        outbuffer[2]  += (inbuffer[0] * l20);
                        outbuffer[3]  += (inbuffer[0] * l30);
                        outbuffer[4]  += (inbuffer[0] * l40);
                        outbuffer[5]  += (inbuffer[0] * l50);
                        outbuffer[6]  += (inbuffer[0] * l60);
                        outbuffer[7]  += (inbuffer[0] * l70);

                        inbuffer  ++;
                        outbuffer +=8;
                        len       --;
                    }
                }
            }
        }
        else if (inchannels == 2)
        {
            unsigned int len;
   
            /* Pan matrix looks like [l 0] which is the normal way. */
            /*                       [0 r]                          */
            /*                       [0 0]                          */
            /*                       [0 0]                          */
            /*                       [0 0]                          */
            /*                       [0 0]                          */
            if (FMOD_FABS(l01) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l10) < DSP_LEVEL_SMALLVAL && 
                FMOD_FABS(l20) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l21) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l30) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l31) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l40) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l41) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l50) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l51) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l60) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l61) < DSP_LEVEL_SMALLVAL &&
                FMOD_FABS(l70) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l71) < DSP_LEVEL_SMALLVAL)
            {                                       
                len = length >> 2;
                while (len)
                {
                    outbuffer[0]   += (inbuffer[0] * l00);
                    outbuffer[1]   += (inbuffer[1] * l11);
//                    outbuffer[2   += 0;
//                    outbuffer[3]  += 0;
//                    outbuffer[4]  += 0;
//                    outbuffer[5]  += 0;
//                    outbuffer[6]  += 0;
//                    outbuffer[7]  += 0;

                    outbuffer[8]   += (inbuffer[2] * l00);
                    outbuffer[9]   += (inbuffer[3] * l11);
//                    outbuffer[10]  += 0;
//                    outbuffer[11]  += 0;
//                    outbuffer[12] += 0;
//                    outbuffer[13] += 0;
//                    outbuffer[14] += 0;
//                    outbuffer[15] += 0;

                    outbuffer[16]  += (inbuffer[4] * l00);
                    outbuffer[17]  += (inbuffer[5] * l11);
//                    outbuffer[18] += 0;
//                    outbuffer[19] += 0;
//                    outbuffer[20] += 0;
//                    outbuffer[21] += 0;
//                    outbuffer[22] += 0;
//                    outbuffer[23] += 0;

                    outbuffer[24]  += (inbuffer[6] * l00);
                    outbuffer[25]  += (inbuffer[7] * l11);
//                   outbuffer[26] += 0;
//                   outbuffer[27] += 0;
//                   outbuffer[28] += 0;
//                   outbuffer[29] += 0;
//                   outbuffer[30] += 0;
//                   outbuffer[31] += 0;
                    
                    outbuffer += 32;
                    inbuffer  += 8;
                    len--;
                }

                len = length & 3;
                if (len)
                {               
                    while (len)
                    {
                        outbuffer[0]  += (inbuffer[0] * l00);
                        outbuffer[1]  += (inbuffer[1] * l11);
                        //outbuffer[2]  += 0;
                        //outbuffer[3]  += 0;
                        //outbuffer[4]  += 0;
                        //outbuffer[5]  += 0;
                        //outbuffer[6]  += 0;
                        //outbuffer[7]  += 0;

                        inbuffer  += 2;
                        outbuffer += 8;
                        len       --;
                    }
                }
            }
            else                                /* Pan matrix looks like [l ?] must have used setSpeakerMix. */
            {                                   /*                       [? r]                               */
#ifdef PLATFORM_XENON
                float volume0to3[4]   = { l00, l10, l20, l30 };
                float volume01to31[4] = { l01, l11, l21, l31 };
                float volume4to7[4]   = { l40, l50, l60, l70 };
                float volume41to71[4] = { l41, l51, l61, l71 };

                FMOD_DSP_Connection_MixStereoTo7_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume01to31, volume4to7, volume41to71);
#else
                len = length >> 1;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[0] += (inbuffer[1] * l01);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[1] += (inbuffer[1] * l11);
                    outbuffer[2] += (inbuffer[0] * l20);
                    outbuffer[2] += (inbuffer[1] * l21);
                    outbuffer[3] += (inbuffer[0] * l30);
                    outbuffer[3] += (inbuffer[1] * l31);
                    outbuffer[4] += (inbuffer[0] * l40);
                    outbuffer[4] += (inbuffer[1] * l41);
                    outbuffer[5] += (inbuffer[0] * l50);
                    outbuffer[5] += (inbuffer[1] * l51);
                    outbuffer[6] += (inbuffer[0] * l60);
                    outbuffer[6] += (inbuffer[1] * l61);
                    outbuffer[7] += (inbuffer[0] * l70);
                    outbuffer[7] += (inbuffer[1] * l71);

                    outbuffer[8] += (inbuffer[2] * l00);
                    outbuffer[8] += (inbuffer[3] * l01);
                    outbuffer[9] += (inbuffer[2] * l10);
                    outbuffer[9] += (inbuffer[3] * l11);
                    outbuffer[10] += (inbuffer[2] * l20);
                    outbuffer[10] += (inbuffer[3] * l21);
                    outbuffer[11] += (inbuffer[2] * l30);
                    outbuffer[11] += (inbuffer[3] * l31);
                    outbuffer[12] += (inbuffer[2] * l40);
                    outbuffer[12] += (inbuffer[3] * l41);
                    outbuffer[13] += (inbuffer[2] * l50);
                    outbuffer[13] += (inbuffer[3] * l51);
                    outbuffer[14] += (inbuffer[2] * l60);
                    outbuffer[14] += (inbuffer[3] * l61);
                    outbuffer[15] += (inbuffer[2] * l70);
                    outbuffer[15] += (inbuffer[3] * l71);

                    inbuffer  += 4;
                    outbuffer += 16;
                    len       --;
                }
#endif            

                len = length & 1;
                while (len)
                {
                    outbuffer[0] += (inbuffer[0] * l00);
                    outbuffer[0] += (inbuffer[1] * l01);
                    outbuffer[1] += (inbuffer[0] * l10);
                    outbuffer[1] += (inbuffer[1] * l11);
                    outbuffer[2] += (inbuffer[0] * l20);
                    outbuffer[2] += (inbuffer[1] * l21);
                    outbuffer[3] += (inbuffer[0] * l30);
                    outbuffer[3] += (inbuffer[1] * l31);
                    outbuffer[4] += (inbuffer[0] * l40);
                    outbuffer[4] += (inbuffer[1] * l41);
                    outbuffer[5] += (inbuffer[0] * l50);
                    outbuffer[5] += (inbuffer[1] * l51);
                    outbuffer[6] += (inbuffer[0] * l60);
                    outbuffer[6] += (inbuffer[1] * l61);
                    outbuffer[7] += (inbuffer[0] * l70);
                    outbuffer[7] += (inbuffer[1] * l71);

                    inbuffer  += 2;
                    outbuffer += 8;
                    len       --;
                }
            }
        }
        else if (inchannels == 8)
        {
#if 0        
            if (mUnity)
            {
                mUnity = mUnity;
            }
            else
#endif
            FMOD_RESULT unity = checkUnity(outchannels, inchannels);
            FMOD_RESULT mono  = FMOD_ERR_TOOMANYCHANNELS;

            if (unity != FMOD_OK)
            {
                mono = checkMono(outchannels, inchannels);
            }

            if (unity == FMOD_OK || mono == FMOD_OK)
            {
                float l[8];

                if (unity == FMOD_OK)
                {
                    l[0] = l00;
                    l[1] = l11;
                    l[2] = l22;
                    l[3] = l33;
                    l[4] = l44;
                    l[5] = l55;
                    l[6] = l66;
                    l[7] = l77;
                }
                else
                {
                    l[0] = l00;
                    l[1] = l10;
                    l[2] = l20;
                    l[3] = l30;
                    l[4] = l40;
                    l[5] = l50;
                    l[6] = l60;
                    l[7] = l70;
                }

                #ifdef FMOD_SUPPORT_SIMD
                if (supportssimd)
                {
                    float volume0to3[4] = { l[0], l[1], l[2], l[3] };
                    float volume4to7[4] = { l[4], l[5], l[6], l[7] };

                    FMOD_DSP_Connection_Mix7_1To7_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to7);
                }
                else
                #endif
                {
                    unsigned int len;
                             
                    len = length >> 1;
                    while (len)
                    {                   
                        outbuffer[0]  += (inbuffer[0]  * l[0]);
                        outbuffer[1]  += (inbuffer[1]  * l[1]);
                        outbuffer[2]  += (inbuffer[2]  * l[2]);
                        outbuffer[3]  += (inbuffer[3]  * l[3]);
                        outbuffer[4]  += (inbuffer[4]  * l[4]);
                        outbuffer[5]  += (inbuffer[5]  * l[5]);
                        outbuffer[6]  += (inbuffer[6]  * l[6]);
                        outbuffer[7]  += (inbuffer[7]  * l[7]);

                        outbuffer[8]  += (inbuffer[8]  * l[0]);
                        outbuffer[9]  += (inbuffer[9]  * l[1]);
                        outbuffer[10] += (inbuffer[10] * l[2]);
                        outbuffer[11] += (inbuffer[11] * l[3]);
                        outbuffer[12] += (inbuffer[12] * l[4]);
                        outbuffer[13] += (inbuffer[13] * l[5]);
                        outbuffer[14] += (inbuffer[14] * l[6]);
                        outbuffer[15] += (inbuffer[15] * l[7]);
                       
                        outbuffer += 16;
                        inbuffer += 16;
                        len--;
                    }
                    
                    len = length & 1;
                    if (len)
                    {
                        while (len)
                        {
                            outbuffer[0]  += (inbuffer[0] * l[0]);
                            outbuffer[1]  += (inbuffer[1] * l[1]);
                            outbuffer[2]  += (inbuffer[2] * l[2]);
                            outbuffer[3]  += (inbuffer[3] * l[3]);
                            outbuffer[4]  += (inbuffer[4] * l[4]);
                            outbuffer[5]  += (inbuffer[5] * l[5]);
                            outbuffer[6]  += (inbuffer[6] * l[6]);
                            outbuffer[7]  += (inbuffer[7] * l[7]);

                            inbuffer  +=8;
                            outbuffer +=8;
                            len       --;
                        }
                    }
                }
            }
            else
            {
                for (count = 0; count < length; count++)
                {
                    int count2;
                    
                    for (count2 = 0; count2 < outchannels; count2++)
                    {
                        float srcval  = 0;
                        float destval = *outbuffer;
                        int count3;

                        for (count3 = 0; count3 < inchannels; count3++)
                        {
                            srcval += (inbuffer[count3] * levelcurrent[count2][count3]);
                        }

                        destval += srcval;
                        *outbuffer++ = destval;
                    }
                    inbuffer += inchannels;
                }
            }
        }
    }
#endif    
    else
    {
        for (count = 0; count < length; count++)
        {
            int count2;
            
            for (count2 = 0; count2 < outchannels; count2++)
            {
                float srcval  = 0;
                float destval = *outbuffer;
                int count3;
 
                for (count3 = 0; count3 < inchannels; count3++)
                {
                    srcval += (inbuffer[count3] * levelcurrent[count2][count3]);
                }

                destval += srcval;
                *outbuffer++ = destval;
            }
            inbuffer += inchannels;
        }
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::mixAndRamp(float * FMOD_RESTRICT outbuffer, float * FMOD_RESTRICT inbuffer, int outchannels, int inchannels, unsigned int length)
{
    unsigned int count;
    #if (DSP_LEVEL_PRECISION == 16)
    float        levelcurrent[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN];
    float        leveldelta[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN];

    for (count = 0; count < (unsigned int)outchannels; count++)
    {
        int count2;

        for (count2 = 0; count2 < inchannels; count2++)
        {
            levelcurrent[count][count2] = DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][count2]);
            leveldelta[count][count2] = DSP_LEVEL_DECOMPRESS(mLevelDelta[count][count2]);
        }
    }
    #endif

    for (count = 0; count < length; count++)
    {
        int count2;
        
        for (count2 = 0; count2 < outchannels; count2++)
        {
            float srcval  = 0;
            float destval = *outbuffer;
            int count3;

            for (count3 = 0; count3 < inchannels; count3++)
            {
                #if (DSP_LEVEL_PRECISION == 32)
                srcval += (inbuffer[count3] * mLevelCurrent[count2][count3]);
                mLevelCurrent[count2][count3] += mLevelDelta[count2][count3];
                #else
                srcval += (inbuffer[count3] * levelcurrent[count2][count3]);
                levelcurrent[count2][count3] += leveldelta[count2][count3];
                #endif
            }

            destval += srcval;
            *outbuffer++ = destval;
        }

        inbuffer += inchannels;
    }

    #if (DSP_LEVEL_PRECISION == 16)
    for (count = 0; count < (unsigned int)outchannels; count++)
    {
        int count2;

        for (count2 = 0; count2 < inchannels; count2++)
        {
            mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(levelcurrent[count][count2]);
            mLevelDelta[count][count2] = DSP_LEVEL_COMPRESS(leveldelta[count][count2]);
        }
    }
    #endif

    mRampCount -= length;

    /*
        Snap the current to the mlevel
    */
    if (!mRampCount)
    {
        int count, count2;
    
        for (count = 0; count < mMaxOutputLevels; count++)
        {
            for (count2 = 0; count2 < mMaxInputLevels; count2++)
            {
                mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(DSP_LEVEL_DECOMPRESS(mLevel[count][count2]) * mVolume);
            }
        }
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::rampTo()
{
    int   count;
    float oorampcount, delta = 0;

    oorampcount = 1.0f / DSP_RAMPCOUNT;

    if (mMaxInputLevels == 6)
    {
        for (count = 0; count < mMaxOutputLevels; count++)
        {
            float value; 
            
            value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][0]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][0])) * oorampcount;
            delta += FMOD_FABS(value);
            mLevelDelta[count][0] = DSP_LEVEL_COMPRESS(value);
            value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][1]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][1])) * oorampcount;
            delta += FMOD_FABS(value);
            mLevelDelta[count][1] = DSP_LEVEL_COMPRESS(value);
            value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][2]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][2])) * oorampcount;
            delta += FMOD_FABS(value);
            mLevelDelta[count][2] = DSP_LEVEL_COMPRESS(value);
            value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][3]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][3])) * oorampcount;
            delta += FMOD_FABS(value);
            mLevelDelta[count][3] = DSP_LEVEL_COMPRESS(value);
            value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][4]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][4])) * oorampcount;
            delta += FMOD_FABS(value);
            mLevelDelta[count][4] = DSP_LEVEL_COMPRESS(value);
            value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][5]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][5])) * oorampcount;
            delta += FMOD_FABS(value);
            mLevelDelta[count][5] = DSP_LEVEL_COMPRESS(value);
        }
    }
    else
    {
        int count2;
        
        for (count = 0; count < mMaxOutputLevels; count++)
        {
            for (count2 = 0; count2 < mMaxInputLevels; count2++)
            {
                float value; 

                value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][count2]) *mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][count2])) * oorampcount;
                delta += FMOD_FABS(value);
                mLevelDelta[count][count2] = DSP_LEVEL_COMPRESS(value);
            }
        }
    }

    if (DSP_LEVEL_COMPRESS(delta) >= 0.000001f)
    {
        mRampCount = DSP_RAMPCOUNT;     /* Only trigger the ramp if anything has changed. */
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::checkUnity(int outchannels, int inchannels)
{
    int count, count2;

    if (inchannels != outchannels)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    if (mRampCount != 0)
    {
        return FMOD_ERR_PAN;
    }

    if (mVolume != 1.0f)
    {
        return FMOD_ERR_PAN;
    }

    for (count = 0; count < outchannels; count++)
    {
        for (count2 = 0; count2 < inchannels; count2++)
        {
            if (count == count2 && DSP_LEVEL_DECOMPRESS(mLevel[count][count2]) != 1.0f)
            {
                return FMOD_ERR_PAN;
            }
            else if (count != count2 && mLevel[count][count2] != 0)
            {
                return FMOD_ERR_PAN;
            }
        }
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::checkMono(int outchannels, int inchannels)
{
    int count, count2;

    if (inchannels == 1)
    {
        return FMOD_OK;
    }

    for (count = 0; count < outchannels; count++)
    {
        for (count2 = 1; count2 < inchannels; count2++)
        {
            if (mLevel[count][count2] != 0)
            {
                return FMOD_ERR_TOOMANYCHANNELS;
            }
        }
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::setPan(float pan, int outchannels, int inchannels, FMOD_SPEAKERMODE speakermode)
{
    int count, count2;

#ifndef PLATFORM_PS3_SPU
    if (!mInputUnit->mSystem)
    {
        return FMOD_ERR_INTERNAL;
    }
#endif

    /*
        First clear out all levels to 0.
    */
    for (count = 0; count < outchannels; count++)
    {
        for (count2 = 0; count2 < inchannels; count2++)
        {
            mLevel[count][count2] = 0;
        }
    }    

    switch (speakermode)
    {
#ifdef FMOD_SUPPORT_SPEAKERMODE_RAW
        case FMOD_SPEAKERMODE_RAW:
        {
            /*
                Map 1 input channel to each output channel sequentially.  For example.

                1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0  or 1 0 0 0 or 1 0  or 1
                0 1 0 0 0 0 0 0    0 1 0 0    0 1    0    0 1 0 0 0 0 0 0     0 1 0 0    0 1     0
                0 0 1 0 0 0 0 0    0 0 1 0    0 0    0
                0 0 0 1 0 0 0 0    0 0 0 1    0 0    0 
                0 0 0 0 1 0 0 0    0 0 0 0    0 0    0
                0 0 0 0 0 1 0 0    0 0 0 0    0 0    0
                0 0 0 0 0 0 1 0    0 0 0 0    0 0    0
                0 0 0 0 0 0 0 1    0 0 0 0    0 0    0
            */
            for (count = 0; count < outchannels; count++)
            {
                for (count2 = 0; count2 < inchannels; count2++)
                {
                    if (count == count2)
                    {
                        mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f);
                    }
                }
            }
            break;
        }
#endif
#ifdef FMOD_SUPPORT_SPEAKERMODE_MONO
        case FMOD_SPEAKERMODE_MONO:
        {
            for (count = 0; count < inchannels; count++)
            {
                mLevel[0][count] = DSP_LEVEL_COMPRESS(1.0f);
            }
            break;
        }
#endif
#ifdef FMOD_SUPPORT_SPEAKERMODE_STEREO
        case FMOD_SPEAKERMODE_STEREO:
        case FMOD_SPEAKERMODE_STEREO_LINEAR:
        {
            float l,r;
                
            pan = (pan + 1.0f) / 2.0f;

            if (inchannels == 1)
            {  
                l = 1.0f - pan;
                r = pan;

                if (speakermode == FMOD_SPEAKERMODE_STEREO)
                {
                    l = FMOD_SQRT(l);
                    r = FMOD_SQRT(r);
                }

                mLevel[0][0] = DSP_LEVEL_COMPRESS(l);
                mLevel[1][0] = DSP_LEVEL_COMPRESS(r);
            }
            else if (inchannels == 2 || inchannels == 4 || inchannels == 6 || inchannels == 8)
            {   
                /*
                    Stereo source panning is 0 = 1.0:0.0.  0.5 = 1.0:1.0.  1.0 = 0:1.0.
                */
                if (pan <= 0.5f)
                {
                    l = 1.0f;
                    r = pan * 2.0f;
                }
                else
                {
                    l = (1.0f - pan) * 2.0f;
                    r = 1.0f;
                }
    
                if (inchannels == 2)
                {
                    mLevel[0][0] = DSP_LEVEL_COMPRESS(l);
                    mLevel[1][1] = DSP_LEVEL_COMPRESS(r);
                }
                else if (inchannels == 4)
                {
                    /*
                        Prologic downmix.  Assuming LCRS
                    */
                    mLevel[0][0] = DSP_LEVEL_COMPRESS(  1.0f   * l);
                    mLevel[0][1] = DSP_LEVEL_COMPRESS(  0.0f   * l);
                    mLevel[0][2] = DSP_LEVEL_COMPRESS(  0.707f * l);
                    mLevel[0][3] = DSP_LEVEL_COMPRESS( -0.707f * l);     /* Discard LFE */

                    mLevel[1][0] = DSP_LEVEL_COMPRESS(  0.0f   * r);
                    mLevel[1][1] = DSP_LEVEL_COMPRESS(  1.0f   * r);
                    mLevel[1][2] = DSP_LEVEL_COMPRESS(  0.707f * r);
                    mLevel[1][3] = DSP_LEVEL_COMPRESS( -0.707f * r);     /* Discard LFE */
                }
                else if (inchannels == 6)
                {
                    /*
                        Prologic 2 downmix.
                    */
                    mLevel[0][0] = DSP_LEVEL_COMPRESS( 1.0f   * l);
                    mLevel[0][1] = DSP_LEVEL_COMPRESS( 0.0f   * l);
                    mLevel[0][2] = DSP_LEVEL_COMPRESS( 0.707f * l);
                    mLevel[0][3] = DSP_LEVEL_COMPRESS( 0.0f   * l);     /* Discard LFE */
                    mLevel[0][4] = DSP_LEVEL_COMPRESS(-0.872f * l);
                    mLevel[0][5] = DSP_LEVEL_COMPRESS(-0.49f  * l);

                    mLevel[1][0] = DSP_LEVEL_COMPRESS( 0.0f   * r);
                    mLevel[1][1] = DSP_LEVEL_COMPRESS( 1.0f   * r);
                    mLevel[1][2] = DSP_LEVEL_COMPRESS( 0.707f * r);
                    mLevel[1][3] = DSP_LEVEL_COMPRESS( 0.0f   * r);     /* Discard LFE */
                    mLevel[1][4] = DSP_LEVEL_COMPRESS( 0.49f  * r);
                    mLevel[1][5] = DSP_LEVEL_COMPRESS( 0.872f * r);
                }
                else if (inchannels == 8)
                {
                    /*
                        Prologic 2x downmix?
                    */
                    mLevel[0][0] = DSP_LEVEL_COMPRESS( 1.0f   * l);
                    mLevel[0][1] = DSP_LEVEL_COMPRESS( 0.0f   * l);
                    mLevel[0][2] = DSP_LEVEL_COMPRESS( 0.707f * l);
                    mLevel[0][3] = DSP_LEVEL_COMPRESS( 0.0f   * l);     /* Discard LFE */
                    mLevel[0][4] = DSP_LEVEL_COMPRESS(-0.872f * l);
                    mLevel[0][5] = DSP_LEVEL_COMPRESS(-0.49f  * l);

                    mLevel[0][6] = DSP_LEVEL_COMPRESS( 1.0f   * l);     /* Not sure here.  Need PLIIx coefficients for 7.1? */
                    mLevel[0][7] = DSP_LEVEL_COMPRESS( 0.0f   * l);     /* Not sure here.  Need PLIIx coefficients for 7.1? */

                    mLevel[1][0] = DSP_LEVEL_COMPRESS( 0.0f   * r);
                    mLevel[1][1] = DSP_LEVEL_COMPRESS( 1.0f   * r);
                    mLevel[1][2] = DSP_LEVEL_COMPRESS( 0.707f * r);
                    mLevel[1][3] = DSP_LEVEL_COMPRESS( 0.0f   * r);     /* Discard LFE */
                    mLevel[1][4] = DSP_LEVEL_COMPRESS( 0.49f  * r);
                    mLevel[1][5] = DSP_LEVEL_COMPRESS( 0.872f * r);

                    mLevel[1][6] = DSP_LEVEL_COMPRESS( 0.0f   * r);     /* Not sure here.  Need PLIIx coefficients for 7.1? */
                    mLevel[1][7] = DSP_LEVEL_COMPRESS( 1.0f   * r);     /* Not sure here.  Need PLIIx coefficients for 7.1? */
                }
            }
            else
            {
                /*
                    Map 1 input channel to each output channel sequentially.  For example.

                    1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0  or 1 0 0 0 or 1 0  or 1
                    0 1 0 0 0 0 0 0    0 1 0 0    0 1    0    0 1 0 0 0 0 0 0     0 1 0 0    0 1     0
                    0 0 1 0 0 0 0 0    0 0 1 0    0 0    0
                    0 0 0 1 0 0 0 0    0 0 0 1    0 0    0 
                    0 0 0 0 1 0 0 0    0 0 0 0    0 0    0
                    0 0 0 0 0 1 0 0    0 0 0 0    0 0    0
                    0 0 0 0 0 0 1 0    0 0 0 0    0 0    0
                    0 0 0 0 0 0 0 1    0 0 0 0    0 0    0
                */
                for (count = 0; count < outchannels; count++)
                {
                    for (count2 = 0; count2 < inchannels; count2++)
                    {
                        if (count == count2)
                        {
                            mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f);
                        }
                    }
                }
            }
            break;
        }
#endif
        case FMOD_SPEAKERMODE_QUAD:
        case FMOD_SPEAKERMODE_SURROUND:
        case FMOD_SPEAKERMODE_5POINT1:
        case FMOD_SPEAKERMODE_7POINT1:
        {
            float l,r;
            
            pan = (pan + 1.0f) / 2.0f;

            if (inchannels == 1)
            {  
                l = 1.0f - pan;
                r = pan;

                l = FMOD_SQRT(l);
                r = FMOD_SQRT(r);

                mLevel[0][0] = DSP_LEVEL_COMPRESS(l);
                mLevel[1][0] = DSP_LEVEL_COMPRESS(r);
            }
            else if (inchannels == 2)
            {   
                /*
                    Stereo source panning is 0.0 = 1.0:0.0.  0.5 = 1.0:1.0.  1.0 = 0.0:1.0.
                */
                if (pan <= 0.5f)
                {
                    l = 1.0f;
                    r = pan * 2.0f;
                }
                else
                {
                    l = (1.0f - pan) * 2.0f;
                    r = 1.0f;
                }

                mLevel[0][0] = DSP_LEVEL_COMPRESS(l);
                mLevel[1][1] = DSP_LEVEL_COMPRESS(r);
            }
            else
            {
                /*
                    Map 1 input channel to each output channel sequentially.  For example.

                    1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0  or 1 0 0 0 or 1 0  or 1
                    0 1 0 0 0 0 0 0    0 1 0 0    0 1    0    0 1 0 0 0 0 0 0     0 1 0 0    0 1     0
                    0 0 1 0 0 0 0 0    0 0 1 0    0 0    0
                    0 0 0 1 0 0 0 0    0 0 0 1    0 0    0 
                    0 0 0 0 1 0 0 0    0 0 0 0    0 0    0
                    0 0 0 0 0 1 0 0    0 0 0 0    0 0    0
                    0 0 0 0 0 0 1 0    0 0 0 0    0 0    0
                    0 0 0 0 0 0 0 1    0 0 0 0    0 0    0
                */
                for (count = 0; count < outchannels; count++)
                {
                    for (count2 = 0; count2 < inchannels; count2++)
                    {
                        if (count == count2)
                        {
                            mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f);
                        }
                    }
                }
            }
            break;
        }
#ifdef FMOD_SUPPORT_SPEAKERMODE_PROLOGIC
        case FMOD_SPEAKERMODE_PROLOGIC:
        {
            float l,r;
            
            pan = (pan + 1.0f) / 2.0f;

            if (inchannels == 1)
            {  
                l = 1.0f - pan;
                r = pan;

                l = FMOD_SQRT(l);
                r = FMOD_SQRT(r);

                mLevel[0][0] = DSP_LEVEL_COMPRESS(l);
                mLevel[1][0] = DSP_LEVEL_COMPRESS(r);
            }
            else if (inchannels == 2)
            {   
                /*
                    Stereo source panning is 0.0 = 1.0:0.0.  0.5 = 1.0:1.0.  1.0 = 0:1.0.
                */
                if (pan < 0.5f)
                {
                    l = 1.0f;
                    r = pan * 2.0f;
                }
                else
                {
                    l = (1.0f - pan) * 2.0f;
                    r = 1.0f;
                }
    
                mLevel[0][0] = DSP_LEVEL_COMPRESS(l);
                mLevel[1][1] = DSP_LEVEL_COMPRESS(r);
            }
            else
            { 
                /*
                    Map 1 input channel to each output channel sequentially.  For example.

                    1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0  or 1 0 0 0 or 1 0  or 1
                    0 1 0 0 0 0 0 0    0 1 0 0    0 1    0    0 1 0 0 0 0 0 0     0 1 0 0    0 1     0
                    0 0 1 0 0 0 0 0    0 0 1 0    0 0    0
                    0 0 0 1 0 0 0 0    0 0 0 1    0 0    0 
                    0 0 0 0 1 0 0 0    0 0 0 0    0 0    0
                    0 0 0 0 0 1 0 0    0 0 0 0    0 0    0
                    0 0 0 0 0 0 1 0    0 0 0 0    0 0    0
                    0 0 0 0 0 0 0 1    0 0 0 0    0 0    0
                */
                for (count = 0; count < outchannels; count++)
                {
                    for (count2 = 0; count2 < inchannels; count2++)
                    {
                        if (count == count2)
                        {
                            mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f);
                        }
                    }
                }
            }
            break;
        }
#endif
        default:
        {
            break;
        }
    };

    mSetLevelsUsed = true;

    return rampTo();
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
1	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::setLevels(float *levels, int numinputlevels)
{
    int count, count2;

    if (!levels)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    if (!numinputlevels)
    {
        return FMOD_OK;
    }

#if (DSP_MAXLEVELS_OUT > 2)
    if (mMaxOutputLevels == 6 && numinputlevels < 3)
    {
        if (numinputlevels == 1)
        {
            mLevel[0][0] = DSP_LEVEL_COMPRESS(levels[0]);
            mLevel[1][0] = DSP_LEVEL_COMPRESS(levels[1]);
            mLevel[2][0] = DSP_LEVEL_COMPRESS(levels[2]);
            mLevel[3][0] = DSP_LEVEL_COMPRESS(levels[3]);
            mLevel[4][0] = DSP_LEVEL_COMPRESS(levels[4]);
            mLevel[5][0] = DSP_LEVEL_COMPRESS(levels[5]);
        }
        else
        {
            mLevel[0][0] = DSP_LEVEL_COMPRESS(levels[0]);
            mLevel[0][1] = DSP_LEVEL_COMPRESS(levels[1]);
            mLevel[1][0] = DSP_LEVEL_COMPRESS(levels[2]);
            mLevel[1][1] = DSP_LEVEL_COMPRESS(levels[3]);
            mLevel[2][0] = DSP_LEVEL_COMPRESS(levels[4]);
            mLevel[2][1] = DSP_LEVEL_COMPRESS(levels[5]);
            mLevel[3][0] = DSP_LEVEL_COMPRESS(levels[6]);
            mLevel[3][1] = DSP_LEVEL_COMPRESS(levels[7]);
            mLevel[4][0] = DSP_LEVEL_COMPRESS(levels[8]);
            mLevel[4][1] = DSP_LEVEL_COMPRESS(levels[9]);
            mLevel[5][0] = DSP_LEVEL_COMPRESS(levels[10]);
            mLevel[5][1] = DSP_LEVEL_COMPRESS(levels[11]);            
        }
    }
    else
#endif
    {
        for (count = 0; count < mMaxOutputLevels; count++)
        {
            for (count2 = 0; count2 < mMaxInputLevels; count2++)
            {
                if (count2 < numinputlevels)
                {
                    mLevel[count][count2] = DSP_LEVEL_COMPRESS(levels[(count * numinputlevels) + count2]);
                }
                else
                {
                    mLevel[count][count2] = 0;
                }
            }
        }
    }

    mSetLevelsUsed = true;

    return rampTo();
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getLevels(float *levels, int numinputlevels)
{
    int count, count2;

    if (!levels)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    for (count = 0; count < mMaxOutputLevels; count++)
    {
        for (count2 = 0; count2 < numinputlevels; count2++)
        {
            if (count2 < mMaxInputLevels)
            {
                levels[(count * numinputlevels) + count2] = DSP_LEVEL_DECOMPRESS(mLevel[count][count2]);
            }
            else
            {
                levels[(count * numinputlevels) + count2] = 0;
            }
        }
    }

    return FMOD_OK;
}

#endif /* #ifdef FMOD_SUPPORT_SOFTWARE */


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::setMix(float volume)
{
#ifdef FMOD_SUPPORT_SOFTWARE
    if (volume < -1.0f)
    {
        volume = -1.0f;
    }
    if (volume > 1.0f)
    {
        volume = 1.0f;
    }

    if (mVolume == volume)
    {
        return FMOD_OK;
    }

    mVolume = volume;

    return rampTo();
#else
    return FMOD_ERR_NEEDSSOFTWARE;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getMix(float *volume)
{
#ifdef FMOD_SUPPORT_SOFTWARE
    if (!volume)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    *volume = mVolume;

    return FMOD_OK;
#else
    return FMOD_ERR_NEEDSSOFTWARE;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::setLevels(FMOD_SPEAKER speaker, float *levels, int numinputlevels)
{
#ifdef FMOD_SUPPORT_SOFTWARE
    int count;

    if (!levels)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    if (!numinputlevels)
    {
        return FMOD_OK;
    }

    if (speaker >= mMaxOutputLevels)
    {
        return FMOD_ERR_INVALID_SPEAKER;
    }

    for (count = 0; count < mMaxInputLevels; count++)
    {
        if (count < numinputlevels)
        {
            mLevel[speaker][count] = DSP_LEVEL_COMPRESS(levels[count]);
        }
        else
        {
            mLevel[speaker][count] = 0;
        }
    }

    mSetLevelsUsed = true;

    return rampTo();
#else
    return FMOD_ERR_NEEDSSOFTWARE;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getLevels(FMOD_SPEAKER speaker, float *levels, int numinputlevels)
{
#ifdef FMOD_SUPPORT_SOFTWARE
    int count;

    if (!levels)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    for (count = 0; count < numinputlevels; count++)
    {
        if (count < mMaxInputLevels)
        {
            levels[count] = DSP_LEVEL_DECOMPRESS(mLevel[speaker][count]);
        }
        else
        {
            levels[count] = 0;
        }
    }

    return FMOD_OK;
#else
    return FMOD_ERR_NEEDSSOFTWARE;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::setUserData(void *userdata)
{
    mUserData = userdata;
    
    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getUserData(void **userdata)
{
    if (!userdata)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    *userdata = mUserData;
    
    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
#ifdef FMOD_SUPPORT_SOFTWARE
FMOD_RESULT DSPConnectionI::copy(DSPConnectionI *source)
{
    int count, count2;
    
    mVolume = source->mVolume;

    for (count = 0; count < mMaxOutputLevels; count++)
    {
        for (count2 = 0; count2 < mMaxInputLevels; count2++)
        {
            mLevel       [count][count2] = source->mLevel       [count][count2];
            mLevelCurrent[count][count2] = source->mLevelCurrent[count][count2];
            mLevelDelta  [count][count2] = source->mLevelDelta  [count][count2];
        }
    }
    
    mRampCount     = source->mRampCount;
    mSetLevelsUsed = source->mSetLevelsUsed;
    
    return FMOD_OK;
}
#endif


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT DSPConnectionI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details)
{
#ifdef FMOD_SUPPORT_MEMORYTRACKER
    GETMEMORYINFO_IMPL
#else
    return FMOD_ERR_UNIMPLEMENTED;
#endif
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/

#if defined(FMOD_SUPPORT_MEMORYTRACKER) && !defined(PLATFORM_PS3_SPU)

FMOD_RESULT DSPConnectionI::getMemoryUsedImpl(MemoryTracker *tracker)
{
    tracker->add(false, FMOD_MEMBITS_DSPCONNECTION, sizeof(*this));

//        LinkedListNode   mInputNode;
//        LinkedListNode   mOutputNode;
//        LinkedListNode  *mNode;         /* Make it a pointer so we can store the node data externally.  PS3 will corrupt it otherwise. */

//        DSP_LEVEL_TYPE *mLevel[DSP_MAXLEVELS_OUT];
//        DSP_LEVEL_TYPE *mLevelCurrent[DSP_MAXLEVELS_OUT];
//        DSP_LEVEL_TYPE *mLevelDelta[DSP_MAXLEVELS_OUT];

//        DSPI           *mInputUnit;
//        DSPI           *mOutputUnit;

    return FMOD_OK;
}

#endif

#if (DSP_LEVEL_PRECISION == 16) && !defined(PLATFORM_XENON)
/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
unsigned short FMOD_DSP_ConnectionI_Single2HalfP(float source)
{
    typedef union
    {
        float f;
        unsigned int i;
    } floatdata;

    floatdata        x;
    unsigned short   hs, he, hm;
    unsigned int     xs, xe, xm;
    int hes; 
    
    x.f = source;

    if( (x.i & 0x7FFFFFFFu) == 0 ) 
    {  // Signed zero
        return (unsigned short) (x.i >> 16);  // Return the signed zero
    } 
    else 
    { // Not zero
        xs = x.i & 0x80000000u;  // Pick off sign bit
        xe = x.i & 0x7F800000u;  // Pick off exponent bits
        xm = x.i & 0x007FFFFFu;  // Pick off mantissa bits
        if( xe == 0 ) 
        {  // Denormal will underflow, return a signed zero
            return (unsigned short) (xs >> 16);
        } else if( xe == 0x7F800000u ) 
        {  // Inf or NaN (all the exponent bits are set)
            if( xm == 0 ) 
            { // If mantissa is zero ...
                return (unsigned short) ((xs >> 16) | 0x7C00u); // Signed Inf
            } else 
            {
                return (unsigned short) 0xFE00u; // NaN, only 1st mantissa bit set
            }
        } else 
        { // Normalized number
            hs = (unsigned short) (xs >> 16); // Sign bit
            hes = ((int)(xe >> 23)) - 127 + 15; // Exponent unbias the single, then bias the halfp
            if( hes >= 0x1F ) 
            {  // Overflow
                return (unsigned short) ((xs >> 16) | 0x7C00u); // Signed Inf
            } else if( hes <= 0 ) 
            {  // Underflow
                if( (14 - hes) > 24 ) 
                {  // Mantissa shifted all the way off & no rounding possibility
                    hm = (unsigned short) 0u;  // Set mantissa to zero
                } 
                else 
                {
                    xm |= 0x00800000u;  // Add the hidden leading bit
                    hm = (unsigned short) (xm >> (14 - hes)); // Mantissa
                    if( (xm >> (13 - hes)) & 0x00000001u ) // Check for rounding
                        hm += (unsigned short) 1u; // Round, might overflow into exp bit, but this is OK
                }
                return (hs | hm); // Combine sign bit and mantissa bits, biased exponent is zero
            } else 
            {
                he = (unsigned short) (hes << 10); // Exponent
                hm = (unsigned short) (xm >> 13); // Mantissa
                if( xm & 0x00001000u ) // Check for rounding
                    return (hs | he | hm) + (unsigned short) 1u; // Round, might overflow to inf, this is OK
                else
                    return (hs | he | hm);  // No rounding
            }
        }
    }
    
    return 0;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

    [PLATFORMS]
    
	[SEE_ALSO]
]
*/
float FMOD_DSP_ConnectionI_HalfP2Single(unsigned short h)
{
    typedef union
    {
        float f;
        unsigned int i;
    } floatdata;

    unsigned short hs, he, hm;
    unsigned int xs, xe, xm;
    signed int xes;
    floatdata xp;
    int e;

    if( (h & 0x7FFFu) == 0 ) 
    {  // Signed zero
        xp.i = ((unsigned int) h) << 16;  // Return the signed zero
    } 
    else 
    { // Not zero
        hs = h & 0x8000u;  // Pick off sign bit
        he = h & 0x7C00u;  // Pick off exponent bits
        hm = h & 0x03FFu;  // Pick off mantissa bits
        if( he == 0 ) 
        {  // Denormal will convert to normalized
            e = -1; // The following loop figures out how much extra to adjust the exponent
            do 
            {
                e++;
                hm <<= 1;
            } while( (hm & 0x0400u) == 0 ); // Shift until leading bit overflows into exponent bit
            xs = ((unsigned int) hs) << 16; // Sign bit
            xes = ((signed int) (he >> 10)) - 15 + 127 - e; // Exponent unbias the halfp, then bias the single
            xe = (unsigned int) (xes << 23); // Exponent
            xm = ((unsigned int) (hm & 0x03FFu)) << 13; // Mantissa
            xp.i = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits
        } 
        else if( he == 0x7C00u ) 
        {  // Inf or NaN (all the exponent bits are set)
            if( hm == 0 ) 
            { // If mantissa is zero ...
                xp.i = (((unsigned int) hs) << 16) | ((unsigned int) 0x7F800000u); // Signed Inf
            } else 
            {
                xp.i = (unsigned int) 0xFFC00000u; // NaN, only 1st mantissa bit set
            }
        } 
        else 
        { // Normalized number
            xs = ((unsigned int) hs) << 16; // Sign bit
            xes = ((signed int) (he >> 10)) - 15 + 127; // Exponent unbias the halfp, then bias the single
            xe = (unsigned int) (xes << 23); // Exponent
            xm = ((unsigned int) hm) << 13; // Mantissa
            xp.i = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits
        }
    }
    return xp.f;
}
#endif

}
