Karya, built on Mon Jul 24 11:39:07 PDT 2017 (patch 33511aca01257b76b88de7c7a2763b7a965c084e)

Cmd.ModifyNotes

Description

Support for high level score modifications. This is companion to Cmd.ModifyEvents, which is for low level transformations.

The main interface to this is Cmd.Repl.LNote.

The score language is code to to be interpreted, not data to be manipulated. This is good for flexibility, but bad for direct transformation. Therefore, all the functions in here rely on a certain amount of conventional structure to tame the flexibility.

The lowest level, represented by a list of Notes, assumes that each note track has a single "branch" of control tracks underneath it, and each note event has control values within its extent, so each note can be sliced out and treated as a unit. So it doesn't support note tracks with multiple parallel children, and it doesn't support order-dependent control tracks, which means that relative controls are out too (TODO actually a relative control track is fine as long as there's only one). Also, since notes only carry along the controls directly underneath them, they can wind up with different control values when they are placed on a different track (TODO it would be possible to deal with this too, by copying the events forward).

I initially attempted to support trees of control tracks in full generality, or even just an ordered list of controls, but there's a problem when different Notes have different controls: where do the control tracks get merged into a tree, relative to each other? Not only do I have to invent an order, but it has to be linear, since there's also no information to merge into a branching skeleton. Since I can't create one with Notes, I felt Notes shouldn't be able to parse them either.

The Notes can be annotated with additional data, such as pitch, but of course will make it more specialized and reliant on convention. For instance, the pitches have to be extracted from the pitch events, which will fail unless there's an easily parseable pitch in there.

TODO it should be possible to get the pitch out of the derivation by finding the corresponding Score.Event by looking for its stack.

Synopsis

# Documentation

data Note Source #

This represents a single event on a note track.

Constructors

 Note Fieldsnote_start :: !TrackTime note_duration :: !TrackTime note_text :: !Text note_controls :: !ControlsThis is the contents of the child tracks, where they overlap this Note's range.note_index :: !Index note_control_track_ids :: ![TrackId]

Instances

 # Methods(==) :: Note -> Note -> Bool #(/=) :: Note -> Note -> Bool # # MethodsshowsPrec :: Int -> Note -> ShowS #show :: Note -> String #showList :: [Note] -> ShowS # # MethodsformatList :: [Note] -> Doc Source #

type Index = Int Source #

Each note has an Index, which indicates which of the selected note tracks it came from, or should be written to.

# controls

data Control Source #

A simplified version of ParseTitle.ControlType, since Notes don't support all the forms of control tracks. Put Pitch first so it sorts first, to support the convention of putting the pitch track right after the note track.

Constructors

 Pitch Pitch.ScaleId Control Control

Instances

 # Methods(==) :: Control -> Control -> Bool #(/=) :: Control -> Control -> Bool # # Methods(<) :: Control -> Control -> Bool #(<=) :: Control -> Control -> Bool #(>) :: Control -> Control -> Bool #(>=) :: Control -> Control -> Bool # # MethodsshowList :: [Control] -> ShowS # # MethodsformatList :: [Control] -> Doc Source #

Put the pitch tracks next to the note, the rest go in alphabetical order.

# selection

type ModifyNotes m = BlockId -> [(Note, TrackId)] -> m [Note] Source #

Modify notes.

notes :: Monad m => ([Note] -> [Note]) -> ModifyNotes m Source #

note :: Monad m => (Note -> Note) -> ModifyNotes m Source #

Modify a single note.

selection :: Cmd.M m => ModifyNotes m -> m () Source #

Modify notes on the selected tracks. Only the top level note tracks are affected, so you can select an entire block and not worry about mangling parent controls.

This may add new tracks, but will not delete tracks that are made empty. It could, but it seems easy enough to delete the tracks by hand once I verify that the transformation worked. TODO revisit this if it's annoying

selected_notes :: Cmd.M m => m [(Note, TrackId)] Source #

Find the top-level note tracks in the selection, and reduce them down to Notes.

## annotated transformations

type Annotated a m = [(Note, a)] -> m [Note] Source #

find_controls :: [(Note, TrackId)] -> Vector.Vector Score.Event -> [(Note, (Maybe Transposed, ControlValMap))] Source #

This finds the controls of each note by looking for its corresponding event in the performance. TODO matching by stack seems like it could be inaccurate, and inefficient too. Shouldn't I look up the signal directly from the performance?

The whole thing fails if a title is unparseable or the control tracks have a fork in the skeleton.

This is similar to slice and I initially spent some time trying to reuse it, but it's different enough that most of the work that slice does doesn't apply here.

# write

data NoteTrack Source #

Constructors

 NoteTrack Events.Events Controls

Instances

 # Methods # MethodsshowList :: [NoteTrack] -> ShowS # # Methodsmconcat :: [NoteTrack] -> NoteTrack #

Arguments

 :: Ui.M m => BlockId -> [TrackId] The TrackIds are expected to line up with NoteTracks. If there are more NoteTracks than TrackIds, new tracks will be created. -> [NoteTrack] -> m ()

Write NoteTracks to the given block. It may create new tracks, but won't delete ones that are made empty.

tracknum_after :: Ui.M m => BlockId -> [TrackId] -> m TrackNum Source #

Get the tracknum after the given tracks.

bottom_track :: Ui.M m => BlockId -> TrackId -> m (Maybe Ui.TrackInfo) Source #

Get the bottom track below the given TrackId. If there are more than one, pick the one with the highest TrackNum.