zwirn-0.2.2.0: src/zwirn-lang/Zwirn/Stream/UI.hs
module Zwirn.Stream.UI where
import Control.Concurrent (readMVar)
import Control.Concurrent.MVar (modifyMVar_, newMVar)
import qualified Data.Map as Map
import Data.Text (pack)
import qualified Data.Text as T
import qualified Sound.Osc.Transport.Fd.Udp as O
import Sound.Tidal.Clock
import qualified Sound.Tidal.Clock as Clock
import Zwirn.Core.Lib.Core (zipApply)
import Zwirn.Core.Lib.Modulate (shift, slow)
import Zwirn.Core.Lib.Number (firstCyclesThen)
import Zwirn.Core.Lib.Structure (segment)
import qualified Zwirn.Core.Time as Zwirn
import Zwirn.Core.Types (silence, toList, unzwirn, value)
import Zwirn.Language.Evaluate.Expression
import Zwirn.Stream.Process
import Zwirn.Stream.Target
import Zwirn.Stream.Types
streamDefaultBPM :: Double
streamDefaultBPM = 138
streamReplace :: Stream -> Targeted Identifier -> Zwirn Expression -> IO ()
streamReplace _ (Targeted _ (TextID "_all")) _ = return ()
streamReplace _ (Targeted _ (TextID "_none")) _ = return ()
streamReplace str (Targeted ts key) p = modifyMVar_ (sPlayMap str) (return . Map.alter alterFunc key)
where
newTargs = if null ts then [sDefaultTarget str] else ts
filterTargs = filter (\t -> t `elem` Map.keys (sTargetMap str)) newTargs
alterFunc Nothing = if null filterTargs then Nothing else Just (Targeted filterTargs (Normal, p, Nothing))
alterFunc (Just (Targeted _ (_, _, fx))) = if null filterTargs then Nothing else Just (Targeted filterTargs (Normal, p, fx))
streamReplaceBus :: Stream -> Targeted Int -> Zwirn Expression -> IO ()
streamReplaceBus str (Targeted ts key) p = modifyMVar_ (sBusMap str) (return . Map.insert key (Targeted filterTargs $ segment (pure 128) p))
where
newTargs = if null ts then [sDefaultTarget str] else ts
filterTargs = filter (\t -> t `elem` Map.keys (sTargetMap str)) newTargs
streamReplaceAction :: Stream -> Identifier -> Zwirn Expression -> IO ()
streamReplaceAction _ (TextID "_all") _ = return ()
streamReplaceAction _ (TextID "_none") _ = return ()
streamReplaceAction str key p = modifyMVar_ (sActionMap str) (return . Map.insert key p)
streamHush :: Stream -> IO ()
streamHush str = do
modifyMVar_ (sPlayMap str) (return . const Map.empty)
modifyMVar_ (sActionMap str) (return . const Map.empty)
modifyMVar_ (sBusMap str) (return . const Map.empty)
streamSet :: Stream -> T.Text -> Expression -> IO ()
streamSet str x ex = modifyMVar_ (sState str) (return . Map.insert x ex)
streamSetFx :: Stream -> Identifier -> Zwirn (Zwirn Expression -> Zwirn Expression) -> IO ()
streamSetFx str (TextID "_all") fx = modifyMVar_ (sPlayMap str) (return . fmap (fmap (\(st, p, _) -> (st, p, Just fx))))
streamSetFx str (TextID "_none") _ = modifyMVar_ (sPlayMap str) (return . fmap (fmap (\(st, p, _) -> (st, p, Nothing))))
streamSetFx str key fx = modifyMVar_ (sPlayMap str) (return . Map.update (\(Targeted ts (st, p, _)) -> Just $ Targeted ts (st, p, Just fx)) key)
streamGet :: Stream -> T.Text -> IO Expression
streamGet str key = do
sm <- readMVar (sState str)
return $ Map.findWithDefault (EZwirn silence) key sm
streamToggle :: Stream -> Identifier -> IO ()
streamToggle str key = case key of
(TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))
_ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)
where
toggle (Mute, p, fx) = (Normal, p, fx)
toggle (_, p, fx) = (Mute, p, fx)
streamMute :: Stream -> Identifier -> IO ()
streamMute str key = case key of
TextID "_all" -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))
TextID "_none" -> streamUnmute str (TextID "_all")
_ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)
where
toggle (Normal, p, fx) = (Mute, p, fx)
toggle (Solo, p, fx) = (Mute, p, fx)
toggle x = x
streamUnmute :: Stream -> Identifier -> IO ()
streamUnmute str key = case key of
TextID "_all" -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))
TextID "_none" -> streamMute str (TextID "_all")
_ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)
where
toggle (Mute, p, fx) = (Normal, p, fx)
toggle x = x
streamSolo :: Stream -> Identifier -> IO ()
streamSolo str key = case key of
(TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))
(TextID "_none") -> streamUnsolo str (TextID "_all")
_ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)
where
toggle (Normal, p, fx) = (Solo, p, fx)
toggle (Mute, p, fx) = (Solo, p, fx)
toggle x = x
streamUnsolo :: Stream -> Identifier -> IO ()
streamUnsolo str key = case key of
(TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))
(TextID "_none") -> streamSolo str (TextID "_all")
_ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)
where
toggle (Solo, p, fx) = (Normal, p, fx)
toggle x = x
streamToggleSolo :: Stream -> Identifier -> IO ()
streamToggleSolo str key = case key of
(TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))
_ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)
where
toggle (Solo, p, fx) = (Normal, p, fx)
toggle (_, p, fx) = (Solo, p, fx)
streamSetCPS :: Stream -> Time -> IO ()
streamSetCPS str c = streamSetBPM str (c * toRational (cBeatsPerCycle (streamConfigClock $ sConfig str) * 60))
-- | set the bpm in the clock and update the tempo variable
streamSetBPM :: Stream -> Time -> IO ()
streamSetBPM s t = streamSet s "tempo" (EZwirn $ pure $ ENum $ realToFrac t) >> Clock.setBPM (sClockRef s) t
-- | read the tempo variable
streamGetBPM :: Stream -> IO Double
streamGetBPM str = do
st <- readMVar (sState str)
(EZwirn zt) <- streamGet str "tempo"
let vs = toList $ unzwirn zt 0 st
case vs of
[] -> return streamDefaultBPM
(v : _) -> case value $ fst v of
ENum x -> return x
_ -> return streamDefaultBPM
streamResetCycles :: Stream -> IO ()
streamResetCycles s = streamSetCycle s 0
streamSetCycle :: Stream -> Time -> IO ()
streamSetCycle s = Clock.setClock (sClockRef s)
streamEnableLink :: Stream -> IO ()
streamEnableLink s = Clock.enableLink (sClockRef s)
streamDisableLink :: Stream -> IO ()
streamDisableLink s = Clock.disableLink (sClockRef s)
streamGetNow :: Stream -> IO Time
streamGetNow s = Clock.getCycleTime (streamConfigClock $ sConfig s) (sClockRef s)
streamFirst :: Stream -> Zwirn Expression -> IO ()
streamFirst str = streamFirstTarget str (sDefaultTarget str)
streamFirstTarget :: Stream -> TargetName -> Zwirn Expression -> IO ()
streamFirstTarget str targ z = do
dummy <- newMVar $ Map.singleton (TextID $ pack "_streamOnceDummy_") (Targeted [targ] (Normal, z, Nothing))
Clock.clockOnce (tickAction dummy (sActionMap str) (sBusMap str) (sState str) (sBusses str) (sTargetMap str) (sLocal str) (streamConfigPrecision $ sConfig str)) (streamConfigClock $ sConfig str) (sClockRef str)
startStream :: StreamConfig -> IO Stream
startStream config = do
let targetConfigs = streamConfigTargets config
conf = (streamConfigClock config) {cFrameTimespan = 10 * realToFrac (streamConfigPrecision config)}
targetMap <- getTargetMap targetConfigs
local <- O.udp_server (streamConfigLocalPort config)
zMV <- newMVar Map.empty
stMV <- newMVar Map.empty
busMapMV <- newMVar Map.empty
actionMapMV <- newMVar Map.empty
bussesMV <- newMVar []
cref <- clocked conf (tickAction zMV actionMapMV busMapMV stMV bussesMV targetMap local (streamConfigPrecision config))
let str = Stream zMV actionMapMV busMapMV stMV bussesMV targetMap "superdirt" local cref config
-- set the default bpm
streamSetBPM str (realToFrac streamDefaultBPM)
return str
evalAction :: Stream -> Zwirn Expression -> IO ()
evalAction str z = do
st <- readMVar (sState str)
let exps = toList $ unzwirn z 0 st
sts = map snd exps
exs = map (value . fst) exps
updateState (sState str) sts
mapM_ evalActionExp exs
where
evalActionExp (EAction i) = i
evalActionExp _ = return ()
transition :: Stream -> Identifier -> (Zwirn Double -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> IO ()
transition str key mapper = do
now <- realToFrac <$> streamGetNow str
modifyMVar_ (sPlayMap str) (return . Map.update (\(Targeted ts (ps, ex, fx)) -> Just $ Targeted ts (ps, mapper (pure now) `zipApply` ex, fx)) key)
transition' :: Stream -> Identifier -> Zwirn Zwirn.Time -> Zwirn Expression -> Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> IO ()
transition' str key dur def sig mapper = do
now <- realToFrac <$> streamGetNow str
let shifted = shift (pure now) $ firstCyclesThen dur def (slow dur sig)
modifyMVar_ (sPlayMap str) (return . Map.update (\(Targeted ts (ps, ex, fx)) -> Just $ Targeted ts (ps, mapper `zipApply` ex `zipApply` shifted, fx)) key)