module Reactive.Banana.MIDI.KeySet where
import qualified Reactive.Banana.MIDI.Note as Note
import qualified Data.Traversable as Trav
import qualified Data.Accessor.Monad.Trans.State as AccState
import qualified Data.Accessor.Basic as Acc
import qualified Control.Monad.Trans.State as MS
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Maybe.HT (toMaybe, )
import Data.Maybe (maybeToList, listToMaybe, )
class C set where
reset :: MS.State (set key value) [Note.Boundary key value]
resetSome :: Ord key => (key -> Bool) -> MS.State (set key value) [Note.Boundary key value]
size :: set key value -> Int
toList :: set key value -> [(key, value)]
index :: Ord key => Int -> set key value -> Maybe (key, value)
change :: Ord key => Note.Boundary key value -> MS.State (set key value) [Note.Boundary key value]
changeExt ::
(Ord key, C set) =>
Note.BoundaryExt key value ->
MS.State (set key value) [Note.Boundary key value]
changeExt e =
case e of
Note.BoundaryExt bnd -> change bnd
Note.AllOff p -> resetSome p
class Map set where
accessMap :: Acc.T (set key value) (Map.Map key value)
newtype Pressed key value = Pressed {deconsPressed :: Map.Map key value}
deriving (Show)
pressed :: Pressed key value
pressed = Pressed Map.empty
instance Map Pressed where
accessMap = Acc.fromWrapper Pressed deconsPressed
instance C Pressed where
reset = releasePlayedKeys
resetSome = releaseSomeKeys
size = sizeGen
toList = toListGen
index = indexGen
change bnd@(Note.Boundary key vel on) = do
AccState.modify accessMap $
if on
then Map.insert key vel
else Map.delete key
return [bnd]
newtype Latch key value = Latch {deconsLatch :: Map.Map key value}
deriving (Show)
latch :: Latch key value
latch = Latch Map.empty
instance Map Latch where
accessMap = Acc.fromWrapper Latch deconsLatch
latchChange ::
Ord key =>
Note.Boundary key value ->
MS.State (Latch key value) (Maybe (Note.Boundary key value))
latchChange (Note.Boundary key vel on) =
Trav.sequence $ toMaybe on $ do
isPressed <- MS.gets (Map.member key . deconsLatch)
if isPressed
then
AccState.modify accessMap (Map.delete key) >>
return (Note.Boundary key vel False)
else
AccState.modify accessMap (Map.insert key vel) >>
return (Note.Boundary key vel True)
instance C Latch where
reset = releasePlayedKeys
resetSome = releaseSomeKeys
size = sizeGen
toList = toListGen
index = indexGen
change = fmap maybeToList . latchChange
data GroupLatch key value =
GroupLatch {
groupLatchPressed_ :: Set.Set key,
groupLatchPlayed_ :: Map.Map key value
} deriving (Show)
groupLatch :: GroupLatch key value
groupLatch = GroupLatch Set.empty Map.empty
groupLatchPressed :: Acc.T (GroupLatch key value) (Set.Set key)
groupLatchPressed =
Acc.fromSetGet
(\mp grp -> grp{groupLatchPressed_ = mp})
groupLatchPressed_
groupLatchPlayed :: Acc.T (GroupLatch key value) (Map.Map key value)
groupLatchPlayed =
Acc.fromSetGet
(\mp grp -> grp{groupLatchPlayed_ = mp})
groupLatchPlayed_
instance Map GroupLatch where
accessMap = groupLatchPlayed
instance C GroupLatch where
reset = releasePlayedKeys
resetSome = releaseSomeKeys
size = sizeGen
toList = toListGen
index = indexGen
change (Note.Boundary key vel on) =
if on
then do
pressd <- AccState.get groupLatchPressed
noteOffs <-
if Set.null pressd
then releasePlayedKeys
else return []
AccState.modify groupLatchPressed (Set.insert key)
played <- AccState.get groupLatchPlayed
noteOn <-
if Map.member key played
then
return []
else do
AccState.modify groupLatchPlayed (Map.insert key vel)
return [Note.Boundary key vel True]
return $
noteOffs ++ noteOn
else
AccState.modify groupLatchPressed (Set.delete key) >>
return []
data SerialLatch key value =
SerialLatch {
serialLatchSize_ :: Int,
serialLatchCursor_ :: Int,
serialLatchPlayed_ :: Map.Map Int (key, value)
} deriving (Show)
serialLatch :: Int -> SerialLatch key value
serialLatch num = SerialLatch num 0 Map.empty
serialLatchCursor :: Acc.T (SerialLatch key value) Int
serialLatchCursor =
Acc.fromSetGet
(\mp grp -> grp{serialLatchCursor_ = mp})
serialLatchCursor_
serialLatchPlayed :: Acc.T (SerialLatch key value) (Map.Map Int (key, value))
serialLatchPlayed =
Acc.fromSetGet
(\mp grp -> grp{serialLatchPlayed_ = mp})
serialLatchPlayed_
instance C SerialLatch where
reset =
fmap (map (uncurry releaseKey) . Map.elems) $
AccState.getAndModify serialLatchPlayed (const Map.empty)
resetSome p =
fmap (map (uncurry releaseKey) . Map.elems) $
AccState.lift serialLatchPlayed $
MS.state $ Map.partition (p . fst)
size = serialLatchSize_
toList = Map.elems . serialLatchPlayed_
index k = Map.lookup k . serialLatchPlayed_
change bnd@(Note.Boundary key vel on) =
if on
then do
n <- MS.gets serialLatchSize_
k <- AccState.getAndModify serialLatchCursor (flip mod n . (1+))
oldKey <- fmap (Map.lookup k) $ AccState.get serialLatchPlayed
AccState.modify serialLatchPlayed (Map.insert k (key, vel))
return $ maybeToList (fmap (uncurry releaseKey) oldKey)
++ [bnd]
else return []
sizeGen :: (Map set) => set key value -> Int
sizeGen = Map.size . Acc.get accessMap
toListGen :: (Map set) => set key value -> [(key, value)]
toListGen = Map.toAscList . Acc.get accessMap
indexGen ::
(Ord key, Map set) =>
Int -> set key value -> Maybe (key, value)
indexGen k =
listToMaybe . drop k . Map.toAscList . Acc.get accessMap
releasePlayedKeys ::
(Map set) =>
MS.State
(set key value)
[Note.Boundary key value]
releasePlayedKeys =
fmap (map (uncurry releaseKey) . Map.toList) $
AccState.getAndModify accessMap $ const Map.empty
releaseSomeKeys ::
(Ord key, Map set) =>
(key -> Bool) ->
MS.State
(set key value)
[Note.Boundary key value]
releaseSomeKeys p =
fmap (map (uncurry releaseKey) . Map.toList) $
AccState.lift accessMap $ MS.state $
Map.partitionWithKey (const . p)
releaseKey ::
key -> value -> Note.Boundary key value
releaseKey key vel =
Note.Boundary key vel False