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