-
Notifications
You must be signed in to change notification settings - Fork 1
/
Stream.hs
169 lines (136 loc) · 5.66 KB
/
Stream.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
{-# LANGUAGE OverloadedStrings, FlexibleInstances, RankNTypes, NoMonomorphismRestriction #-}
module Stream where
import Data.Maybe
import Sound.OSC.FD
import Sound.OpenSoundControl
import Control.Applicative
import Network.Netclock.Client
import Control.Concurrent
import Control.Concurrent.MVar
import Pattern
import Data.Ratio
--import Control.Exception
import Parse
import qualified Data.Map as Map
data Param = S {name :: String, sDefault :: Maybe String}
| F {name :: String, fDefault :: Maybe Double}
| I {name :: String, iDefault :: Maybe Int}
instance Eq Param where
a == b = name a == name b
instance Ord Param where
compare a b = compare (name a) (name b)
instance Show Param where
show p = name p
data OscShape = OscShape {path :: String,
params :: [Param],
timestamp :: Bool
}
type OscMap = Map.Map Param (Maybe Datum)
type OscSequence = Sequence OscMap
type OscSignal = Signal OscMap
defaultDatum :: Param -> Maybe Datum
defaultDatum (S _ (Just x)) = Just $ String x
defaultDatum (I _ (Just x)) = Just $ Int x
defaultDatum (F _ (Just x)) = Just $ Float x
defaultDatum _ = Nothing
hasDefault :: Param -> Bool
hasDefault (S _ Nothing) = False
hasDefault (I _ Nothing) = False
hasDefault (F _ Nothing) = False
hasDefault _ = True
defaulted :: OscShape -> [Param]
defaulted = filter hasDefault . params
defaultMap :: OscShape -> OscMap
defaultMap s
= Map.fromList $ map (\x -> (x, defaultDatum x)) (defaulted s)
required :: OscShape -> [Param]
required = filter (not . hasDefault) . params
hasRequired :: OscShape -> OscMap -> Bool
hasRequired s m = isSubset (required s) (Map.keys (Map.filter (\x -> x /= Nothing) m))
isSubset :: (Eq a) => [a] -> [a] -> Bool
isSubset xs ys = all (\x -> elem x ys) xs
tpb = 1
toMessage :: OscShape -> BpsChange -> Int -> (Double, OscMap) -> Maybe Bundle
toMessage s change ticks (o, m) =
do m' <- applyShape' s m
let beat = fromIntegral ticks / fromIntegral tpb
latency = 0.019
logicalNow = (logicalTime change beat)
beat' = (fromIntegral ticks + 1) / fromIntegral tpb
logicalPeriod = (logicalTime change (beat + 1)) - logicalNow
logicalOnset = ntpr_to_ut $ logicalNow + (logicalPeriod * o) + latency
sec = floor logicalOnset
usec = floor $ 1000000 * (logicalOnset - (fromIntegral sec))
oscdata = catMaybes $ mapMaybe (\x -> Map.lookup x m') (params s)
oscdata' = ((Int sec):(Int usec):oscdata)
osc | timestamp s = Bundle (immediately) [Message (path s) oscdata']
| otherwise = Bundle (immediately) [Message (path s) oscdata]
return osc
applyShape' :: OscShape -> OscMap -> Maybe OscMap
applyShape' s m | hasRequired s m = Just $ Map.union m (defaultMap s)
| otherwise = Nothing
start :: String -> String -> String -> String -> Int -> OscShape -> IO (MVar (OscSequence))
start client server name address port shape
= do patternM <- newMVar silence
putStrLn $ "connecting " ++ (show address) ++ ":" ++ (show port)
s <- openUDP address port
putStrLn $ "connected "
let ot = (onTick s shape patternM) :: BpsChange -> Int -> IO ()
forkIO $ clocked name client server 1 ot
return patternM
stream :: String -> String -> String -> String -> Int -> OscShape -> IO (OscSequence -> IO ())
stream client server name address port shape
= do patternM <- start client server name address port shape
return $ \p -> do swapMVar patternM p
return ()
streamcallback :: (OscSequence -> IO ()) -> String -> String -> String -> String -> Int -> OscShape -> IO (OscSequence -> IO ())
streamcallback callback client server name address port shape
= do f <- stream client server name address port shape
let f' p = do callback p
f p
return f'
onTick :: UDP -> OscShape -> MVar (OscSequence) -> BpsChange -> Int -> IO ()
onTick s shape patternM change ticks
= do p <- readMVar patternM
let tpb' = 2 :: Integer
ticks' = (fromIntegral ticks) :: Integer
a = ticks' % tpb'
--a = (ticks' `mod` tpb') % tpb'
b = 1 % tpb'
messages = mapMaybe
(toMessage shape change ticks)
(seqToRelOnsets (a, Just b) p)
--putStrLn $ (show a) ++ ", " ++ (show b)
--putStrLn $ "tick " ++ show ticks ++ " = " ++ show messages
catch (mapM_ (sendOSC s) messages) (\msg -> putStrLn $ "oops " ++ show msg)
return ()
ticker :: IO (MVar Rational)
ticker = do mv <- newMVar 0
forkIO $ clocked "ticker" "127.0.0.1" "127.0.0.1" tpb (f mv)
return mv
where f mv change ticks = do swapMVar mv ((fromIntegral ticks) / (fromIntegral tpb))
return ()
tpb = 32
make :: ParseablePattern p => (a -> Datum) -> OscShape -> String -> p a -> (p OscMap)
make toOsc s nm p = fmap (\x -> Map.singleton nParam (defaultV x)) p
where nParam = param s nm
defaultV a = Just $ toOsc a
--defaultV Nothing = defaultDatum nParam
makeS = make String
makeF = make Float
makeI = make Int
param :: OscShape -> String -> Param
param shape n = head $ filter (\x -> name x == n) (params shape)
-- Merge any pattern with a sequence
merge :: ParseablePattern p => OscSequence -> p OscMap -> OscSequence
merge x y = Map.union <$> x <~> y
merge' :: ParseablePattern p => OscSequence -> p OscMap -> OscSequence
merge' x y = Map.union <$> x <~> y
infixr 1 ~~
(~~) = merge
infixr 1 |+|
(|+|) :: OscSequence -> OscSequence -> OscSequence
(|+|) = (~~)
infixr 1 |+~
(|+~) :: OscSequence -> OscSignal -> OscSequence
(|+~) = (~~)