-- | Type-safe bindings to EsounD with monadic regions. module Sound.EsounD ( Frame , Channels , Mono , Stereo , Player , openPlayer ) where import Bindings.EsounD import Control.Monad.IO.Class import Control.Monad.Trans.Region import Control.Monad.Trans.Region.OnExit import Control.Monad.Unicode import Data.Bits import Data.Int import Foreign.C.String import Network import Prelude.Unicode import Sound.EsounD.Internals import System.IO import System.IO.SaferFileHandles.Unsafe class Frame fr where frameFmt ∷ fr → C'esd_format_t instance Frame Int8 where frameFmt _ = c'ESD_BITS8 instance Frame Int16 where frameFmt _ = c'ESD_BITS16 class Channels ch where channelFmt ∷ ch → C'esd_format_t data Mono instance Channels Mono where channelFmt _ = c'ESD_MONO data Stereo instance Channels Stereo where channelFmt _ = c'ESD_STEREO -- ^ An ESD handle for playing a stream. data Player fr ch (r ∷ * → *) = Player { plRate ∷ !Int -- THINKME: We really want to use RegionalFileHandle but we -- can't, because safer-file-handles currently provides no ways -- to wrap ordinary handles. , plHandle ∷ !Handle , plCloseH ∷ !(CloseHandle r) } instance Dup (Player fr ch) where dup pl = do ch' ← dup (plCloseH pl) return pl { plCloseH = ch' } -- | Open an ESD handle for playing a stream. openPlayer ∷ ∀fr ch s pr. ( Frame fr , Channels ch , MonadIO pr ) ⇒ Int -- ^ sample rate for the stream. → HostName -- ^ host to connect to. → Maybe String -- ^ name used to identify this stream to -- ESD (if any). → RegionT s pr (Player fr ch (RegionT s pr)) openPlayer rate host name = do h ← liftIO openSocket ch ← onExit $ sanitizeIOError $ closeSocket h return Player { plRate = rate , plHandle = h , plCloseH = ch } where fmt :: C'esd_format_t fmt = frameFmt ((⊥) ∷ fr) .&. channelFmt ((⊥) ∷ ch) .&. c'ESD_STREAM .&. c'ESD_PLAY openSocket :: IO Handle openSocket = withCString host $ \hostPtr → withCStrOrNull name $ \namePtr → c'esd_play_stream fmt (fromIntegral rate) hostPtr namePtr ≫= wrapSocket "esd_play_stream() returned an error"