module Model where import qualified Data.Set as Set import Data.Maybe type LoopID = Int type StepID = Int type ChannelID = Int type Time = Int type TimeInterval = Int type Trigger = (LoopID,StepID,ChannelID) data Model = Model { m_channels :: [ChannelID], m_stepRange :: StepID, m_loopRange :: LoopID, m_triggers :: Set.Set Trigger, m_loop :: LoopID, m_stepTime :: TimeInterval, m_stepOffset :: TimeInterval, m_refreshTime :: TimeInterval, m_repaintOffset :: TimeInterval, -- transient stuff m_prevLoop :: Maybe (Time,LoopID) } deriving (Show) data Action = Repaint | FlipBuffer | Play ChannelID deriving (Show,Eq) nextEvent :: Time -> Model -> (Time,[Action]) nextEvent now m = (now',actions) where (now',actions) = fromJust $ chooseActions [ nextRepaint, nextRefresh, nextTriggers ] nextRepaint = next (m_refreshTime m) (m_repaintOffset m) (const [Repaint]) nextRefresh = next (m_refreshTime m) 0 (const [FlipBuffer]) nextTriggers = next (m_stepTime m) (m_stepOffset m) getTriggers getTriggers si = [Play cid | cid <- m_channels m, Set.member (m_loop m, si `mod`m_stepRange m,cid) (m_triggers m)] next :: TimeInterval -> TimeInterval -> (Int -> [Action]) -> (Time,[Action]) next period offset afn = (i * period + offset,afn i) where i = (now - offset) `div` period + 1 chooseActions :: [ (Time,[Action]) ] -> Maybe (Time,[Action]) chooseActions = foldr f Nothing where f ev Nothing = Just ev f ev1@(t1,a1) (Just ev2@(t2,a2)) | t1 < t2 = Just ev1 | t2 < t1 = Just ev2 | otherwise = Just (t1,a1++a2) defaultModel = Model { m_channels = [0..3], m_stepRange = 16, m_loopRange = 4, m_triggers = Set.fromList [], m_loop = 0, m_stepTime = 150, m_stepOffset = 0, m_refreshTime = 10, m_repaintOffset = (-5), m_prevLoop = Nothing } events :: Time -> Model -> [ (Time,[Action]) ] events t0 m = let (t,as) = nextEvent t0 m in (t,as):events t m loopTriggers :: LoopID -> Model -> [Trigger] loopTriggers l m = [(l,s,c) | s <- [0..m_stepRange m-1], c <- [0..channels-1]] where channels = length (m_channels m) updateTrigger :: (Bool -> Bool) -> Trigger -> Model -> Model updateTrigger ufn t m = if ufn (Set.member t ts) then m{m_triggers=Set.insert t ts} else m{m_triggers=Set.delete t ts} where ts = m_triggers m periodti :: Time -> Model -> TimeInterval periodti t m = (t - m_stepOffset m) `mod` period where period = m_stepTime m * m_stepRange m updateStepTime :: Time -> (TimeInterval->TimeInterval) -> Model -> Model updateStepTime t adj m = m{m_stepTime=newst,m_stepOffset=newoff} where newoff = t - ((t - m_stepOffset m) * newst `div` oldst) newst = adj oldst oldst = m_stepTime m