1 {-# LANGUAGE Arrows #-}
3 module RMCA.Layer.Board ( boardRun
7 import qualified Data.IntMap as M
8 import Data.List ((\\))
10 import RMCA.Global.Clock
11 import RMCA.Layer.LayerConf
14 data SwitchBoard = StartBoard StaticLayerConf
18 updatePhOnSwitch :: Board -> [PlayHead] -> SwitchBoard -> [PlayHead]
19 updatePhOnSwitch _ _ (StopBoard {}) = []
20 updatePhOnSwitch board _ (StartBoard {}) = startHeads board
21 updatePhOnSwitch board oldPhs (ContinueBoard {}) = oldPhs ++ startHeads board
23 noStopBoard :: Event SwitchBoard -> Event SwitchBoard
24 noStopBoard (Event (StopBoard {})) = NoEvent
28 genPlayHeads :: Board -> SwitchBoard -> [PlayHead]
29 genPlayHeads _ (StopBoard {}) = []
30 genPlayHeads board _ = startHeads board
33 continueBoard :: Event SwitchBoard -> Event [PlayHead]
34 continueBoard board (Event (ContinueBoard {})) = Event $ startHeads board
35 continueBoard _ _ = NoEvent
37 startBoard :: Event SwitchBoard -> Event StaticLayerConf
38 startBoard (Event (StartBoard st)) = Event st
39 startBoard _ = NoEvent
41 stopBoard :: Event SwitchBoard -> Event SwitchBoard
42 stopBoard e@(Event StopBoard) = e
45 -- singleboard is a simple running board. Given an initial list of
46 -- play heads, it runs the board by the beat. It produces events but
47 -- also a constant output of the states of the play heads to allow for
49 singleBoard :: [PlayHead]
50 -> SF (Board,DynLayerConf,Event BeatNo)
51 (Event [Note], [PlayHead])
52 singleBoard iPh = proc (board, DynLayerConf { relPitch = rp
55 (phs,notes) <- accumHoldBy advanceHeads' (iPh,[])
56 -< ebno `tag` (board, fromEvent ebno, rp, s)
57 returnA -< (ebno `tag` notes, phs)
58 where advanceHeads' (ph,_) (board,bno,rp,s) = advanceHeads board bno rp s ph
60 -- dynSingleBoard differs from singleBoard in that it receives a
61 -- SwitchBoard event allowing it to start/stop the board.
62 dynSingleBoard :: SF (Board, DynLayerConf, Event BeatNo, Event SwitchBoard)
63 (Event [Note], [PlayHead])
64 dynSingleBoard = proc (board, dynConf, ebno, esb) -> do
66 res@(_,curPhs) <- rSwitch $ singleBoard []
67 -< ( (board, dynConf, ebno)
68 , fmap (singleBoard . updatePhOnSwitch board curPhs') esb)
69 curPhs' <- iPre [] -< curPhs
72 boardSF :: StaticLayerConf
73 -> SF (Event AbsBeat, Board, DynLayerConf, Event SwitchBoard)
74 (Event [Note], [PlayHead])
75 boardSF (StaticLayerConf { beatsPerBar = bpb }) =
76 proc (eabs, board, dynConf, esb) -> do
77 ebno <- rSwitch never -< ( (eabs,dynConf)
78 , layerMetronome <$> startBoard esb)
79 dynSingleBoard -< (board,dynConf,ebno,esb)
81 ----------------------------------------------------------------------------
82 -- Machinery to make boards run in parallel
83 ----------------------------------------------------------------------------
85 boardRun' :: M.IntMap (SF (Event AbsBeat,Board,DynLayerConf,Event SwitchBoard)
86 (Event [Note], [PlayHead]))
87 -> SF (Event AbsBeat, M.IntMap (Board,DynLayerConf,Event SwitchBoard))
88 (M.IntMap (Event [Note], [PlayHead]))
89 boardRun' iSF = boardRun'' iSF (lengthChange iSF)
90 where boardRun'' iSF swSF = pSwitch routeBoard iSF swSF contSwitch
91 contSwitch contSig (oldSig, newSig) = boardRun'' newSF
92 (lengthChange newSF >>> notYet)
93 where defaultBoardSF = boardSF defaultStaticLayerConf
94 newSF = foldr (\k m -> M.insert k defaultBoardSF m)
95 (foldr M.delete contSig oldSig) newSig
96 lengthChange iSig = edgeBy diffSig ik <<^ M.keys <<^ (\(_,x) -> x) <<^ fst
97 where ik = M.keys iSig
98 -- Old elements removed in nL are on the left, new elements added to
99 -- nL are on the right.
100 diffSig :: [Int] -> [Int] -> Maybe ([Int],[Int])
103 | otherwise = Just (oL \\ nL, nL \\ oL)
104 routeBoard :: (Event AbsBeat,M.IntMap (Board,DynLayerConf,Event SwitchBoard))
106 -> M.IntMap ((Event AbsBeat,Board,DynLayerConf,Event SwitchBoard),sf)
107 routeBoard (evs,map) sfs =
108 M.intersectionWith (,) ((\(b,l,ebs) -> (evs,b,l,ebs)) <$> map) sfs
110 boardRun :: M.IntMap StaticLayerConf
111 -> SF (Tempo, M.IntMap (Board,DynLayerConf,Event SwitchBoard))
112 (M.IntMap (Event [Note], [PlayHead]))
113 boardRun iMap = mkBeat >>> (boardRun' $ fmap boardSF iMap)
114 where mkBeat = proc (t,map) -> do
115 esb <- arr (foldr selEvent NoEvent) <<^ fmap (\(_,_,e) -> e) -< map
116 eab <- rSwitch never -< (t, lMerge (stopBoard esb `tag` never)
117 (startBoard esb `tag` metronome))
119 selEvent x NoEvent = x
120 selEvent e@(Event (StopBoard {})) _ = e
121 selEvent (Event (StartBoard {})) f@(Event (StopBoard {})) = f