{-# LANGUAGE ForeignFunctionInterface #-}

{- | Direct FFI bindings to libfluidsynth's C API. Each binding is a
thin, 1:1 wrapper around the identically-named @fluid_*@ function from
@fluidsynth.h@ — see that header (or the FluidSynth API docs) for
parameter\/return semantics. "Termtab.Audio.FluidSynth" is the only
caller; it wraps these raw pointer-passing calls in the
'Termtab.Types'-typed, exception-safe 'Termtab.Audio.FluidSynth.FluidSynthEngine'
API that the rest of the codebase uses.
-}
module Termtab.Audio.FluidSynth.Raw (
    -- * Opaque C types
    C'fluid_settings_t,
    C'fluid_synth_t,
    C'fluid_audio_driver_t,

    -- * Settings
    c'new_fluid_settings,
    c'delete_fluid_settings,
    c'fluid_settings_setstr,
    c'fluid_settings_setint,

    -- * Synth
    c'new_fluid_synth,
    c'delete_fluid_synth,
    c'fluid_synth_sfload,

    -- * Audio driver
    c'new_fluid_audio_driver,
    c'delete_fluid_audio_driver,

    -- * Note playback
    c'fluid_synth_noteon,
    c'fluid_synth_noteoff,

    -- * Program change
    c'fluid_synth_program_change,
) where

import Foreign.C.String (CString)
import Foreign.C.Types (CInt (..))
import Foreign.Ptr (Ptr)

{- | Uninhabited marker type for @fluid_settings_t*@; values only ever exist
behind a 'Ptr' obtained from 'c'new_fluid_settings'.
-}
data C'fluid_settings_t

{- | Uninhabited marker type for @fluid_synth_t*@, obtained from
'c'new_fluid_synth'.
-}
data C'fluid_synth_t

{- | Uninhabited marker type for @fluid_audio_driver_t*@, obtained from
'c'new_fluid_audio_driver'.
-}
data C'fluid_audio_driver_t

-- Settings

-- | Create a new settings object; must precede 'c'new_fluid_synth'.
foreign import ccall "fluidsynth.h new_fluid_settings"
    c'new_fluid_settings :: IO (Ptr C'fluid_settings_t)

-- | Free a settings object, once its synth (if any) is already freed.
foreign import ccall "fluidsynth.h delete_fluid_settings"
    c'delete_fluid_settings :: Ptr C'fluid_settings_t -> IO ()

{- | Set a string-valued setting (used by "Termtab.Audio.FluidSynth" to
force @audio.driver@).
-}
foreign import ccall "fluidsynth.h fluid_settings_setstr"
    c'fluid_settings_setstr :: Ptr C'fluid_settings_t -> CString -> CString -> IO CInt

-- | Set an integer-valued setting.
foreign import ccall "fluidsynth.h fluid_settings_setint"
    c'fluid_settings_setint :: Ptr C'fluid_settings_t -> CString -> CInt -> IO CInt

-- Synth

-- | Create a synth from a settings object.
foreign import ccall "fluidsynth.h new_fluid_synth"
    c'new_fluid_synth :: Ptr C'fluid_settings_t -> IO (Ptr C'fluid_synth_t)

-- | Free a synth, before freeing its settings.
foreign import ccall "fluidsynth.h delete_fluid_synth"
    c'delete_fluid_synth :: Ptr C'fluid_synth_t -> IO ()

{- | Load a SoundFont (@.sf2@) file into a synth; the 'CInt' argument is
the \"reset presets\" flag (1 to reset MIDI channel programs).
-}
foreign import ccall "fluidsynth.h fluid_synth_sfload"
    c'fluid_synth_sfload :: Ptr C'fluid_synth_t -> CString -> CInt -> IO CInt

-- Audio driver

{- | Start an audio driver for a synth, using the driver named by the
settings object's @audio.driver@ key.
-}
foreign import ccall "fluidsynth.h new_fluid_audio_driver"
    c'new_fluid_audio_driver :: Ptr C'fluid_settings_t -> Ptr C'fluid_synth_t -> IO (Ptr C'fluid_audio_driver_t)

-- | Stop and free an audio driver, before freeing its synth.
foreign import ccall "fluidsynth.h delete_fluid_audio_driver"
    c'delete_fluid_audio_driver :: Ptr C'fluid_audio_driver_t -> IO ()

-- Note playback

-- | Start a note: synth, channel, key, velocity.
foreign import ccall "fluidsynth.h fluid_synth_noteon"
    c'fluid_synth_noteon :: Ptr C'fluid_synth_t -> CInt -> CInt -> CInt -> IO CInt

-- | Stop a note: synth, channel, key.
foreign import ccall "fluidsynth.h fluid_synth_noteoff"
    c'fluid_synth_noteoff :: Ptr C'fluid_synth_t -> CInt -> CInt -> IO CInt

-- Program change

-- | Change a channel's General MIDI program: synth, channel, program number.
foreign import ccall "fluidsynth.h fluid_synth_program_change"
    c'fluid_synth_program_change :: Ptr C'fluid_synth_t -> CInt -> CInt -> IO CInt