/'===============================================================================================
 Pitch detection example.
 Copyright (c), Firelight Technologies Pty, Ltd 2004-2011.

 This example combines recording with spectrum analysis to determine the pitch of the sound 
 being recorded.
==============================================================================================='/
#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

static as const zstring ptr note(120-1) = _
{ _
    @"C 0", @"C#0", @"D 0", @"D#0", @"E 0", @"F 0", @"F#0", @"G 0", @"G#0", @"A 0", @"A#0", @"B 0", _
    @"C 1", @"C#1", @"D 1", @"D#1", @"E 1", @"F 1", @"F#1", @"G 1", @"G#1", @"A 1", @"A#1", @"B 1", _
    @"C 2", @"C#2", @"D 2", @"D#2", @"E 2", @"F 2", @"F#2", @"G 2", @"G#2", @"A 2", @"A#2", @"B 2", _
    @"C 3", @"C#3", @"D 3", @"D#3", @"E 3", @"F 3", @"F#3", @"G 3", @"G#3", @"A 3", @"A#3", @"B 3", _
    @"C 4", @"C#4", @"D 4", @"D#4", @"E 4", @"F 4", @"F#4", @"G 4", @"G#4", @"A 4", @"A#4", @"B 4", _
    @"C 5", @"C#5", @"D 5", @"D#5", @"E 5", @"F 5", @"F#5", @"G 5", @"G#5", @"A 5", @"A#5", @"B 5", _
    @"C 6", @"C#6", @"D 6", @"D#6", @"E 6", @"F 6", @"F#6", @"G 6", @"G#6", @"A 6", @"A#6", @"B 6", _
    @"C 7", @"C#7", @"D 7", @"D#7", @"E 7", @"F 7", @"F#7", @"G 7", @"G#7", @"A 7", @"A#7", @"B 7", _
    @"C 8", @"C#8", @"D 8", @"D#8", @"E 8", @"F 8", @"F#8", @"G 8", @"G#8", @"A 8", @"A#8", @"B 8", _
    @"C 9", @"C#9", @"D 9", @"D#9", @"E 9", @"F 9", @"F#9", @"G 9", @"G#9", @"A 9", @"A#9", @"B 9" _
}

static as const float notefreq(120-1) = _
{ _
      16.35f,   17.32f,   18.35f,   19.45f,    20.60f,    21.83f,    23.12f,    24.50f,    25.96f,    27.50f,    29.14f,    30.87f, _
      32.70f,   34.65f,   36.71f,   38.89f,    41.20f,    43.65f,    46.25f,    49.00f,    51.91f,    55.00f,    58.27f,    61.74f, _
      65.41f,   69.30f,   73.42f,   77.78f,    82.41f,    87.31f,    92.50f,    98.00f,   103.83f,   110.00f,   116.54f,   123.47f, _
     130.81f,  138.59f,  146.83f,  155.56f,   164.81f,   174.61f,   185.00f,   196.00f,   207.65f,   220.00f,   233.08f,   246.94f, _
     261.63f,  277.18f,  293.66f,  311.13f,   329.63f,   349.23f,   369.99f,   392.00f,   415.30f,   440.00f,   466.16f,   493.88f, _
     523.25f,  554.37f,  587.33f,  622.25f,   659.26f,   698.46f,   739.99f,   783.99f,   830.61f,   880.00f,   932.33f,   987.77f, _
    1046.50f, 1108.73f, 1174.66f, 1244.51f,  1318.51f,  1396.91f,  1479.98f,  1567.98f,  1661.22f,  1760.00f,  1864.66f,  1975.53f, _
    2093.00f, 2217.46f, 2349.32f, 2489.02f,  2637.02f,  2793.83f,  2959.96f,  3135.96f,  3322.44f,  3520.00f,  3729.31f,  3951.07f, _
    4186.01f, 4434.92f, 4698.64f, 4978.03f,  5274.04f,  5587.65f,  5919.91f,  6271.92f,  6644.87f,  7040.00f,  7458.62f,  7902.13f, _
    8372.01f, 8869.84f, 9397.27f, 9956.06f, 10548.08f, 11175.30f, 11839.82f, 12543.85f, 13289.75f, 14080.00f, 14917.24f, 15804.26f _
}

#define OUTPUTRATE          48000
#define SPECTRUMSIZE        8192
#define SPECTRUMRANGE       (cast(float,OUTPUTRATE / 2.0f))      /' 0 to nyquist '/

#define BINSIZE      (SPECTRUMRANGE / cast(float,SPECTRUMSIZE))

' 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_CREATESOUNDEXINFO exinfo
    dim as string                    key
    dim as integer driver = any, recorddriver = any, numdrivers = any, count = any, bin_ = 0
    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

    /' 
        System initialization
    '/
    printf(!"---------------------------------------------------------\n")    
    printf(!"Select OUTPUT type\n")    
    printf(!"---------------------------------------------------------\n")    
    printf(!"1 :  DirectSound\n")
    printf(!"2 :  Windows Multimedia WaveOut\n")
    printf(!"3 :  ASIO\n")
    printf(!"---------------------------------------------------------\n")
    printf(!"Press a corresponding number or ESC to quit\n")

    do
        key = inkey
sleep 10
    loop while (key = "")

    select case key
        case "1" :  result = FMOD_System_SetOutput(fsystem, FMOD_OUTPUTTYPE_DSOUND)
        case "2" :  result = FMOD_System_SetOutput(fsystem, FMOD_OUTPUTTYPE_WINMM)
        case "3" :  result = FMOD_System_SetOutput(fsystem, FMOD_OUTPUTTYPE_ASIO)
        case else  :  end 1 
    end select
    ERRCHECK(result)
    
    /'
        Enumerate playback devices
    '/

    result = FMOD_System_GetNumDrivers(fsystem, @numdrivers)
    ERRCHECK(result)

    printf(!"---------------------------------------------------------\n")    
    printf(!"Choose a PLAYBACK driver\n")
    printf(!"---------------------------------------------------------\n")    
    for count=0 to numdrivers-1
        dim as zstring * 256 sname = any

        result = FMOD_System_GetDriverInfo(fsystem, count, @sname, 256, 0)
        ERRCHECK(result)

        printf(!"%d : %s\n", count + 1, @sname)
    next
    printf(!"---------------------------------------------------------\n")
    printf(!"Press a corresponding number or ESC to quit\n")

    do
        key = inkey
        if (key = chr(27)) then
            end 0
        end if
        driver = asc(key) - asc("1")
sleep 10
    loop while (driver < 0 or driver >= numdrivers)

    result = FMOD_System_SetDriver(fsystem, driver)
    ERRCHECK(result)

    /'
        Enumerate record devices
    '/

    result = FMOD_System_GetRecordNumDrivers(fsystem, @numdrivers)
    ERRCHECK(result)

    printf(!"---------------------------------------------------------\n")    
    printf(!"Choose a RECORD driver\n")
    printf(!"---------------------------------------------------------\n")    
    for count=0 to numdrivers-1
        dim as zstring * 256 sname = any

        result = FMOD_System_GetRecordDriverInfo(fsystem, count, @sname, 256, 0)
        ERRCHECK(result)

        printf(!"%d : %s\n", count + 1, @sname)
    next
    printf(!"---------------------------------------------------------\n")
    printf(!"Press a corresponding number or ESC to quit\n")

    recorddriver = 0
    do
        key = inkey
        if (key = chr(27)) then
           end 0
        end if
        recorddriver = asc(key) - asc("1")
sleep 10
    loop while (recorddriver < 0 or recorddriver >= numdrivers)

    printf(!"\n")
 
    result = FMOD_System_SetSoftwareFormat(fsystem, OUTPUTRATE, FMOD_SOUND_FORMAT_PCM16, 1, 0, FMOD_DSP_RESAMPLER_LINEAR)
    ERRCHECK(result)

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

    /'
        Create a sound to record to.
    '/
    exinfo.cbsize           = sizeof(FMOD_CREATESOUNDEXINFO)
    exinfo.numchannels      = 1
    exinfo.format           = FMOD_SOUND_FORMAT_PCM16
    exinfo.defaultfrequency = OUTPUTRATE
    exinfo.length           = exinfo.defaultfrequency * sizeof(short) * exinfo.numchannels * 5
    
    result = FMOD_System_CreateSound(fsystem, 0, FMOD_2D or FMOD_SOFTWARE or FMOD_LOOP_NORMAL or FMOD_OPENUSER, @exinfo, @sound)
    ERRCHECK(result)

    /'
        Start the interface
    '/
    printf(!"=========================================================================\n")
    printf(!"Pitch detection example.  Copyright (c) Firelight Technologies 2004-2011.\n")
    printf(!"=========================================================================\n")
    printf(!"\n")
    printf(!"Record something through the selected recording device and FMOD will\n")
    printf(!"Determine the pitch.  Sustain the tone for at least a second to get an\n")
    printf(!"accurate reading.\n")
    printf(!"Press 'Esc' to quit\n")
    printf(!"\n")

    result = FMOD_System_RecordStart(fsystem, recorddriver, sound, TRUE)
    ERRCHECK(result)
    
    Sleep(200)                         /' Give it some time to record something '/
    
    result = FMOD_System_PlaySound(fsystem, FMOD_CHANNEL_REUSE, sound, FALSE, @channel)
    ERRCHECK(result)

    /' Dont hear what is being recorded otherwise it will feedback.  Spectrum analysis is done before volume scaling in the DSP chain '/
    result = FMOD_Channel_SetVolume(channel, 0)
    ERRCHECK(result)


    /'
        Main loop.
    '/
    do
        static as float spectrum(SPECTRUMSIZE) = any
        dim as float        dominanthz = 0
        dim as float        maximum = 0
        dim as integer          dominantnote = 0

            key = inkey


        result = FMOD_Channel_GetSpectrum(channel, @spectrum(0), SPECTRUMSIZE, 0, FMOD_DSP_FFT_WINDOW_TRIANGLE) 
        ERRCHECK(result)

        for count = 0 to SPECTRUMSIZE-1
            if (spectrum(count) > 0.01f and spectrum(count) > maximum) then
                maximum = spectrum(count)
                bin_ = count
            end if
        next

        dominanthz  = bin_ * BINSIZE       /' dominant frequency min '/

        for count = 0 to 120-1
             if (dominanthz >= notefreq(count) and dominanthz < notefreq(count + 1)) then
                /' which is it closer to.  This note or the next note '/
                if (fabs(dominanthz - notefreq(count)) < fabs(dominanthz - notefreq(count+1))) then
                    dominantnote = count
                else
                    dominantnote = count + 1
                end if
                exit for
             end if
        next

        printf(!"Detected rate : %7.1f -> %7.1f hz.  Detected musical note. %-3s (%7.1f hz)\r", _
dominanthz, (bin_ + 0.99f) * BINSIZE, note(dominantnote), notefreq(dominantnote))

        FMOD_System_Update(fsystem)

        Sleep(10)

    loop while (key <> chr(27))

    printf(!"\n")

    /'
        Shut down
    '/
    result = FMOD_Sound_Release(sound)
    ERRCHECK(result)

    result = FMOD_System_Release(fsystem)
    ERRCHECK(result)
