]> gitweb @ CieloNegro.org - EsounD.git/blob - Sound/EsounD.hs
82bd72afeb3d22cbe7ed2b9155c48fae9b096b54
[EsounD.git] / Sound / EsounD.hs
1 -- | Type-safe bindings to EsounD with monadic regions.
2 module Sound.EsounD
3     ( Frame
4
5     , Channels
6     , Mono
7     , Stereo
8
9     , Player
10     , openPlayer
11     )
12     where
13
14 import Bindings.EsounD
15 import Control.Monad.IO.Class
16 import Control.Monad.Trans.Region
17 import Control.Monad.Trans.Region.OnExit
18 import Control.Monad.Unicode
19 import Data.Bits
20 import Data.Int
21 import Foreign.C.String
22 import Network
23 import Prelude.Unicode
24 import Sound.EsounD.Internals
25 import System.IO
26 import System.IO.SaferFileHandles.Unsafe
27
28 class Frame fr where
29     frameFmt ∷ fr → C'esd_format_t
30
31 instance Frame Int8 where
32     frameFmt _ = c'ESD_BITS8
33
34 instance Frame Int16 where
35     frameFmt _ = c'ESD_BITS16
36
37 class Channels ch where
38     channelFmt ∷ ch → C'esd_format_t
39
40 data Mono
41 instance Channels Mono where
42     channelFmt _ = c'ESD_MONO
43
44 data Stereo
45 instance Channels Stereo where
46     channelFmt _ = c'ESD_STEREO
47
48
49 -- ^ An ESD handle for playing a stream.
50 data Player fr ch (r ∷ * → *)
51     = Player {
52         plRate   ∷ !Int
53       -- THINKME: We really want to use RegionalFileHandle but we
54       -- can't, because safer-file-handles currently provides no ways
55       -- to wrap ordinary handles.
56       , plHandle ∷ !Handle
57       , plCloseH ∷ !(CloseHandle r)
58       }
59
60 instance Dup (Player fr ch) where
61     dup pl = do ch' ← dup (plCloseH pl)
62                 return pl { plCloseH = ch' }
63
64 -- | Open an ESD handle for playing a stream.
65 openPlayer ∷ ∀fr ch s pr.
66                ( Frame fr
67                , Channels ch
68                , MonadIO pr
69                )
70            ⇒ Int          -- ^ sample rate for the stream.
71            → HostName     -- ^ host to connect to.
72            → Maybe String -- ^ name used to identify this stream to
73                            --   ESD (if any).
74            → RegionT s pr (Player fr ch (RegionT s pr))
75 openPlayer rate host name
76     = do h  ← liftIO openSocket
77          ch ← onExit $ sanitizeIOError $ closeSocket h
78          return Player {
79                       plRate   = rate
80                     , plHandle = h
81                     , plCloseH = ch
82                     }
83     where
84       fmt :: C'esd_format_t
85       fmt = frameFmt   ((⊥) ∷ fr) .&.
86             channelFmt ((⊥) ∷ ch) .&.
87             c'ESD_STREAM            .&.
88             c'ESD_PLAY
89
90       openSocket :: IO Handle
91       openSocket = withCString    host $ \hostPtr →
92                    withCStrOrNull name $ \namePtr →
93                        c'esd_play_stream
94                        fmt
95                        (fromIntegral rate)
96                        hostPtr
97                        namePtr
98                        ≫= wrapSocket "esd_play_stream() returned an error"