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

 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 "windows.bi"
#include "crt.bi"
#include "fmodex/fmodex.bi"
#include "fmodex/fmod_errors.bi"

sub ERRCHECK(byval result as FMOD_RESULT)
    if (result <> FMOD_OK) then
        printf(!"FMOD error! (%d) %s\n", result, FMOD_ErrorString(result))
        end -1
end if
end sub


' program start
    dim as FMOD_SYSTEM ptr fsystem = any
    dim as FMOD_SOUND ptr sound = any
    dim as FMOD_CHANNEL ptr channel = any
    dim as FMOD_DSP ptr  dsplowpass = any, dspchorus = any, dsphead = any, dspchannelmixer = any
	dim as FMOD_DSPCONNECTION ptr dsplowpassconnection = any, dspchorusconnection = any
    dim as FMOD_RESULT result = any
    dim as string key
    dim as integer        version = any
    dim as float pan = 0

    /'
        Create a System object and initialize.
    '/
    result = FMOD_System_Create(@fsystem)
    ERRCHECK(result)

    result = FMOD_System_GetVersion(fsystem, @version)
    ERRCHECK(result)

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

    result = FMOD_System_Init(fsystem, 32, FMOD_INIT_NORMAL, 0)
    ERRCHECK(result)

    result = FMOD_System_CreateSound(fsystem, "media/drumloop.wav", FMOD_SOFTWARE or FMOD_LOOP_NORMAL, 0, @sound)
    ERRCHECK(result)

    printf(!"===============================================================================\n")
    printf(!"DSP Effect per speaker example. Copyright (c) Firelight Technologies 2004-2011.\n")
    printf(!"===============================================================================\n")
    printf(!"Press 'L' to toggle lowpass 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(fsystem, FMOD_CHANNEL_FREE, sound, FALSE, @channel)
    ERRCHECK(result)

    /'
        Create the DSP effects.
    '/  
    result = FMOD_System_CreateDSPByType(fsystem, FMOD_DSP_TYPE_LOWPASS, @dsplowpass)
    ERRCHECK(result)

    result = FMOD_System_CreateDSPByType(fsystem, 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(fsystem, @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]
    '/
    scope
        dim as float leftinputon(1)  = { 1.0f, 0.0f }
        dim as float rightinputon(2) = { 0.0f, 1.0f }
        dim as float inputsoff(1)    = { 0.0f, 0.0f }

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

        result = FMOD_DSPConnection_SetLevels(dspchorusconnection, FMOD_SPEAKER_FRONT_LEFT, @inputsoff(0), 2)
        ERRCHECK(result)
        result = FMOD_DSPConnection_SetLevels(dspchorusconnection, FMOD_SPEAKER_FRONT_RIGHT, @rightinputon(0), 2)
        ERRCHECK(result)
    end scope

    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
key = inkey
            select case key
                case "l","L" : 
                    static as integer lowpass = FALSE

                    FMOD_DSP_SetBypass(dsplowpass, lowpass)

                    lowpass xor= 1
                case "r","R" : 
                    static as integer chorus = FALSE

                    FMOD_DSP_SetBypass(dspchorus, chorus)

                    chorus xor= 1
                case "[" :
                    FMOD_Channel_GetPan(channel, @pan)
                    pan -= 0.1f
                    if (pan < -1) then
                        pan = -1
                    end if
                    FMOD_Channel_SetPan(channel, pan)
                case "]" :
                    FMOD_Channel_GetPan(channel, @pan)
                    pan += 0.1f
                    if (pan > 1) then
                        pan = 1
                    end if
                    FMOD_Channel_SetPan(channel, pan)
            end select

        FMOD_System_Update(fsystem)

        scope
            dim as integer  channelsplaying = 0

            FMOD_System_GetChannelsPlaying(fsystem, @channelsplaying)

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

        Sleep(10)

    loop while (key <> chr(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(fsystem)
    ERRCHECK(result)
    result = FMOD_System_Release(fsystem)
    ERRCHECK(result)
