packages feed

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 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