]> gitweb @ CieloNegro.org - EsounD.git/blob - Sound/EsounD/Player.hs
7f7eb223a88ddb2c593c7dfd695b58a2076c7f9d
[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 Network
23 import Prelude.Unicode
24 import Sound.EsounD.Streams
25 import Sound.EsounD.Internals
26 import System.IO
27 import System.IO.SaferFileHandles.Unsafe
28
29
30 -- ^ An opaque ESD handle for playing a stream.
31 data Player fr ch (r ∷ ★ → ★)
32     = Player {
33         plRate   ∷ !Int
34       -- THINKME: We really want to use RegionalFileHandle but we
35       -- can't, because safer-file-handles currently provides no ways
36       -- to wrap ordinary handles into safer handles.
37       , plHandle ∷ !Handle
38       , plCloseH ∷ !(FinalizerHandle r)
39       }
40
41 instance Dup (Player fr ch) where
42     dup pl = do ch' ← dup (plCloseH pl)
43                 return pl { plCloseH = ch' }
44
45 instance Frame fr ⇒ Writable (Player fr Mono) (L.Vector fr) where 
46     write pl v
47         = liftIO $ sanitizeIOError $ L.hPut (plHandle pl) v
48
49 instance Frame fr ⇒ Writable (Player fr Stereo) (L.Vector fr, L.Vector fr) where
50     write pl (l, r)
51         = liftIO $ sanitizeIOError $ L.hPut (plHandle pl) (interleave l r)
52
53 -- | Open an ESD handle for playing a stream.
54 openPlayer ∷ ∀fr ch s pr.
55                ( Frame fr
56                , Channels ch
57                , MonadIO pr
58                )
59            ⇒ Int            -- ^ sample rate for the stream.
60            → Maybe HostName -- ^ host to connect to.
61            → Maybe String   -- ^ name used to identify this stream to
62                              --   ESD (if any).
63            → RegionT s pr (Player fr ch (RegionT s pr))
64 openPlayer rate host name
65     = do h  ← liftIO openSocket
66          ch ← onExit $ sanitizeIOError $ closeSocket h
67          return Player {
68                       plRate   = rate
69                     , plHandle = h
70                     , plCloseH = ch
71                     }
72     where
73       fmt :: C'esd_format_t
74       fmt = frameFmt   ((⊥) ∷ fr) .|.
75             channelFmt ((⊥) ∷ ch) .|.
76             c'ESD_STREAM            .|.
77             c'ESD_PLAY
78
79       openSocket :: IO Handle
80       openSocket = withCStrOrNull host $ \hostPtr →
81                    withCStrOrNull name $ \namePtr →
82                        c'esd_play_stream
83                        fmt
84                        (fromIntegral rate)
85                        hostPtr
86                        namePtr
87                        ≫= wrapSocket
88                                ( "esd_play_stream("
89                                  ⧺ show fmt
90                                  ⧺ ", "
91                                  ⧺ show rate
92                                  ⧺ ", "
93                                  ⧺ show host
94                                  ⧺ ", "
95                                  ⧺ show name
96                                  ⧺ ") returned an error"
97                                )