Karya, built on Sun Nov 26 01:04:37 PST 2017 (patch 0a920b2bde70c0cbac8ee09d158064798b61bbe5)

Safe HaskellNone

Perform.Lilypond.Process

Contents

Description

Convert Lilypond Events to lilypond code.

It's a terrible name, but what else am I supposed to call it? Render? Realize? Perform?

Synopsis

Documentation

process Source #

Arguments

:: Types.Config 
-> Types.Time 
-> [Meter.Meter]

one for each measure

-> [Types.Event] 
-> Either Log.Msg [Either Voices Ly] 

This is the top level function which converts a stream of Events on one staff into lilypond code.

. First pass finds voice boundaries. . Then insert rests. . Convert [Event]->[Ly]: keys, meter splitting, tuplet and tremolo. . Merge 0 dur events FreeCode in [Ly] since they have been split according to the meter.

convert_to_rests :: [Either Voices Ly] -> [Ly] Source #

Convert a staff to all rests, keeping the key, clef, and meter changes. have predicates that recognize those, and keep those Codes

data Ly Source #

Ultimately, the contants of Ly is just a bit of lilypond code. They are all converted to text via Types.to_lily and concatenated to form the full score. But I encode a fair amount of structure into the data type, which is convenient for debugging. It could also could theoretically be further modified, though I don't think I ever do that. However, ly_duration at least is used to merge FreeCode into a Ly stream.

Instances

Show Ly # 

Methods

showsPrec :: Int -> Ly -> ShowS #

show :: Ly -> String #

showList :: [Ly] -> ShowS #

Pretty.Pretty Ly # 
Types.ToLily Ly # 

Methods

to_lily :: Ly -> Text Source #

data Note Source #

Constructors

Note 

Fields

Instances

newtype Voices Source #

Each Ly list should be the same duration and have the same number of barlines.

Constructors

Voices (VoiceMap Ly) 

simple_articulations :: [(Attrs.Attributes, Code)] Source #

Automatically add lilypond code for certain attributes.

modal_articulations :: [(Attrs.Attributes, Code, Code)] Source #

Certain attributes are modal, in that they emit one thing when they start, and another when they stop.

convert_to_rests

convert_to_rests :: [Either Voices Ly] -> [Ly] Source #

Convert a staff to all rests, keeping the key, clef, and meter changes. have predicates that recognize those, and keep those Codes

process

process Source #

Arguments

:: Types.Config 
-> Types.Time 
-> [Meter.Meter]

one for each measure

-> [Types.Event] 
-> Either Log.Msg [Either Voices Ly] 

This is the top level function which converts a stream of Events on one staff into lilypond code.

. First pass finds voice boundaries. . Then insert rests. . Convert [Event]->[Ly]: keys, meter splitting, tuplet and tremolo. . Merge 0 dur events FreeCode in [Ly] since they have been split according to the meter.

collect_chunks :: [Types.Event] -> Either Error [Chunk] Source #

Group voice and non-voice Events into Chunks.

collect_voices :: [Types.Event] -> Either Text (VoiceMap Types.Event, [Types.Event]) Source #

Span events until they don't have a v_voice val.

Previously I tried to only split voices where necessary by only spanning overlapping notes, or notes with differing voices. But even when it worked as intended, joining voices this aggressively led to oddities because it would turn any two voices with the same duration notes into a chord. So now I simplify voices only at the measure level, in simplify_voices.

insert_rests :: Maybe.Maybe Types.Time -> Types.Time -> [Types.Event] -> (Types.Time, [Types.Event]) Source #

Fill gaps between events with explicit rests. Zero duration note code events have the effect of splitting up the rests.

convert

merge_free_code :: Types.Time -> [FreeCode] -> [Either Voices Ly] -> [Either Voices Ly] Source #

Mix code events into the Lys, depending on their prepend or append attrs.

until_complete :: Monad m => ([a] -> m ([b], [a])) -> [a] -> m [b] Source #

convert_chunk

convert_chunk :: Bool -> [Types.Event] -> ConvertM ([Ly], [Types.Event]) Source #

Convert the rests for the first event, and a single slice of time. If notes had to be split and tied, they are put back into the remaining events. TODO the name should change, now that Chunk is something else

focus :: [a] -> [(a, [a])] Source #

Focus on each element in turn, removing it from the list.

convert_tuplet

convert_tuplet Source #

Arguments

:: Types.Time 
-> Types.Time

score duration of notes of the tuplet

-> Types.Time 
-> [Types.Event]

extract the overlapped events and render in the tuplet

-> ConvertM ([Ly], [Types.Event]) 

Collect the notes inside the duration, run a special convert_chunk on them where the meter is ok with any duration, then wrap that in tuplet, and increment time by duration * 3/2.

tuplet_code :: Types.Time -> Rational -> Int -> NonEmpty.NonEmpty Ly -> Ly Source #

Convention is that the number on the tuplet is at least the number of notes inside, but lilypond doesn't do that automatically.

convert_tremolo

convert_tremolo :: Types.Event -> [Types.Event] -> ConvertM ([Ly], [Types.Event]) Source #

Get just pitch and code from in_tremolo notes, force them to 32nd note duration, wrap in (), and wrap the whole thing in repeat tremolo { ... }.

TODO This is sort of an ad-hoc reimplementation of convert_chord, which is grody. But the rules are different in a tremolo, duration-wise it's like a chord, but code always has to go on the notes inside, not after the whole thing.

zip_first_last :: [a] -> [(Bool, a, Bool)] Source #

convert_chord

convert_chord :: Bool -> NonEmpty.NonEmpty Types.Event -> ConvertM ([Ly], [Types.Event]) Source #

This is the lowest level of conversion. It converts a vertical slice of notes starting at the first event, and returns the rest.

consume_subdivisions :: [Types.Event] -> ConvertM [Types.Event] Source #

Handle any Constants.v_subdivision events and filter them out.

make_note Source #

Arguments

:: Types.Config 
-> Types.Time 
-> Attrs.Attributes

Previous note's Attributes, to track modal_articulations.

-> Maybe.Maybe Meter.Meter 
-> NonEmpty.NonEmpty Types.Event

Events that occur at the same time.

-> Maybe.Maybe Types.Time

start of the next note

-> (Ly, Types.Time, Attrs.Attributes, [Types.Event])

(note, note end time, clipped)

min_if :: Ord a => Maybe.Maybe a -> a -> a Source #

ly code env vars

attrs_to_code Source #

Arguments

:: Attrs.Attributes 
-> Attrs.Attributes 
-> ([Code], Attrs.Attributes)

(code to append, prev attrs for the next note)

voices_to_ly

voices_to_ly :: VoiceMap Types.Event -> ConvertM (VoiceMap Ly) Source #

Like convert_chunk, but converts within a voice, which means no nested voices are expected.

simplify_voices :: VoiceMap Ly -> [Either Voices Ly] Source #

If a whole measure of a voice is empty, omit the voice for that measure.

Previously, I tried a more fine-grained approach, documented in span_voices. This way is more complicated because it has to operate on Lys since it needs to know where the measure boundaries are, but gives much better results.

split_voices_at :: [Types.Time] -> VoiceMap Ly -> [VoiceMap Ly] Source #

Split voices every place any of them has a full measure rest.

[(1, xs), (2, ys)] -> [(1, x), (2, y)], [(1, x), (2, y)]
[(1, xs), (2, ys)] -> [(1, [xs]), (2, [ys])]

ly_start_times :: Types.Time -> [Ly] -> [Types.Time] Source #

drop 1 for end times.

misc

advance_measure :: Types.Time -> ConvertM (Maybe.Maybe Ly) Source #

Advance now to the given time, up to and including the end of the measure, but it's an error to try to go past. Return Ly with a LyBarline if this is a new measure.

If I wanted to emit Barlines automatically I'd have to collect the output [Ly] in the State, which I'd then need to parameterize since it can be [Either Voices Ly] too.

advance_unmetered :: Types.Time -> ConvertM () Source #

Advance time without regard to meter or barlines.

get_subdivision :: ConvertM Meter.Meter Source #

Get the current subdivision and check it against the meter. This is a way to override the meter for the purposes of how durations are spelled.

ConvertM

data State Source #

Constructors

State 

Fields

util

types

data Ly Source #

Ultimately, the contants of Ly is just a bit of lilypond code. They are all converted to text via Types.to_lily and concatenated to form the full score. But I encode a fair amount of structure into the data type, which is convenient for debugging. It could also could theoretically be further modified, though I don't think I ever do that. However, ly_duration at least is used to merge FreeCode into a Ly stream.

Instances

Show Ly # 

Methods

showsPrec :: Int -> Ly -> ShowS #

show :: Ly -> String #

showList :: [Ly] -> ShowS #

Pretty.Pretty Ly # 
Types.ToLily Ly # 

Methods

to_lily :: Ly -> Text Source #

data Nested Source #

This represents a bit of nested lilypond code, e.g. something { contents }.

Note

data Note Source #

Constructors

Note 

Fields

Instances

data Tie Source #

Constructors

NoTie 
TieNeutral 
TieUp 
TieDown 

Instances

Show Tie # 

Methods

showsPrec :: Int -> Tie -> ShowS #

show :: Tie -> String #

showList :: [Tie] -> ShowS #

Types.ToLily Tie # 

Methods

to_lily :: Tie -> Text Source #

type Code = Text Source #

Arbitrary bit of lilypond code. This type isn't used for non-arbitrary chunks, like note_pitches.

Rest

data Rest Source #

Constructors

Rest 

Instances

Key

data Key Source #

Constructors

Key !Text !Mode 

Instances

Eq Key # 

Methods

(==) :: Key -> Key -> Bool #

(/=) :: Key -> Key -> Bool #

Show Key # 

Methods

showsPrec :: Int -> Key -> ShowS #

show :: Key -> String #

showList :: [Key] -> ShowS #

Pretty.Pretty Key # 
Types.ToLily Key # 

Methods

to_lily :: Key -> Text Source #

type Mode = Text Source #

voice

newtype Voices Source #

Each Ly list should be the same duration and have the same number of barlines.

Constructors

Voices (VoiceMap Ly) 

type VoiceMap a = [(Voice, [a])] Source #

Voices shouldn't be repeated, so this would be more appropriate as a Map Voice [a], but it turns out all the consumers work best with a list so list it is.

Event

clip_event :: Types.Time -> Types.Event -> Maybe.Maybe Types.Event Source #

Clip off the part of the event before the given time, or Nothing if it was entirely clipped off.