/'===============================================================================================
 User Created Sound Example
 Copyright (c), Firelight Technologies Pty, Ltd 2004-2011.

 This example shows how create a sound with data filled by the user.
 It shows a user created static sample, followed by a user created stream.
 The former allocates all memory needed for the sound and is played back as a static sample, 
 while the latter streams the data in chunks as it plays, using far less memory.
==============================================================================================='/
#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

function pcmreadcallback(byval sound as FMOD_SOUND ptr, byval sdata as any ptr, byval datalen as uinteger) as FMOD_RESULT
    dim as uinteger  count = any
    static as float  t1 = 0, t2 = 0        ' time
    static as float  v1 = 0, v2 = 0        ' velocity
    dim as short ptr stereo16bitbuffer = sdata

    for count=0  to (datalen shr 2) - 1
        *stereo16bitbuffer = (sin(t1) * 32767.0f)    ' left channel
        stereo16bitbuffer+=1
        *stereo16bitbuffer = (sin(t2) * 32767.0f)    ' right channel
        stereo16bitbuffer+=1

        t1 += 0.01f   + v1
        t2 += 0.0142f + v2
        v1 += (sin(t1) * 0.002f)
        v2 += (sin(t2) * 0.002f)
    next

    return FMOD_OK
end function


function pcmsetposcallback(byval sound as FMOD_SOUND ptr, byval subsound as integer, byval position as uinteger, byval postype as FMOD_TIMEUNIT) as FMOD_RESULT
    /'
        This is useful if the user calls FMOD_Channel_SetPosition and you want to seek your data accordingly.
    '/
    return FMOD_OK
end function

' program start
    dim as FMOD_SYSTEM            ptr fsystem = any
    dim as FMOD_SOUND             ptr sound = any
    dim as FMOD_CHANNEL           ptr channel = 0
    dim as FMOD_RESULT             result = any
    dim as FMOD_MODE               mode = FMOD_2D or FMOD_OPENUSER or FMOD_LOOP_NORMAL or FMOD_HARDWARE
    dim as string                     key
    dim as integer                     channels = 2
    dim as FMOD_CREATESOUNDEXINFO  createsoundexinfo
    dim as uinteger            version = any

    /'
        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, NULL)
    ERRCHECK(result)
        
    printf(!"============================================================================\n")
    printf(!"User Created Sound Example.  Copyright (c) Firelight Technologies 2004-2011.\n")
    printf(!"============================================================================\n")
    printf(!"Sound played here is generated in realtime.  It will either play as a stream\n")
    printf(!"which means it is continually filled as it is playing, or it will play as a \n")
    printf(!"static sample, which means it is filled once as the sound is created, then  \n")
    printf(!"when played it will just play that short loop of data.                      \n")
    printf(!"============================================================================\n")
    printf(!"\n")

    do
        printf(!"Press 1 to play as a runtime decoded stream. (will carry on infinitely)\n")
        printf(!"Press 2 to play as a static in memory sample. (loops a short block of data)\n")
        printf(!"Press Esc to quit.\n\n")
        key = inkey

sleep 10
    loop while (key <> chr(27) AndAlso key <> "1" )

    if (key = chr(27)) then
        end 0
    elseIf (key = "1") then
        mode or= FMOD_CREATESTREAM
    end if

    createsoundexinfo.cbsize            = sizeof(FMOD_CREATESOUNDEXINFO)              /' required. '/
    createsoundexinfo.decodebuffersize  = 44100                                       /' Chunk size of stream update in samples.  This will be the amount of data passed to the user callback. '/
    createsoundexinfo.length            = 44100 * channels * sizeof(short) * 5 /' Length of PCM data in bytes of whole song (for Sound::getLength) '/
    createsoundexinfo.numchannels       = channels                                    /' Number of channels in the sound. '/
    createsoundexinfo.defaultfrequency  = 44100                                       /' Default playback rate of sound. '/
    createsoundexinfo.format            = FMOD_SOUND_FORMAT_PCM16                     /' Data format of sound. '/
    createsoundexinfo.pcmreadcallback   = @pcmreadcallback                             /' User callback for reading. '/
    createsoundexinfo.pcmsetposcallback = @pcmsetposcallback                           /' User callback for seeking. '/

    result = FMOD_System_CreateSound(fsystem, 0, mode, @createsoundexinfo, @sound)
    ERRCHECK(result)

    printf(!"Press space to pause, Esc to quit\n")
    printf(!"\n")

    /'
        Play the sound.
    '/

    result = FMOD_System_PlaySound(fsystem, FMOD_CHANNEL_FREE, sound, 0, @channel)
    ERRCHECK(result)

    /'
        Main loop.
    '/
    do
            key = inkey

            select case  key
                case " " :
                    dim as integer paused = any
                    FMOD_Channel_GetPaused(channel, @paused)
paused xor = 1
                    FMOD_Channel_SetPaused(channel, paused)
end select
        FMOD_System_Update(fsystem)

        if (channel) then
            dim as uinteger ms = any
            dim as uinteger lenms = any
            dim as integer          playing = any
            dim as integer          paused = any

            FMOD_Channel_IsPlaying(channel, @playing)
            if ((result <> FMOD_OK) AndAlso (result <> FMOD_ERR_INVALID_HANDLE) AndAlso (result <> FMOD_ERR_CHANNEL_STOLEN)) then
                ERRCHECK(result)
            end if

            result = FMOD_Channel_GetPaused(channel, @paused)
            if ((result <> FMOD_OK) AndAlso (result <> FMOD_ERR_INVALID_HANDLE) AndAlso (result <> FMOD_ERR_CHANNEL_STOLEN)) then
                ERRCHECK(result)
            end if

            result = FMOD_Channel_GetPosition(channel, @ms, FMOD_TIMEUNIT_MS)
            if ((result <> FMOD_OK) AndAlso (result <> FMOD_ERR_INVALID_HANDLE) AndAlso (result <> FMOD_ERR_CHANNEL_STOLEN)) then
                ERRCHECK(result)
            end if

            result = FMOD_Sound_GetLength(sound, @lenms, FMOD_TIMEUNIT_MS)
            if ((result <> FMOD_OK) AndAlso (result <> FMOD_ERR_INVALID_HANDLE) AndAlso (result <> FMOD_ERR_CHANNEL_STOLEN)) then
               ERRCHECK(result)
            end if

            printf(!"Time %02d:%02d:%02d/%02d:%02d:%02d : %s\r", _
ms \ 1000 \ 60, ms \ 1000 mod 60, ms \ 10 mod 100, lenms \ 1000 \ 60, lenms \ 1000 mod 60, lenms \ 10 mod 100, _
iif(paused,@"Paused ",iif(playing,@"Playing",@"Stopped")))
        end if

        Sleep(10)

    loop while (key <> chr(27))

    printf(!"\n")

    /'
        Shut down
    '/
    result = FMOD_Sound_Release(sound)
    ERRCHECK(result)
    result = FMOD_System_Close(fsystem)
    ERRCHECK(result)
    result = FMOD_System_Release(fsystem)
    ERRCHECK(result)
