reactive-midyim 0.2.1 → 0.3
raw patch · 7 files changed
+260/−264 lines, 7 filesdep ~reactive-banana
Dependency ranges changed: reactive-banana
Files
- reactive-midyim.cabal +3/−4
- src/Reactive/Banana/MIDI/IndexedMonad.hs +0/−26
- src/Reactive/Banana/MIDI/Pattern.hs +61/−61
- src/Reactive/Banana/MIDI/Process.hs +145/−152
- src/Reactive/Banana/MIDI/Program.hs +2/−1
- src/Reactive/Banana/MIDI/Time.hs +6/−7
- src/Reactive/Banana/MIDI/Utility.hs +43/−13
reactive-midyim.cabal view
@@ -1,5 +1,5 @@ Name: reactive-midyim-Version: 0.2.1+Version: 0.3 License: BSD3 License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -45,11 +45,11 @@ Source-Repository this type: darcs location: http://hub.darcs.net/thielema/reactive-midyim/- tag: 0.2.1+ tag: 0.3 Library Build-Depends:- reactive-banana >=0.8 && <0.9,+ reactive-banana >=1.1 && <1.2, midi >=0.2 && <0.3, event-list >=0.1 && < 0.2, non-negative >=0.1 && <0.2,@@ -74,7 +74,6 @@ Reactive.Banana.MIDI.Pitch Reactive.Banana.MIDI.Note Reactive.Banana.MIDI.Time- Reactive.Banana.MIDI.IndexedMonad Reactive.Banana.MIDI.Program Reactive.Banana.MIDI.Controller Other-Modules:
− src/Reactive/Banana/MIDI/IndexedMonad.hs
@@ -1,26 +0,0 @@-{--This module could as well live in a separate package.--}-module Reactive.Banana.MIDI.IndexedMonad where--import Control.Applicative (Applicative, pure, (<*>), )-import Control.Monad (liftM, ap, )---class C m where- point :: a -> m s a- bind :: m s a -> (a -> m s b) -> m s b---newtype Wrap m s a = Wrap {unwrap :: m s a}--instance C m => Functor (Wrap m s) where- fmap = liftM--instance C m => Applicative (Wrap m s) where- pure = return- (<*>) = ap--instance C m => Monad (Wrap m s) where- return = Wrap . point- Wrap x >>= k = Wrap $ bind x (unwrap . k)
src/Reactive/Banana/MIDI/Pattern.hs view
@@ -30,7 +30,7 @@ import qualified Data.Traversable as Trav import qualified Data.Foldable as Fold -import Control.Monad (guard, )+import Control.Monad (guard, liftM, ) import Control.Applicative (pure, (<*>), ) import Data.Maybe (mapMaybe, maybeToList, ) import Data.Bool.HT (if', )@@ -42,19 +42,19 @@ -- * reactive patterns -type T t time set key value =- RB.Behavior t (set key value) ->- RB.Event t time ->- RB.Event t [Note.Boundary key value]+type T m time set key value =+ RB.Behavior (set key value) ->+ RB.Event time ->+ m (RB.Event [Note.Boundary key value]) mono ::- (KeySet.C set) =>+ (RB.MonadMoment m) => Selector set key Velocity i ->- RB.Behavior t (set key Velocity) ->- RB.Event t i ->- RB.Event t [Note.Boundary key Velocity]+ RB.Behavior (set key Velocity) ->+ RB.Event i ->+ m (RB.Event [Note.Boundary key Velocity]) mono select pressed pattern =- fst $ RBU.sequence [] $+ liftM fst $ RBU.sequence [] $ pure (\set i -> do off <- MS.get@@ -72,13 +72,13 @@ poly ::- (KeySet.C set) =>+ (RB.MonadMoment m) => Selector set key Velocity i ->- RB.Behavior t (set key Velocity) ->- RB.Event t [IndexNote i] ->- RB.Event t [Note.Boundary key Velocity]+ RB.Behavior (set key Velocity) ->+ RB.Event [IndexNote i] ->+ m (RB.Event [Note.Boundary key Velocity]) poly select pressed pattern =- fst $ RBU.sequence EventList.empty $+ liftM fst $ RBU.sequence EventList.empty $ pure (\set is -> do off <- MS.get@@ -197,11 +197,12 @@ cycleUpIndex, cycleDownIndex, pingPongIndex ::- RB.Behavior t Int ->- RB.Event t time ->- RB.Event t Int+ (RB.MonadMoment m) =>+ RB.Behavior Int ->+ RB.Event time ->+ m (RB.Event Int) cycleUpIndex numbers times =- fst $ RB.mapAccum 0 $+ liftM fst $ RB.mapAccum 0 $ pure (\number _time i -> (i, mod (succ i) (max 1 number))) <*> numbers@@ -215,7 +216,7 @@ <@> times pingPongIndex numbers times =- fst $ RB.mapAccum (0,1) $+ liftM fst $ RB.mapAccum (0,1) $ pure (\number _time (i,d0) -> (i, let j = i+d0@@ -227,10 +228,12 @@ <@> times crossSumIndex ::- RB.Behavior t Int ->- RB.Event t time ->- RB.Event t Int+ (RB.MonadMoment m) =>+ RB.Behavior Int ->+ RB.Event time ->+ m (RB.Event Int) crossSumIndex numbers times =+ flip liftM (fromList [0..] times) $ \ts -> pure (\number i -> let m = fromIntegral number@@ -238,19 +241,22 @@ then 0 else fromInteger $ flip mod m $ sum $ decomposePositional m i) <*> numbers- <@> fromList [0..] times+ <@> ts crossSumStaticIndex ::+ (RB.MonadMoment m) => Int ->- RB.Event t time ->- RB.Event t Int+ RB.Event time ->+ m (RB.Event Int) crossSumStaticIndex number = fromList (flipSeq number) -fromList :: [a] -> RB.Event t time -> RB.Event t a+fromList ::+ (RB.MonadMoment m) =>+ [a] -> RB.Event time -> m (RB.Event a) fromList xs times =- RB.filterJust $ fst $ RB.mapAccum xs $+ liftM (RB.filterJust . fst) $ RB.mapAccum xs $ fmap (\_time xs0 -> case xs0 of@@ -260,27 +266,27 @@ cycleUp, cycleDown, pingPong, crossSum ::- (KeySet.C set, Ord key) =>- RB.Behavior t Int -> T t time set key Velocity+ (RB.MonadMoment m, KeySet.C set, Ord key) =>+ RB.Behavior Int -> T m time set key Velocity cycleUp numbers sets times =- mono selectFromChord sets (cycleUpIndex numbers times)+ mono selectFromChord sets =<< cycleUpIndex numbers times cycleDown numbers sets times =- mono selectFromChord sets (cycleDownIndex numbers times)+ mono selectFromChord sets =<< cycleDownIndex numbers times pingPong numbers sets times =- mono selectFromChord sets (pingPongIndex numbers times)+ mono selectFromChord sets =<< pingPongIndex numbers times crossSum numbers sets times =- mono selectFromChord sets (crossSumIndex numbers times)+ mono selectFromChord sets =<< crossSumIndex numbers times bruijn ::- (KeySet.C set, Ord key) =>- Int -> Int -> T t time set key Velocity+ (RB.MonadMoment m, KeySet.C set, Ord key) =>+ Int -> Int -> T m time set key Velocity bruijn n k sets times =- mono selectFromChord sets $+ mono selectFromChord sets =<< fromList (cycle $ DeBruijn.lexLeast n k) times binaryStaccato, binaryLegato, binaryAccident ::- (KeySet.C set, Ord key) => T t time set key Velocity+ (RB.MonadMoment m, KeySet.C set, Ord key) => T m time set key Velocity {- binary number Pattern.T: 0@@ -293,9 +299,7 @@ 3 -} binaryStaccato sets times =- poly- selectFromChord- sets+ poly selectFromChord sets =<< (flip fromList times $ map (map (IndexNote 1 . fst) .@@ -305,9 +309,7 @@ [0..]) binaryLegato sets times =- poly- selectFromChord- sets+ poly selectFromChord sets =<< (flip fromList times $ map (\m ->@@ -322,9 +324,7 @@ It was not what I wanted, but it sounded nice. -} binaryAccident sets times =- poly- selectFromChord- sets+ poly selectFromChord sets =<< (flip fromList times $ map (zipWith IndexNote (iterate (2*) 1) .@@ -345,23 +345,23 @@ in recourse cycleUpOctave ::- (KeySet.C set, Ord pitch, Pitch.C pitch) =>- RB.Behavior t Int -> T t time set pitch Velocity+ (RB.MonadMoment m, KeySet.C set, Ord pitch, Pitch.C pitch) =>+ RB.Behavior Int -> T m time set pitch Velocity cycleUpOctave numbers sets times =- mono selectFromOctaveChord sets (cycleUpIndex numbers times)+ mono selectFromOctaveChord sets =<< cycleUpIndex numbers times random ::- (KeySet.C set, Ord key) =>- T t time set key Velocity+ (RB.MonadMoment m, KeySet.C set, Ord key) =>+ T m time set key Velocity random sets times =- mono selectFromChordRatio sets $- fst $ RB.mapAccum (Rnd.mkStdGen 42) $+ (mono selectFromChordRatio sets =<<) $+ liftM fst $ RB.mapAccum (Rnd.mkStdGen 42) $ fmap (const $ Rnd.randomR (0,1)) times randomInversions ::- (KeySet.C set, Pitch.C pitch) =>- T t time set pitch Velocity+ (RB.MonadMoment m, KeySet.C set, Pitch.C pitch) =>+ T m time set pitch Velocity randomInversions = inversions $ map sum $@@ -370,17 +370,17 @@ Rnd.mkStdGen 42 cycleUpInversions ::- (KeySet.C set, Pitch.C pitch) =>- Int -> T t time set pitch Velocity+ (RB.MonadMoment m, KeySet.C set, Pitch.C pitch) =>+ Int -> T m time set pitch Velocity cycleUpInversions n = inversions $ cycle $ take n $ map (\i -> fromInteger i / fromIntegral n) [0..] inversions ::- (KeySet.C set, Pitch.C pitch) =>- [Double] -> T t time set pitch Velocity+ (RB.MonadMoment m, KeySet.C set, Pitch.C pitch) =>+ [Double] -> T m time set pitch Velocity inversions rs sets times =- mono selectInversion sets (fromList rs times)+ mono selectInversion sets =<< fromList rs times
src/Reactive/Banana/MIDI/Process.hs view
@@ -2,10 +2,11 @@ RelativeTicks, AbsoluteTicks, RelativeSeconds,- Moment(liftMoment),+ MomentIO(liftMomentIO), Reactor(reserveSchedule), scheduleQueue, initialEvent,+ unionM, beat, beatQuant, beatVar,@@ -37,7 +38,6 @@ import qualified Reactive.Banana.MIDI.KeySet as KeySet import qualified Reactive.Banana.MIDI.Pitch as Pitch import qualified Reactive.Banana.MIDI.Utility as RBU-import qualified Reactive.Banana.MIDI.IndexedMonad as IxMonad import qualified Reactive.Banana.MIDI.Common as Common import Reactive.Banana.MIDI.Common (PitchChannel(PitchChannel),@@ -46,7 +46,6 @@ import qualified Reactive.Banana.Combinators as RB import qualified Reactive.Banana.Frameworks as RBF-import qualified Reactive.Banana.Switch as RBS import Reactive.Banana.Combinators ((<@>), ) import qualified Sound.MIDI.Message.Class.Construct as Construct@@ -65,10 +64,10 @@ import qualified Control.Monad.Trans.State as MS import qualified Data.Traversable as Trav-import Control.Monad (join, mplus, when, )+import Control.Monad (join, mplus, when, liftM, ) import Control.Applicative (pure, liftA2, (<*>), (<$>), ) import Data.Monoid (mempty, mappend, )-import Data.Tuple.HT (mapPair, mapFst, mapSnd, )+import Data.Tuple.HT (mapPair, mapSnd, ) import Data.Ord.HT (comparing, limit, ) import Data.Maybe.HT (toMaybe, ) import Data.Maybe (catMaybes, )@@ -85,48 +84,44 @@ type AbsoluteTicks m = Time.T m Time.Absolute Time.Ticks type RelativeSeconds m = Time.T m Time.Relative Time.Seconds -class Moment moment where- liftMoment :: RBS.Moment t a -> moment t a+class MomentIO moment where+ liftMomentIO :: RBF.MomentIO a -> moment a -instance Moment RBS.Moment where- liftMoment = id+instance MomentIO RBF.MomentIO where+ liftMomentIO = id -class (Moment reactor, Time.Timed reactor) => Reactor reactor where+class (MomentIO reactor, Time.Timed reactor) => Reactor reactor where {- | Provide a function for registering future beats- and the return the reactive event list- that results from the sent beats.+ and return the reactive event list that results from the sent beats. -} reserveSchedule ::- (RBF.Frameworks t) =>- reactor t+ reactor ([AbsoluteTicks reactor] -> IO (), IO (),- RB.Event t (AbsoluteTicks reactor))+ RB.Event (AbsoluteTicks reactor)) reactimate ::- (Moment reactor, RBF.Frameworks t) =>- RB.Event t (IO ()) -> IxMonad.Wrap reactor t ()-reactimate = IxMonad.Wrap . liftMoment . RBF.reactimate+ (MomentIO reactor) =>+ RB.Event (IO ()) -> reactor ()+reactimate = liftMomentIO . RBF.reactimate reactimate' ::- (Moment reactor, RBF.Frameworks t) =>- RB.Event t (RBF.Future (IO ())) -> IxMonad.Wrap reactor t ()-reactimate' = IxMonad.Wrap . liftMoment . RBF.reactimate'+ (MomentIO reactor) =>+ RB.Event (RBF.Future (IO ())) -> reactor ()+reactimate' = liftMomentIO . RBF.reactimate' -liftIO ::- (Moment m, RBF.Frameworks t) =>- IO a -> IxMonad.Wrap m t a-liftIO = IxMonad.Wrap . liftMoment . RBF.liftIO+liftIO :: (MomentIO m) => IO a -> m a+liftIO = liftMomentIO . RBF.liftIO scheduleQueue ::- (Reactor reactor, RBF.Frameworks t) =>- RB.Behavior t (AbsoluteTicks reactor) ->- RB.Event t (Common.Bundle reactor a) -> reactor t (RB.Event t a)-scheduleQueue times e = IxMonad.unwrap $ do- (send, _cancel, eEcho) <- IxMonad.Wrap reserveSchedule+ (Reactor reactor) =>+ RB.Behavior (AbsoluteTicks reactor) ->+ RB.Event (Common.Bundle reactor a) -> reactor (RB.Event a)+scheduleQueue times e = do+ (send, _cancel, eEcho) <- reserveSchedule let -- maintain queue and generate Echo events remove echoTime = MS.state $ uncurry $ \_lastTime ->@@ -148,11 +143,12 @@ (Time.subSat time lastTime) old) return (Nothing, send $ map (flip Time.inc time . Common.futureTime) new) - -- (Queue that keeps track of events to schedule- -- , duration of the new alarm if applicable)- (eEchoEvent, _bQueue) =+ -- (Queue that keeps track of events to schedule+ -- , duration of the new alarm if applicable)+ (eEchoEvent, _bQueue) <- RBU.sequence (mempty, EventList.empty) $- RB.union (fmap remove eEcho) (pure add <*> times <@> e)+ RBU.union "scheduleQueue"+ (fmap remove eEcho) (pure add <*> times <@> e) reactimate $ fmap snd eEchoEvent return $ RBU.mapMaybe fst eEchoEvent@@ -163,26 +159,32 @@ Generate an event at the first time point. -} initialEvent ::- (Reactor reactor, RBF.Frameworks t) =>- a -> reactor t (RB.Event t a)-initialEvent x = IxMonad.unwrap $ do- (send, _cancel, eEcho) <- IxMonad.Wrap reserveSchedule+ (Reactor reactor) =>+ a -> reactor (RB.Event a)+initialEvent x = do+ (send, _cancel, eEcho) <- reserveSchedule liftIO $ send [mempty] return $ fmap (const x) eEcho +{- |+The second event stream is delayed by an infinitesimal amount.+-}+unionM :: (MomentIO m) => RB.Event a -> RB.Event a -> m (RB.Event a)+unionM xs = liftMomentIO . RBU.unionM xs + {- | Generate a beat according to the tempo control. The input signal specifies the period between two beats. The output events hold the times, where they occur. -} beat ::- (Reactor reactor, RBF.Frameworks t) =>- RB.Behavior t (RelativeTicks reactor) ->- reactor t (RB.Event t (AbsoluteTicks reactor))-beat tempo = IxMonad.unwrap $ do- (send, _cancel, eEcho) <- IxMonad.Wrap reserveSchedule+ (Reactor reactor) =>+ RB.Behavior (RelativeTicks reactor) ->+ reactor (RB.Event (AbsoluteTicks reactor))+beat tempo = do+ (send, _cancel, eEcho) <- reserveSchedule liftIO $ send [mempty] @@ -206,12 +208,12 @@ and thus may miss some tempo changes. -} beatQuant ::- (Reactor reactor, RBF.Frameworks t) =>+ (Reactor reactor) => RelativeTicks reactor ->- RB.Behavior t (RelativeTicks reactor) ->- reactor t (RB.Event t (AbsoluteTicks reactor))-beatQuant maxDur tempo = IxMonad.unwrap $ do- (send, _cancel, eEcho) <- IxMonad.Wrap reserveSchedule+ RB.Behavior (RelativeTicks reactor) ->+ reactor (RB.Event (AbsoluteTicks reactor))+beatQuant maxDur tempo = do+ (send, _cancel, eEcho) <- reserveSchedule liftIO $ send [mempty] @@ -226,8 +228,7 @@ send [Time.inc dur time] {- print (dur, time, dt, portion) -} ) - eEchoEvent =- fst $ RBU.sequence 0 $ fmap next tempo <@> eEcho+ eEchoEvent <- liftM fst $ RBU.sequence 0 $ fmap next tempo <@> eEcho reactimate $ fmap snd eEchoEvent return $ RBU.mapMaybe fst eEchoEvent@@ -276,19 +277,19 @@ and alter the tempo of the queue timer. -} beatVar ::- (Reactor reactor, RBF.Frameworks t) =>- RB.Behavior t (AbsoluteTicks reactor) ->- RB.Behavior t (RelativeTicks reactor) ->- reactor t (RB.Event t (AbsoluteTicks reactor))-beatVar time tempo = IxMonad.unwrap $ do- (send, cancel, eEcho) <- IxMonad.Wrap reserveSchedule+ (Reactor reactor) =>+ RB.Behavior (AbsoluteTicks reactor) ->+ RB.Behavior (RelativeTicks reactor) ->+ reactor (RB.Event (AbsoluteTicks reactor))+beatVar time tempo = do+ (send, cancel, eEcho) <- reserveSchedule let sendSingle = send . (:[]) liftIO $ sendSingle mempty (tempoInit, tempoChanges) <-- IxMonad.Wrap $ liftMoment $- liftA2 (,) (RBF.initial tempo) (plainChanges tempo)+ liftMomentIO $+ liftA2 (,) (RB.valueBLater tempo) (plainChanges tempo) let next t = mapSnd (return . sendSingle) <$> beatVarNext t @@ -296,11 +297,11 @@ ta <- beatVarChange p1 t1 return (Nothing, return $ cancel >> sendSingle ta) - eEchoEvent =- fst $ RBU.sequence (mempty, 0, tempoInit) $- RB.union- (fmap next eEcho)- (fmap (flip change) time <@> tempoChanges)+ eEchoEvent <-+ liftM fst $ RBU.sequence (mempty, 0, tempoInit) $+ RBU.union "beatVar"+ (fmap next eEcho)+ (fmap (flip change) time <@> tempoChanges) reactimate' $ fmap snd eEchoEvent return $ RBU.mapMaybe fst eEchoEvent@@ -312,65 +313,64 @@ since this uses precisely timed delivery by ALSA. -} delaySchedule ::- (Reactor reactor, RBF.Frameworks t) =>+ (Reactor reactor) => RelativeTicks reactor ->- RB.Behavior t (AbsoluteTicks reactor) ->- RB.Event t a -> reactor t (RB.Event t a)+ RB.Behavior (AbsoluteTicks reactor) ->+ RB.Event a -> reactor (RB.Event a) delaySchedule dt times =- scheduleQueue times .- fmap ((:[]) . Common.Future dt)+ scheduleQueue times . fmap ((:[]) . Common.Future dt) delay :: RelativeTicks m ->- RB.Event t ev -> RB.Event t (Common.Future m ev)+ RB.Event ev -> RB.Event (Common.Future m ev) delay dt = fmap (Common.Future dt) delayAdd ::+ (MomentIO m) => RelativeTicks m ->- RB.Event t ev -> RB.Event t (Common.Future m ev)+ RB.Event ev -> m (RB.Event (Common.Future m ev)) delayAdd dt evs =- RB.union (fmap Common.now evs) $ delay dt evs+ unionM (fmap Common.now evs) $ delay dt evs {- | register pressed keys -} pressed ::- (KeySet.C set, Ord key) =>+ (RB.MonadMoment m, KeySet.C set, Ord key) => set key value ->- RB.Event f (Note.BoundaryExt key value) ->- (RB.Event f [Note.Boundary key value], RB.Behavior f (set key value))+ RB.Event (Note.BoundaryExt key value) ->+ m (RB.Event [Note.Boundary key value], RB.Behavior (set key value)) pressed empty = RBU.traverse empty KeySet.changeExt latch ::- (Ord key) =>- RB.Event f (Note.Boundary key value) ->- (RB.Event f (Note.Boundary key value),- RB.Behavior f (Map.Map key value))+ (RB.MonadMoment m, Ord key) =>+ RB.Event (Note.Boundary key value) ->+ m (RB.Event (Note.Boundary key value),+ RB.Behavior (Map.Map key value)) latch =- mapPair (RB.filterJust, fmap KeySet.deconsLatch) .+ liftM (mapPair (RB.filterJust, fmap KeySet.deconsLatch)) . RBU.traverse KeySet.latch KeySet.latchChange controllerRaw ::- (Check.C ev) =>+ (RB.MonadMoment m, Check.C ev) => Channel -> Controller -> Int ->- RB.Event t ev -> RB.Behavior t Int+ RB.Event ev -> m (RB.Behavior Int) controllerRaw chan ctrl deflt =- RB.stepper deflt .- RBU.mapMaybe (Check.controller chan ctrl)+ RB.stepper deflt . RBU.mapMaybe (Check.controller chan ctrl) controllerExponential ::- (Floating a, Check.C ev) =>+ (RB.MonadMoment m, Floating a, Check.C ev) => Channel -> Controller -> a -> (a,a) ->- RB.Event t ev -> RB.Behavior t a+ RB.Event ev -> m (RB.Behavior a) controllerExponential chan ctrl deflt (lower,upper) = let k = log (upper/lower) / 127 in RB.stepper deflt .@@ -379,11 +379,11 @@ . Check.controller chan ctrl) controllerLinear ::- (Fractional a, Check.C ev) =>+ (RB.MonadMoment m, Fractional a, Check.C ev) => Channel -> Controller -> a -> (a,a) ->- RB.Event t ev -> RB.Behavior t a+ RB.Event ev -> m (RB.Behavior a) controllerLinear chan ctrl deflt (lower,upper) = let k = (upper-lower) / 127 in RB.stepper deflt .@@ -392,16 +392,20 @@ . Check.controller chan ctrl) +-- | FuncHT.mapFst+mapFstM :: Monad m => (a -> m c) -> (a, b) -> m (c, b)+mapFstM f ~(a,b) = liftM (flip (,) b) $ f a+ tempoCtrl ::- (Check.C ev) =>+ (RB.MonadMoment m, Check.C ev) => Channel -> Controller -> RelativeTicks m -> (RelativeTicks m, RelativeTicks m) ->- RB.Event t ev ->- (RB.Behavior t (RelativeTicks m), RB.Event t ev)+ RB.Event ev ->+ m (RB.Behavior (RelativeTicks m), RB.Event ev) tempoCtrl chan ctrl deflt (lower,upper) =- mapFst (RB.stepper deflt) .+ mapFstM (RB.stepper deflt) . RBU.partitionMaybe (fmap (Ctrl.duration (lower, upper)) . Check.controller chan ctrl)@@ -419,19 +423,17 @@ The disadvantage is that there are distinct distances between the pitches. -} snapSelect ::- (Moment moment, RBF.Frameworks t, KeySet.C set,- Pitch.C pitch, Eq pitch, Eq value) =>- RB.Behavior t (set pitch value) ->- RB.Behavior t Int ->- moment t (RB.Event t [Note.Boundary pitch value])+ (MomentIO moment, KeySet.C set, Pitch.C pitch, Eq pitch, Eq value) =>+ RB.Behavior (set pitch value) ->+ RB.Behavior Int ->+ moment (RB.Event [Note.Boundary pitch value]) snapSelect set ctrl =- liftMoment $- fmap- (flip RBU.mapAdjacent Nothing+ liftMomentIO $+ (flip RBU.mapAdjacent Nothing (\oldNote newNote -> let note on (pc, v) = Note.Boundary pc v on in catMaybes [fmap (note False) oldNote,- fmap (note True) newNote])) $+ fmap (note True) newNote]) =<<) $ uniqueChanges $ liftA2 (\s x ->@@ -442,12 +444,11 @@ uniqueChanges ::- (Moment moment, RBF.Frameworks t, Eq a) =>- RB.Behavior t a -> moment t (RB.Event t a)-uniqueChanges x = liftMoment $ do- x0 <- RBF.initial x+ (MomentIO moment, Eq a) => RB.Behavior a -> moment (RB.Event a)+uniqueChanges x = liftMomentIO $ do+ x0 <- RB.valueBLater x xs <- plainChanges x- return $ RB.filterJust $+ fmap RB.filterJust $ flip RBU.mapAdjacent x0 (\old new -> toMaybe (new/=old) new) xs @@ -456,13 +457,11 @@ Can we also use it for JACK? If not, we can create something of type - RB.Behavior t a -> RBS.Moment t (RB.Event t ())+ RB.Behavior a -> RB.Moment (RB.Event ()) and attach the Behavior values using (<@). -}-plainChanges ::- (RBF.Frameworks t) =>- RB.Behavior t a -> RBS.Moment t (RB.Event t a)+plainChanges :: RB.Behavior a -> RBF.MomentIO (RB.Event a) plainChanges x = do (evs, handle) <- RBF.newEvent xs <- RBF.changes x@@ -471,30 +470,28 @@ sweep ::- (RBF.Frameworks t, Reactor reactor) =>+ (Reactor reactor) => RelativeSeconds reactor -> (Double -> Double) ->- RB.Behavior t Double ->- reactor t- (RB.Event t (AbsoluteTicks reactor),- RB.Behavior t Double)-sweep durSecs wave speed = IxMonad.unwrap $ do- bt <-- IxMonad.Wrap . beat . pure =<<- IxMonad.Wrap (Time.ticksFromSeconds durSecs)+ RB.Behavior Double ->+ reactor+ (RB.Event (AbsoluteTicks reactor),+ RB.Behavior Double)+sweep durSecs wave speed = do+ bt <- beat . pure =<< Time.ticksFromSeconds durSecs let dur = realToFrac $ Time.unSeconds $ Time.decons durSecs- return- (bt,- fmap wave $ RB.accumB 0 $- fmap (\d _ phase -> fraction (phase + dur * d)) speed <@> bt)+ phases <-+ RB.accumB 0 $+ fmap (\d _ phase -> fraction (phase + dur * d)) speed <@> bt+ return (bt, fmap wave phases) makeControllerLinear :: (Construct.C msg) => Channel -> Controller ->- RB.Behavior t Int ->- RB.Behavior t Int ->- RB.Event t time -> RB.Behavior t Double ->- RB.Event t msg+ RB.Behavior Int ->+ RB.Behavior Int ->+ RB.Event time -> RB.Behavior Double ->+ RB.Event msg makeControllerLinear chan cc depthCtrl centerCtrl bt ctrl = pure (\y depth center _time ->@@ -509,13 +506,11 @@ cyclePrograms ::- (Construct.C msg, Query.C msg) =>+ (RB.MonadMoment m, Construct.C msg, Query.C msg) => [Program] ->- RB.Event t msg -> RB.Event t (Maybe msg)+ RB.Event msg -> m (RB.Event (Maybe msg)) cyclePrograms pgms =- fst .- RBU.traverse (cycle pgms)- (Program.traverseSeek (length pgms))+ liftM fst . RBU.traverse (cycle pgms) (Program.traverseSeek (length pgms)) {- |@@ -532,12 +527,12 @@ the program would be reset to the initial program. -} cycleProgramsDefer ::- (Construct.C msg, Query.C msg) =>+ (RB.MonadMoment m, Construct.C msg, Query.C msg) => RelativeTicks m -> [Program] ->- RB.Behavior t (AbsoluteTicks m) ->- RB.Event t msg -> RB.Event t (Maybe msg)+ RB.Behavior (AbsoluteTicks m) ->+ RB.Event msg -> m (RB.Event (Maybe msg)) cycleProgramsDefer defer pgms times =- fst .+ liftM fst . RBU.traverse (cycle pgms, mempty) (\(eventTime,e) -> fmap join $ Trav.sequence $@@ -558,13 +553,11 @@ noteSequence ::- (Construct.C msg) => RelativeTicks m -> Bool -> [Bool -> msg] -> Common.Bundle m msg noteSequence stepTime on =- zipWith Common.Future (iterate (mappend stepTime) mempty) .- map ($on)+ zipWith Common.Future (iterate (mappend stepTime) mempty) . map ($on) {- | This process simulates playing chords on a guitar.@@ -587,13 +580,13 @@ or two keys, one for each direction. -} guitar ::- (Construct.C msg, KeySet.C set) =>+ (RB.MonadMoment m, Construct.C msg, KeySet.C set) => RelativeTicks m ->- RB.Behavior t (set PitchChannel Velocity) ->- RB.Event t Bool ->- RB.Event t (Common.Bundle m msg)+ RB.Behavior (set PitchChannel Velocity) ->+ RB.Event Bool ->+ m (RB.Event (Common.Bundle m msg)) guitar stepTime pressd trigger =- fst $+ liftM fst $ RBU.traverse [] (\(set, on) -> do played <- MS.get@@ -667,18 +660,18 @@ The Reactor monad is only needed for sending the initial notes. -} trainer ::- (Reactor reactor, RBF.Frameworks t,+ (Reactor reactor, Query.C msg, Construct.C msg, Time.Quantity time) => Channel -> Time.T reactor Time.Relative time -> Time.T reactor Time.Relative time -> [([Pitch], [Pitch])] ->- RB.Behavior t (AbsoluteTicks reactor) ->- RB.Event t msg ->- reactor t (RB.Event t (Common.Bundle reactor msg))-trainer chan pauseSecs durationSecs sets0 times evs0 = IxMonad.unwrap $ do- pause <- IxMonad.Wrap $ Time.ticksFromAny pauseSecs- duration <- IxMonad.Wrap $ Time.ticksFromAny durationSecs+ RB.Behavior (AbsoluteTicks reactor) ->+ RB.Event msg ->+ reactor (RB.Event (Common.Bundle reactor msg))+trainer chan pauseSecs durationSecs sets0 times evs0 = do+ pause <- Time.ticksFromAny pauseSecs+ duration <- Time.ticksFromAny durationSecs let makeSeq sets = case sets of (target, _) : _ ->@@ -692,9 +685,9 @@ [] -> ([], mempty) let (initial, initIgnoreUntil) = makeSeq sets0- initEv <- IxMonad.Wrap $ initialEvent initial+ initEv <- initialEvent initial - return $ RB.union initEv $ fst $+ liftM (RBU.union "trainer" initEv . fst) $ flip (RBU.traverse (sets0, [], Time.inc initIgnoreUntil mempty)) (fmap (,) times <@> evs0) $ \(time,ev) -> case Query.noteExplicitOff ev of
src/Reactive/Banana/MIDI/Program.hs view
@@ -1,5 +1,6 @@ module Reactive.Banana.MIDI.Program (- traverse, traverseSeek, next, seek, maybeNoteOn,+ Reactive.Banana.MIDI.Program.traverse, traverseSeek,+ next, seek, maybeNoteOn, asBanks, ) where
src/Reactive/Banana/MIDI/Time.hs view
@@ -1,7 +1,6 @@ module Reactive.Banana.MIDI.Time where -import qualified Reactive.Banana.MIDI.IndexedMonad as IxMonad-import qualified Reactive.Banana.Frameworks as RBF+import qualified Reactive.Banana.Combinators as RB import qualified Numeric.NonNegative.Class as NonNeg @@ -20,7 +19,7 @@ This way we can prevent unlimited growth of denominators. -} -- the Const type helps us to avoid explicit kind signature extension-newtype T m t a = Cons (Const a (m () t))+newtype T m t a = Cons (Const a (m t)) instance Show a => Show (T m t a) where showsPrec n x =@@ -93,17 +92,17 @@ mapPair (cons, mapSnd cons) $ split (decons x) (decons y) -class IxMonad.C m => Timed m where- ticksFromSeconds :: (RBF.Frameworks s) => T m t Seconds -> m s (T m t Ticks)+class RB.MonadMoment m => Timed m where+ ticksFromSeconds :: T m t Seconds -> m (T m t Ticks) class Quantity a where- ticksFromAny :: (Timed m, RBF.Frameworks s) => T m t a -> m s (T m t Ticks)+ ticksFromAny :: (Timed m) => T m t a -> m (T m t Ticks) instance Quantity Seconds where ticksFromAny = ticksFromSeconds instance Quantity Ticks where- ticksFromAny = IxMonad.point+ ticksFromAny = return consRel :: String -> Rational -> T m Relative Seconds
src/Reactive/Banana/MIDI/Utility.hs view
@@ -2,14 +2,16 @@ module Reactive.Banana.MIDI.Utility where import qualified Reactive.Banana.Combinators as RB+import qualified Reactive.Banana.Frameworks as RBF import qualified Control.Monad.Trans.State as MS+import Control.Monad (liftM, liftM2, ) import Prelude hiding (sequence, ) partition ::- (a -> Bool) -> RB.Event f a -> (RB.Event f a, RB.Event f a)+ (a -> Bool) -> RB.Event a -> (RB.Event a, RB.Event a) partition p = (\x -> (fmap snd $ RB.filterE fst x,@@ -17,38 +19,66 @@ fmap (\a -> (p a, a)) mapMaybe ::- (a -> Maybe b) -> RB.Event f a -> RB.Event f b+ (a -> Maybe b) -> RB.Event a -> RB.Event b mapMaybe f = RB.filterJust . fmap f partitionMaybe ::- (a -> Maybe b) -> RB.Event f a -> (RB.Event f b, RB.Event f a)+ (a -> Maybe b) -> RB.Event a -> (RB.Event b, RB.Event a) partitionMaybe f = (\x -> (mapMaybe fst x, mapMaybe (\(mb,a) -> maybe (Just a) (const Nothing) mb) x)) . fmap (\a -> (f a, a)) +union :: String -> RB.Event a -> RB.Event a -> RB.Event a+union name = RB.unionWith (error $ name ++ ": clashing events")++{- |+The second event stream is delayed by an infinitesimal amount.+-}+unionM :: RB.Event a -> RB.Event a -> RBF.MomentIO (RB.Event a)+unionM xs = fmap (union "Utility.unionM" xs) . delayEps++delayEps :: RB.Event a -> RBF.MomentIO (RB.Event a)+delayEps xs = do+ (evs, handle) <- RBF.newEvent+ RBF.reactimate $ fmap handle xs+ return evs+ bypass :: (a -> Maybe b) ->- (RB.Event f a -> RB.Event f c) ->- (RB.Event f b -> RB.Event f c) ->- RB.Event f a -> RB.Event f c+ (RB.Event a -> RB.Event c) ->+ (RB.Event b -> RB.Event c) ->+ RB.Event a -> RB.Event c bypass p fa fb evs = let (eb,ea) = partitionMaybe p evs- in RB.union (fb eb) (fa ea)+ in union "bypass" (fb eb) (fa ea) +bypassM ::+ (Monad m) =>+ (a -> Maybe b) ->+ (RB.Event a -> m (RB.Event c)) ->+ (RB.Event b -> m (RB.Event c)) ->+ RB.Event a -> m (RB.Event c)+bypassM p fa fb evs =+ let (eb,ea) = partitionMaybe p evs+ in liftM2 (union "bypass") (fb eb) (fa ea)+ traverse ::- s -> (a -> MS.State s b) -> RB.Event f a ->- (RB.Event f b, RB.Behavior f s)+ (RB.MonadMoment m) =>+ s -> (a -> MS.State s b) -> RB.Event a ->+ m (RB.Event b, RB.Behavior s) traverse s f = sequence s . fmap f sequence ::- s -> RB.Event f (MS.State s a) ->- (RB.Event f a, RB.Behavior f s)+ (RB.MonadMoment m) =>+ s -> RB.Event (MS.State s a) ->+ m (RB.Event a, RB.Behavior s) sequence s = RB.mapAccum s . fmap MS.runState -mapAdjacent :: (a -> a -> b) -> a -> RB.Event f a -> RB.Event f b+mapAdjacent ::+ (RB.MonadMoment m) => (a -> a -> b) -> a -> RB.Event a -> m (RB.Event b) mapAdjacent f a0 =- fst . RB.mapAccum a0 . fmap (\new old -> (f old new, new))+ liftM fst . RB.mapAccum a0 . fmap (\new old -> (f old new, new))