@end ignore
+@item
+Arpeggios may now use ``parenthesis'' style brackets:
+
+@lilypond[relative,ragged-right]
+ \override Arpeggio #'stencil = #ly:arpeggio::brew-chord-slur
+
+ % Note: does not work for cross staff arpeggios.
+ \override Arpeggio #'X-extent = #ly:grob::stencil-width
+ <c g' c>2\arpeggio ~ c
+@end lilypond
+
@item
Enclosing text within boxes with rounded corners is now possible,
using the @code{rounded-box} command.
but more complex scores.
@menu
-* How LilyPond input files work::
-* Voices contain music::
-* Contexts and engravers::
-* Extending the templates::
+* How LilyPond input files work::
+* Voices contain music::
+* Contexts and engravers::
+* Extending the templates::
@end menu
@node Introduction to the LilyPond file structure
@subsection Introduction to the LilyPond file structure
+@cindex input format
+@cindex file structure
+
A basic example of a LilyPond input file is
@example
There are many variations of this basic pattern, but this
example serves as a useful starting place.
+@funindex \book
+@funindex \score
+@cindex book
+@cindex score
+
Up to this point none of the examples you have seen has used a
@code{\score@{@}} command. This is because LilyPond automatically
adds the extra commands which are needed when you give it simple
LilyPond will interpret the file as though the music expression
was wrapped up inside the commands shown above.
+@cindex implicit contexts
+
@strong{A word of warning!} Many of the examples in the LilyPond
documentation will omit the @code{\new Staff} and @code{\new Voice}
commands, leaving them to be created implicitly. For simple
@}
@end example
+@funindex \header
+@funindex \layout
+@funindex \midi
+@cindex header
+@cindex layout
+@cindex midi
+
@noindent
Note that these three commands -- @code{\header}, @code{\layout}
and @code{\midi} -- are special: unlike all other commands which
Notation Reference -- @ruser{Score layout} and
@ruser{Creating MIDI files}.
+@cindex scores, multiple
+
You may code multiple @code{\score} blocks. Each will be
treated as a separate score, but they will be all combined into
a single output file. A @code{\book} command is not necessary
When LilyPond looks at this file, it takes the value of
@code{melody} (everything after the equals sign) and inserts it
whenever it sees @code{\melody}. There's nothing special about
-the names -- it could be @code{melody}, @code{global},
+the names -- it could be @code{melody}, @code{global},
@code{TimeKey},
@code{pianorighthand}, or @code{foofoobarbaz}. For more details,
see @ref{Saving typing with variables and functions}.
Remember that you can use almost any name you like as long
as it contains just alphabetic characters and is distinct from
LilyPond command names. The exact
-limitations on variable names are detailed in
+limitations on variable names are detailed in
@ruser{File structure}.
@node Score is a (single) compound musical expression
@subsection Score is a (single) compound musical expression
+@funindex \score
+@cindex score
@cindex Compound music expression
@cindex Music expression, compound
A whole Wagner opera would easily double the length of this
manual, so let's just add a singer and piano. We don't need a
@code{GrandStaff} for this ensemble, which simply groups a number
-of staves together with a brace at the left, so we shall remove
+of staves together with a brace at the left, so we shall remove
it. We @emph{do} need a singer and a piano, though.
@example
part at the same time, not one after the other! However, the
@code{<< ... >>} construct is not really necessary for the Singer
staff, as it contains only one music expression, but Staves often
-do require simultaneous Voices within them, so using
+do require simultaneous Voices within them, so using
@code{<< ... >>}
rather than braces is a good habit to adopt. We'll add some real
music later; for now let's just put in some dummy notes and lyrics.
But if we did that, the @code{\score} section would get pretty
long, and it would be harder to understand what was happening. So
let's use variables instead. These were introduced at the end
-of the previous section, remember? So, adding a few notes, we
+of the previous section, remember? So, adding a few notes, we
now have a piece of real music:
@lilypond[verbatim,quote,ragged-right]
@node Nesting music expressions
@subsection Nesting music expressions
-It is not essential to declare all staves at the beginning;
+@cindex staves, temporary
+@cindex ossias
+
+It is not essential to declare all staves at the beginning;
they may be introduced temporarily at any point. This is
-particularly useful for creating ossia sections
+particularly useful for creating ossia sections
(see @rglos{ossia}). Here is a simple example showing how
to introduce a new staff temporarily for the duration of
three notes:
\new Staff {
\relative g' {
r4 g8 g c4 c8 d |
- e4 r8
+ e4 r8
<<
{ f c c }
\new Staff {
at the beginning of the line. This is usual for clefs printed
in the middle of a line.
+@cindex staff, positioning
+
The ossia section may be placed above the staff
as follows:
}
@end lilypond
-This example uses @code{\with}, which will be explained more
+This example uses @code{\with}, which will be explained more
fully later. It is a means of modifying the default behavior
-of a single Staff. Here it says that the new staff should be
-placed above the staff called @qq{main} instead of the default
+of a single Staff. Here it says that the new staff should be
+placed above the staff called @qq{main} instead of the default
position which is below.
Ossia are often written without clef and without
@node On the un-nestedness of brackets and ties
@subsection On the un-nestedness of brackets and ties
+@cindex brackets, nesting
+
You have already met a number of different types of bracket in
writing the input file to LilyPond. These obey different rules
-which can be confusing at first. Before we explain the rules
+which can be confusing at first. Before we explain the rules
let's first review the different types of bracket.
@c attempt to force this onto a new page
@section Voices contain music
Singers need voices to sing, and so does LilyPond.
-The actual music for all instruments in a score
-is contained in Voices -- the most fundamental
+The actual music for all instruments in a score
+is contained in Voices -- the most fundamental
of all LilyPond's concepts.
@menu
-* I'm hearing Voices::
-* Explicitly instantiating voices::
-* Voices and vocals::
+* I'm hearing Voices::
+* Explicitly instantiating voices::
+* Voices and vocals::
@end menu
@node I'm hearing Voices
@cindex polyphony
@cindex layers
+@cindex multiple voices
@cindex Voice context
+@cindex context, Voice
+@cindex simultaneous music
+@cindex concurrent music
-The lowest, most fundamental or innermost layers in a LilyPond
+The lowest, most fundamental or innermost layers in a LilyPond
score are called @q{Voice contexts} or just @q{Voices} for short.
-Voices are sometimes called @q{layers} in other notation
+Voices are sometimes called @q{layers} in other notation
packages.
In fact, a Voice layer or context is the only one which can
contain music. If a Voice context is not explicitly declared
one is created automatically, as we saw at the beginning of
-this chapter. Some instruments such as an
+this chapter. Some instruments such as an
Oboe can play only one note at a time. Music written for
such instruments is monophonic and requires just a single
voice. Instruments which can play more than one note at a
time like the piano will often require multiple voices to
encode the different concurrent notes and rhythms they are
-capable of playing.
+capable of playing.
A single voice can contain many notes in a chord, of course,
so when exactly are multiple voices needed? Look first at
<d g>4 <d fis> <d a'> <d g>
@end lilypond
-This can be expressed using just the single angle bracket chord
-symbols, @code{< ... >}, and for this just a single voice is
+This can be expressed using just the single angle bracket chord
+symbols, @code{< ... >}, and for this just a single voice is
needed. But suppose the F-sharp were actually an eighth-note
followed by an eighth-note G, a passing note on the way to the A?
Now we have two notes which start at the same time but have
notes would be entered into a single voice, which would usually
cause errors. This technique is particularly suited to pieces of
music which are largely monophonic with occasional short sections
-of polyphony.
+of polyphony.
Here's how we split the chords above into two voices and add both
the passing note and a slur:
}
@end lilypond
+@funindex \voiceXXXStyle
+
The commands @code{\voiceXXXStyle} are mainly intended for use in
educational documents such as this one. They modify the color
of the note head, the stem and the beams, and the style of the
voice three to green crossed circles, and voice four (not used
here) to magenta crosses. We shall see later how commands like
these may be created by the user.
-See @ref{Visibility and color of objects}
-TODO Add link to using variables for tweaks
+See @ref{Visibility and color of objects} and
+@ref{Using variables for tweaks}.
Polyphony does not change the relationship of notes within a
@code{\relative @{ @}} block. Each note is still calculated
\relative c' @{ noteE ... @}
@end example
-Let us finally analyze the voices in a more complex piece of
+Let us finally analyze the voices in a more complex piece of
music. Here are
the notes from the first two bars of the second of Chopin's
Deux Nocturnes, Op 32. This example will be used at later
<<
{ % Voice one
\voiceOneStyle
- c2 aes4. bes8
+ c2 aes4. bes8
}
\\ % Voice two
{ \voiceTwoStyle
- aes2 f4 fes
+ aes2 f4 fes
}
\\ % No Voice three (we want stems down)
\\ % Voice four
\once \override NoteColumn #'force-hshift = #0
<ees c>2
\once \override NoteColumn #'force-hshift = #0.5
- des2
+ des2
}
>> |
<c ees aes c>1 |
@end lilypond
@noindent
-We see that this fixes the stem direction, but exposes a
-problem sometimes encountered with multiple voices -- the
+We see that this fixes the stem direction, but exposes a
+problem sometimes encountered with multiple voices -- the
stems of the notes in one voice can collide with the note heads
in other voices. In laying out the notes, LilyPond allows the
notes or chords from two voices to occupy the same vertical
LilyPond provides several ways to adjust the horizontal placing
of notes. We are not quite ready yet to see how to correct this,
so we shall leave this problem until a later section
-(see the force-hshift property in @ref{Fixing overlapping
+(see the force-hshift property in @ref{Fixing overlapping
notation} )
@node Explicitly instantiating voices
@funindex \voiceTwo
@funindex \voiceThree
@funindex \voiceFour
+@funindex \new Voice
+@cindex Voice contexts, creating
Voice contexts can also be created manually
inside a @code{<< >>} block to create polyphonic music, using
@end example
@noindent
-is equivalent to
+is equivalent to
@example
\new Staff <<
notes, and fingerings. @code{\voiceOne} and @code{\voiceThree}
make these objects point upwards, while @code{\voiceTwo} and
@code{\voiceFour} make them point downwards. These commands also
-generate a horizontal shift for each voice when this is required
-to avoid clashes of note heads. The command @code{\oneVoice}
+generate a horizontal shift for each voice when this is required
+to avoid clashes of note heads. The command @code{\oneVoice}
reverts the settings back to the normal values for a single voice.
-Let us see in some simple examples exactly what effect
+Let us see in some simple examples exactly what effect
@code{\oneVoice}, @code{\voiceOne} and @code{voiceTwo} have on
markup, ties, slurs, and dynamics:
@cindex nesting music expressions
@cindex nesting simultaneous constructs
+@cindex voices, temporary
+@cindex voices, nesting
+
More deeply nested polyphony constructs are possible, and if a
voice appears only briefly this might be a more natural way to
typeset the music.
}
@end lilypond
+@cindex spacing notes
This method of nesting new voices briefly is useful
when only small sections of the music
Vocal music presents a special difficulty: we need to combine two
expressions -- notes and lyrics.
+@funindex \new Lyrics
+@funindex \lyricsto
+@cindex Lyrics context, creating
+@cindex lyrics, linking to voice
+
You have already seen the @code{\addlyrics@{@}} command, which
handles simple scores well. However, this technique is
quite limited. For more complex music, you must introduce the
>>
@end lilypond
-Note that the lyrics must be linked to a @code{Voice} context,
-@emph{not} a @code{Staff} context. This is a case where it is
+Note that the lyrics must be linked to a @code{Voice} context,
+@emph{not} a @code{Staff} context. This is a case where it is
necessary to create @code{Staff} and @code{Voice} contexts
explicitly.
+@cindex lyrics and beaming
+@cindex beaming and lyrics
+@funindex \autoBeamOff
+
The automatic beaming which LilyPond uses by default works well
for instrumental music, but not so well for music with lyrics,
where beaming is either not required at all or is used to indicate
melismata in the lyrics. In the example above we use the command
@code{\autoBeamOff} to turn off the automatic beaming.
+@funindex \new ChoirStaff
+@funindex \lyricmode
+@cindex vocal score structure
+
Let us reuse the earlier example from Judas Maccabæus to
illustrate this more flexible technique. We first recast
it to use variables so the music and lyrics can be separated
and the variables containing the music can easily be placed
in separate files should they become too long.
+@cindex hymn structure
+
Here is a example of the first line of a hymn with four
verses, set for SATB. In this case the words for all four
parts are the same.
}
@end lilypond
+@cindex verse and refrain
+
We end with an example to show how we might code a solo verse which
continues into a two-part refrain in two staves. The
positioning of the sequential and simultaneous sections to achieve
@node Contexts explained
@subsection Contexts explained
+@cindex contexts explained
+
When music is printed, many notational elements which do not
appear explicitly in the input file must be added to the
output. For example, compare the input and output of the
@node Creating contexts
@subsection Creating contexts
+@funindex \new
+@cindex new contexts
+@cindex creating contexts
+@cindex contexts, creating
+
There can be only one top level context: the
@code{Score}
context. This is created with the @code{\score} command,
The simplest command that does this is @code{\new}.
It is prepended to a music expression, for example
-@funindex \new
-@cindex new contexts
-@cindex Context, creating
-
@example
\new @var{type} @var{music-expression}
@end example
the single top-level @code{Score} context is introduced
with @code{\score}.
+@cindex contexts, naming
+@cindex naming contexts
+
The @code{\new} command may also give a identifying name to the
context to distinguish it from other contexts of the same type,
the pitch reference point on the staff by drawing a clef symbol.
Here are some of the most common engravers together with their
-function. You will see it is easy to guess the function from
-the name, or vice versa.
+function. You will see it is usually easy to guess the function
+from the name, or vice versa.
@multitable @columnfractions .3 .7
@headitem Engraver
We shall see later how the output of LilyPond can be changed
by modifying the action of Engravers.
-
+
@node Modifying context properties
@subsection Modifying context properties
@cindex context properties
+@cindex context properties, modifying
+@cindex modifying context properties
@funindex \set
@funindex \unset
@end lilypond
We have now seen how to set the values of several different
-types of property. Note that integers and numbers are alway
+types of property. Note that integers and numbers are always
preceded by a hash sign, @code{#}, while a true or false value
is specified by ##t and ##f, with two hash signs. A text
property should be enclosed in double quotation signs, as above,
@funindex \with
+@cindex context properties, setting with \with
Context properties may also be set at the time the context is
created. Sometimes this is a clearer way of specifying a
@node Adding and removing engravers
@subsection Adding and removing engravers
-@cindex Engravers, adding
-@cindex Engravers, removing
+@cindex engravers, adding
+@cindex engravers, removing
@funindex \consists
@funindex \remove
}
<<
\new Voice
- \relative c'' {
+ \relative c'' {
\voiceOne
- c a b g
+ c a b g
}
\new Voice
\relative c' {
@subsubheading Changing all contexts of the same type
+@funindex \layout
+
The examples above show how to remove or add engravers to
-individual contexts. It is also possible to remove or add
+individual contexts. It is also possible to remove or add
engravers to every context of a specific type by placing the
commands in the appropriate context in a @code{\layout}
block. For example, if we wanted to show an ambitus for every
@end lilypond
@noindent
-The default values of context properties may also be set
+The values of context properties may also be set
for all contexts of a particular type by including the
@code{\set} command in a @code{\context} block in the
same way.
@node Extending the templates
@section Extending the templates
-You've read the tutorial, you know how to write music, you
+You've read the tutorial, you know how to write music, you
understand the fundamental concepts. But how can you
-get the staves that you want? Well, you can find lots of
-templates (see @ref{Templates}) which may give you a start.
+get the staves that you want? Well, you can find lots of
+templates (see @ref{Templates}) which may give you a start.
But what
if you want something that isn't covered there? Read on.
TODO Add links to templates after they have been moved to LSR
@menu
-* Soprano and cello::
-* Four-part SATB vocal score::
-* Building a score from scratch::
+* Soprano and cello::
+* Four-part SATB vocal score::
+* Building a score from scratch::
@end menu
@node Soprano and cello
@subsection Soprano and cello
+@cindex template, modifying
+
Start off with the template that seems closest to what you want to end
up with. Let's say that you want to write something for soprano and
cello. In this case, we would start with @q{Notes and lyrics} (for the
@}
@end example
-We don't need two @code{\version} commands. We'll need the
+We don't need two @code{\version} commands. We'll need the
@code{melody} section. We don't want two @code{\score} sections
-- if we had two @code{\score}s, we'd get the two parts separately.
-We want them together, as a duet. Within the @code{\score}
+We want them together, as a duet. Within the @code{\score}
section, we don't need two @code{\layout} or @code{\midi}.
-If we simply cut and paste the @code{melody} section, we would
+If we simply cut and paste the @code{melody} section, we would
end up with two @code{melody} definitions. This would not generate
an error, but the second one would be used for both melodies.
-So let's rename them to make them distinct. We'll call the
+So let's rename them to make them distinct. We'll call the
section for the soprano @code{sopranoMusic} and the section for
the cello @code{celloMusic}. While we're doing this, let's rename
@code{text} to be @code{sopranoLyrics}. Remember to rename both
@code{melody = \relative c' @{ } part) and the name's use (in the
@code{\score} section).
-While we're doing this, let's change the cello part's staff --
-celli normally use bass clef. We'll also give the cello some
+While we're doing this, let's change the cello part's staff --
+celli normally use bass clef. We'll also give the cello some
different notes.
@example
@noindent
underneath the soprano stuff. We also need to add @code{<<} and
@code{>>} around the music -- that tells LilyPond that there's
-more than one thing (in this case, two @code{Staves}) happening
+more than one thing (in this case, two @code{Staves}) happening
at once. The @code{\score} looks like this now
@c Indentation in this example is deliberately poor
@node Four-part SATB vocal score
@subsection Four-part SATB vocal score
+@cindex template, SATB
+@cindex SATB template
+
Most vocal scores of music written for four-part mixed choir
with orchestral accompaniment such as Mendelssohn's Elijah or
Handel's Messiah have the choral music and words on four
the piano reduction.
The order in which the contexts appear in the ChoirStaff of
-the template do not correspond with the order in the vocal
+the template do not correspond with the order in the vocal
score shown above. We need to rearrange them so there are
four staves with the words written directly underneath the
notes for each part.
the default, so the @code{\voiceXXX} commands should be removed.
We also need to specify the tenor clef for the tenors.
The way in which lyrics are specified in the template has not yet
-been encountered so we need to use the method with which we are
+been encountered so we need to use the method with which we are
familiar. We should also add the names of each staff.
Doing this gives for our ChoirStaff:
stacked one above the other:
@example
-<< % combine ChoirStaff and PianoStaff one above the other
+<< % combine ChoirStaff and PianoStaff one above the other
\new ChoirStaff <<
\new Staff = "sopranos" <<
\new Voice = "sopranos" @{ \global \sopMusic @}
>>
}
@end lilypond
-
+
@node Building a score from scratch
@subsection Building a score from scratch
+@cindex template, writing your own
+
After gaining some facility with writing LilyPond code you
may find that it is easier to build a score from scratch
rather than modifying one of the templates. You can also
develop your own style this way to suit the sort of music you
-like. Let's see how to put together the score for an organ
+like. Let's see how to put together the score for an organ
prelude as an example.
We begin with a header section. Here go the title, name
This goes underneath the PianoStaff, but it must
be simultaneous with it, so we need angle brackets
round the two. Missing these out would generate
-an error in the log file. It's a common mistake
+an error in the log file. It's a common mistake
which you'll make sooner or later! Try copying
the final example at the end of this section,
remove these angle brackets, and compile it to
@end example
It is not strictly necessary to use the simultaneous construct
-@code{<< >>} for the manual two staff and the pedal organ staff,
+@code{<< .. >>} for the manual two staff and the pedal organ staff,
since they contain only one music expression, but it does no harm
and always using angle brackets after @code{\new Staff} is a good
-habit to cultivate in case there are multiple voices.
+habit to cultivate in case there are multiple voices. The opposite
+is true for Voices: these should habitually be followed by braces
+@code{@{ .. @}} in case your music is coded in several variables
+which need to run consecutively.
Let's add this structure to the score block, and adjust the
indenting. We also add the appropriate clefs, ensure the
That completes the structure. Any three-staff organ music
will have a similar structure, although the number of voices
-may vary. All that remains now
+may vary. All that remains now
is to add the music, and combine all the parts together.
@lilypond[quote,verbatim,ragged-right,addversion]
Use relative octave mode. By default, notes are specified relative to
middle@tie{}C. The optional integer argument specifies the octave of
the starting note, where the default @code{1} is middle C.
+@code{relative} option only works when @code{fragment} option is set,
+so @code{fragment} is automatically implied by @code{relative},
+regardless of the presence of any @code{(no)fragment} option in the
+source.
@end table
LilyPond also uses @command{lilypond-book} to produce its own
PACKAGE_NAME=LilyPond
MAJOR_VERSION=2
MINOR_VERSION=11
-PATCH_LEVEL=45
+PATCH_LEVEL=46
MY_PATCH_LEVEL=
Drul_array ()
{
}
- Drul_array (T t1, T t2)
+ Drul_array (T const &t1, T const &t2)
+ {
+ set (t1, t2);
+ }
+ void set (T const &t1, T const &t2)
{
array_[0] = t1;
array_[1] = t2;
--- /dev/null
+
+\header {
+
+ texidoc = "There is a variant of the arpeggio sign that uses a
+ `vertical slur' instead of the wiggle."
+
+}
+
+\version "2.11.46"
+
+\relative c' {
+ \override Arpeggio #'stencil = #ly:arpeggio::brew-chord-slur
+
+ % Note: does not work for cross staff arpeggios.
+ \override Arpeggio #'X-extent = #ly:grob::stencil-width
+ <c g' c>2\arpeggio ~ c
+}
--- /dev/null
+\header {
+ texidoc = "You can put lyrics under completion heads."
+}
+\version "2.11.45"
+
+mel = \relative c'' {
+ c1. c1.
+}
+
+lyr = \lyricmode {
+ One two
+}
+
+\score {
+ \new Score <<
+ \new Staff <<
+ \new Voice = "completion" \with {
+ \remove "Note_heads_engraver"
+ \remove "Forbid_line_break_engraver"
+ \consists "Completion_heads_engraver"
+ } \mel
+ >>
+ \new Lyrics \lyricsto "completion" \lyr
+ >>
+}
--- /dev/null
+
+\version "2.11.45"
+
+\header {
+
+ texidoc = "In the single tie case, broken ties peek across line
+ boundaries to determine which direction to take."
+
+}
+
+
+\paper {
+ ragged-right = ##t
+}
+
+\relative c'' {
+ bes1~ \break
+ bes2.
+ \stemUp bes4 ~ \break
+ bes1
+}
*/
#include "align-interface.hh"
-#include "spanner.hh"
-#include "item.hh"
#include "axis-group-interface.hh"
-#include "pointer-group-interface.hh"
-#include "hara-kiri-group-spanner.hh"
#include "grob-array.hh"
+#include "hara-kiri-group-spanner.hh"
#include "international.hh"
+#include "item.hh"
+#include "paper-column.hh"
+#include "pointer-group-interface.hh"
+#include "spanner.hh"
+#include "skyline-pair.hh"
#include "system.hh"
#include "warn.hh"
-#include "paper-column.hh"
/*
TODO: for vertical spacing, should also include a rod & spring
#include "arpeggio.hh"
+#include "bezier.hh"
+#include "font-interface.hh"
#include "grob.hh"
+#include "lookup.hh"
#include "output-def.hh"
-#include "stem.hh"
+#include "pointer-group-interface.hh"
#include "staff-symbol-referencer.hh"
#include "staff-symbol.hh"
+#include "stem.hh"
#include "warn.hh"
-#include "font-interface.hh"
-#include "lookup.hh"
-#include "pointer-group-interface.hh"
Grob *
Arpeggio::get_common_y (Grob *me)
return mol.smobbed_copy ();
}
+MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
+SCM
+Arpeggio::brew_chord_slur (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ Interval heads = robust_scm2interval (me->get_property ("positions"),
+ Interval())
+ * Staff_symbol_referencer::staff_space (me);
+
+ Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
+ Real dy = heads.length ();
+
+ Real height_limit = 1.5;
+ Real ratio = .33;
+ Bezier curve = slur_shape (dy, height_limit, ratio);
+ curve.rotate (M_PI / 2);
+
+ Stencil mol (Lookup::slur (curve, lt, lt));
+ mol.translate_axis (heads[LEFT], Y_AXIS);
+ return mol.smobbed_copy ();
+}
+
/*
We have to do a callback, because print () triggers a
vertical alignment if it is cross-staff.
#include "paper-column.hh"
#include "paper-score.hh"
#include "separation-item.hh"
+#include "skyline-pair.hh"
#include "stencil.hh"
#include "system.hh"
#include "warn.hh"
return scm_from_double (ret);
}
-extern bool debug_skylines;
MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
SCM
Axis_group_interface::print (SCM smob)
Stencil ret;
if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
{
- ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS)).in_color (255, 0, 255));
- ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS)).in_color (0, 255, 255));
+ ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
+ .in_color (255, 0, 255));
+ ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
+ .in_color (0, 255, 255));
}
return ret.smobbed_copy ();
}
event->set_property ("pitch", pits);
event->set_property ("duration", note_dur.smobbed_copy ());
+ event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
Item *note = make_note_head (event);
}
left_to_do_ -= note_dur.get_length ();
-
if (left_to_do_)
- get_global_context ()->add_moment_to_process (now.main_part_ + left_to_do_);
+ get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length());
/*
don't do complicated arithmetic with grace notes.
*/
note_events_.clear ();
prev_notes_.clear ();
}
+ context ()->set_property ("completionBusy",
+ ly_bool2scm (note_events_.size ()));
}
Completion_heads_engraver::Completion_heads_engraver ()
"measureLength ",
/* write */
- ""
+ "completionBusy "
);
--- /dev/null
+/*
+ dynamic-align-engraver.cc -- implement Dynamic_align_engraver
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2008 Han-Wen Nienhuys <hanwen@lilypond.org>
+
+*/
+
+#include <set>
+
+#include "engraver.hh"
+
+#include "axis-group-interface.hh"
+#include "directional-element-interface.hh"
+#include "item.hh"
+#include "side-position-interface.hh"
+#include "spanner.hh"
+#include "stream-event.hh"
+
+#include "translator.icc"
+
+class Dynamic_align_engraver : public Engraver
+{
+ TRANSLATOR_DECLARATIONS (Dynamic_align_engraver);
+ DECLARE_ACKNOWLEDGER (note_column);
+ DECLARE_ACKNOWLEDGER (dynamic);
+ DECLARE_END_ACKNOWLEDGER (dynamic);
+
+protected:
+ virtual void stop_translation_timestep ();
+
+private:
+ void create_line_spanner (Stream_event *cause);
+ Spanner* line_;
+
+ vector<Spanner*> ended_;
+ vector<Spanner*> started_;
+ vector<Grob*> scripts_;
+ vector<Grob*> support_;
+
+ set<Spanner*> running_;
+};
+
+Dynamic_align_engraver::Dynamic_align_engraver ()
+{
+ line_ = 0;
+}
+
+ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
+ADD_ACKNOWLEDGER (Dynamic_align_engraver, note_column);
+ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
+
+void
+Dynamic_align_engraver::create_line_spanner (Stream_event* event)
+{
+ if (!line_)
+ line_ = make_spanner ("DynamicLineSpanner",
+ event ? event->self_scm() : SCM_EOL);
+}
+
+void
+Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info)
+{
+ if (Spanner::has_interface(info.grob()))
+ ended_.push_back (info.spanner ());
+}
+
+void
+Dynamic_align_engraver::acknowledge_note_column (Grob_info info)
+{
+ support_.push_back (info.grob ());
+}
+
+void
+Dynamic_align_engraver::acknowledge_dynamic (Grob_info info)
+{
+ Stream_event *cause = info.event_cause ();
+ create_line_spanner (cause);
+ if (Spanner::has_interface(info.grob()))
+ started_.push_back (info.spanner ());
+ else if (info.item())
+ scripts_.push_back (info.item());
+ else
+ info.grob ()->programming_error ("Unknown dynamic grob.");
+
+ Axis_group_interface::add_element (line_, info.grob ());
+
+ if (cause)
+ {
+ if (Direction d = to_dir (cause->get_property ("direction")))
+ set_grob_direction (line_, d);
+ }
+}
+
+void
+Dynamic_align_engraver::stop_translation_timestep ()
+{
+ for (vsize i = 0; i < started_.size(); i++)
+ running_.insert (started_[i]);
+ for (vsize i = 0; i < ended_.size(); i++)
+ {
+ Spanner *sp = ended_[i];
+
+ set<Spanner*>::iterator it = running_.find (sp);
+ if (it != running_.end())
+ running_.erase (it);
+ else
+ started_[i]->programming_error ("Lost track of this dynamic spanner.");
+ }
+
+ bool end = line_ && running_.empty ();
+ Direction d = LEFT;
+ do
+ {
+ if (line_
+ && ((d == LEFT && !line_->get_bound (LEFT))
+ || (end && d == RIGHT && !line_->get_bound (RIGHT))))
+ {
+ vector<Spanner*> const &spanners =
+ (d == LEFT) ? started_ : ended_;
+
+ Grob *bound = 0;
+ if (scripts_.size())
+ bound = scripts_[0];
+ else if (spanners.size())
+ bound = spanners[0]->get_bound (d);
+ else
+ {
+ programming_error ("Started DynamicLineSpanner but have no left bound.");
+ bound = unsmob_grob (get_property ("currentMusicalColumn"));
+ }
+
+ line_->set_bound (d, bound);
+ }
+ }
+ while (flip (&d) != LEFT);
+
+ for (vsize i = 0; line_ && i < support_.size (); i++)
+ Side_position_interface::add_support (line_, support_[i]);
+
+ if (end)
+ line_ = 0;
+
+ ended_.clear ();
+ started_.clear ();
+ scripts_.clear ();
+ support_.clear ();
+}
+
+
+ADD_TRANSLATOR (Dynamic_align_engraver,
+ /* doc */
+ "Align hairpins and dynamic texts on a horizontal line",
+
+ /* create */
+ "DynamicLineSpanner ",
+
+ /* read */
+ "currentMusicalColumn ",
+
+ /* write */
+ ""
+ );
#include "axis-group-interface.hh"
#include "context.hh"
+#include "engraver.hh"
#include "dimensions.hh"
#include "directional-element-interface.hh"
#include "engraver.hh"
cresc_->set_property ("style", s);
context ()->set_property ((start_type
+ "Spanner").c_str (), SCM_EOL);
- s = get_property ((start_type + "Text").c_str ());
+ s = get_property ((start_type + "Text").c_str ());
if (Text_interface::is_markup (s))
{
cresc_->set_property ("text", s);
cresc_->set_bound (LEFT, script_);
add_bound_item (line_spanner_, cresc_->get_bound (LEFT));
}
-
Axis_group_interface::add_element (line_spanner_, cresc_);
}
}
{
cause = m->to_event ()->unprotect ();
}
- if (unsmob_stream_event (cause) || unsmob_grob (cause))
+ if (e->get_property ("cause") == SCM_EOL
+ && (unsmob_stream_event (cause) || unsmob_grob (cause)))
e->set_property ("cause", cause);
Grob_info i (this, e);
{
if (!group.continuation_line_)
{
- Spanner * line = make_spanner ("BassFigureContinuation", SCM_EOL);
+ Spanner * line
+ = make_spanner ("BassFigureContinuation", SCM_EOL);
Item * item = group.figure_item_;
group.continuation_line_ = line;
line->set_bound (LEFT, item);
void
Figured_bass_engraver::create_grobs ()
{
- Grob *muscol = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
+ Grob *muscol
+ = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
if (!alignment_)
{
alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
LY_DEFINE (ly_make_global_context, "ly:make-global-context",
1, 0, 0, (SCM output_def),
"Set up a global interpretation context, using the output"
- " block @var{output_def}. The context is returned.\n")
+ " block @var{output_def}. The context is returned.")
{
LY_ASSERT_SMOB (Output_def, output_def, 1);
Output_def *odef = unsmob_output_def (output_def);
Grob *
common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
{
- for (vsize i = arr.size (); i--;)
- if (Grob *s = arr[i])
- {
- if (common)
- common = common->common_refpoint (s, a);
- else
- common = s;
- }
+ for (vsize i = 0; i < arr.size (); i++)
+ if (common)
+ common = common->common_refpoint (arr[i], a);
+ else
+ common = arr[i];
return common;
}
&& ly_is_equal (bounds[RIGHT]->get_column ()->get_property ("when"),
bounds[LEFT]->get_property ("when")))
me->suicide ();
-
}
MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
/*
should do relative to staff-symbol staff-space?
*/
-
Stencil mol;
Real x = 0.0;
DECLARE_SCHEME_CALLBACK (print, (SCM));
DECLARE_SCHEME_CALLBACK (calc_positions, (SCM));
DECLARE_SCHEME_CALLBACK (brew_chord_bracket, (SCM));
+ DECLARE_SCHEME_CALLBACK (brew_chord_slur, (SCM));
DECLARE_SCHEME_CALLBACK (width, (SCM));
DECLARE_SCHEME_CALLBACK (height, (SCM));
DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM));
class Simultaneous_music_iterator;
class Skyline;
class Skyline_entry;
+class Skyline_pair;
class Slur_configuration;
class Slur_score_state;
class Source_file;
formatted content of the grob is put into a
Property_object. Page-breaking handles Property_object objects.
*/
+
class Prob
{
DECLARE_SMOBS (Prob);
Prob (Prob const &);
virtual string name () const;
SCM type () const { return type_; }
- SCM get_property_alist (bool mutble) const;
+ SCM get_property_alist (bool _mutable) const;
SCM internal_get_property (SCM sym) const;
void instrumented_set_property (SCM, SCM, const char*, int, const char*);
void internal_set_property (SCM sym, SCM val);
};
+
DECLARE_UNSMOB(Prob,prob);
+
SCM ly_prob_set_property_x (SCM system, SCM sym, SCM value);
SCM ly_prob_property (SCM system, SCM sym, SCM dfault);
+SCM ly_prob_type_p (SCM obj, SCM sym);
+
#endif /* PROPERTY_OBJECT_HH */
--- /dev/null
+/*
+ skyline-pair.hh -- declare Skyline_pair
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2008 Han-Wen Nienhuys <hanwen@lilypond.org>
+
+*/
+
+#ifndef SKYLINE_PAIR_HH
+#define SKYLINE_PAIR_HH
+
+#include "skyline.hh"
+
+class Skyline_pair
+{
+private:
+ Drul_array<Skyline> skylines_;
+
+ DECLARE_SIMPLE_SMOBS(Skyline_pair);
+public:
+ Skyline_pair ();
+ Skyline_pair (vector<Box> const &boxes, Real horizon_padding, Axis a);
+ Skyline_pair (Box const &, Real horizon_padding, Axis a);
+ void raise (Real);
+ void shift (Real);
+ void insert (Box const &, Real horizon_padding, Axis);
+ void merge (Skyline_pair const &other);
+ Skyline &operator [] (Direction d);
+ Skyline const &operator [] (Direction d) const;
+ bool is_empty () const;
+ void print () const;
+ void print_points () const;
+};
+
+#endif /* SKYLINE_PAIR_HH */
bool is_empty () const;
};
-class Skyline_pair
-{
-private:
- Drul_array<Skyline> skylines_;
-
- DECLARE_SIMPLE_SMOBS(Skyline_pair);
-public:
- Skyline_pair ();
- Skyline_pair (vector<Box> const &boxes, Real horizon_padding, Axis a);
- Skyline_pair (Box const &, Real horizon_padding, Axis a);
- void raise (Real);
- void shift (Real);
- void insert (Box const &, Real horizon_padding, Axis);
- void merge (Skyline_pair const &other);
- Skyline &operator [] (Direction d);
- Skyline const &operator [] (Direction d) const;
- bool is_empty () const;
-};
+extern bool debug_skylines;
#endif /* SKYLINE_HH */
// todo: move to somewhere else.
Real get_broken_left_end_align () const;
void substitute_one_mutable_property (SCM sym, SCM val);
- bool fast_substitute_grob_array (SCM sym, Grob_array *);
Interval_t<Moment> spanned_time () const;
virtual Interval_t<int> spanned_rank_interval () const;
void set_my_columns ();
virtual Grob *clone () const;
virtual void do_break_processing ();
+ bool fast_substitute_grob_array (SCM sym, Grob_array *);
};
void add_bound_item (Spanner *, Grob *);
int single_tie_region_size_;
int multi_tie_region_size_;
+ Direction neutral_direction_;
Tie_details ();
void from_grob (Grob *);
{
if (music_context_)
{
- music_context_->event_source ()->remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
+ music_context_->event_source ()->
+ remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
}
music_context_ = to;
if (to)
{
- to->event_source ()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
+ to->event_source ()->add_listener (GET_LISTENER (set_busy),
+ ly_symbol2scm ("music-event"));
}
}
--- /dev/null
+/*
+ new-dynamic-engraver.cc -- implement New_dynamic_engraver
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2008 Han-Wen Nienhuys <hanwen@lilypond.org>
+
+*/
+
+
+#include "engraver.hh"
+
+#include "item.hh"
+#include "pointer-group-interface.hh"
+#include "text-interface.hh"
+#include "note-column.hh"
+#include "self-alignment-interface.hh"
+#include "spanner.hh"
+#include "stream-event.hh"
+
+#include "translator.icc"
+
+class New_dynamic_engraver : public Engraver
+{
+ TRANSLATOR_DECLARATIONS (New_dynamic_engraver);
+ DECLARE_ACKNOWLEDGER (note_column);
+ DECLARE_TRANSLATOR_LISTENER (absolute_dynamic);
+ DECLARE_TRANSLATOR_LISTENER (span_dynamic);
+
+protected:
+ virtual void process_music ();
+ virtual void stop_translation_timestep ();
+private:
+ Drul_array<Stream_event *> accepted_spanevents_drul_;
+ Spanner *current_spanner_;
+ Spanner *finished_spanner_;
+
+ Item *script_;
+ Stream_event *script_event_;
+ Stream_event *current_span_event_;
+};
+
+New_dynamic_engraver::New_dynamic_engraver ()
+{
+ script_event_ = 0;
+ current_span_event_ = 0;
+ script_ = 0;
+ finished_spanner_ = 0;
+ current_spanner_ = 0;
+ accepted_spanevents_drul_.set (0, 0);
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (New_dynamic_engraver, absolute_dynamic);
+void
+New_dynamic_engraver::listen_absolute_dynamic (Stream_event *ev)
+{
+ ASSIGN_EVENT_ONCE (script_event_, ev);
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (New_dynamic_engraver, span_dynamic);
+void
+New_dynamic_engraver::listen_span_dynamic (Stream_event *ev)
+{
+ Direction d = to_dir (ev->get_property ("span-direction"));
+
+ ASSIGN_EVENT_ONCE (accepted_spanevents_drul_[d], ev);
+}
+
+
+void
+New_dynamic_engraver::process_music ()
+{
+ if (current_spanner_
+ && (accepted_spanevents_drul_[STOP] || script_event_ || accepted_spanevents_drul_[START]))
+ {
+ Stream_event* ender = accepted_spanevents_drul_[STOP];
+ if (!ender)
+ ender = script_event_;
+
+ if (!ender)
+ ender = accepted_spanevents_drul_[START];
+
+ finished_spanner_ = current_spanner_;
+ announce_end_grob (finished_spanner_, ender->self_scm ());
+ current_spanner_ = 0;
+ current_span_event_ = 0;
+ }
+
+ if (accepted_spanevents_drul_[START])
+ {
+ current_span_event_ = accepted_spanevents_drul_[START];
+
+ SCM start_sym = current_span_event_->get_property ("class");
+ string start_type;
+
+ if (start_sym == ly_symbol2scm ("decrescendo-event"))
+ start_type = "decrescendo";
+ else if (start_sym == ly_symbol2scm ("crescendo-event"))
+ start_type = "crescendo";
+ else
+ {
+ programming_error ("unknown dynamic spanner type");
+ return;
+ }
+
+ SCM cresc_type = get_property ((start_type + "Spanner").c_str ());
+
+ if (cresc_type == ly_symbol2scm ("text"))
+ {
+ current_spanner_
+ = make_spanner ("DynamicTextSpanner",
+ accepted_spanevents_drul_[START]->self_scm ());
+
+ SCM text = get_property ((start_type + "Text").c_str ());
+ if (Text_interface::is_markup (text))
+ {
+ current_spanner_->set_property ("text", text);
+ }
+ }
+ else
+ {
+ if (cresc_type != ly_symbol2scm ("hairpin"))
+ {
+ // Fixme: should put value in error message.
+ ly_display_scm (cresc_type);
+ current_span_event_
+ ->origin()->warning ("unknown crescendo style; defaulting to hairpin.");
+ }
+ current_spanner_ = make_spanner ("Hairpin",
+ current_span_event_->self_scm ());
+ if (finished_spanner_)
+ {
+ Pointer_group_interface::add_grob (finished_spanner_,
+ ly_symbol2scm ("adjacent-hairpins"),
+ current_spanner_);
+
+ Pointer_group_interface::add_grob (current_spanner_,
+ ly_symbol2scm ("adjacent-hairpins"),
+ finished_spanner_);
+ }
+ }
+ }
+
+ if (script_event_)
+ {
+ script_ = make_item ("DynamicText", script_event_->self_scm ());
+ script_->set_property ("text",
+ script_event_->get_property ("text"));
+
+ if (finished_spanner_)
+ finished_spanner_->set_bound (RIGHT, script_);
+ if (current_spanner_)
+ {
+ current_spanner_->set_bound (LEFT, script_);
+ set_nested_property (current_spanner_,
+ scm_list_3 (ly_symbol2scm ("bound-details"),
+ ly_symbol2scm ("left"),
+ ly_symbol2scm ("attach-dir")
+ ),
+ scm_from_int (RIGHT));
+
+ }
+ }
+}
+
+
+
+void
+New_dynamic_engraver::stop_translation_timestep ()
+{
+ if (finished_spanner_ && !finished_spanner_->get_bound (RIGHT))
+ finished_spanner_->set_bound (RIGHT,
+ unsmob_grob (get_property ("currentMusicalColumn")));
+
+ if (current_spanner_ && !current_spanner_->get_bound (LEFT))
+ current_spanner_->set_bound (LEFT,
+ unsmob_grob (get_property ("currentMusicalColumn")));
+ script_ = 0;
+ script_event_ = 0;
+ accepted_spanevents_drul_.set (0, 0);
+ finished_spanner_ = 0;
+}
+
+void
+New_dynamic_engraver::acknowledge_note_column (Grob_info info)
+{
+ if (script_ && !script_->get_parent (X_AXIS))
+ {
+ extract_grob_set (info.grob (), "note-heads", heads);
+ if (heads.size ())
+ {
+ Grob *head = heads[0];
+ script_->set_parent (head, X_AXIS);
+ Self_alignment_interface::set_center_parent (script_, X_AXIS);
+ }
+ }
+
+ if (current_spanner_ && !current_spanner_->get_bound (LEFT))
+ current_spanner_->set_bound (LEFT, info.grob ());
+ if (finished_spanner_ && !finished_spanner_->get_bound (RIGHT))
+ finished_spanner_->set_bound (RIGHT, info.grob ());
+}
+
+ADD_ACKNOWLEDGER (New_dynamic_engraver, note_column);
+ADD_TRANSLATOR (New_dynamic_engraver,
+ /* doc */
+ "Create hairpins, dynamic texts, and their vertical"
+ " alignments. The symbols are collected onto a"
+ " @code{DynamicLineSpanner} grob which takes care of vertical"
+ " positioning.",
+
+ /* create */
+ "DynamicTextSpanner "
+ "DynamicText "
+ "Hairpin "
+ "TextSpanner ",
+
+ /* read */
+ "currentMusicalColumn ",
+
+ /* write */
+ ""
+ );
#include "paper-column.hh"
-#include "break-align-interface.hh"
-#include "moment.hh"
-#include "paper-score.hh"
-#include "warn.hh"
#include "axis-group-interface.hh"
-#include "spaceable-grob.hh"
-#include "text-interface.hh"
-#include "lookup.hh"
+#include "break-align-interface.hh"
#include "font-interface.hh"
-#include "output-def.hh"
-#include "pointer-group-interface.hh"
#include "grob-array.hh"
-#include "system.hh"
-#include "spring.hh"
#include "lookup.hh"
+#include "lookup.hh"
+#include "moment.hh"
+#include "output-def.hh"
+#include "paper-score.hh"
+#include "pointer-group-interface.hh"
#include "separation-item.hh"
+#include "skyline-pair.hh"
+#include "spaceable-grob.hh"
+#include "spring.hh"
#include "string-convert.hh"
+#include "system.hh"
+#include "text-interface.hh"
+#include "warn.hh"
Grob *
Paper_column::clone () const
--- /dev/null
+/*
+ paper-system-scheme.cc -- implement Paper_system bindings.
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2008 Han-Wen Nienhuys <hanwen@lilypond.org>
+
+*/
+
+#include "prob.hh"
+
+#include "skyline-pair.hh"
+
+LY_DEFINE (ly_paper_system_p, "ly:paper-system?",
+ 1, 0, 0, (SCM obj),
+ "Type predicate.")
+{
+ return ly_prob_type_p (obj, ly_symbol2scm ("paper-system"));
+}
+
+LY_DEFINE (ly_paper_system_minimum_distance, "ly:paper-system-minimum-distance",
+ 2, 0, 0, (SCM sys1, SCM sys2),
+ "Measure the minimum distance between these two paper-systems,"
+ " using their stored skylines if possible and falling back to"
+ " their extents otherwise.")
+{
+ Real ret = 0;
+ Prob *p1 = unsmob_prob (sys1);
+ Prob *p2 = unsmob_prob (sys2);
+ Skyline_pair *sky1 = Skyline_pair::unsmob (p1->get_property ("vertical-skylines"));
+ Skyline_pair *sky2 = Skyline_pair::unsmob (p2->get_property ("vertical-skylines"));
+
+ if (sky1 && sky2)
+ ret = (*sky1)[DOWN].distance ((*sky2)[UP]);
+ else
+ {
+ Stencil *s1 = unsmob_stencil (p1->get_property ("stencil"));
+ Stencil *s2 = unsmob_stencil (p2->get_property ("stencil"));
+ Interval iv1 = s1->extent (Y_AXIS);
+ Interval iv2 = s2->extent (Y_AXIS);
+ ret = iv2[UP] - iv1[DOWN];
+ }
+ return scm_from_double (ret);
+}
*/
#include "prob.hh"
-#include "skyline.hh"
LY_DEFINE (ly_prob_set_property_x, "ly:prob-set-property!",
2, 1, 0, (SCM obj, SCM sym, SCM value),
return pr->unprotect ();
}
-
-LY_DEFINE (ly_paper_system_p, "ly:paper-system?",
- 1, 0, 0, (SCM obj),
- "Type predicate.")
+
+LY_DEFINE (ly_prob_mutable_properties, "ly:prob-mutable-properties",
+ 1, 0, 0,
+ (SCM prob),
+ "Retrieve an alist of mutable properties.")
{
- return ly_prob_type_p (obj, ly_symbol2scm ("paper-system"));
+ LY_ASSERT_SMOB (Prob, prob, 1);
+ Prob *ps = unsmob_prob (prob);
+ return ps->get_property_alist (true);
}
-LY_DEFINE (ly_paper_system_minimum_distance, "ly:paper-system-minimum-distance",
- 2, 0, 0, (SCM sys1, SCM sys2),
- "Measure the minimum distance between these two paper-systems,"
- " using their stored skylines if possible and falling back to"
- " their extents otherwise.")
+LY_DEFINE (ly_prob_immutable_properties, "ly:prob-immutable-properties",
+ 1, 0, 0,
+ (SCM prob),
+ "Retrieve an alist of mutable properties.")
{
- Real ret = 0;
- Prob *p1 = unsmob_prob (sys1);
- Prob *p2 = unsmob_prob (sys2);
- Skyline_pair *sky1 = Skyline_pair::unsmob (p1->get_property ("vertical-skylines"));
- Skyline_pair *sky2 = Skyline_pair::unsmob (p2->get_property ("vertical-skylines"));
-
- if (sky1 && sky2)
- ret = (*sky1)[DOWN].distance ((*sky2)[UP]);
- else
- {
- Stencil *s1 = unsmob_stencil (p1->get_property ("stencil"));
- Stencil *s2 = unsmob_stencil (p2->get_property ("stencil"));
- Interval iv1 = s1->extent (Y_AXIS);
- Interval iv2 = s2->extent (Y_AXIS);
- ret = iv2[UP] - iv1[DOWN];
- }
- return scm_from_double (ret);
+ LY_ASSERT_SMOB (Prob, prob, 1);
+ Prob *ps = unsmob_prob (prob);
+ return ps->get_property_alist (false);
}
+
#include "separation-item.hh"
+#include "accidental-placement.hh"
#include "axis-group-interface.hh"
#include "lookup.hh"
#include "note-head.hh"
-#include "stencil.hh"
-#include "skyline.hh"
#include "paper-column.hh"
-#include "warn.hh"
#include "pointer-group-interface.hh"
-#include "accidental-placement.hh"
+#include "skyline-pair.hh"
+#include "stencil.hh"
+#include "warn.hh"
void
Separation_item::add_item (Grob *s, Item *i)
return out;
}
-extern bool debug_skylines;
MAKE_SCHEME_CALLBACK (Separation_item, print, 1)
SCM
Separation_item::print (SCM smob)
--- /dev/null
+/*
+ skyline-pair.cc -- implement Skyline_pair
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2008 Han-Wen Nienhuys <hanwen@lilypond.org>
+
+*/
+
+#include "skyline-pair.hh"
+
+#include "ly-smobs.icc"
+
+Skyline_pair::Skyline_pair ()
+ : skylines_ (Skyline (DOWN), Skyline (UP))
+{
+}
+
+Skyline_pair::Skyline_pair (vector<Box> const &boxes, Real padding, Axis a)
+ : skylines_ (Skyline (boxes, padding, a, DOWN), Skyline (boxes, padding, a, UP))
+{
+}
+
+Skyline_pair::Skyline_pair (Box const &b, Real padding, Axis a)
+ : skylines_ (Skyline (b, padding, a, DOWN), Skyline (b, padding, a, UP))
+{
+}
+
+void
+Skyline_pair::raise (Real r)
+{
+ skylines_[UP].raise (r);
+ skylines_[DOWN].raise (r);
+}
+
+void
+Skyline_pair::shift (Real r)
+{
+ skylines_[UP].shift (r);
+ skylines_[DOWN].shift (r);
+}
+
+void
+Skyline_pair::insert (Box const &b, Real padding, Axis a)
+{
+ skylines_[UP].insert (b, padding, a);
+ skylines_[DOWN].insert (b, padding, a);
+}
+
+void
+Skyline_pair::merge (Skyline_pair const &other)
+{
+ skylines_[UP].merge (other[UP]);
+ skylines_[DOWN].merge (other[DOWN]);
+}
+
+void
+Skyline_pair::print () const
+{
+ skylines_[UP].print ();
+ skylines_[DOWN].print ();
+}
+
+void
+Skyline_pair::print_points () const
+{
+ skylines_[UP].print ();
+ skylines_[DOWN].print ();
+}
+
+
+bool
+Skyline_pair::is_empty () const
+{
+ return skylines_[UP].is_empty ()
+ && skylines_[DOWN].is_empty ();
+}
+
+Skyline&
+Skyline_pair::operator [] (Direction d)
+{
+ return skylines_[d];
+}
+
+Skyline const&
+Skyline_pair::operator [] (Direction d) const
+{
+ return skylines_[d];
+}
+
+IMPLEMENT_SIMPLE_SMOBS (Skyline_pair);
+IMPLEMENT_TYPE_P (Skyline_pair, "ly:skyline-pair?");
+IMPLEMENT_DEFAULT_EQUAL_P (Skyline_pair);
+
+SCM
+Skyline_pair::mark_smob (SCM)
+{
+ return SCM_EOL;
+}
+
+int
+Skyline_pair::print_smob (SCM s, SCM port, scm_print_state *)
+{
+ Skyline_pair *r = (Skyline_pair *) SCM_CELL_WORD_1 (s);
+ (void) r;
+
+ scm_puts ("#<Skyline-pair>", port);
+ return 1;
+}
return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
}
-Skyline_pair::Skyline_pair ()
- : skylines_ (Skyline (DOWN), Skyline (UP))
-{
-}
-
-Skyline_pair::Skyline_pair (vector<Box> const &boxes, Real padding, Axis a)
- : skylines_ (Skyline (boxes, padding, a, DOWN), Skyline (boxes, padding, a, UP))
-{
-}
-
-Skyline_pair::Skyline_pair (Box const &b, Real padding, Axis a)
- : skylines_ (Skyline (b, padding, a, DOWN), Skyline (b, padding, a, UP))
-{
-}
-
-void
-Skyline_pair::raise (Real r)
-{
- skylines_[UP].raise (r);
- skylines_[DOWN].raise (r);
-}
-
-void
-Skyline_pair::shift (Real r)
-{
- skylines_[UP].shift (r);
- skylines_[DOWN].shift (r);
-}
-
-void
-Skyline_pair::insert (Box const &b, Real padding, Axis a)
-{
- skylines_[UP].insert (b, padding, a);
- skylines_[DOWN].insert (b, padding, a);
-}
-
-void
-Skyline_pair::merge (Skyline_pair const &other)
-{
- skylines_[UP].merge (other[UP]);
- skylines_[DOWN].merge (other[DOWN]);
-}
-
-bool
-Skyline_pair::is_empty () const
-{
- return skylines_[UP].is_empty ()
- && skylines_[DOWN].is_empty ();
-}
-
-Skyline&
-Skyline_pair::operator [] (Direction d)
-{
- return skylines_[d];
-}
-
-Skyline const&
-Skyline_pair::operator [] (Direction d) const
-{
- return skylines_[d];
-}
/****************************************************************/
IMPLEMENT_TYPE_P (Skyline, "ly:skyline?");
IMPLEMENT_DEFAULT_EQUAL_P (Skyline);
-IMPLEMENT_SIMPLE_SMOBS (Skyline_pair);
-IMPLEMENT_TYPE_P (Skyline_pair, "ly:skyline-pair?");
-IMPLEMENT_DEFAULT_EQUAL_P (Skyline_pair);
-
SCM
Skyline::mark_smob (SCM)
{
return 1;
}
-
-SCM
-Skyline_pair::mark_smob (SCM)
-{
- return SCM_EOL;
-}
-
-int
-Skyline_pair::print_smob (SCM s, SCM port, scm_print_state *)
-{
- Skyline_pair *r = (Skyline_pair *) SCM_CELL_WORD_1 (s);
- (void) r;
-
- scm_puts ("#<Skyline-pair>", port);
- return 1;
-}
#include "paper-column.hh"
#include "separation-item.hh"
#include "skyline.hh"
+#include "skyline-pair.hh"
#include "system.hh"
/* return the right-pointing skyline of the left-items and the left-pointing
#include "paper-score.hh"
#include "pointer-group-interface.hh"
#include "separation-item.hh"
+#include "skyline-pair.hh"
#include "spaceable-grob.hh"
#include "spacing-interface.hh"
#include "staff-spacing.hh"
#include "axis-group-interface.hh"
#include "grob-array.hh"
#include "international.hh"
+#include "lookup.hh"
#include "main.hh"
#include "output-def.hh"
#include "paper-column.hh"
#include "paper-score.hh"
#include "paper-system.hh"
#include "pointer-group-interface.hh"
+#include "skyline-pair.hh"
#include "staff-symbol-referencer.hh"
#include "warn.hh"
-#include "lookup.hh"
-
-extern bool debug_skylines;
System::System (System const &src)
: Spanner (src)
Skyline_pair *skylines = Skyline_pair::unsmob (get_property ("vertical-skylines"));
if (skylines)
{
- sys_stencil.add_stencil (Lookup::points_to_line_stencil (0.1, (*skylines)[UP].to_points (X_AXIS)).in_color (255, 0, 0));
- sys_stencil.add_stencil (Lookup::points_to_line_stencil (0.1, (*skylines)[DOWN].to_points (X_AXIS)).in_color (0, 255, 0));
+ Stencil up
+ = Lookup::points_to_line_stencil (0.1, (*skylines)[UP].to_points (X_AXIS));
+ Stencil down
+ = Lookup::points_to_line_stencil (0.1, (*skylines)[DOWN].to_points (X_AXIS));
+ sys_stencil.add_stencil (up.in_color (255, 0, 0));
+ sys_stencil.add_stencil (down.in_color (0, 255, 0));
}
}
{
staff_symbol_referencer_ = me;
staff_space_ = Staff_symbol_referencer::staff_space (me);
-
+
+ neutral_direction_ = to_dir (me->get_property ("neutral-direction"));
+ if (!neutral_direction_)
+ neutral_direction_ = DOWN;
SCM details = me->get_property ("details");
tie_configs->at (0).dir_ = Direction (sign (tie_configs->at (0).position_));
if (!tie_configs->at (0).dir_)
- tie_configs->at (0).dir_ = DOWN;
+ tie_configs->at (0).dir_
+ = (tie_configs->size() > 1) ? DOWN : details_.neutral_direction_;
}
if (!tie_configs->back ().dir_)
#include "note-head.hh"
#include "output-def.hh"
#include "paper-column.hh"
+#include "pointer-group-interface.hh"
#include "rhythmic-head.hh"
#include "spanner.hh"
#include "staff-symbol-referencer.hh"
Direction d = LEFT;
do
{
- Grob *stem = head (me, d) ? Rhythmic_head::get_stem (head (me, d)) : 0;
+ Grob *one_head = head (me, d);
+ if (!one_head && dynamic_cast<Spanner*> (me))
+ one_head = Tie::head (dynamic_cast<Spanner*> (me)->broken_neighbor (d), d);
+
+ Grob *stem = one_head ? Rhythmic_head::get_stem (one_head) : 0;
if (stem)
stem = Stem::is_invisible (stem) ? 0 : stem;
else if (int p = get_position (me))
return Direction (sign (p));
- return UP;
+ return to_dir (me->get_property("neutral-direction"));
}
// && unsmob_grob_array (yparent->get_object ("ties"))->size () > 1
)
{
+ extract_grob_set (yparent, "ties", ties);
+ if (ties.size() == 1
+ && !to_dir (me->get_property_data ("direction")))
+ {
+ assert (ties[0] == me);
+ set_grob_direction (me, Tie::get_default_dir (me));
+ }
+
/* trigger positioning. */
(void) yparent->get_property ("positioning-done");
}
"dash-period "
"details "
"direction "
- "separation-item "
"head-direction "
"line-thickness "
+ "neutral-direction "
"quant-score "
+ "separation-item "
"staff-position "
"thickness "
);
\consists "Part_combine_engraver"
\consists "Text_engraver"
- \consists "Dynamic_engraver"
+ \consists "New_dynamic_engraver"
+ \consists "Dynamic_align_engraver"
+% \consists "Dynamic_engraver"
\consists "Fingering_engraver"
\consists "Bend_engraver"
middleCClefPosition = #-6
middleCPosition = #-6
firstClef = ##t
+
+ crescendoSpanner = #'hairpin
+ decrescendoSpanner = #'hairpin
defaultBarType = #"|"
barNumberVisibility = #first-bar-number-invisible
setTextCresc = {
\set crescendoText = \markup { \italic "cresc." }
- \set crescendoSpanner = #'dashed-line
+ \set crescendoSpanner = #'text
+ \override DynamicTextSpanner #'style = #'dashed-line
}
setTextDecresc = {
\set decrescendoText = \markup { \italic "decresc." }
- \set decrescendoSpanner = #'dashed-line
+ \set decrescendoSpanner = #'text
+ \override DynamicTextSpanner #'style = #'dashed-line
}
setTextDecr = {
\set decrescendoText = \markup { \italic "decr." }
- \set decrescendoSpanner = #'dashed-line
+ \set decrescendoSpanner = #'text
+ \override DynamicTextSpanner #'style = #'dashed-line
}
setTextDim = {
\set decrescendoText = \markup { \italic "dim." }
- \set decrescendoSpanner = #'dashed-line
+ \set decrescendoSpanner = #'text
+ \override DynamicTextSpanner #'style = #'dashed-line
}
+
setHairpinCresc = {
\unset crescendoText
\unset crescendoSpanner
}
+
setHairpinDecresc = {
\unset decrescendoText
\unset decrescendoSpanner
}
+
setHairpinDim = {
\unset decrescendoText
\unset decrescendoSpanner
(clefPosition ,number? "Where should the center of the clef
symbol go, measured in half staff spaces from the center of the
staff.")
+ (completionBusy ,boolean? "Whether a completion-note head is playing.")
(connectArpeggios ,boolean? "If set, connect arpeggios across
piano staff.")
(countPercentRepeats ,boolean? "If set, produce counters for
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-public default-melisma-properties
- '(melismaBusy slurMelismaBusy tieMelismaBusy beamMelismaBusy))
+ '(melismaBusy slurMelismaBusy tieMelismaBusy beamMelismaBusy completionBusy))
(springs-and-rods . ,ly:spanner::set-spacing-rods)
(avoid-slur . inside)
(direction . ,ly:tie::calc-direction)
+ (neutral-direction . ,UP)
(stencil . ,ly:tie::print)
(font-size . -6)
(details . (
(define-builtin-markup-command (with-url layout props url arg)
(string? markup?)
- other
+ graphic
()
"
@cindex inserting URL links into text
(define-builtin-markup-command (underline layout props arg)
(markup?)
- other
+ music
((thickness 1))
"
@cindex underlining text
(define-builtin-markup-command (box layout props arg)
(markup?)
- other
+ font
((thickness 1)
(font-size 0)
(box-padding 0.2))
(define-builtin-markup-command (filled-box layout props xext yext blot)
(number-pair? number-pair? number?)
- other
+ graphic
()
"
@cindex drawing solid boxes within text
(define-builtin-markup-command (rounded-box layout props arg)
(markup?)
- other
+ graphic
((thickness 1)
(corner-radius 1)
(font-size 0)
(define-builtin-markup-command (rotate layout props ang arg)
(number? markup?)
- other
+ align
()
"
@cindex rotating text
(define-builtin-markup-command (pad-markup layout props padding arg)
(number? markup?)
- other
+ align
()
"
@cindex padding text
;; todo: fix negative space
(define-builtin-markup-command (hspace layout props amount)
(number?)
- other
+ align
()
"
@cindex creating horizontal spaces in text
(define-builtin-markup-command (simple layout props str)
(string?)
- other
+ font
()
"
@cindex simple text strings
(define-builtin-markup-command (tied-lyric layout props str)
(string?)
- other
+ music
()
"
@cindex simple text strings with tie characters
(define-builtin-markup-command (concat layout props args)
(markup-list?)
- other
+ align
()
"
@cindex concatenating text
(define-builtin-markup-command (combine layout props m1 m2)
(markup? markup?)
- other
+ align
()
"
@cindex merging text
(define-builtin-markup-command (pad-around layout props amount arg)
(number? markup?)
- other
+ align
()
"Add padding @var{amount} all around @var{arg}."
(let* ((m (interpret-markup layout props arg))
(define-builtin-markup-command (pad-x layout props amount arg)
(number? markup?)
- other
+ align
()
"
@cindex padding text horizontally
(define-builtin-markup-command (put-adjacent layout props arg1 axis dir arg2)
(markup? integer? ly:dir? markup?)
- other
+ align
()
"Put @var{arg2} next to @var{arg1}, without moving @var{arg1}."
(let ((m1 (interpret-markup layout props arg1))
(define-builtin-markup-command (pad-to-box layout props x-ext y-ext arg)
(number-pair? number-pair? markup?)
- other
+ align
()
"Make @var{arg} take at least @var{x-ext}, @var{y-ext} space."
(let* ((m (interpret-markup layout props arg))
(define-builtin-markup-command (hcenter-in layout props length arg)
(number? markup?)
- other
+ align
()
"Center @var{arg} horizontally within a box of extending
@var{length}/2 to the left and right."
(define-builtin-markup-command (lower layout props amount arg)
(number? markup?)
- other
+ align
()
"
@cindex lowering text
(define-builtin-markup-command (raise layout props amount arg)
(number? markup?)
- other
+ align
()
"
@cindex raising text
(define-builtin-markup-command (normal-size-super layout props arg)
(markup?)
- other
+ font
((baseline-skip))
"
@cindex setting superscript in standard font size
(define-builtin-markup-command (super layout props arg)
(markup?)
- other
+ font
((font-size 0)
(baseline-skip))
"
(define-builtin-markup-command (translate layout props offset arg)
(number-pair? markup?)
- other
+ align
()
"
@cindex translating text
(define-builtin-markup-command (sub layout props arg)
(markup?)
- other
+ font
((font-size 0)
(baseline-skip))
"
(define-builtin-markup-command (normal-size-sub layout props arg)
(markup?)
- other
+ font
((baseline-skip))
"
@cindex setting subscript in standard font size
(define-builtin-markup-command (hbracket layout props arg)
(markup?)
- other
+ graphic
()
"
@cindex placing horizontal brackets around text
(define-builtin-markup-command (bracket layout props arg)
(markup?)
- other
+ graphic
()
"
@cindex placing vertical brackets around text
#:name "Text markup commands"
#:desc ""
#:text "The following commands can all be used inside @code{\\markup @{ @}}."
- #:children (let ((categories (sort (hash-fold (lambda (category function+properties categories)
- (cons category categories))
- (list)
- markup-functions-by-category)
- (lambda (c1 c2)
- (string<? (symbol->string c1)
- (symbol->string c2))))))
+ #:children (let* (;; when a new category is defined, update `ordered-categories'
+ (ordered-categories '(font align graphic music fret-diagram other))
+ (raw-categories (hash-fold (lambda (category functions categories)
+ (cons category categories))
+ (list)
+ markup-functions-by-category))
+ (categories (append ordered-categories
+ (filter (lambda (cat)
+ (not (memq cat ordered-categories)))
+ raw-categories))))
(map markup-category-doc-node categories))))
(define (markup-list-doc-node)
'grob-property gprop))
(define direction-polyphonic-grobs
- '(Stem Tie Rest Slur PhrasingSlur Script TextScript Dots DotColumn Fingering))
+ '(DotColumn
+ Dots
+ Fingering
+ LaissezVibrerTie
+ PhrasingSlur
+ RepeatTie
+ Rest
+ Script
+ Slur
+ Stem
+ TextScript
+ Tie))
(define-safe-public (make-voice-props-set n)
(make-sequential-music
if k not in self.option_dict:
self.option_dict[k] = default_ly_options[k]
+ # RELATIVE does not work without FRAGMENT;
+ # make RELATIVE imply FRAGMENT
+ has_relative = self.option_dict.has_key (RELATIVE)
+ if has_relative and not self.option_dict.has_key (FRAGMENT):
+ self.option_dict[FRAGMENT] = None
+
if not has_line_width:
if type == 'lilypond' or FRAGMENT in self.option_dict:
self.option_dict[RAGGED_RIGHT] = None