Karya, built on 2023-08-29T07:47:28 (patch 7a412d5d6ba4968ca4155ef276a062ccdeb9109a)
Safe HaskellSafe-Inferred

Derive.Call.Post

Description

Post-processing utils. These are transformers that directly modify the output of a deriver, as opposed to simply modifying the Dynamic.

Unfortunately things are complicated by the presence of LEvent.Logs in the output stream. I haven't been able to figure out how to cleanly abstract that away, so I wind up with a collection of functions to handle specific kinds of maps.

There are variants for each axis:

  • monadic vs. pure
  • state vs. stateless
  • 1:1 vs. 1:many
  • preserves order vs. doesn't preserve order

TODO

One big problem with this is the permutations. Another is that I should be able to fuse composed maps, but I think it'll mostly be defeated by the monadic bits, and maybe state. But even monadic bits should be theoretically fusible since I don't mind if the effects (i.e. exceptions) are interleaved. A job for pipes maybe?

Synopsis

map events

non-monadic

emap1_ :: (a -> b) -> Stream.Stream a -> Stream.Stream b Source #

1:1 non-monadic map without state.

TODO this is expected to not destroy the order, but that isn't checked. That means either the event doesn't move, or it doesn't move past its neighbors. Way back when events didn't have their start times, I could express this by only mapping over the event, but I'm not sure how to do it now. And in any case, "don't destroy order" is more permissive than "don't move."

emap1_ord_ :: (a -> Score.Event) -> Stream.Stream a -> Stream.Stream Score.Event Source #

Map on Score.Events. The function is allowed to move the events, since it sorts them afterwards.

emap1 :: (state -> a -> (state, b)) -> state -> Stream.Stream a -> (state, Stream.Stream b) Source #

1:1 non-monadic map with state. This is like mapAccumL.

emap :: (state -> a -> (state, [Score.Event])) -> state -> Stream.Stream a -> (state, Stream.Stream Score.Event) Source #

1:n non-monadic map with state.

emap_asc :: (state -> a -> (state, [Score.Event])) -> state -> Stream.Stream a -> (state, Stream.Stream Score.Event) Source #

This is emap, but it promises to emit events in sorted order. TODO except that's not enforced, and maybe I should just always sort.

monadic

apply :: ([a] -> [b]) -> Stream.Stream a -> Stream.Stream b Source #

Apply a function to the non-log events. TODO assumes the function doesn't destroy the order.

apply_m :: Functor f => ([a] -> f [b]) -> Stream.Stream a -> f (Stream.Stream b) Source #

emap1m_ :: (a -> Score.Event) -> (a -> Deriver b) -> Stream.Stream a -> Deriver (Stream.Stream b) Source #

1:1 monadic map without state.

emap_m Source #

Arguments

:: (a -> Score.Event) 
-> (state -> a -> Deriver (state, [b]))

Process an event. Exceptions are caught and logged.

-> state 
-> Stream.Stream a 
-> Deriver (state, Stream.Stream b) 

Monadic map with state. The event type is polymorphic, so you can use LEvent.zip and co. to zip up unthreaded state, constructed with control and nexts and such.

emap_asc_m Source #

Arguments

:: (a -> Score.Event) 
-> (state -> a -> Deriver (state, [Score.Event]))

Process an event. Exceptions are caught and logged.

-> state 
-> Stream.Stream a 
-> Deriver (state, Stream.Stream Score.Event) 

emap_m_ :: (a -> Score.Event) -> (a -> Deriver [b]) -> Stream.Stream a -> Deriver (Stream.Stream b) Source #

emap_m without the state.

emap_s_ :: (a -> Score.Event) -> (a -> NoteDeriver) -> Stream.Stream a -> NoteDeriver Source #

Postprocess each event with a NoteDeriver. This is necessary if you need to generate more notes, e.g. with Call.note.

only

only :: (a -> event) -> (event -> Bool) -> (a -> event) -> a -> event Source #

Only process the events that match, otherwise pass unchanged.

unthreaded state

neighbors :: Stream.Stream a -> Stream.Stream ([a], a, [a]) Source #

Zip each event up with its neighbors.

neighbors_by :: Eq key => (a -> key) -> Stream.Stream a -> Stream.Stream (Maybe a, a, Maybe a) Source #

Zip each event with its nearest neighbor with the same key. A key might be Score.event_instrument, hand_key, or voice_key.

TODO it's awkward how calls that are not instrument-specific still have to choose between hand or voice when they want the next "relevant" note. Perhaps hand and voice should be merged into a single concept. They have to be distinct for the lilypond backend though.

nexts_by :: Eq key => (a -> key) -> Stream.Stream a -> Stream.Stream (a, [a]) Source #

next_by :: Eq key => (a -> key) -> Stream.Stream a -> Stream.Stream (a, Maybe a) Source #

Like neighbors_by, but only the next neighbor.

prev_by :: Eq key => (a -> key) -> Stream.Stream a -> Stream.Stream (Maybe a, a) Source #

nexts :: [a] -> [[a]] Source #

Extract subsequent events.

prevs :: [a] -> [[a]] Source #

Extract previous events.

misc maps

map_first :: (a -> Deriver a) -> Stream.Stream a -> Deriver (Stream.Stream a) Source #

Apply a function on the first Event of an LEvent stream. TODO this shouldn't destroy the order, but it isn't checkded.

map_head_tail :: (a -> Stream.Stream a -> Deriver (Stream.Stream a)) -> Stream.Stream a -> Deriver (Stream.Stream a) Source #

Transform the first event and the rest of the events.

signal

signal :: Monoid sig => (sig -> sig) -> Deriver (Stream.Stream sig) -> Deriver (Stream.Stream sig) Source #

Transform a pitch or control signal.

delayed events

make_delayed :: PassedArgs a -> RealTime.RealTime -> [DeriveT.Val] -> NoteDeriver Source #

Make a delayed event.

A delayed event should be realized by an accompanying postproc call. It has an EnvKey.args, which are the arguments to the postproc call, and so it's a little bit like a closure or a delayed thunk.

It's awkward because you have to manually call the postproc, which then has to extract the args and re-typecheck them. I considered storing actual thunks as functions, and running a generic postproc which forces them, but I think each one is likely to require a different context. E.g. previous and next events for the same instrument, or with the same hand, or map over groups of events, etc. TODO wait until I have more experience.

TODO this stuff is now unused, but maybe I'll find a use for it again some day.

delayed_args :: Expr.Symbol -> Score.Event -> Maybe [DeriveT.Val] Source #

Return the args if this is a delayed event created by the given call.

modify events

set_instrument Source #

Arguments

:: (ScoreT.Instrument, Instrument)

unaliased instrument name, from get_instrument

-> Score.Event 
-> Score.Event 

misc

add_event_stack :: Score.Event -> Log.Msg -> Log.Msg Source #

Like with_event_stack, but directly add the event's innermost stack to a log msg. TODO unused