Skip to content

Commit

Permalink
etc
Browse files Browse the repository at this point in the history
  • Loading branch information
ejconlon committed Aug 21, 2024
1 parent 51040a8 commit 5457bbe
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 109 deletions.
70 changes: 17 additions & 53 deletions libremidi/src/Libremidi.hs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
module Libremidi where

import Control.Exception (Exception)
import Control.Monad (when)
import Control.Monad.Except (ExceptT, MonadError (..), runExceptT)
import Control.Monad.Free (Free)
import Control.Monad.IO.Class (MonadIO (..))
import Data.Coerce (coerce)
import Data.Text (Text)
import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrBytes, withForeignPtr)
import Foreign.Ptr (castPtr)
import Libremidi.Common (BitEnum)
import Libremidi.Common (BitEnum (..), M, assocM, takeM, textM)
import Libremidi.Foreign qualified as F

data TimestampMode
Expand Down Expand Up @@ -56,64 +50,34 @@ data Version

instance BitEnum F.CVersion Version

newtype Err = Err F.CErr
deriving stock (Eq, Ord, Show)

instance Exception Err

data F a where
FErr :: Err -> F a
FCont :: (forall r. (a -> IO r) -> IO r) -> F a

instance Functor F where
fmap f = \case
FErr e -> FErr e
FCont k -> FCont (\g -> k (g . f))

newtype M a = M {unM :: Free F a}
deriving (Functor, Applicative, Monad)

instance MonadIO M where
liftIO = undefined

instance MonadError Err M where
throwError = undefined

runM :: M a -> IO (Either Err a)
runM m = undefined

useM :: (forall r. (a -> IO r) -> IO r) -> M a
useM x = undefined

guardM :: IO F.CErr -> M ()
guardM eact = do
e <- liftIO eact
when (e /= 0) (throwError (Err e))

ipClone :: F.InPort -> M F.InPort
ipClone ip = do
fp <- liftIO (mallocForeignPtrBytes 0)
ptr <- useM (withForeignPtr fp)
cip <- useM (F.ipWithPtr ip)
guardM (F.libremidi_midi_in_port_clone cip (castPtr ptr))
ptr <- assocM ip
fp <- takeM (F.libremidi_midi_in_port_clone ptr . coerce)
pure (F.InPort fp)

ipName :: F.InPort -> M Text
ipName = error "TODO"
ipName ip = do
ptr <- assocM ip
textM (F.libremidi_midi_in_port_name (coerce ptr))

opClone :: F.OutPort -> M F.OutPort
opClone op = do
fp <- liftIO (mallocForeignPtrBytes 0)
ptr <- useM (withForeignPtr fp)
cop <- useM (F.opWithPtr op)
guardM (F.libremidi_midi_out_port_clone cop (castPtr ptr))
ptr <- assocM op
fp <- takeM (F.libremidi_midi_out_port_clone ptr . coerce)
pure (F.OutPort fp)

opName :: F.OutPort -> M Text
opName = error "TODO"
opName op = do
ptr <- assocM op
textM (F.libremidi_midi_out_port_name (coerce ptr))

obsNew :: F.ObsConfig -> F.ApiConfig -> M F.ObsHandle
obsNew oc ac = error "TODO"
obsNew oc ac = do
ocptr <- assocM oc
acptr <- assocM ac
fp <- takeM (F.libremidi_midi_observer_new ocptr acptr . coerce)
pure (F.ObsHandle fp)

-- TODO expose these
-- libremidi_midi_in_port_clone :: CInPort -> Ptr CInPort -> IO CErr
Expand Down
109 changes: 109 additions & 0 deletions libremidi/src/Libremidi/Common.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,116 @@
module Libremidi.Common where

import Control.Exception (Exception)
import Control.Monad (when)
import Control.Monad.Except (ExceptT, MonadError (..), runExceptT)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans.Free (FreeF (..), FreeT, MonadFree (..), liftF, runFreeT)
import Data.Coerce (coerce)
import Data.Text (Text)
import Data.Text.Foreign qualified as TF
import Foreign.C.String (CString)
import Foreign.C.Types (CInt, CSize)
import Foreign.Concurrent qualified as FC
import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrBytes, newForeignPtr, withForeignPtr)
import Foreign.Marshal.Alloc (allocaBytes, finalizerFree)
import Foreign.Marshal.Utils (fillBytes)
import Foreign.Ptr (FunPtr, Ptr, castFunPtrToPtr, castPtrToFunPtr, freeHaskellFunPtr, nullPtr)
import Foreign.Storable (peek, poke)

class (Integral a, Enum b, Bounded b) => BitEnum a b | b -> a where
fromBitEnum :: a -> b
fromBitEnum = toEnum . fromIntegral
toBitEnum :: b -> a
toBitEnum = fromIntegral . fromEnum

class AssocPtr fp p | fp -> p, fp -> p where
withAssocPtr :: fp -> (p -> IO a) -> IO a

instance AssocPtr (ForeignPtr x) (Ptr x) where
withAssocPtr = withForeignPtr

type Field a b = a -> Ptr b

ptrSize :: Int
ptrSize = 8 -- hope you're on a 64-bit machine!

mallocForeignPtrBytes0 :: Int -> IO (ForeignPtr a)
mallocForeignPtrBytes0 len = do
fp <- mallocForeignPtrBytes len
withForeignPtr fp (\p -> fillBytes p 0 len)
pure fp

allocaPtr :: (Ptr x -> IO a) -> IO a
allocaPtr f = allocaBytes ptrSize (\p -> fillBytes p 0 ptrSize >> f p)

newtype Cb x = Cb {unCb :: ForeignPtr x}

cbWithPtr :: Cb x -> (FunPtr x -> IO a) -> IO a
cbWithPtr (Cb fp) f = withForeignPtr fp (f . castPtrToFunPtr)

cbMalloc :: (x -> IO (FunPtr x)) -> x -> IO (Cb x)
cbMalloc g x = do
y <- g x
fp <- FC.newForeignPtr (castFunPtrToPtr y) (freeHaskellFunPtr y)
pure (Cb fp)

takePtr :: Ptr (Ptr x) -> IO (ForeignPtr x)
takePtr pptr = do
ptr <- peek pptr
fp <- newForeignPtr finalizerFree ptr
poke pptr nullPtr
pure fp

type CErr = CInt

newtype Err = Err CErr
deriving stock (Eq, Ord, Show)

instance Exception Err

data F a where
FCont :: (forall r. (a -> IO r) -> IO r) -> F a

instance Functor F where
fmap f = \case
FCont k -> FCont (\g -> k (g . f))

newtype M a = M {unM :: ExceptT Err (FreeT F IO) a}
deriving newtype (Functor, Applicative, Monad, MonadFree F, MonadError Err, MonadIO)

runM :: M a -> IO (Either Err a)
runM (M m) = go (runExceptT m)
where
go n = do
o <- runFreeT n
case o of
Pure a -> pure a
Free fa -> case fa of
FCont k -> k go

useM :: (forall r. (a -> IO r) -> IO r) -> M a
useM k = liftF (FCont k)

guardM :: IO CErr -> M ()
guardM eact = do
e <- liftIO eact
when (e /= 0) (throwError (Err e))

textM :: (Ptr CString -> Ptr CSize -> IO CErr) -> M Text
textM f = do
sptr <- useM allocaPtr
lptr <- useM allocaPtr
guardM (f sptr lptr)
liftIO $ do
s <- peek sptr
l <- peek lptr
TF.fromPtr (coerce s) (fromIntegral l)

takeM :: (Ptr (Ptr x) -> IO CErr) -> M (ForeignPtr x)
takeM f = do
ptr <- useM allocaPtr
guardM (f (coerce ptr))
liftIO (takePtr ptr)

assocM :: (AssocPtr fp p) => fp -> M p
assocM fp = useM (withAssocPtr fp)
Loading

0 comments on commit 5457bbe

Please sign in to comment.