/*===============================================================================================
 DSP Effect per speaker Example
 Copyright (c), Firelight Technologies Pty, Ltd 2004-2009.

 This example shows how to manipulate a DSP network and as an example, creates 2 dsp effects,
 and splits a single sound into 2 audio paths, which it then filters seperately.
 To only have each audio path come out of one speaker each, FMOD_DSPConnection_setLevels is used just
 before the 2 branches merge back together again.

 For more speakers:
 1. Use FMOD_System_SetSpeakerMode or FMOD_System_SetOutputFormat.
 2. Create more effects, currently 2 for stereo (reverb and chorus), create one per speaker.
 3. Under the 'Now connect the 2 effects to channeldsp head.' section, connect the extra effects
    by duplicating the code more times.
 4. Filter each effect to each speaker by calling FMOD_DSP_SetInputLevels.  Expand the existing code
    by extending the level arrays from 2 to the number of speakers you require, and change the
    numlevels parameter in FMOD_DSP_SetInputLevels from 2 to the correct number accordingly.

===============================================================================================*/
#include "../common/wincompat.h"
#include "../../api/inc/fmod.h"
#include "../../api/inc/fmod_errors.h"

void ERRCHECK(FMOD_RESULT result)
{
    if (result != FMOD_OK)
    {
        printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
        exit(-1);
    }
}


int main(int argc, char *argv[])
{
    FMOD_SYSTEM        *system;
    FMOD_SOUND         *sound;
    FMOD_CHANNEL       *channel;
    FMOD_DSP           *dsplowpass, *dspchorus, *dsphead, *dspchannelmixer;
	FMOD_DSPCONNECTION *dsplowpassconnection, *dspchorusconnection;
    FMOD_RESULT         result;
    int                 key;
    unsigned int        version;
    float               pan = 0;

    /*
        Create a System object and initialize.
    */
    result = FMOD_System_Create(&system);
    ERRCHECK(result);

    result = FMOD_System_GetVersion(system, &version);
    ERRCHECK(result);

    if (version < FMOD_VERSION)
    {
        printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION);
        return 0;
    }

    result = FMOD_System_Init(system, 32, FMOD_INIT_NORMAL, 0);
    ERRCHECK(result);

    result = FMOD_System_CreateSound(system, "../media/drumloop.wav", FMOD_SOFTWARE | FMOD_LOOP_NORMAL, 0, &sound);
    ERRCHECK(result);

    printf("===============================================================================\n");
    printf("DSP Effect per speaker example. Copyright (c) Firelight Technologies 2004-2009.\n");
    printf("===============================================================================\n");
    printf("Press 'L' to toggle reverb on/off on left speaker only\n");
    printf("Press 'R' to toggle chorus on/off on right speaker only\n");
    printf("Press '[' to pan sound left\n");
    printf("Press ']' to pan sound right\n");
    printf("Press 'Esc' to quit\n");
    printf("\n");

    result = FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, sound, FALSE, &channel);
    ERRCHECK(result);

    /*
        Create the DSP effects.
    */  
    result = FMOD_System_CreateDSPByType(system, FMOD_DSP_TYPE_REVERB, &dsplowpass);
    ERRCHECK(result);

    result = FMOD_System_CreateDSPByType(system, FMOD_DSP_TYPE_CHORUS, &dspchorus);
    ERRCHECK(result);

    /*
        Connect up the DSP network
    */

    /*
        When a sound is played, a subnetwork is set up in the DSP network which looks like this.
        Wavetable is the drumloop sound, and it feeds its data from right to left.

        [DSPHEAD]<------------[DSPCHANNELMIXER]
    */  
    result = FMOD_System_GetDSPHead(system, &dsphead);
    ERRCHECK(result);

    result = FMOD_DSP_GetInput(dsphead, 0, &dspchannelmixer, 0);
    ERRCHECK(result);

    /*
        Now disconnect channeldsp head from wavetable to look like this.

        [DSPHEAD]             [DSPCHANNELMIXER]
    */
    result = FMOD_DSP_DisconnectFrom(dsphead, dspchannelmixer);
    ERRCHECK(result);

    /*
        Now connect the 2 effects to channeldsp head.
		Store the 2 connections this makes so we can set their speakerlevels later.

                  [DSPLOWPASS]
                 /x           
        [DSPHEAD]             [DSPCHANNELMIXER]
                 \y           
                  [DSPCHORUS]
    */
    result = FMOD_DSP_AddInput(dsphead, dsplowpass, &dsplowpassconnection); /* x = dsplowpassconnection */
    ERRCHECK(result);
    result = FMOD_DSP_AddInput(dsphead, dspchorus, &dspchorusconnection);   /* y = dspchorusconnection */
    ERRCHECK(result);
    
    /*
        Now connect the wavetable to the 2 effects

                  [DSPLOWPASS]
                 /x          \
        [DSPHEAD]             [DSPCHANNELMIXER]
                 \y          /
                  [DSPCHORUS]
    */
    result = FMOD_DSP_AddInput(dsplowpass, dspchannelmixer, NULL);  /* Null for connection - we dont care about it. */
    ERRCHECK(result);
    result = FMOD_DSP_AddInput(dspchorus, dspchannelmixer, NULL);   /* Null for connection - we dont care about it. */
    ERRCHECK(result);

    /*
        Now the drumloop will be twice as loud, because it is being split into 2, then recombined at the end.
        What we really want is to only feed the dspchannelmixer->dsplowpass through the left speaker, and 
        dspchannelmixer->dspchorus to the right speaker.
        We can do that simply by setting the pan, or speaker levels of the connections.

                  [DSPLOWPASS]
                 /x=1,0      \
        [DSPHEAD]             [DSPCHANNELMIXER]
                 \y=0,1      /
                  [DSPCHORUS]
    */
    {
        float leftinputon[2]  = { 1.0f, 0.0f };
        float rightinputon[2] = { 0.0f, 1.0f };
        float inputsoff[2]    = { 0.0f, 0.0f };

        result = FMOD_DSPConnection_SetLevels(dsplowpassconnection, FMOD_SPEAKER_FRONT_LEFT, leftinputon, 2);
        ERRCHECK(result);
        result = FMOD_DSPConnection_SetLevels(dsplowpassconnection, FMOD_SPEAKER_FRONT_RIGHT, inputsoff, 2);
        ERRCHECK(result);

        result = FMOD_DSPConnection_SetLevels(dspchorusconnection, FMOD_SPEAKER_FRONT_LEFT, inputsoff, 2);
        ERRCHECK(result);
        result = FMOD_DSPConnection_SetLevels(dspchorusconnection, FMOD_SPEAKER_FRONT_RIGHT, rightinputon, 2);
        ERRCHECK(result);
    }

    result = FMOD_DSP_SetBypass(dsplowpass, TRUE);
    result = FMOD_DSP_SetBypass(dspchorus, TRUE);

    result = FMOD_DSP_SetActive(dsplowpass, TRUE);
    result = FMOD_DSP_SetActive(dspchorus, TRUE);

    /*
        Main loop.
    */
    do
    {
        if (kbhit())
        {
            key = getch();

            switch (key)
            {
                case 'l' : 
                case 'L' : 
                {
                    static int reverb = FALSE;

                    FMOD_DSP_SetBypass(dsplowpass, reverb);

                    reverb = !reverb;
                    break;
                }
                case 'r' : 
                case 'R' : 
                {
                    static int chorus = FALSE;

                    FMOD_DSP_SetBypass(dspchorus, chorus);

                    chorus = !chorus;
                    break;
                }
                case '[' :
                {
                    FMOD_Channel_GetPan(channel, &pan);
                    pan -= 0.1f;
                    if (pan < -1)
                    {
                        pan = -1;
                    }
                    FMOD_Channel_SetPan(channel, pan);
                    break;
                }
                case ']' :
                {
                    FMOD_Channel_GetPan(channel, &pan);
                    pan += 0.1f;
                    if (pan > 1)
                    {
                        pan = 1;
                    }
                    FMOD_Channel_SetPan(channel, pan);
                    break;
                }
            }
        }

        FMOD_System_Update(system);

        {
            int  channelsplaying = 0;

            FMOD_System_GetChannelsPlaying(system, &channelsplaying);

            printf("Channels Playing %2d : Pan = %.02f\r", channelsplaying, pan);
        }

        fflush(stdout);
        Sleep(10);

    } while (key != 27);

    printf("\n");

    /*
        Shut down
    */
    result = FMOD_Sound_Release(sound);
    ERRCHECK(result);

    result = FMOD_DSP_Release(dsplowpass);
    ERRCHECK(result);
    result = FMOD_DSP_Release(dspchorus);
    ERRCHECK(result);

    result = FMOD_System_Close(system);
    ERRCHECK(result);
    result = FMOD_System_Release(system);
    ERRCHECK(result);

    return 0;
}


