11module Sound.Tidal.Stream.Target where
22
3- import Control.Concurrent (forkOS , threadDelay )
4- import Data.Maybe (fromJust , isJust )
3+ import Control.Concurrent
4+ ( forkIO ,
5+ forkOS ,
6+ newMVar ,
7+ readMVar ,
8+ swapMVar ,
9+ threadDelay ,
10+ )
11+ import Control.Monad (when )
12+ import Data.Maybe (catMaybes , fromJust , isJust )
513import Foreign (Word8 )
614import qualified Network.Socket as N
715import qualified Sound.Osc.Fd as O
16+ import qualified Sound.Osc.Time.Timeout as O
817import qualified Sound.Osc.Transport.Fd.Udp as O
918import Sound.Tidal.Pattern
1019import Sound.Tidal.Stream.Config
@@ -32,43 +41,79 @@ getCXs :: Config -> [(Target, [OSC])] -> IO [Cx]
3241getCXs config oscmap =
3342 mapM
3443 ( \ (target, os) -> do
35- remote_addr <- resolve (oAddress target) (show $ oPort target)
36- remote_bus_addr <-
37- if isJust $ oBusPort target
38- then Just <$> resolve (oAddress target) (show $ fromJust $ oBusPort target)
39- else return Nothing
44+ remote_addr <- resolve (oAddress target) (oPort target)
45+ remote_bus_addr <- mapM (resolve (oAddress target)) (oBusPort target)
46+ remote_busses <- sequence (oBusPort target >> Just (newMVar [] ))
47+
4048 let broadcast = if cCtrlBroadcast config then 1 else 0
4149 u <-
4250 O. udp_socket
43- ( \ sock sockaddr -> do
44- N. setSocketOption sock N. Broadcast broadcast
45- N. connect sock sockaddr
46- )
51+ (\ sock _ -> do N. setSocketOption sock N. Broadcast broadcast)
4752 (oAddress target)
4853 (oPort target)
49- return $ Cx {cxUDP = u, cxAddr = remote_addr, cxBusAddr = remote_bus_addr, cxTarget = target, cxOSCs = os}
54+ let cx = Cx {cxUDP = u, cxAddr = remote_addr, cxBusAddr = remote_bus_addr, cxBusses = remote_busses, cxTarget = target, cxOSCs = os}
55+ _ <- forkIO $ handshake cx config
56+ return cx
5057 )
5158 oscmap
5259
53- resolve :: String -> String -> IO N. AddrInfo
60+ resolve :: String -> Int -> IO N. AddrInfo
5461resolve host port = do
5562 let hints = N. defaultHints {N. addrSocketType = N. Stream }
56- addr : _ <- N. getAddrInfo (Just hints) (Just host) (Just port)
63+ addr : _ <- N. getAddrInfo (Just hints) (Just host) (Just $ show port)
5764 return addr
5865
66+ handshake :: Cx -> Config -> IO ()
67+ handshake Cx {cxUDP = udp, cxBusses = Just bussesMV, cxAddr = addr} c = sendHandshake >> listen 0
68+ where
69+ sendHandshake :: IO ()
70+ sendHandshake = O. sendTo udp (O. Packet_Message $ O. Message " /dirt/handshake" [] ) (N. addrAddress addr)
71+ listen :: Int -> IO ()
72+ listen waits = do
73+ ms <- recvMessagesTimeout 2 udp
74+ if null ms
75+ then do
76+ checkHandshake waits -- there was a timeout, check handshake
77+ listen (waits + 1 )
78+ else do
79+ mapM_ respond ms
80+ listen 0
81+ checkHandshake :: Int -> IO ()
82+ checkHandshake waits = do
83+ busses <- readMVar bussesMV
84+ when (null busses) $ do
85+ when (waits == 0 ) $ verbose c $ " Waiting for SuperDirt (v.1.7.2 or higher).."
86+ sendHandshake
87+ respond :: O. Message -> IO ()
88+ respond (O. Message " /dirt/hello" _) = sendHandshake
89+ respond (O. Message " /dirt/handshake/reply" xs) = do
90+ prev <- swapMVar bussesMV $ bufferIndices xs
91+ -- Only report the first time..
92+ when (null prev) $ verbose c $ " Connected to SuperDirt."
93+ respond _ = return ()
94+ bufferIndices :: [O. Datum ] -> [Int ]
95+ bufferIndices [] = []
96+ bufferIndices (x : xs')
97+ | x == O. AsciiString (O. ascii " &controlBusIndices" ) = catMaybes $ takeWhile isJust $ map O. datum_integral xs'
98+ | otherwise = bufferIndices xs'
99+ handshake _ _ = return ()
100+
101+ recvMessagesTimeout :: (O. Transport t ) => Double -> t -> IO [O. Message ]
102+ recvMessagesTimeout n sock = fmap (maybe [] O. packetMessages) $ O. recvPacketTimeout n sock
103+
59104-- send has three modes:
60105-- Send events early using timestamp in the OSC bundle - used by Superdirt
61106-- Send events early by adding timestamp to the OSC message - used by Dirt
62107-- Send events live by delaying the thread
63- send :: Maybe O. Udp -> Cx -> Double -> Double -> (Double , Bool , O. Message ) -> IO ()
64- send listen cx latency extraLatency (time, isBusMsg, m)
65- | oSchedule target == Pre BundleStamp = sendBndl isBusMsg listen cx $ O. Bundle timeWithLatency [m]
66- | oSchedule target == Pre MessageStamp = sendO isBusMsg listen cx $ addtime m
108+ send :: Cx -> Double -> Double -> (Double , Bool , O. Message ) -> IO ()
109+ send cx latency extraLatency (time, isBusMsg, m)
110+ | oSchedule target == Pre BundleStamp = sendBndl isBusMsg cx $ O. Bundle timeWithLatency [m]
111+ | oSchedule target == Pre MessageStamp = sendO isBusMsg cx $ addtime m
67112 | otherwise = do
68113 _ <- forkOS $ do
69114 now <- O. time
70115 threadDelay $ floor $ (timeWithLatency - now) * 1000000
71- sendO isBusMsg listen cx m
116+ sendO isBusMsg cx m
72117 return ()
73118 where
74119 addtime (O. Message mpath params) = O. Message mpath ((O. int32 sec) : ((O. int32 usec) : params))
@@ -80,21 +125,19 @@ send listen cx latency extraLatency (time, isBusMsg, m)
80125 target = cxTarget cx
81126 timeWithLatency = time - latency + extraLatency
82127
83- sendBndl :: Bool -> ( Maybe O. Udp ) -> Cx -> O. Bundle -> IO ()
84- sendBndl isBusMsg ( Just listen) cx bndl = O. sendTo listen (O. Packet_Bundle bndl) (N. addrAddress addr)
128+ sendBndl :: Bool -> Cx -> O. Bundle -> IO ()
129+ sendBndl isBusMsg cx bndl = O. sendTo (cxUDP cx) (O. Packet_Bundle bndl) (N. addrAddress addr)
85130 where
86131 addr
87132 | isBusMsg && isJust (cxBusAddr cx) = fromJust $ cxBusAddr cx
88133 | otherwise = cxAddr cx
89- sendBndl _ Nothing cx bndl = O. sendBundle (cxUDP cx) bndl
90134
91- sendO :: Bool -> ( Maybe O. Udp ) -> Cx -> O. Message -> IO ()
92- sendO isBusMsg ( Just listen) cx msg = O. sendTo listen (O. Packet_Message msg) (N. addrAddress addr)
135+ sendO :: Bool -> Cx -> O. Message -> IO ()
136+ sendO isBusMsg cx msg = O. sendTo (cxUDP cx) (O. Packet_Message msg) (N. addrAddress addr)
93137 where
94138 addr
95139 | isBusMsg && isJust (cxBusAddr cx) = fromJust $ cxBusAddr cx
96140 | otherwise = cxAddr cx
97- sendO _ Nothing cx msg = O. sendMessage (cxUDP cx) msg
98141
99142superdirtTarget :: Target
100143superdirtTarget =
0 commit comments