-- -*-haskell-*- {-# LANGUAGE ForeignFunctionInterface #-} {- JACK bindings for Haskell Copyright (C) 2011 Henning Thielemann Copyright (C) 2007 Soenke Hahn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -} module Sound.JACK.FFI where import Foreign.Ptr (Ptr, FunPtr) import Foreign.C.String (CString) import qualified Foreign.C.Error as E import qualified Foreign.C.Types as C import qualified Data.EnumSet as ES import Data.Word (Word, Word32, Word64, ) import Data.Ix (Ix(range, inRange, rangeSize, index)) import Data.Monoid (Monoid, mempty, mappend, ) import Numeric.NonNegative.Class as NonNeg #include "jack/types.h" data Client = Client data Port typ = Port -- | Type argument for Jack input ports data Input = Input -- | Type argument for Jack output ports data Output = Output foreign import ccall "static jack/jack.h jack_client_open" client_open :: CString -> OpenOptionSet -> Ptr StatusSet -> CString -> IO (Ptr Client) {-# DEPRECATED client_new "use client_open instead" #-} foreign import ccall "static jack/jack.h jack_client_new" client_new :: CString -> IO (Ptr Client) foreign import ccall "static jack/jack.h jack_get_sample_rate" get_sample_rate :: Ptr Client -> IO C.CInt type OpenOptionSet = ES.T C.CULong OpenOptions data OpenOptions = NoStartServer | UseExactName | ServerName deriving (Enum, Eq, Ord, Show, Ix) wordNullOption, wordNoStartServer, wordUseExactName, wordServerName :: OpenOptionSet wordNullOption = ES.empty wordNoStartServer = ES.fromEnum NoStartServer wordUseExactName = ES.fromEnum UseExactName wordServerName = ES.fromEnum ServerName type StatusSet = ES.T C.CULong Status data Status = Failure | InvalidOption | NameNotUnique | ServerStarted | ServerFailed | ServerError | NoSuchClient | LoadFailure | InitFailure | ShmFailure | VersionError deriving (Enum, Eq, Ord, Show, Ix) wordFailure, wordInvalidOption, wordNameNotUnique, wordServerStarted, wordServerFailed, wordServerError, wordNoSuchClient, wordLoadFailure, wordInitFailure, wordShmFailure, wordVersionError :: StatusSet wordFailure = ES.fromEnum Failure wordInvalidOption = ES.fromEnum InvalidOption wordNameNotUnique = ES.fromEnum NameNotUnique wordServerStarted = ES.fromEnum ServerStarted wordServerFailed = ES.fromEnum ServerFailed wordServerError = ES.fromEnum ServerError wordNoSuchClient = ES.fromEnum NoSuchClient wordLoadFailure = ES.fromEnum LoadFailure wordInitFailure = ES.fromEnum InitFailure wordShmFailure = ES.fromEnum ShmFailure wordVersionError = ES.fromEnum VersionError type PortFlagSet = ES.T C.CULong PortFlag data PortFlag = PortIsInput | PortIsOutput | PortIsPhysical | PortCanMonitor | PortIsTerminal deriving (Enum, Eq, Ord, Show, Ix) newtype PortName = PortName {deconsPortName :: CString} newtype PortType = PortType {deconsPortType :: CString} portIsInput, portIsOutput :: PortFlagSet portIsInput = ES.fromEnum PortIsInput portIsOutput = ES.fromEnum PortIsOutput foreign import ccall "static jack/jack.h jack_port_register" port_register :: Ptr Client -> PortName -> PortType -> PortFlagSet -> C.CULong -> IO (Ptr (Port a)) -- | represents absolute frame time newtype NFrames = NFrames #{type jack_nframes_t} deriving (Show, Eq, Ord) nframesToWord :: NFrames -> Word nframesToWord (NFrames n) = fromIntegral n instance Ix NFrames where range (a,b) = map (NFrames . fromIntegral) $ range (nframesToWord a, nframesToWord b) index (a,b) i = index (nframesToWord a, nframesToWord b) (nframesToWord i) inRange (a,b) i = inRange (nframesToWord a, nframesToWord b) (nframesToWord i) rangeSize (a,b) = rangeSize (nframesToWord a, nframesToWord b) instance Monoid NFrames where mempty = NFrames 0 mappend (NFrames x) (NFrames y) = NFrames (x+y) instance NonNeg.C NFrames where split = NonNeg.splitDefault (\(NFrames n) -> n) NFrames nframesIndices :: NFrames -> [NFrames] nframesIndices (NFrames n) = take (fromIntegral n) $ map NFrames $ iterate (1+) 0 nframesBounds :: NFrames -> (NFrames,NFrames) nframesBounds (NFrames n) = (NFrames 0, NFrames $ n - 1) type Process arg = NFrames -> Ptr arg -> IO E.Errno foreign import ccall "static jack/jack.h jack_set_process_callback" set_process_callback :: Ptr Client -> FunPtr (Process arg) -> Ptr arg -> IO E.Errno type ClientRegistration arg = CString -> C.CInt -> Ptr arg -> IO () foreign import ccall "static jack/jack.h jack_set_client_registration_callback" set_client_registration_callback :: Ptr Client -> FunPtr (ClientRegistration arg) -> Ptr arg -> IO E.Errno newtype PortId = PortId #{type jack_port_id_t} deriving (Eq, Ord, Show) {- | Type argument for Jack ports where the type of samples transported by the port is unknown. -} data UnknownType = UnknownType type PortRegistration arg = PortId -> C.CInt -> Ptr arg -> IO () foreign import ccall "static jack/jack.h jack_set_port_registration_callback" set_port_registration_callback :: Ptr Client -> FunPtr (PortRegistration arg) -> Ptr arg -> IO E.Errno type PortConnect arg = PortId -> PortId -> C.CInt -> Ptr arg -> IO () foreign import ccall "static jack/jack.h jack_set_port_connect_callback" set_port_connect_callback :: Ptr Client -> FunPtr (PortConnect arg) -> Ptr arg -> IO E.Errno foreign import ccall "static jack/jack.h jack_port_by_id" port_by_id :: Ptr Client -> PortId -> IO (Ptr (Port UnknownType)) foreign import ccall "static jack/jack.h jack_port_by_name" port_by_name :: Ptr Client -> PortName -> IO (Ptr (Port UnknownType)) foreign import ccall "static jack/jack.h jack_port_name" port_name :: Ptr (Port typ) -> IO PortName foreign import ccall "static jack/jack.h jack_port_name_size" port_name_size :: IO C.CInt foreign import ccall "static jack/jack.h jack_port_get_aliases" port_get_aliases :: Ptr (Port typ) -> Ptr CString -> IO C.CInt foreign import ccall "static jack/jack.h jack_port_short_name" port_short_name :: Ptr (Port typ) -> IO CString foreign import ccall "static jack/jack.h jack_port_flags" port_flags :: Ptr (Port typ) -> IO PortFlagSet foreign import ccall "static jack/jack.h jack_port_type" port_type :: Ptr (Port UnknownType) -> IO PortType foreign import ccall "static jack/jack.h jack_get_ports" get_ports :: Ptr Client -> CString -> CString -> C.CULong -> IO (Ptr CString) -- get_ports :: Ptr Client -> CString -> CString -> C.CULong -> IO (Ptr PortName) foreign import ccall "static jack/jack.h jack_port_get_all_connections" port_get_all_connections :: Ptr Client -> Ptr (Port typ) -> IO (Ptr CString) foreign import ccall "static jack/jack.h jack_last_frame_time" last_frame_time :: Ptr Client -> IO NFrames foreign import ccall "static jack/jack.h jack_port_get_buffer" port_get_buffer :: Ptr (Port a) -> NFrames -> IO (Ptr a) foreign import ccall "static jack/jack.h jack_get_buffer_size" get_buffer_size :: Ptr Client -> IO (C.CUInt) foreign import ccall "static jack/jack.h jack_activate" activate :: Ptr Client -> IO E.Errno foreign import ccall "static jack/jack.h jack_client_close" client_close :: Ptr Client -> IO E.Errno -- may return eEXIST foreign import ccall "static jack/jack.h jack_connect" connect :: Ptr Client -> PortName -> PortName -> IO E.Errno foreign import ccall "static jack/jack.h jack_disconnect" disconnect :: Ptr Client -> PortName -> PortName -> IO E.Errno foreign import ccall "static jack/jack.h jack_port_unregister" port_unregister :: Ptr Client -> Ptr (Port a) -> IO E.Errno foreign import ccall "static jack/jack.h jack_deactivate" deactivate :: Ptr Client -> IO E.Errno _dummy :: (Word32, Word64) _dummy = undefined