Karya, built on 2022-03-21T01:30:44 (patch 89d1651424c35e564138d93424a157ff87457245)
Safe HaskellSafe-Inferred



This module is sister to Derive.Deriver.Lib, except that it contains functions which are normally only used by the built-in track derivation scheme, and are not used when writing most normal calls.


generic state access

local :: (Dynamic -> Dynamic) -> Deriver a -> Deriver a Source #

This is a little different from Reader.local because only a portion of the state is used Reader-style.

Note that this doesn't restore the state on an exception. I think this is ok because exceptions are always "caught" at the event evaluation level since it runs each one separately. Since the state dynamic state (i.e. except Collect) from the sub derivation is discarded, whatever state it's in after the exception shouldn't matter.

detached_local :: (Dynamic -> Dynamic) -> Deriver a -> Deriver (Either Error a) Source #

A version of local that catches exceptions and ignores any changes to Collect. This is appropriate for sub-calls that are below normal track derivation.


merge_collect :: Collect -> Deriver () Source #

Collect is only ever accumulated.

Direct modification would be potentially more efficient, but according to profiling it doesn't make a difference.

local_collect :: Deriver a -> Deriver (a, Collect) Source #

Run with an empty Collect, restore the original Collect, and return the sub-deriver's Collect.

trim_track_warps :: Maybe RealTime -> Maybe RealTime -> Deriver a -> Deriver a Source #

Modify the collect_warp_map to reduce the start and end by the given times. This is useful if you're going to clip off some events. The TrackWarps, and hence playback cursor, can't know you're going to do this, so you have to tell it.

with_collect :: (Collect -> Collect) -> Deriver a -> Deriver a Source #

Run the deriver and modify the Collect it returns.

modify_collect :: (Collect -> Collect) -> Deriver () Source #

TODO this is sketchy, you're supposed to use merge_collect.


record_track_dynamic :: Dynamic -> Maybe TrackDynamic Source #

Figure out the current block and track, and record the current environ in the Collect. It only needs to be recorded once per track.

record_track_dynamic_for :: BlockId -> TrackId -> Deriver () Source #

record_track_dynamic for when I already know BlockId and TrackId.

misc Dynamic state


ui state

get_track :: TrackId -> Deriver Track.Track Source #

Because Deriver is not a UiStateMonad.

TODO I suppose it could be, but then I'd be tempted to make a ReadOnlyUiStateMonad. And I'd have to merge the exceptions. Or just rethrow, right?

eval_ui :: CallStack.Stack => Ui.StateId a -> Deriver a Source #

Evaluate a Ui.M computation, rethrowing any errors.

lookup_id :: (Ord k, Show k) => k -> Map k a -> Deriver a Source #

Lookup map!key, throwing if it doesn't exist.


with_stack_block :: BlockId -> Deriver a -> Deriver a Source #

Make a quick trick block stack.

with_stack_track :: TrackId -> Deriver a -> Deriver a Source #

Make a quick trick track stack.

add_stack_frame :: Stack.Frame -> Dynamic -> Dynamic Source #

Add a new stack frame and hash it with the random seed.

I skip Stack.Call for seed changes. This is so I can use calls like log-seed to record the seed to hardcode it later, which is the whole point of doing this thing where I hash on every stack frame. Otherwise, the presence of the log-seed call itself would be enough to change the seed.

In addition, calculating the seed eagerly this way is actually pretty expensive, since the stack is constantly being updated, so updating it less frequently is good for performance. It's possible I could update the seed lazily to reduce this cost, but not doing something at all is still cheaper than lazy, and can't be accidentally forced.

The risk is that this makes the seed too stable, and things which should be randomized become identical.

time and duration


place :: ScoreTime -> ScoreTime -> Deriver a -> Deriver a Source #

at and stretch in one. It's a little more efficient than using them separately. The order is stretch, then shift.

warp :: Warp.Warp -> Deriver a -> Deriver a Source #

Compose warps.

track warp

add_new_track_warp :: Maybe TrackId -> Deriver () Source #

Start a new track warp for the current block_id.

This must be called for each block, and it must be called after the tempo is warped for that block so it can install the new warp.

block_logical_range :: BlockId -> Deriver (TrackTime, TrackTime) Source #

Sub-derived blocks are stretched according to their length, and this function defines the length of a block. This is therefore the logical duration of the block, which may be shorter or lorger than the end of the last event, or the ruler.

block_event_end :: BlockId -> Deriver ScoreTime Source #

Get the duration of the block according to the last event.


record_empty_tracks :: [TrackId] -> Deriver () Source #

The deriver strips out tracks that can't be derived because they have no notes. But that means the track warps and track dynamics aren't recorded, which means they don't have tempo or a playback monitor, which makes them annoying.


get_ruler :: Deriver Ruler.Marklists Source #

Get the meter marklists, if there is a ruler track here. This is called in all contexts, due to control_at, so it has to be careful to not require a ruler.



is_root_block :: Deriver Bool Source #

Am I deriving the toplevel block?