hmidi 0.2.2.1 → 0.2.3.1
raw patch · 9 files changed
+252/−116 lines, 9 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ System.MIDI.Base: MidiException :: String -> MidiException
+ System.MIDI.Base: data MidiException
+ System.MIDI.Base: instance GHC.Exception.Type.Exception System.MIDI.Base.MidiException
+ System.MIDI.Base: instance GHC.Show.Show System.MIDI.Base.MidiException
- System.MIDI.Sync: openSourceWithSync :: Source -> (Maybe Beats -> MidiEvent -> IO ()) -> IO (Connection, IO (Maybe Beats), IO BPM)
+ System.MIDI.Sync: openSourceWithSync :: Source -> (BPM -> Maybe Beats -> MidiEvent -> IO ()) -> IO (Connection, IO (Maybe Beats), IO BPM)
Files
- LICENSE +1/−1
- README.md +44/−0
- System/MIDI/Base.hs +16/−1
- System/MIDI/MacOSX.hs +22/−9
- System/MIDI/Sync.hs +35/−21
- System/MIDI/Win32.hs +5/−2
- System/MacOSX/CoreMIDI.hs +73/−39
- System/Win32/MIDI.hs +38/−26
- hmidi.cabal +18/−17
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2008-2016, Balazs Komuves+Copyright (c) 2008-2020, Balazs Komuves All rights reserved. Redistribution and use in source and binary forms, with or without
+ README.md view
@@ -0,0 +1,44 @@++hmidi: Haskell bindings to the OS level MIDI services+=====================================================++Partial implementation of the MIDI 1.0 standard to communicate with physical +or virtual MIDI devices, eg. MIDI keyboards and synthesizers. ++Supported operating systems are MacOS and Windows. +See also the `alsa-midi` library for similar functionality under Linux. ++Installation & Usage+--------------------++It should install with `cabal` without problems on Mac OS X and Windows. +Linux is not supported. ++As usual, just run the command:++ cabal install ++When using the library, you *have* to link against the threaded runtime+(otherwise random crashes *will* occur); for example:++ ghc --make -threaded -O monitor.hs++Example applications+--------------------++There are some example command line applications in the `examples` subdirectory:++* `monitor.hs` - very basic MIDI monitor+* `chords.hs` - convert NoteOn messages to simple chords+* `playmidi.hs` - play simple `.mid` files on General MIDI compatible synths+* `osx_host.hs` - create a MIDI host (as opposed to connecting to existing host). Only works on MacOs.++You can build them with `ghc --make -threaded` like above.++Differences between Windows and MacOS+-------------------------------------++We tried to create a unified Haskell API, but there are still differences between+the Windows and Mac implementations. In particular, you cannot create MIDI hosts+on Windows.+
System/MIDI/Base.hs view
@@ -1,7 +1,7 @@ -- |The hardware-independent part of the MIDI binding. -{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, DeriveDataTypeable #-} module System.MIDI.Base ( TimeStamp , MidiMessage'(..)@@ -12,12 +12,15 @@ , translateShortMessage , untranslateShortMessage , shortMessage+ , MidiException(..) ) where -------------------------------------------------------------------------------- import Data.Bits import Data.Word+import Data.Typeable+import Control.Exception.Base type TimeStamp = Word32 @@ -149,3 +152,15 @@ , sm_byte2 :: Word8 } deriving Show +--------------------------------------------------------------------------------++data MidiException + = MidiException String + deriving (Show,Typeable)++instance Exception MidiException where+#if MIN_VERSION_base(4,8,0)+ displayException (MidiException msg) = msg+#endif++--------------------------------------------------------------------------------
System/MIDI/MacOSX.hs view
@@ -1,5 +1,5 @@ --- |A lowest common denominator interface to the Win32 and MacOSX MIDI bindings, MacOSX part.+-- | A lowest common denominator interface to the Win32 and MacOSX MIDI bindings, MacOSX part. module System.MIDI.MacOSX ( module System.MIDI.Base@@ -59,6 +59,7 @@ import Control.Concurrent.STM import Control.Concurrent.STM.TChan+import Control.Exception.Base import Data.List import Foreign@@ -124,7 +125,7 @@ -- | Gets the next event from a buffered connection. getNextEvent' :: Connection -> STM (Maybe MidiEvent) getNextEvent' conn = case cn_fifo_cb conn of- Right _ -> fail "this is not a buffered connection"+ Right _ -> stmFail "this is not a buffered connection" Left chan -> do b <- isEmptyTChan chan if b @@ -136,7 +137,7 @@ -- | Checks the next event from a buffered connection, but does not remove it from the buffer checkNextEvent' :: Connection -> STM (Maybe MidiEvent) checkNextEvent' conn = case cn_fifo_cb conn of- Right _ -> fail "this is not a buffered connection"+ Right _ -> stmFail "this is not a buffered connection" Left chan -> do b <- isEmptyTChan chan if b @@ -146,6 +147,9 @@ unGetTChan chan x return (Just x) +stmFail :: String -> STM a+stmFail msg = throwSTM (MidiException msg)+ -------------------------------------------------------------------------------- type Client = MIDIClientRef@@ -163,6 +167,7 @@ , cn_fifo_cb :: Either (TChan MidiEvent) ClientCallback , cn_midiproc :: FunPtr (MIDIReadProc () ()) , cn_mydata :: StablePtr (MVar Connection)+ , cn_sysex :: MVar SysExStatus -- osx api for long sysexs is fucking stupid } ----- automatic client creation @@ -218,11 +223,15 @@ Nothing -> return () Just conn -> do time0 <- readMVar (cn_time conn)- list1 <- depackMIDIPacketList packets+ + sysex_status <- takeMVar (cn_sysex conn)+ (list1,sysex_status') <- depackMIDIPacketList packets sysex_status+ putMVar (cn_sysex conn) sysex_status'+ let (normal,sysex') = partition (\(_,bytes) -> isShortMessage bytes) list1 sysexs <- forM sysex' $ \(ts',bytes) -> do ts <- audioConvertHostTimeToNanos ts'- return $ MidiEvent (nanoToMili $ ts-time0) (SysEx $ tail bytes)+ return $ MidiEvent (nanoToMili $ ts-time0) (SysEx $ init $ tail bytes) normals <- mapM (convertShortMessage time0) normal let events = sysexs ++ normals case (cn_fifo_cb conn) of@@ -251,7 +260,9 @@ inport <- newInputPort client "Input Port" the_callback (castStablePtrToPtr sp) - let conn = Connection True False inport endpoint time alive fifo_cb the_callback sp + sysex <- newMVar NoSysEx+ + let conn = Connection True False inport endpoint time alive fifo_cb the_callback sp sysex putMVar myData conn return conn @@ -264,7 +275,7 @@ alive <- newMVar True time <- newEmptyMVar - let conn = Connection False False outport endpoint time alive undefined undefined undefined + let conn = Connection False False outport endpoint time alive undefined undefined undefined undefined return conn sendShortMessage :: Connection -> ShortMessage -> IO ()@@ -349,8 +360,10 @@ Source endpoint <- newDestination client name the_callback (castStablePtrToPtr sp) + sysex <- newMVar NoSysEx+ let inport = error "createDestination/inport"- conn = Connection True True inport endpoint time alive fifo_cb the_callback sp + conn = Connection True True inport endpoint time alive fifo_cb the_callback sp sysex putMVar myData conn return conn @@ -365,7 +378,7 @@ alive <- newMVar True time <- newEmptyMVar - let conn = Connection False True outport endpoint time alive undefined undefined undefined + let conn = Connection False True outport endpoint time alive undefined undefined undefined undefined return conn -- | Dispose a connection we created earlier.
System/MIDI/Sync.hs view
@@ -52,16 +52,17 @@ -- (start/stop/continue) or send messages on clock if you want. -- openSourceWithSync - :: Source -- ^ midi source- -> (Maybe Beats -> MidiEvent -> IO ()) -- ^ user callback- -> IO (Connection, IO (Maybe Beats), IO BPM) -- ^ (connection, song_position, estimated_bpm) + :: Source -- ^ midi source+ -> (BPM -> Maybe Beats -> MidiEvent -> IO ()) -- ^ user callback+ -> IO (Connection, IO (Maybe Beats), IO BPM) -- ^ (connection, song_position, estimated_bpm) openSourceWithSync src userCallback = do - theLastPos <- newMVar 0 :: IO (MVar Beats) -- last song position - theBpmEst <- newMVar 120 :: IO (MVar BPM) -- last bpm estimation- thePlayFlag <- newMVar False :: IO (MVar Bool) -- whether we are playing or stopped- theLastClock <- newMVar 0 :: IO (MVar TimeStamp) -- timestamp of last clock signal - theLastQuery <- newMVar 0 :: IO (MVar Beats) -- last queried pos + theLastPos <- newMVar 0 :: IO (MVar Beats) -- last song position + theBpmEst <- newMVar 120 :: IO (MVar BPM) -- last bpm estimation+ thePlayFlag <- newMVar False :: IO (MVar Bool) -- whether we are playing or stopped+ theLastClock <- newMVar 0 :: IO (MVar TimeStamp) -- timestamp of last clock signal + theLastQuery <- newMVar 0 :: IO (MVar Beats) -- last queried pos + theLastClocks <- newMVar [0] :: IO (MVar [TimeStamp]) -- timestamps of last clock signal let queryPos tstamp = do b <- readMVar thePlayFlag@@ -88,14 +89,15 @@ replaceMVar theLastQuery pos SRTStart -> do- replaceMVar theLastPos 0- replaceMVar theLastQuery 0- replaceMVar theLastClock tstamp- replaceMVar thePlayFlag True+ replaceMVar theLastPos 0+ replaceMVar theLastQuery 0+ replaceMVar theLastClock tstamp+ replaceMVar theLastClocks [tstamp]+ replaceMVar thePlayFlag True SRTStop -> replaceMVar thePlayFlag False - SRTContinue -> do+ SRTContinue -> do replaceMVar theLastClock tstamp replaceMVar thePlayFlag True @@ -106,23 +108,35 @@ replaceMVar theBpmEst 120 SRTClock -> do- lastclock <- takeMVar theLastClock- bpm <- takeMVar theBpmEst- lastpos <- takeMVar theLastPos+ lastclock <- takeMVar theLastClock+ lastclocks <- takeMVar theLastClocks+ bpm <- takeMVar theBpmEst+ lastpos <- takeMVar theLastPos let lastpos' = lastpos + oneTwentyFourth- let tdiff = fromIntegral (tstamp - lastclock) / 60000.0 :: Double -- in minutes- let bpm' = (1-lambda)*bpm + lambda*(oneTwentyFourth/tdiff) - putMVar theLastClock tstamp++ let tdiff = fromIntegral (tstamp - lastclock ) / 60000.0 :: Double + let tdiff12 = if length lastclocks < 12 + then 0+ else fromIntegral (tstamp - lastclocks !! 11) / 60000.0 :: Double ++ let bpm' = if length lastclocks < 12+ then (1-lambda)*(max 30 $ min 480 bpm) + lambda*(max 30 $ min 480 $ oneTwentyFourth/tdiff) + else (1-lambda)*(max 30 $ min 480 bpm) + lambda*(max 30 $ min 480 $ 0.5/tdiff12)+ + putMVar theLastClock tstamp+ putMVar theLastClocks $ take 24 (tstamp:lastclocks) putMVar theLastPos lastpos' putMVar theBpmEst bpm' - print (bpm',tdiff,1/24/tdiff) + -- print (bpm',(tdiff,1/24/tdiff),(tdiff12,0.5/tdiff12)) + _ -> return () let syncCallback event@(MidiEvent tstamp _) = do handle event mbpos <- queryPos tstamp- userCallback mbpos event+ bpm <- queryBPM + userCallback bpm mbpos event conn <- openSource src (Just syncCallback) return (conn, currentTime conn >>= queryPos, queryBPM)
System/MIDI/Win32.hs view
@@ -103,7 +103,7 @@ -- | Gets the next event from a buffered connection. getNextEvent' :: Connection -> STM (Maybe MidiEvent) getNextEvent' conn = case cn_fifo_cb conn of- Right _ -> fail "this is not a buffered connection"+ Right _ -> stmFail "this is not a buffered connection" Left chan -> do b <- isEmptyTChan chan if b @@ -115,7 +115,7 @@ -- | Checks the next event from a buffered connection, but does not remove it from the buffer checkNextEvent' :: Connection -> STM (Maybe MidiEvent) checkNextEvent' conn = case cn_fifo_cb conn of- Right _ -> fail "this is not a buffered connection"+ Right _ -> stmFail "this is not a buffered connection" Left chan -> do b <- isEmptyTChan chan if b @@ -124,6 +124,9 @@ x <- readTChan chan unGetTChan chan x return (Just x)++stmFail :: String -> STM a+stmFail msg = throwSTM (MidiException msg) --------------------------------------------------------------------------------
System/MacOSX/CoreMIDI.hs view
@@ -2,7 +2,7 @@ -- |Low-level (partial) binding to the CoreMIDI services present in Mac OS X. -- Error \"handling\" is via `fail`-s in the IO monad. -{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}+{-# LANGUAGE BangPatterns, ForeignFunctionInterface, EmptyDataDecls #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} module System.MacOSX.CoreMIDI (@@ -58,6 +58,7 @@ , ShortMessage(..) -- helper functions to write callbacks+ , SysExStatus(..) , depackMIDIPacketList , depackSingleMIDIPacket , decodeShortMessage @@ -76,6 +77,8 @@ import System.MacOSX.CoreFoundation import System.MacOSX.CoreAudio +-- import qualified Data.ByteString as B+ -------------------------------------------------------------------------------- data OpaqueMIDIClient@@ -333,6 +336,7 @@ 13 -> [cmd,bt1] -- channel pressure 14 -> [cmd,bt1,bt2] -- pitchwheel 15 -> case chn of+ 1 -> [cmd,bt1] -- midi timing code 2 -> [cmd,bt1,bt2] -- song position 3 -> [cmd,bt1] -- song select 0 -> error "SysEx is not a short message!"@@ -356,51 +360,64 @@ [a] -> (a,0) [a,b] -> (a,b) _ -> error "a short message shouldn't be longer than 3 bytes!"- -depackMIDIPacketList :: Ptr MIDIPacket -> IO [ (MIDITimeStamp, [Word8]) ]-depackMIDIPacketList p = ++-- | OSX can split sysex message into multiple parts, +-- and it does not signal this in any way... apart from missing 0xf7 bytes +data SysExStatus + = NoSysEx+ | PartialSysEx [Word8]+ deriving Show+ +depackMIDIPacketList :: Ptr MIDIPacket -> SysExStatus -> IO ( [ (MIDITimeStamp, [Word8]) ] , SysExStatus )+depackMIDIPacketList p initial_sysex_status = do npackets <- peek (castPtr p) :: IO UInt32- depack' (p `plusPtr` 4) npackets + -- print ("number of packets = ",npackets)+ -- print ("sysex status = ",initial_sysex_status)+ depack' (p `plusPtr` 4) initial_sysex_status npackets where- depack' _ 0 = return []- depack' p k = do- ( n , ts , msgs ) <- depackSingleMIDIPacket p + depack' _ sysex_status 0 = return ([],sysex_status)+ depack' !p sysex_status !k = do+ ( n , ts , msgs , sysex_status' ) <- depackSingleMIDIPacket p sysex_status let xs = zip (repeat ts) msgs- ys <- depack' (p `plusPtr` n) (k-1) - return (xs++ys) + (ys , sysex_status'') <- depack' (p `plusPtr` n) sysex_status' (k-1) + return ( xs++ys , sysex_status'' ) -- decodes a single MIDIPacket, and returns the length (in bytes), the timestamp, and the list of midi messages-depackSingleMIDIPacket :: Ptr MIDIPacket -> IO ( Int , MIDITimeStamp , [[Word8]] )-depackSingleMIDIPacket p = do+depackSingleMIDIPacket :: Ptr MIDIPacket -> SysExStatus -> IO ( Int , MIDITimeStamp , [[Word8]] , SysExStatus )+depackSingleMIDIPacket p sysex_status = do ts <- peek (castPtr p ) :: IO MIDITimeStamp len' <- peek (castPtr p `plusPtr` 8) :: IO UInt16 let len = fromIntegral len'- msglist <- depackMsgList (castPtr p `plusPtr` 10 :: Ptr Word8) len- return ( len + 8 + 2, ts, msglist )+ -- print ("number of bytes in packet = ",len)+ ( msglist , sysex_status' ) <- depackMsgList (castPtr p `plusPtr` 10 :: Ptr Word8) sysex_status len+ return ( len + 8 + 2 , ts , msglist , sysex_status' ) -- helper function -depackMsgList :: Ptr Word8 -> Int -> IO [[Word8]]-depackMsgList _ 0 = return []-depackMsgList p n = if n < 0+depackMsgList :: Ptr Word8 -> SysExStatus -> Int -> IO ( [[Word8]] , SysExStatus )+depackMsgList _ sysex_status 0 = return ( [] , sysex_status )+depackMsgList !p !sysex_status !n = if n < 0 then fail "fatal error while depacking MIDI messages" else do- (k,x) <- depackSingleMessage p- xs <- depackMsgList (p `plusPtr` k) (n-k)- return (x:xs)+ (k , mbx , sysex_status') <- depackSingleMessage p sysex_status n+ -- print (n,(k,mbx,sysex_status')) -- DEBUGGING+ (xs , sysex_status'') <- depackMsgList (p `plusPtr` k) sysex_status' (n-k)+ case mbx of + Nothing -> return ( xs , sysex_status'' ) + Just x -> return ( (x:xs) , sysex_status'' ) -depackSingleMessage :: Ptr Word8 -> IO (Int,[Word8])-depackSingleMessage p = do+depackSingleMessage :: Ptr Word8 -> SysExStatus -> Int -> IO (Int , Maybe [Word8] , SysExStatus)+depackSingleMessage !p NoSysEx !maxlen = do cmd <- peek p let hi = shiftR cmd 4 lo = cmd .&. 15 - let ret :: Int -> IO (Int,[Word8])+ let ret :: Int -> IO (Int , Maybe[Word8] , SysExStatus) ret k = do xs <- mapM (peekElemOff p) [0..k-1]- return $ ( k , xs )+ return $ ( k , Just xs , NoSysEx ) case hi of 8 -> ret 3 @@ -411,24 +428,41 @@ 13 -> ret 2 14 -> ret 3 15 -> case lo of- 2 -> ret 3- 3 -> ret 2- 0 -> sysex p- _ -> ret 1++ 0 -> do -- 0xF0 ... 0xF7 = sysex+ (k,bytes,finished) <- sysex p maxlen + if finished + then return (k, Just bytes , NoSysEx )+ else return (k, Nothing , PartialSysEx bytes)++ 1 -> ret 2 -- 0xF1 bb = midi timing code+ 2 -> ret 3 -- 0xF2 bb cc = song position+ 3 -> ret 2 -- 0xF3 bb = song select+ _ -> ret 1 -- 0xFz _ -> fail "fatal error while interpreting a MIDI message"- --- does not include the terminating 0xf7 byte! -sysex :: Ptr Word8 -> IO (Int,[Word8])-sysex p = do - n <- sysexHelper p 2 - xs <- mapM (peekElemOff p) [0..n]- return ( n+2 , xs ) -sysexHelper :: Ptr Word8 -> Int -> IO Int-sysexHelper q i = do- x <- peekElemOff q i- if x == 0xf7 then return (i-1) else sysexHelper q (i+1) +depackSingleMessage !p (PartialSysEx partial) !maxlen = do+ (n,bytes,finished) <- sysex p maxlen+ return $ if finished + then (n , Just (partial ++ bytes) , NoSysEx)+ else (n , Nothing , PartialSysEx (partial ++ bytes) ) +-- CHANGE in 2020: now we include the terminating 0xf7 byte! +-- this is because longer sysex messages can be broken into several parts...+sysex :: Ptr Word8 -> Int -> IO (Int,[Word8],Bool)+sysex !p !maxlen = do + (n,finished) <- sysexHelper p maxlen 0+ xs <- mapM (peekElemOff p) [0..n-1]+ return ( n , xs , finished) ++sysexHelper :: Ptr Word8 -> Int -> Int -> IO (Int,Bool)+sysexHelper !q !maxlen = go where+ go !i = if (i < maxlen) + then do+ x <- peekElemOff q i+ if x == 0xf7 then return (i+1 , True) else go (i+1) + else return (maxlen , False)+ -------------------------------------------------------------------------------- -- * Send messages to destinations we connected to
System/Win32/MIDI.hs view
@@ -1,8 +1,8 @@ --- |Low-level binding to the Win32 MIDI services.+-- | Low-level binding to the Win32 MIDI services. -- Error handling is via `fail`-s in the IO monad. -{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}+{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-} module System.Win32.MIDI ( Source(..) , Destination(..)@@ -62,10 +62,22 @@ import System.Win32.Types import System.MIDI.Base +--------------------------------------------------------------------------------++#if defined(i386_HOST_ARCH)+#define WINDOWS_CCONV stdcall+#elif defined(x86_64_HOST_ARCH)+#define WINDOWS_CCONV ccall+#else+#error Unknown mingw32 arch+#endif++--------------------------------------------------------------------------------+ maxPNAMELEN = 32 :: Int maxERRORLENGTH = 256 :: UINT -midiMapperID = -1 :: UINT+midiMapperID = negate 1 :: UINT type HMIDI = HANDLE type HMIDIIN = HANDLE@@ -75,7 +87,7 @@ type MIDIINPROC a = HMIDIIN -> UINT -> Ptr a -> DWORD -> DWORD -> IO () type MIDIOUTPROC a = HMIDIOUT -> UINT -> Ptr a -> DWORD -> DWORD -> IO () -foreign import stdcall safe "wrapper" +foreign import WINDOWS_CCONV safe "wrapper" mkMIDIPROC :: MIDIPROC () -> IO (FunPtr (MIDIPROC ())) -----@@ -94,7 +106,7 @@ ----- Time -- Returns the system time in miliseconds.-foreign import stdcall unsafe "mmsystem.h timeGetTime"+foreign import WINDOWS_CCONV unsafe "mmsystem.h timeGetTime" timeGetTime :: IO DWORD ----- Errors@@ -115,10 +127,10 @@ mmOutError :: MMRESULT -> IO a mmOutError mmresult = do { txt <- midiOutGetErrorText mmresult ; fail txt } -foreign import stdcall unsafe "mmsystem.h midiInGetErrorTextW"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiInGetErrorTextW" c_midiInGetErrorText :: MMRESULT -> LPTSTR -> UINT -> IO UINT -foreign import stdcall unsafe "mmsystem.h midiOutGetErrorTextW"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiOutGetErrorTextW" c_midiOutGetErrorText :: MMRESULT -> LPTSTR -> UINT -> IO UINT midiInGetErrorText :: MMRESULT -> IO String@@ -139,10 +151,10 @@ ----- Devices -foreign import stdcall unsafe "mmsystem.h midiInGetNumDevs"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiInGetNumDevs" c_midiInGetNumDevs :: IO UINT -foreign import stdcall unsafe "mmsystem.h midiOutGetNumDevs"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiOutGetNumDevs" c_midiOutGetNumDevs :: IO UINT -- |Eumerates the MIDI sources.@@ -219,10 +231,10 @@ return $ MidiOutCaps mid pid ver nam tec voi not chm sup poke p mic = fail "not implemented yet" -foreign import stdcall unsafe "mmsystem.h midiInGetDevCapsW"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiInGetDevCapsW" c_midiInGetDevCaps :: UINT -> LPMIDIINCAPS -> UINT -> IO MMRESULT -foreign import stdcall unsafe "mmsystem.h midiOutGetDevCapsW"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiOutGetDevCapsW" c_midiOutGetDevCaps :: UINT -> LPMIDIOUTCAPS -> UINT -> IO MMRESULT -- |`Source`'s and `Destinations` have names, models and manufacturers (though the last two is not really supported...)@@ -273,55 +285,55 @@ ----- Open / Close (these should be "safe", as they are generating immediate callbacks!) -foreign import stdcall safe "mmsystem.h midiInOpen"+foreign import WINDOWS_CCONV safe "mmsystem.h midiInOpen" c_midiInOpen :: Ptr HMIDIIN -> UINT -> FunPtr (MIDIINPROC a) -> Ptr a -> DWORD -> IO MMRESULT -foreign import stdcall safe "mmsystem.h midiOutOpen"+foreign import WINDOWS_CCONV safe "mmsystem.h midiOutOpen" c_midiOutOpen :: Ptr HMIDIOUT -> UINT -> FunPtr (MIDIOUTPROC a) -> Ptr a -> DWORD -> IO MMRESULT -foreign import stdcall safe "mmsystem.h midiInClose"+foreign import WINDOWS_CCONV safe "mmsystem.h midiInClose" c_midiInClose :: HMIDIIN -> IO MMRESULT -foreign import stdcall safe "mmsystem.h midiOutClose"+foreign import WINDOWS_CCONV safe "mmsystem.h midiOutClose" c_midiOutClose :: HMIDIOUT -> IO MMRESULT ----- Start / Stop / Reset -foreign import stdcall unsafe "mmsystem.h midiInStart"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiInStart" c_midiInStart :: HMIDIIN -> IO MMRESULT -foreign import stdcall unsafe "mmsystem.h midiInStop"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiInStop" c_midiInStop :: HMIDIIN -> IO MMRESULT -foreign import stdcall safe "mmsystem.h midiInReset"+foreign import WINDOWS_CCONV safe "mmsystem.h midiInReset" c_midiInReset :: HMIDIIN -> IO MMRESULT -- the resetting functions must be safe, as they are doing callbacks!!! -foreign import stdcall safe "mmsystem.h midiOutReset"+foreign import WINDOWS_CCONV safe "mmsystem.h midiOutReset" c_midiOutReset :: HMIDIIN -> IO MMRESULT ----- Send Messages data MIDIHDR -foreign import stdcall unsafe "mmsystem.h midiInAddBuffer" -- this should be called midiInLongMsg ???+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiInAddBuffer" -- this should be called midiInLongMsg ??? c_midiInAddBuffer :: HMIDIIN -> Ptr MIDIHDR -> UINT -> IO MMRESULT -foreign import stdcall unsafe "mmsystem.h midiOutShortMsg"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiOutShortMsg" c_midiOutShortMsg :: HMIDIOUT -> DWORD -> IO MMRESULT -foreign import stdcall unsafe "mmsystem.h midiOutLongMsg"+foreign import WINDOWS_CCONV unsafe "mmsystem.h midiOutLongMsg" c_midiOutLongMsg :: HMIDIOUT -> Ptr MIDIHDR -> UINT -> IO MMRESULT -foreign import stdcall unsafe "mmystem.h midiInPrepareHeader"+foreign import WINDOWS_CCONV unsafe "mmystem.h midiInPrepareHeader" c_midiInPrepareHeader :: HMIDIIN -> Ptr MIDIHDR -> UINT -> IO MMRESULT -foreign import stdcall unsafe "mmystem.h midiOutPrepareHeader"+foreign import WINDOWS_CCONV unsafe "mmystem.h midiOutPrepareHeader" c_midiOutPrepareHeader :: HMIDIOUT -> Ptr MIDIHDR -> UINT -> IO MMRESULT -foreign import stdcall unsafe "mmystem.h midiInUnprepareHeader"+foreign import WINDOWS_CCONV unsafe "mmystem.h midiInUnprepareHeader" c_midiInUnprepareHeader :: HMIDIIN -> Ptr MIDIHDR -> UINT -> IO MMRESULT -foreign import stdcall unsafe "mmystem.h midiOutUnprepareHeader"+foreign import WINDOWS_CCONV unsafe "mmystem.h midiOutUnprepareHeader" c_midiOutUnprepareHeader :: HMIDIOUT -> Ptr MIDIHDR -> UINT -> IO MMRESULT type CPrepare = HMIDI -> Ptr MIDIHDR -> UINT -> IO MMRESULT
hmidi.cabal view
@@ -1,26 +1,26 @@-+Cabal-Version: 2.0 Name: hmidi-Version: 0.2.2.1+Version: 0.2.3.1 Synopsis: Binding to the OS level MIDI services Description: Partial implementation of the MIDI 1.0 standard to communicate - with physical or virtual MIDI devices, eg. MIDI keyboards. - Supported operating systems are Mac OS X and Windows.- See also the alsa-midi library for similar- function under Linux. Please note that there was no effort made (yet) to+ with physical or virtual MIDI devices, eg. MIDI keyboards and synths. + Supported operating systems are MacOS and Windows.+ See also the @alsa-midi@ library for similar functionality under Linux. + Please note that there was no effort made (yet) to be compatible with the other existing Haskell MIDI libraries. License: BSD3 License-file: LICENSE Author: Balazs Komuves-Copyright: (c) 2008-2015 Balazs Komuves+Copyright: (c) 2008-2020 Balazs Komuves Maintainer: bkomuves (plus) hackage (at) gmail (dot) com-Homepage: http://code.haskell.org/~bkomuves/+Homepage: http://moire.be/haskell/ Stability: Experimental-Category: Sound, System-Tested-With: GHC == 7.8.3-Cabal-Version: >= 1.6+Category: Music, System+Tested-With: GHC == 8.6.5 Build-Type: Simple -extra-source-files: examples/monitor.hs, +extra-source-files: README.md,+ examples/monitor.hs, examples/chords.hs, examples/playmidi.hs, examples/SMF.hs, @@ -28,8 +28,8 @@ examples/osx_host.hs source-repository head- type: darcs- location: http://code.haskell.org/~bkomuves/projects/hmidi/+ type: darcs+ location: https://hub.darcs.net/bkomuves/hmidi Flag noNoteOff Description: Translates NoteOff events to NoteOn events with velocity=0.@@ -59,10 +59,11 @@ -- this is just to be able to produce a Haddock documentation on a Linux system if !os(darwin) && !os(windows) other-modules: System.MIDI.Placeholder- - Extensions: ForeignFunctionInterface, CPP, TypeSynonymInstances, FlexibleInstances, EmptyDataDecls+ + Default-language: Haskell2010 + Default-extensions: ForeignFunctionInterface, CPP, TypeSynonymInstances, FlexibleInstances, EmptyDataDecls - ghc-options: -threaded + -- ghc-options: -threaded if flag(noNoteOff) cpp-options: -DHMIDI_NO_NOTEOFF