module Sound.Tidal.MIDI.Control where import qualified Sound.Tidal.Stream as S import Sound.Tidal.Params import Sound.Tidal.MIDI.Params type RangeMapFunc = (Int, Int) -> Double -> Int data ControlChange = CC { param :: S.Param, midi :: Int, range :: (Int, Int), vdefault :: Double, scalef :: RangeMapFunc } | NRPN { param :: S.Param, midi :: Int, range :: (Int, Int), vdefault :: Double, scalef :: RangeMapFunc } | SysEx { param :: S.Param, midi :: Int, range :: (Int, Int), vdefault :: Double, scalef :: RangeMapFunc } data ControllerShape = ControllerShape { controls :: [ControlChange], latency :: Double } toShape :: ControllerShape -> S.Shape toShape cs = let params = [dur_p, note_p, velocity_p] ++ params' params' = [param p | p <- (controls cs)] in S.Shape { S.params = params, S.cpsStamp = False, S.latency = latency cs } passThru :: (Int, Int) -> Double -> Int passThru (_, _) = floor -- no sanitizing of rangeā€¦ mapRange :: (Int, Int) -> Double -> Int mapRange (low, high) = floor . (+ (fromIntegral low)) . (* ratio) where ratio = fromIntegral $ high - low mCC :: S.Param -> Int -> ControlChange mCC p m = CC {param=p, midi=m, range=(0, 127), vdefault=0, scalef=mapRange } mNRPN :: S.Param -> Int -> ControlChange mNRPN p m = NRPN {param=p, midi=m, range=(0, 127), vdefault=0, scalef=mapRange } mrNRPN :: S.Param -> Int -> (Int, Int) -> Double -> ControlChange mrNRPN p m r d = NRPN {param=p, midi=m, range=r, vdefault=d, scalef=mapRange } toParams :: ControllerShape -> [S.Param] toParams shape = map param (controls shape) ctrlN :: Num b => ControllerShape -> S.Param -> Maybe b ctrlN shape x = fmap fromIntegral $ fmap midi (paramN shape x) paramN :: ControllerShape -> S.Param -> Maybe ControlChange paramN shape x | x `elem` names = paramX $ matching p | otherwise = Nothing -- error $ "No such Controller param: " ++ show x where names = toParams shape paramX [] = Nothing paramX (h:_) = Just h matching = filter ((== x) . param) p = controls shape