packages feed

RtMidi 0.5.0.3 → 0.6.0.0

raw patch · 9 files changed

+175/−124 lines, 9 filesdep −unliftio-coresetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies removed: unliftio-core

API changes (from Hackage documentation)

- Sound.RtMidi: apiDisplayName :: MonadIO m => Api -> m String
+ Sound.RtMidi: apiDisplayName :: Api -> IO String
- Sound.RtMidi: apiName :: MonadIO m => Api -> m String
+ Sound.RtMidi: apiName :: Api -> IO String
- Sound.RtMidi: cancelCallback :: MonadIO m => InputDevice -> m ()
+ Sound.RtMidi: cancelCallback :: InputDevice -> IO ()
- Sound.RtMidi: closePort :: (MonadIO m, IsDevice d) => d -> m ()
+ Sound.RtMidi: closePort :: IsDevice d => d -> IO ()
- Sound.RtMidi: compiledApiByName :: MonadIO m => String -> m Api
+ Sound.RtMidi: compiledApiByName :: String -> IO Api
- Sound.RtMidi: compiledApis :: MonadIO m => m [Api]
+ Sound.RtMidi: compiledApis :: IO [Api]
- Sound.RtMidi: createInput :: MonadIO m => Api -> String -> Int -> m InputDevice
+ Sound.RtMidi: createInput :: Api -> String -> Int -> IO InputDevice
- Sound.RtMidi: createOutput :: MonadIO m => Api -> String -> m OutputDevice
+ Sound.RtMidi: createOutput :: Api -> String -> IO OutputDevice
- Sound.RtMidi: currentApi :: MonadIO m => IsDevice d => d -> m Api
+ Sound.RtMidi: currentApi :: IsDevice d => d -> IO Api
- Sound.RtMidi: defaultInput :: MonadIO m => m InputDevice
+ Sound.RtMidi: defaultInput :: IO InputDevice
- Sound.RtMidi: defaultOutput :: MonadIO m => m OutputDevice
+ Sound.RtMidi: defaultOutput :: IO OutputDevice
- Sound.RtMidi: findPort :: (MonadIO m, IsDevice d) => d -> (String -> Bool) -> m (Maybe Int)
+ Sound.RtMidi: findPort :: IsDevice d => d -> (String -> Bool) -> IO (Maybe Int)
- Sound.RtMidi: getMessage :: MonadIO m => InputDevice -> m (Double, Vector Word8)
+ Sound.RtMidi: getMessage :: InputDevice -> IO (Double, Vector Word8)
- Sound.RtMidi: getMessageMutable :: MonadIO m => InputDevice -> IOVector Word8 -> m (Double, Int)
+ Sound.RtMidi: getMessageMutable :: InputDevice -> IOVector Word8 -> IO (Double, Int)
- Sound.RtMidi: getMessageSized :: MonadIO m => InputDevice -> Int -> m (Double, Vector Word8)
+ Sound.RtMidi: getMessageSized :: InputDevice -> Int -> IO (Double, Vector Word8)
- Sound.RtMidi: ignoreTypes :: MonadIO m => InputDevice -> Bool -> Bool -> Bool -> m ()
+ Sound.RtMidi: ignoreTypes :: InputDevice -> Bool -> Bool -> Bool -> IO ()
- Sound.RtMidi: listPorts :: (MonadIO m, IsDevice d) => d -> m [(Int, String)]
+ Sound.RtMidi: listPorts :: IsDevice d => d -> IO [(Int, String)]
- Sound.RtMidi: openPort :: (MonadIO m, IsDevice d) => d -> Int -> String -> m ()
+ Sound.RtMidi: openPort :: IsDevice d => d -> Int -> String -> IO ()
- Sound.RtMidi: openVirtualPort :: (MonadIO m, IsDevice d) => d -> String -> m ()
+ Sound.RtMidi: openVirtualPort :: IsDevice d => d -> String -> IO ()
- Sound.RtMidi: portCount :: (MonadIO m, IsDevice d) => d -> m Int
+ Sound.RtMidi: portCount :: IsDevice d => d -> IO Int
- Sound.RtMidi: portName :: (MonadIO m, IsDevice d) => d -> Int -> m (Maybe String)
+ Sound.RtMidi: portName :: IsDevice d => d -> Int -> IO (Maybe String)
- Sound.RtMidi: ready :: (MonadIO m, IsDevice d) => d -> m Bool
+ Sound.RtMidi: ready :: IsDevice d => d -> IO Bool
- Sound.RtMidi: sendMessage :: MonadIO m => OutputDevice -> Vector Word8 -> m ()
+ Sound.RtMidi: sendMessage :: OutputDevice -> Vector Word8 -> IO ()
- Sound.RtMidi: setCallback :: MonadUnliftIO m => InputDevice -> (Double -> Vector Word8 -> m ()) -> m ()
+ Sound.RtMidi: setCallback :: InputDevice -> (Double -> Vector Word8 -> IO ()) -> IO ()
- Sound.RtMidi: setForeignCallback :: MonadIO m => InputDevice -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> Ptr () -> m ()
+ Sound.RtMidi: setForeignCallback :: InputDevice -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> Ptr () -> IO ()
- Sound.RtMidi: setUnsafeCallback :: MonadIO m => InputDevice -> (Double -> Ptr Word8 -> Int -> IO ()) -> m ()
+ Sound.RtMidi: setUnsafeCallback :: InputDevice -> (Double -> Ptr Word8 -> Int -> IO ()) -> IO ()
- Sound.RtMidi.Report: buildApiReport :: MonadIO m => Api -> m ApiReport
+ Sound.RtMidi.Report: buildApiReport :: Api -> IO ApiReport
- Sound.RtMidi.Report: buildCustomReport :: MonadIO m => Bool -> m Report
+ Sound.RtMidi.Report: buildCustomReport :: Bool -> IO Report
- Sound.RtMidi.Report: buildReport :: MonadIO m => m Report
+ Sound.RtMidi.Report: buildReport :: IO Report

Files

README.md view
@@ -1,7 +1,5 @@ # RtMidi -[![CircleCI](https://circleci.com/gh/riottracker/RtMidi/tree/master.svg?style=svg)](https://circleci.com/gh/riottracker/RtMidi/tree/master)- Haskell wrapper for [RtMidi](http://www.music.mcgill.ca/~gary/rtmidi/), the lightweight, cross-platform MIDI I/O library.  ## How to use
RtMidi.cabal view
@@ -1,6 +1,6 @@ cabal-version:       3.0 name:                RtMidi-version:             0.5.0.3+version:             0.6.0.0 synopsis:            Haskell wrapper for RtMidi, the lightweight, cross-platform MIDI I/O library. description:         Please see the README on GitHub at <https://github.com/riottracker/RtMidi#readme> category:            Sound@@ -34,7 +34,6 @@   other-modules:       Sound.RtMidi.Foreign   build-depends:       base >=4.14 && <5                      , deepseq >= 1.4.4.0 && < 2-                     , unliftio-core >= 0.1.2.0 && < 1                      , vector >= 0.12.1.2 && < 1   default-language:    Haskell2010   default-extensions:  DeriveGeneric
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
Sound/RtMidi.hs view
@@ -35,13 +35,12 @@   , createOutput   , sendMessage   , currentApi-  ) where+  )+where  import Control.DeepSeq (NFData) import Control.Exception (Exception, throwIO) import Control.Monad (unless, void)-import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.IO.Unlift (MonadUnliftIO (..)) import Data.Coerce (coerce) import qualified Data.Vector.Storable as VS import qualified Data.Vector.Storable.Mutable as VSM@@ -51,6 +50,33 @@ import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr) import GHC.Generics (Generic) import Sound.RtMidi.Foreign+  ( Api (..)+  , Wrapper (..)+  , fromApi+  , rtmidi_api_display_name+  , rtmidi_api_name+  , rtmidi_close_port+  , rtmidi_compiled_api_by_name+  , rtmidi_get_compiled_api+  , rtmidi_get_port_count+  , rtmidi_get_port_name+  , rtmidi_in_cancel_callback+  , rtmidi_in_create+  , rtmidi_in_create_default+  , rtmidi_in_free+  , rtmidi_in_get_current_api+  , rtmidi_in_get_message+  , rtmidi_in_ignore_types+  , rtmidi_in_set_callback+  , rtmidi_open_port+  , rtmidi_open_virtual_port+  , rtmidi_out_create+  , rtmidi_out_create_default+  , rtmidi_out_free+  , rtmidi_out_get_current_api+  , rtmidi_out_send_message+  , toApi+  )  -- The default message size (in bytes) expected from 'getMessage' -- Mostly just needs to be bigger than the max MIDI message size, which is 3 bytes@@ -59,13 +85,13 @@ defaultMessageSize = 4  -- | Allows us to discriminate in/out functions in generic contexts-data DeviceType =-    InputDeviceType+data DeviceType+  = InputDeviceType   | OutputDeviceType   deriving stock (Eq, Show, Ord, Enum, Bounded, Generic)   deriving anyclass (NFData) -newtype Device = Device { unDevice :: ForeignPtr Wrapper }+newtype Device = Device {unDevice :: ForeignPtr Wrapper}   deriving stock (Eq, Show)  -- | Generalizes 'InputDevice' and 'OutputDevice' for use in common functions@@ -74,7 +100,7 @@   getDeviceType :: d -> DeviceType  -- | A handle to a device to be used for input-newtype InputDevice = InputDevice { unInputDevice :: Device }+newtype InputDevice = InputDevice {unInputDevice :: Device}   deriving stock (Eq, Show)  instance IsDevice InputDevice where@@ -85,7 +111,7 @@ newInputDevice = fmap (InputDevice . Device) . newForeignPtr rtmidi_in_free  -- | A handle to a device to be used for input-newtype OutputDevice = OutputDevice { unOutputDevice :: Device }+newtype OutputDevice = OutputDevice {unOutputDevice :: Device}   deriving stock (Eq, Show)  instance IsDevice OutputDevice where@@ -96,7 +122,7 @@ newOutputDevice = fmap (OutputDevice . Device) . newForeignPtr rtmidi_out_free  -- | An internal RtMidi error-newtype Error = Error { unError :: String }+newtype Error = Error {unError :: String}   deriving stock (Eq, Show, Generic)   deriving anyclass (NFData) @@ -119,24 +145,24 @@ withDevicePtr d f = withDevicePtrUnguarded d (\dptr -> f dptr <* guardError dptr)  -- | Get the display name for the given 'Api'.-apiDisplayName :: MonadIO m => Api -> m String-apiDisplayName api = liftIO (rtmidi_api_display_name (fromApi api) >>= peekCString)+apiDisplayName :: Api -> IO String+apiDisplayName api = rtmidi_api_display_name (fromApi api) >>= peekCString  -- | Get the internal name for the given 'Api'.-apiName :: MonadIO m => Api -> m String-apiName api = liftIO (rtmidi_api_name (fromApi api) >>= peekCString)+apiName :: Api -> IO String+apiName api = rtmidi_api_name (fromApi api) >>= peekCString  -- | Lookup a compiled 'Api' by name.-compiledApiByName :: MonadIO m => String -> m Api-compiledApiByName name = liftIO (fmap toApi (withCString name rtmidi_compiled_api_by_name))+compiledApiByName :: String -> IO Api+compiledApiByName name = fmap toApi (withCString name rtmidi_compiled_api_by_name)  -- | Check if a device is ok-ready :: (MonadIO m, IsDevice d) => d -> m Bool-ready d = liftIO (withDevicePtr d (fmap ok . peek))+ready :: IsDevice d => d -> IO Bool+ready d = withDevicePtr d (fmap ok . peek)  -- | A static function to determine MIDI 'Api's built in.-compiledApis :: MonadIO m => m [Api]-compiledApis = liftIO $ do+compiledApis :: IO [Api]+compiledApis = do   n <- fmap fromIntegral (rtmidi_get_compiled_api nullPtr 0)   as <- allocaArray n $ \ptr -> do     rtmidi_get_compiled_api ptr (fromIntegral n)@@ -144,37 +170,40 @@   pure (map toApi as)  -- | Open a MIDI connection-openPort :: (MonadIO m, IsDevice d)-         => d-         -> Int          -- ^ port number-         -> String       -- ^ name for the application port that is used-         -> m ()-openPort d n name = liftIO $ withDevicePtr d $ \dptr ->+openPort+  :: IsDevice d+  => d+  -> Int+  -- ^ port number+  -> String+  -- ^ name for the application port that is used+  -> IO ()+openPort d n name = withDevicePtr d $ \dptr ->   withCString name (rtmidi_open_port dptr (toEnum n))  -- | This function creates a virtual MIDI output port to which other software applications can connect. -- -- This type of functionality is currently only supported by the Macintosh OS X, Linux ALSA and JACK APIs -- (the function does nothing with the other APIs).-openVirtualPort :: (MonadIO m, IsDevice d) => d -> String -> m ()-openVirtualPort d name = liftIO $ withDevicePtr d $ \dptr -> do+openVirtualPort :: IsDevice d => d -> String -> IO ()+openVirtualPort d name = withDevicePtr d $ \dptr -> do   withCString name (rtmidi_open_virtual_port dptr)  -- | Close an open MIDI connection (if one exists).-closePort :: (MonadIO m, IsDevice d) => d -> m ()-closePort d = liftIO (withDevicePtr d rtmidi_close_port)+closePort :: IsDevice d => d -> IO ()+closePort d = withDevicePtr d rtmidi_close_port  -- | Return the number of MIDI ports available to the 'Device'.-portCount :: (MonadIO m, IsDevice d) => d -> m Int-portCount d = liftIO $ withDevicePtr d $ \dptr -> do+portCount :: IsDevice d => d -> IO Int+portCount d = withDevicePtr d $ \dptr -> do   x <- rtmidi_get_port_count dptr   pure (fromIntegral x)  -- | Return a string identifier for the specified MIDI port number. -- -- 'Nothing' is returned if an invalid port specifier is provided.-portName :: (MonadIO m, IsDevice d) => d -> Int -> m (Maybe String)-portName d n = liftIO $ withDevicePtrUnguarded d $ \dptr -> do+portName :: IsDevice d => d -> Int -> IO (Maybe String)+portName d n = withDevicePtrUnguarded d $ \dptr -> do   x <- rtmidi_get_port_name dptr (toEnum n)   guardError dptr   s <- peekCString x@@ -186,14 +215,15 @@ -- -- Note that the underlying library does not offer an "atomic" interface for this -- so results may be inconsistent if you connect/disconnect ports during this call.-listPorts :: (MonadIO m, IsDevice d) => d -> m [(Int, String)]-listPorts d = liftIO $ portCount d >>= go [] 0 where+listPorts :: IsDevice d => d -> IO [(Int, String)]+listPorts d = portCount d >>= go [] 0+ where   go acc i c =     if i >= c       then pure (reverse acc)       else do         mn <- portName d i-        let acc' = maybe acc (\n -> (i, n):acc) mn+        let acc' = maybe acc (\n -> (i, n) : acc) mn         go acc' (succ i) c  -- | Convenience function to lookup the first port satisfying the predicate.@@ -208,8 +238,9 @@ -- -- Note that if you are performing many lookups, it's better to use 'listPorts' and -- do the lookups yourself (see the caveats there too).-findPort :: (MonadIO m, IsDevice d) => d -> (String -> Bool) -> m (Maybe Int)-findPort d f = liftIO $ portCount d >>= go 0 where+findPort :: IsDevice d => d -> (String -> Bool) -> IO (Maybe Int)+findPort d f = portCount d >>= go 0+ where   go i c =     if i >= c       then pure Nothing@@ -220,19 +251,22 @@           _ -> go (succ i) c  -- | Default constructor for a 'Device' to use for input.-defaultInput :: MonadIO m => m InputDevice-defaultInput = liftIO $ do+defaultInput :: IO InputDevice+defaultInput = do   dptr <- rtmidi_in_create_default   guardError dptr   newInputDevice dptr  -- | Create a new 'Device' to use for input.-createInput :: MonadIO m-            => Api        -- ^ API to use-            -> String     -- ^ client name-            -> Int        -- ^ size of the MIDI input queue-            -> m InputDevice-createInput api clientName queueSizeLimit = liftIO $ do+createInput+  :: Api+  -- ^ API to use+  -> String+  -- ^ client name+  -> Int+  -- ^ size of the MIDI input queue+  -> IO InputDevice+createInput api clientName queueSizeLimit = do   dptr <- withCString clientName (\str -> rtmidi_in_create (fromApi api) str (fromIntegral queueSizeLimit))   guardError dptr   newInputDevice dptr@@ -253,21 +287,22 @@ -- The callback function will be called whenever an incoming MIDI message is received. -- While not absolutely necessary, it is best to set the callback function before opening a MIDI port to avoid leaving -- some messages in the queue.-setCallback :: MonadUnliftIO m-            => InputDevice-            -> (Double -> VS.Vector Word8 -> m ())  -- ^ Function that takes a timestamp and a MIDI message as arguments-            -> m ()-setCallback d c = withRunInIO $ \run -> withDevicePtr d $ \dptr -> do-  f <- mkCallbackPointer (adaptCallbackCTypes (\ts bytes -> run (c ts bytes)))+setCallback+  :: InputDevice+  -> (Double -> VS.Vector Word8 -> IO ())+  -- ^ Function that takes a timestamp and a MIDI message as arguments+  -> IO ()+setCallback d c = withDevicePtr d $ \dptr -> do+  f <- mkCallbackPointer (adaptCallbackCTypes c)   rtmidi_in_set_callback dptr f nullPtr  -- | A variant of 'setCallback' that takes a raw pointer and length. It is unsafe to share or reference the pointer beyond the -- scope of the callback, as the RtMidi-owned memory it references may have been changed or freed.-setUnsafeCallback :: MonadIO m-                  => InputDevice-                  -> (Double -> Ptr Word8 -> Int -> IO ())-                  -> m ()-setUnsafeCallback d c = liftIO $ withDevicePtr d $ \dptr -> do+setUnsafeCallback+  :: InputDevice+  -> (Double -> Ptr Word8 -> Int -> IO ())+  -> IO ()+setUnsafeCallback d c = withDevicePtr d $ \dptr -> do   f <- mkCallbackPointer (adaptUnsafeCallbackCTypes c)   rtmidi_in_set_callback dptr f nullPtr @@ -275,36 +310,40 @@ -- -- This variant allows you to set the callback to a C function pointer so we're not forced -- to enter a Haskell wrapper every invocation.-setForeignCallback :: MonadIO m-                   => InputDevice-                   -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ())-                   -> Ptr () -- ^ Pointer to context that will be passed into the callback-                   -> m ()-setForeignCallback d f ctx = liftIO $ withDevicePtr d $ \dptr ->+setForeignCallback+  :: InputDevice+  -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ())+  -> Ptr ()+  -- ^ Pointer to context that will be passed into the callback+  -> IO ()+setForeignCallback d f ctx = withDevicePtr d $ \dptr ->   rtmidi_in_set_callback dptr f ctx  -- | Cancel use of the current callback function (if one exists). -- -- Subsequent incoming MIDI messages will be written to the queue and can be retrieved with the `getMessage` function.-cancelCallback :: MonadIO m => InputDevice -> m ()-cancelCallback d = liftIO (withDevicePtr d rtmidi_in_cancel_callback)+cancelCallback :: InputDevice -> IO ()+cancelCallback d = withDevicePtr d rtmidi_in_cancel_callback  -- | Specify whether certain MIDI message types should be queued or ignored during input. -- -- By default, MIDI timing and active sensing messages are ignored during message input because of their -- relative high data rates. MIDI sysex messages are ignored by default as well. -- Variable values of 'True' imply that the respective message type will be ignored.-ignoreTypes :: MonadIO m-            => InputDevice-            -> Bool       -- ^ SysEx messages-            -> Bool       -- ^ Time messages-            -> Bool       -- ^ Sense messages-            -> m ()-ignoreTypes d x y z = liftIO (withDevicePtrUnguarded d (\dptr -> rtmidi_in_ignore_types dptr x y z))+ignoreTypes+  :: InputDevice+  -> Bool+  -- ^ SysEx messages+  -> Bool+  -- ^ Time messages+  -> Bool+  -- ^ Sense messages+  -> IO ()+ignoreTypes d x y z = withDevicePtrUnguarded d (\dptr -> rtmidi_in_ignore_types dptr x y z)  -- | Variant of 'getMessage' that allows you to fill a shared buffer, returning timestamp and size.-getMessageMutable :: MonadIO m => InputDevice -> VSM.IOVector Word8 -> m (Double, Int)-getMessageMutable d buf = liftIO $ alloca $ \s -> VSM.unsafeWith buf $ \m -> withDevicePtrUnguarded d $ \dptr -> do+getMessageMutable :: InputDevice -> VSM.IOVector Word8 -> IO (Double, Int)+getMessageMutable d buf = alloca $ \s -> VSM.unsafeWith buf $ \m -> withDevicePtrUnguarded d $ \dptr -> do   poke s (fromIntegral (VSM.length buf))   CDouble !timestamp <- rtmidi_in_get_message dptr (coerce m) s   guardError dptr@@ -313,8 +352,8 @@   pure (timestamp, size)  -- | Variant of 'getMessage' that allows you to set message buffer size (typically for large sysex messages).-getMessageSized :: MonadIO m => InputDevice -> Int -> m (Double, VS.Vector Word8)-getMessageSized d n = liftIO $ do+getMessageSized :: InputDevice -> Int -> IO (Double, VS.Vector Word8)+getMessageSized d n = do   buf <- VSM.new n   (!timestamp, !size) <- getMessageMutable d buf   vec <- VSM.unsafeWith buf (VS.generateM size . peekElemOff)@@ -325,34 +364,36 @@ -- This function returns immediately whether a new message is available or not. -- A valid message is indicated by whether the list contains any elements. -- Note that large sysex messages will be silently dropped! Use 'getMessageSized' or use a callback to get these safely.-getMessage :: MonadIO m => InputDevice -> m (Double, VS.Vector Word8)-getMessage d = liftIO (getMessageSized d defaultMessageSize)+getMessage :: InputDevice -> IO (Double, VS.Vector Word8)+getMessage d = getMessageSized d defaultMessageSize  -- | Default constructor for a 'Device' to use for output.-defaultOutput :: MonadIO m => m OutputDevice-defaultOutput = liftIO $ do+defaultOutput :: IO OutputDevice+defaultOutput = do   dptr <- rtmidi_out_create_default   guardError dptr   newOutputDevice dptr  -- | Create a new 'Device' to use for output.-createOutput :: MonadIO m-             => Api        -- ^ API to use-             -> String     -- ^ client name-             -> m OutputDevice-createOutput api clientName = liftIO $ do+createOutput+  :: Api+  -- ^ API to use+  -> String+  -- ^ client name+  -> IO OutputDevice+createOutput api clientName = do   dptr <- withCString clientName (rtmidi_out_create (fromApi api))   guardError dptr   newOutputDevice dptr  -- | Immediately send a single message out an open MIDI output port.-sendMessage :: MonadIO m => OutputDevice -> VS.Vector Word8 -> m ()-sendMessage d buf = liftIO $ VS.unsafeWith buf $ \m -> withDevicePtr d $ \dptr ->+sendMessage :: OutputDevice -> VS.Vector Word8 -> IO ()+sendMessage d buf = VS.unsafeWith buf $ \m -> withDevicePtr d $ \dptr ->   void (rtmidi_out_send_message dptr (coerce m) (fromIntegral (VS.length buf)))  -- | Returns the specifier for the MIDI 'Api' in use-currentApi :: MonadIO m => IsDevice d => d -> m Api-currentApi d = liftIO $ withDevicePtr d $ \dptr -> do+currentApi :: IsDevice d => d -> IO Api+currentApi d = withDevicePtr d $ \dptr -> do   res <-     case getDeviceType d of       InputDeviceType -> rtmidi_in_get_current_api dptr
Sound/RtMidi/Report.hs view
@@ -7,13 +7,24 @@   , buildApiReport   , buildCustomReport   , buildReport-  ) where+  )+where  import Control.DeepSeq (NFData)-import Control.Monad.IO.Class (MonadIO) import Data.List (nub) import GHC.Generics (Generic) import Sound.RtMidi+  ( Api+  , apiDisplayName+  , apiName+  , compiledApis+  , createInput+  , createOutput+  , currentApi+  , defaultInput+  , defaultOutput+  , listPorts+  )  -- | MIDI system information specific to a particular API. data ApiReport = ApiReport@@ -22,19 +33,21 @@   , apiRepDisplayName :: !String   , apiInPorts :: ![(Int, String)]   , apiOutPorts :: ![(Int, String)]-  } deriving stock (Eq, Show, Generic)-    deriving anyclass (NFData)+  }+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)  -- | MIDI system information for any number of APIs. data Report = Report   { defaultInApi :: !Api   , defaultOutApi :: !Api   , apiReports :: ![ApiReport]-  } deriving stock (Eq, Show, Generic)-    deriving anyclass (NFData)+  }+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)  -- | Gather information about the given 'Api', including port information.-buildApiReport :: MonadIO m => Api -> m ApiReport+buildApiReport :: Api -> IO ApiReport buildApiReport api = do   name <- apiName api   displayName <- apiDisplayName api@@ -45,9 +58,10 @@   pure (ApiReport api name displayName inPorts outPorts)  -- | Variant of 'buildReport' that allows you to restrict it to the default APIs.-buildCustomReport :: MonadIO m-                  => Bool  -- ^ True to report on default APIs, False to report on all compiled APIs.-                  -> m Report+buildCustomReport+  :: Bool+  -- ^ True to report on default APIs, False to report on all compiled APIs.+  -> IO Report buildCustomReport defaultOnly = do   inDev <- defaultInput   defInApi <- currentApi inDev@@ -58,5 +72,5 @@   pure (Report defInApi defOutApi apiReps)  -- | Gather information about all compiled APIs.-buildReport :: MonadIO m => m Report+buildReport :: IO Report buildReport = buildCustomReport False
examples/callback.hs view
@@ -4,7 +4,7 @@ import Sound.RtMidi (closePort, defaultInput, openPort, setCallback)  callback :: Double -> VS.Vector Word8 -> IO ()-callback delta msg = putStrLn $ (VS.foldr (showHex . fromEnum) "" msg) ++ " - " ++ (show delta)+callback delta msg = putStrLn $ VS.foldr (showHex . fromEnum) "" msg ++ " - " ++ show delta  main :: IO () main = do
examples/playback.hs view
@@ -1,13 +1,13 @@ import Control.Concurrent (threadDelay) import qualified Data.Vector.Storable as VS-import Sound.RtMidi (closePort, defaultOutput, openPort, sendMessage, portCount, portName)+import Sound.RtMidi (closePort, defaultOutput, openPort, portCount, portName, sendMessage)  main :: IO () main = do   device <- defaultOutput   numPorts <- portCount device-  ports <- mapM (portName device) [0..numPorts-1]-  mapM_ (\t -> putStrLn $ show t) $ zip [0..] ports+  ports <- mapM (portName device) [0 .. numPorts - 1]+  mapM_ print $ zip [0 ..] ports   putStrLn "select port: "   selection <- getLine   openPort device (read selection) "RtMidi"
examples/poll.hs view
@@ -1,14 +1,12 @@ import Control.Concurrent (forkIO, killThread)-import Control.Monad (forever)+import Control.Monad (forever, when) import qualified Data.Vector.Storable as VS import Sound.RtMidi (InputDevice, closePort, defaultInput, getMessage, openPort)  mainLoop :: InputDevice -> IO () mainLoop d = do   m <- getMessage d-  if VS.length (snd m) > 0 then-    putStrLn $ show m-  else return ()+  when (VS.length (snd m) > 0) $ print m  main :: IO () main = do
test/Main.hs view
@@ -3,11 +3,12 @@ import Control.Concurrent (threadDelay) import Control.Monad (replicateM_, unless, when) import Data.Foldable (for_)-import Data.IORef (IORef, newIORef, readIORef, modifyIORef)+import Data.IORef (IORef, modifyIORef, newIORef, readIORef) import Data.List (isInfixOf)+import Data.Maybe (fromMaybe) import qualified Data.Vector.Storable as VS import Data.Word (Word8)-import Sound.RtMidi (Api (..), apiName, apiDisplayName, closePort, compiledApiByName, compiledApis, createInput, createOutput, currentApi, findPort, sendMessage, setCallback, openPort, openVirtualPort)+import Sound.RtMidi (Api (..), apiDisplayName, apiName, closePort, compiledApiByName, compiledApis, createInput, createOutput, currentApi, findPort, openPort, openVirtualPort, sendMessage, setCallback) import Sound.RtMidi.Report (Report, buildReport) import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.HUnit (assertFailure, testCase, (@?=))@@ -75,7 +76,7 @@   outApi <- currentApi outDev   outApi @?= api   maybePortNum <- findPort outDev (isInfixOf portName)-  let portNum = maybe (error "Could not find port") id maybePortNum+  let portNum = fromMaybe (error "Could not find port") maybePortNum   openPort outDev portNum portName   -- Send messages   replicateM_ expectedCount (sendMessage outDev exampleMessage)@@ -94,10 +95,12 @@   apis <- compiledApis   let rwTests = fmap testVirtualReadWrite (filter (/= DummyApi) apis)       rwGroup = testGroup "R/W" rwTests-  defaultMain $ testGroup "RtMidi" $-    [ testApiName-    , testApiDisplayName-    , testCompiledApiByName-    , testBuildReport-    , rwGroup-    ]+  defaultMain $+    testGroup+      "RtMidi"+      [ testApiName+      , testApiDisplayName+      , testCompiledApiByName+      , testBuildReport+      , rwGroup+      ]