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