2004-02-24 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ * lily/lyric-phrasing-engraver.cc: remove
+
+ * lily/lyric-engraver.cc: rewrite so lyric-phrasing-engraver no
+ longer necessary.
+
+ * lily/extender-engraver.cc (stop_translation_timestep): rewrite
+ so lyric-phrasing-engraver is no longer necessary.
+
+ * ly/engraver-init.ly: move Break_forbid_engraver to Voice, remove
+ Grob_pq_engraver from Staff.
+
+ * lily/grob-pq-engraver.cc: remove current_grobs array, read/write
+ busyGrobs directly.
+
+ * input/regression/quote-transposition.ly: new file.
+
+ * lily/recording-group-engraver.cc (stop_translation_timestep):
+ store instrumentTuning too.
+
+ * lily/quote-iterator.cc (process): transpose events using
+ instrumentTuning.
+
+ * scripts/convert-ly.py (FatalConversionError.subst): \set transposing
+ -> \tuning <pitch>
+
+ * lily/parser.yy (command_req): add \tuning command for setting
+ instrument tuning of staff.
+
+ * Documentation/user/GNUmakefile
+ ($(outdir)/lilypond/lilypond.html): ugh: use perl iso. sed.
+
* Documentation/user/refman.itely (Customized accidental rules):
typo.
@itemize @bullet
+
@item Cue notes can now be quoted directly from the parts that
-contain them. For example,
+contain them. This will take into account tunings of source and target
+instrument. For example,
@verbatim
-\addquote oboe \notes\relative c' { fis4 fis fis fis }
+\addquote clarinet \notes\relative c' {
+ \tuning bes
+ fis4 fis fis fis
+}
\score {
\notes \relative c'' {
}
@end verbatim
+@item The transposition of an instrument can be specified using the
+@code{\tuning} command. The following command specifies an E-flat alto
+saxophone:
+@example
+ \tuning es'
+@end example
@item The naming of exported Scheme functions now follows Scheme conventions.
Changes be applied to Scheme files with convert-ly:
mkdir -p $(dir $@)
$(MAKEINFO) -I$(outdir) --output=$(outdir)/lilypond --html $<
$(MAKEINFO) -I$(outdir) --output=$@ --html --no-split --no-headers $<
- sed -i 's!../lilypond-internals!lilypond-internals/!g' $(outdir)/lilypond.html
+ perl -i~ -pe 's!../lilypond-internals!lilypond-internals/!g' $(outdir)/lilypond.html
rm -f $(outdir)/lilypond/*.png $(outdir)/lilypond/*.ly
-ln -f $(outdir)/*.png $(outdir)/*.ly $(outdir)/lilypond/
@chapter Introduction
There are a lot of programs that let you print sheet music with a
-computer, but most of them do not do good job. Most computer
+computer, but most of them do not do a good job. Most computer
printouts have a bland, mechanical look, and are unpleasant to play
from. If you agree with us on that, then you will like LilyPond: we
have tried to capture the original look of hand-engraved music. We
For tackling the first problem, notation, we have broken up the
problem into digestible (and programmable) chunks: every type of
symbol is handled by a separate program module, a so-called plug-in.
-Each plug-in are completely modular and independent, so each can be
+Each plug-in is completely modular and independent, so each can be
developed and improved separately. When put together, the plug-ins
-can solve the music notation program in cooperation. People that put
-graphics to musical ideas are called copyists or engravers, so by
+can solve the music notation problem in cooperation. People that translate
+musical ideas to graphic symbols are called copyists or engravers, so by
analogy, each plug-in is also called @code{engraver}.
In the following example, we see how we start out with a note head
In this situation, the accidentals and staff are shared, but the
stems, slurs, beams, etc. are private to each voice. Hence, engravers
-should be grouped. The engravers for note head, stems, slurs, etc. go
+should be grouped. The engravers for note heads, stems, slurs, etc. go
into a group called ``Voice context,'' while the engravers for key,
accidental, bar, etc. go into a group called ``Staff context.'' In the
case of polyphony, a single Staff context contains more than one Voice
stamping the music into zinc or pewter plates, in mirror image. The plate
would be inked, and the depressions caused by the cutting and stamping
would hold ink. An image was formed by pressing paper to the
-plate. The stamping and cutting was completely done by hand. Making
+plate. The stamping and cutting was done completely by hand. Making
corrections was cumbersome, so engraving had to be done correctly in
one go. Of course, this was a highly specialized skill, much more so
than the traditional process of printing books.
Sheet music is performance material: everything is done to aid the
-musician in letting him perform better. Music often is far away from
+musician in letting him perform better. Music is often far away from
its reader---it might be on a music stand. To make it clearly
readable, traditionally printed sheet music always uses bold symbols,
on heavy staff lines, and is printed on large sheets of paper. This
combination should be put closer together, all depending on the
combined vertical positions of the notes. The first two measures are
printed with this correction, the last two measures without. The notes
-in the last two measures form down-stem/up-stems clumps of notes.
+in the last two measures form down-stem/up-stem clumps of notes.
@node Typography and program architecture
@section Typography and program architecture
to produce better and more complex pieces. A similar situation is
present when putting typographical knowledge into a computer program.
It is not possible to come up with a definitive solution for a problem
-at the first try. Instead, we start out with simple solution that
+on the first try. Instead, we start out with a simple solution that
might cover 75% of the cases, and gradually refine that solution over
-the course of months or years, so 90 or 95 % of the cases are
+the course of months or years, so that 90 or 95 % of the cases are
handled.
This has an important implication for the design of the program: at
these 25%, 10% or 5% of the cases that are not handled
automatically. In these cases, a user must be able to override
formatting decisions. To accomplish this we store decisions in generic
-variables, and let the user manipulate thosed. For example, consider
+variables, and let the user manipulate those. For example, consider
the following fragment of notation:
@lilypond
\once \override DynamicLineSpanner #'padding = #4.0
@end example
It increases the amount of space (@code{padding}) between the note and
-the dynamic symbol to 4.0 (which is measured in staff space, so 4.0
+the dynamic symbol to 4.0 (which is measured in staff spaces, so 4.0
equals the height of a staff). The keyword @code{\once} indicates that
this is a tweak: it is only done one time.
and stems. The variables are a means to adjust formatting details in
individual cases, but they are used in a more general manner.
-Consider the case of a publisher that is not satisfied with the in the
+Consider the case of a publisher that is not satisfied with the
default layout, and wants heavier stems. Normally, they are @code{1.3}
times the thickness of staff lines, but suppose that their editions
require them to be twice the thickness of the staff lines. The same
mechanism can be used to adjust a setting globally. By issuing the
-following command, the entire piece is now formatted with thicker stems:
+following command, the entire piece is now formatted with thicker
+stems:
@example
\override Score.Stem #'thickness = #3.0
@end example
there is no need to fear: normally, it is not necessary to define
style-sheets or rewrite formatting functions. In fact, LilyPond gets a
lot of formatting right automatically, so adjusting individual layout
-situations is not needed often at all.
+situations is often not needed at all.
@node Music representation
* Bar numbers::
* Instrument names::
* Transpose::
+* Instrument transpositions::
* Multi measure rests::
* Automatic part combining::
* Hiding staves::
* Different editions from one source::
* Quoting other voices::
-* Sound output for transposing instruments::
@end menu
@node Multiple staff contexts
@code{\relative} will have no effect music that appears inside a
@code{\transpose}.
+@node Instrument transpositions
+@subsection Instrument transpositions
+The key of a transposing instrument can also be specified. This
+applies to many wind instruments, for example, clarinets (B-flat, A and
+E-flat), horn (F) and trumpet (B-flat, C, D and E-flat).
+
+@syntax
+
+The transposition is entered after the keyword @code{\transposition}:
+
+@example
+ \transposition bes %% B-flat clarinet
+@end example
+
+This command sets the property @code{instrumentTuning}. The value of
+this property is used for MIDI output and quotations. It does not
+affect how notes are printed in the current staff.
+
+@cindex transposition, MIDI
+@cindex transposition, instrument
@node Multi measure rests
@node Quoting other voices
@subsection Quoting other voices
-TODO: document!
+With quotations, fragments of other parts can be inserted into a part
+directly. Before a part can be quoted, it must be marked especially as
+quotable. This is done with code @code{\addquote} command. The
+quotation may then be done with @code{\quote}
+@example
+ \addquote @var{name} @var{music}
+ \quote @var{name} @var{duration}
+@end example
+@noindent
+Here, @var{name} is an identifying string. The @var{music} is any kind
+of music. This is an example of @code{\addquote}:
+@verbatim
+\addquote clarinet \notes\relative c' {
+ f4 fis g gis
+}
+@end verbatim
-@node Sound output for transposing instruments
-@subsection Sound output for transposing instruments
+During a part, a piece of music can be quoted with the @code{\quote}
+command.
+
+@verbatim
+ \quote clarinet 2.
+@end verbatim
+
+This would cite 3 quarter notes (a dotted half note) of the previously
+added clarinet voice.
+
+Quotations take into account the transposition both source and target
+instruments, if they are specified using the @code{\transposition} command.
+
+@lilypond[verbatim fragment]
+\addquote clarinet \notes\relative c' {
+ \transposition bes
+ f4 fis g gis
+}
+\score {
+ \notes {
+ e'8 f'8 \quote clarinet 2
+} }
+@end lilypond
+
+@refbugs
+
+Only the contents of the first @internalsref{Voice} occurring in an
+@code{\addquote} command will be considered for quotation, so
+@var{music} can not contain @code{\new} and @code{\context Voice}
+statements that would switch to a different Voice.
+
+
+@seealso
+
+In this manual: @ref{Instrument transpositions}.
+
+Examples: @inputfileref{input/regression,quote.ly}
+@inputfileref{input/regression,quote-transposition.ly}
+
+Internals: @internalsref{QuoteMusic}.
-When you want to make a MIDI file from a score containing transposed
-and untransposed instruments, you have to instruct LilyPond the pitch
-offset (in semitones) for the transposed instruments. This is done
-using the @code{transposing} property. It does not affect printed
-output:
-@cindex @code{transposing}
-@example
- \set Staff.instrument = #"Cl. in B-flat"
- \set Staff.transposing = #-2
-@end example
@node Ancient notation
\header { texidoc= "In lyric extenders, a syllable may be extended over several notes. "}
- \paper { raggedright= ##t }
+\paper { raggedright= ##t }
\score{
\notes \relative c' <<
- \context Staff {
+ \context Voice = melody {
c8[ ( d] )
r4 f4 }
- \context Lyrics \lyrics { xxx __ \skip 4 x }
+ \lyricsto melody \context Lyrics \lyrics { ah __ ha }
>>
}
+++ /dev/null
-\version "2.1.26"
-\header {
-
-texidoc = "
- Normally, the lyric is centered on the note head. However, on
- melismata, the text is left aligned on the left-side of the note head.
-
-"
-}
-
-
-\score{
-<< \notes \relative c' \context Voice = "bla" {
- \autoBeamOff
- c4( c16 d c b) c4
- d16[ e f g]
-
- }
- \lyrics \lyricsto "bla" \context Lyrics {
- al tijd
- izzz
- } >>
-
- \paper { raggedright = ##t }
-}
-
\version "2.1.26"
-\header{
- texidoc="
- The multiple stanzas of lyric phrasing are aligned according to the start
- and end of a phrase.
-
- By default, lyrics are centered with respect to the corresponding notes.
-
-@example
- | | | | |
- x| x| x| x| x|
+\header {
- 1: Start sentence melisma end.
- 2: x x x______ x
-@end example
+texidoc = "
+ Normally, the lyric is centered on the note head. However, on
+ melismata, the text is left aligned on the left-side of the note head.
- While there is a melisma, lyrics are followed by '__' and they
- are left-aligned, in this case the third x."
+"
}
-\paper { raggedright = ##t}
-\score {
- \addlyrics
- \context Voice = "v" \notes \relative c'' {
- \autoBeamOff
- a a a8 ( a) a4
- }
- <<
- \new Lyrics \lyricsto "v" \lyrics {
- \set stanza = "1:"
- Start sentence melisma end.
- }
- \new Lyrics \lyricsto "v" \lyrics {
- \set stanza = "2:"
- x x x __ x.
- }
- >>
-}
+\score{
+<< \notes \relative c' \context Voice = "bla" {
+ \autoBeamOff
+ c4( c16 d c b) c4
+ d16[ e f g]
+
+ }
+ \lyrics \lyricsto "bla" \context Lyrics {
+ alllll __ tijd
+ izzz
+ } >>
+
+ \paper { raggedright = ##t }
+}
--- /dev/null
+
+\header
+{
+
+ texidoc = "Quotations take into account the transposition of both source and target.
+In this example, all instruments play sounding central C, the target is a instrument in F."
+
+}
+\version "2.1.26"
+
+
+\addquote clarinet \notes {
+ \transposition bes
+ d'16 d'16 d'8
+ d'16 d'16 d'8
+ d'16 d'16 d'8
+ d'16 d'16 d'8
+ }
+\addquote sax \notes {
+ \transposition es'
+ a8 a a a a a a a
+ }
+
+\score {
+ \notes{
+ \transposition f % french horn
+
+ g'4
+ << \quote clarinet 4 s4^"clar" >>
+ << \quote sax 4 s4^"sax" >>
+ }
+}
+
}
\version "2.1.26"
-\addquote bla \notes\relative c' { fis4 g a b }
+\addquote bla \notes\relative c' {
+ fis4 g a b }
\score {
\notes \relative c'' {
- c8 d8 \quote 2 bla es8 gis
+ c8 d8 \quote bla 2 es8 gis
}
}
\version "2.1.26"
\header {
-texidoc = "Stanza numbers are put left of their lyric."
+texidoc = "Stanza numbers are put left of their lyric. Theyr are aligned in a column."
}
\score {
<<
- \notes { r4 r4 c4 c4 }
- \context Lyrics
- \lyrics {
+ \context Voice = "A" \notes \relative c'' { r4 r4 c4 c4 }
+ \lyricsto A \new Lyrics \lyrics {
\skip 2
\set stanza = "1."
- Foo8 Bar8
+ Foo8
+ }
+ \lyricsto A \new Lyrics \lyrics {
+ \skip 2
+ \set stanza = "2."
+ FFFooooo8
}
>>
\paper { raggedright = ##t }
-}
+}
#include "lyric-extender.hh"
#include "item.hh"
#include "engraver.hh"
+#include "context.hh"
+#include "group-interface.hh"
class Extender_engraver : public Engraver
{
Music* ev_;
Spanner* extender_;
- Spanner * finished_extender_;
+ Spanner * pending_extender_;
public:
TRANSLATOR_DECLARATIONS(Extender_engraver);
Extender_engraver::Extender_engraver ()
{
extender_ = 0;
- finished_extender_ = 0;
+ pending_extender_ = 0;
ev_ = 0;
}
+bool
+Extender_engraver::try_music (Music* r)
+{
+ if (ev_)
+ return false;
+
+ ev_ = r;
+ return true;
+}
+
+
+void
+Extender_engraver::process_music ()
+{
+ if (ev_)
+ {
+ extender_ = make_spanner ("LyricExtender");
+ announce_grob (extender_, ev_->self_scm());
+ }
+}
+
+
void
Extender_engraver::acknowledge_grob (Grob_info i)
{
Item * item = dynamic_cast<Item*> (i.grob_);
- // -> text_item
- if (item && item->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface")))
+
+ if (item
+ && item->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface")))
{
if (extender_)
extender_->set_bound (LEFT, item);
- if (finished_extender_)
- finished_extender_->set_bound (RIGHT, item);
+ if (pending_extender_)
+ pending_extender_->set_bound (RIGHT, item);
}
}
-
-bool
-Extender_engraver::try_music (Music* r)
+void
+Extender_engraver::stop_translation_timestep ()
{
- if (ev_)
- return false;
+ if (pending_extender_ && pending_extender_->get_bound (RIGHT))
+ {
+ typeset_grob (pending_extender_);
+ pending_extender_ = 0;
+ }
- ev_ = r;
- return true;
+ if (extender_ || pending_extender_)
+ {
+ Context *voice = get_voice_to_lyrics (daddy_context_);
+ Grob* h = (voice) ? get_current_note_head (voice) : 0;
+
+ if (h)
+ {
+ if (extender_)
+ Pointer_group_interface::add_grob (extender_,
+ ly_symbol2scm ("heads"), h);
+ if (pending_extender_)
+ Pointer_group_interface::add_grob (pending_extender_,
+ ly_symbol2scm ("heads"), h);
+ }
+
+ if (extender_)
+ {
+ pending_extender_ = extender_;
+ extender_ = 0;
+ }
+ }
+
+ ev_ = 0;
}
void
extender_ = 0;
}
- if (finished_extender_)
+ if (pending_extender_)
{
- completize_extender (finished_extender_);
-
- if (!finished_extender_->get_bound (RIGHT))
- finished_extender_->warning (_("unterminated extender"));
- typeset_grob (finished_extender_);
- finished_extender_ =0;
- }
-}
+ completize_extender (pending_extender_);
-void
-Extender_engraver::process_music ()
-{
- if (ev_)
- {
- extender_ = make_spanner ("LyricExtender");
- announce_grob (extender_, ev_->self_scm());
+ if (!pending_extender_->get_bound (RIGHT))
+ pending_extender_->warning (_("unterminated extender"));
+ typeset_grob (pending_extender_);
+ pending_extender_ =0;
}
}
-void
-Extender_engraver::stop_translation_timestep ()
-{
- if (finished_extender_ && finished_extender_->get_bound (RIGHT))
- {
- typeset_grob (finished_extender_);
- finished_extender_ = 0;
- }
-
- if (finished_extender_ && extender_)
- {
- programming_error ("Haven't finished extender yet.");
- typeset_grob (finished_extender_);
- finished_extender_ =0;
- }
-
- if (extender_)
- finished_extender_ = extender_;
- extender_ = 0;
-
- ev_ = 0;
-}
Forbid_line_break_engraver::Forbid_line_break_engraver(){}
-ENTER_DESCRIPTION(Forbid_line_break_engraver,
-/* descr */ "Forbid line breaks when note heads are still playing at some point.",
-/* creats*/ "",
-/* accepts */ "",
-/* acks */ "",
-/* reads */ "busyGrobs",
-/* write */ "");
-
void
Forbid_line_break_engraver::start_translation_timestep()
{
busy = gh_cdr(busy);
}
}
+
+
+ENTER_DESCRIPTION(Forbid_line_break_engraver,
+/* descr */ "Forbid line breaks when note heads are still playing at some point.",
+/* creats*/ "",
+/* accepts */ "",
+/* acks */ "",
+/* reads */ "busyGrobs",
+/* write */ "");
#include "grob.hh"
#include "warn.hh"
-/*
- TODO: should junk this engraver.
- */
-
-struct Grob_mom
-{
- Grob * grob_ ;
- Moment end_;
- Grob_mom () {}
- Grob_mom (Grob*gr, Moment e)
- {
- grob_ = gr;
- end_ = e;
- }
-};
-
-int compare (Grob_mom const &a, Grob_mom const &b)
-{
- return Moment::compare (a.end_, b.end_);
-}
-
-/****************/
-
class Grob_pq_engraver: public Engraver
{
public:
TRANSLATOR_DECLARATIONS(Grob_pq_engraver);
-
- Array<Grob_mom> current_grobs_;
protected:
virtual void initialize ();
virtual void acknowledge_grob (Grob_info);
{
}
-
void
Grob_pq_engraver::initialize ()
{
daddy_context_->set_property ("busyGrobs", SCM_EOL);
}
+LY_DEFINE(ly_grob_pq_less_p,
+ "ly:grob-pq-less?", 2 , 0 ,0, (SCM a, SCM b),
+ "Compare 2 Grob PQ entries. Internal")
+{
+ if (Moment::compare (*unsmob_moment (gh_car (a)),
+ *unsmob_moment (gh_car (b))) < 0)
+ return SCM_BOOL_T;
+ else
+ return SCM_BOOL_F;
+}
+
void
Grob_pq_engraver::acknowledge_grob (Grob_info gi)
{
l.main_part_ = 0;
}
- current_grobs_.push (Grob_mom (gi.grob_, n + l));
+ Moment end = n + l;
+ SCM lst = scm_acons (end.smobbed_copy (),
+ gi.grob_->self_scm(),
+ SCM_EOL);
+
+ SCM busy= get_property ("busyGrobs");
+ busy = scm_merge_x (lst, busy, ly_grob_pq_less_p_proc);
+ daddy_context_->set_property ("busyGrobs", busy);
}
}
-LY_DEFINE(ly_grob_pq_less_p,
- "ly:grob-pq-less?", 2 , 0 ,0, (SCM a, SCM b),
- "Compare 2 Grob PQ entries. Internal")
-{
- if ( Moment::compare (*unsmob_moment (gh_car (a)),
- *unsmob_moment (gh_car (b))) < 0)
- return SCM_BOOL_T;
- else
- return SCM_BOOL_F;
-}
-
void
Grob_pq_engraver::stop_translation_timestep ()
{
- Moment now = now_mom();
-
- current_grobs_.sort (&compare);
- SCM current_list = SCM_EOL;
- for (int i = current_grobs_.size(); i--;)
- current_list = scm_cons (scm_cons (current_grobs_[i].end_.smobbed_copy(),
- current_grobs_[i].grob_->self_scm ()), current_list);
-
- /*
- We generate some garbage here.
- */
- SCM busy = get_property ("busyGrobs");
+ Moment now = now_mom ();
+ SCM start_busy = get_property ("busyGrobs");
+ SCM busy = start_busy;
while (gh_pair_p (busy) && *unsmob_moment (gh_caar (busy)) == now)
{
busy = gh_cdr (busy);
}
-
- busy = scm_merge_x (current_list, busy, ly_grob_pq_less_p_proc);
- current_grobs_.clear ();
- daddy_context_->set_property ("busyGrobs", busy);
+
+ if (start_busy != busy)
+ daddy_context_->set_property ("busyGrobs", busy);
}
void
Todo: do something sensible. The grob-pq-engraver is not water
tight, and stuff like tupletSpannerDuration confuses it.
*/
- programming_error (_f("Skipped something?\nGrob %s ended before I expected it to end.", unsmob_grob (gh_cdar (busy))->name().to_str0()));
+ programming_error (_f("Skipped something?\nGrob %s ended before "
+ "I expected it to end.",
+ unsmob_grob (gh_cdar (busy))->name().to_str0()));
busy = gh_cdr (busy);
}
completize_hyphen (hyphen_);
if (!hyphen_->get_bound (RIGHT))
- hyphen_->warning (_ ("unterminated hyphen"));
+ {
+ hyphen_->warning (_ ("unterminated hyphen; removing"));
+ hyphen_->suicide ();
+ }
+
typeset_grob (hyphen_);
hyphen_ = 0;
}
SCM updated_grob_properties (Context* tg, SCM sym);
Context * find_context_below (Context * where,
String type, String id);
+bool melisma_busy (Context*);
-Context * unsmob_context (SCM);
+Context *get_voice_to_lyrics (Context *lyrics);
+Grob *get_current_note_head (Context * voice);
+Context *unsmob_context (SCM);
DECLARE_UNSMOB(Context,context);
DECLARE_UNSMOB(Music,music);
Music* make_music_by_name (SCM sym);
+SCM ly_deep_mus_copy (SCM);
#endif // MUSIC_HH
DOUBLE_SHARP,
};
+SCM ly_pitch_diff (SCM pitch, SCM root);
SCM ly_pitch_transpose (SCM p, SCM delta);
DECLARE_UNSMOB(Pitch,pitch);
Translator*get_translator (SCM s);
DECLARE_UNSMOB(Translator,translator);
-bool melisma_busy (Translator*);
#endif // TRANSLATOR_HH
};
-#include "translator.hh"
-
bool
-melisma_busy (Translator* tr)
+melisma_busy (Context* tr)
{
- SCM melisma_properties = tr->daddy_context_->get_property ("melismaBusyProperties");
+ SCM melisma_properties = tr->get_property ("melismaBusyProperties");
bool busy = false;
for (; gh_pair_p (melisma_properties);
#include "engraver.hh"
#include "event.hh"
#include "item.hh"
-#include "paper-def.hh"
+#include "context.hh"
#include "font-metric.hh"
-#include "side-position-interface.hh"
+#include "note-head.hh"
/**
Generate texts for lyric syllables. We only do one lyric at a time.
public:
TRANSLATOR_DECLARATIONS(Lyric_engraver);
private:
- Music * req_;
+ Music * event_;
Item* text_;
-};
-
-
+ Context* get_voice_context ();
+};
Lyric_engraver::Lyric_engraver ()
{
text_ =0;
- req_ =0;
+ event_ =0;
}
bool
{
if (r->is_mus_type ("lyric-event"))
{
- if (req_)
+ if (event_)
return false;
- req_ =r;
+ event_ =r;
return true;
}
return false;
void
Lyric_engraver::process_music ()
{
- if (req_)
+ if (event_)
{
text_= make_item ("LyricText");
- text_->set_property ("text", req_->get_property ("text"));
- announce_grob (text_, req_->self_scm());
+ text_->set_property ("text", event_->get_property ("text"));
+ announce_grob (text_, event_->self_scm());
}
}
+
+Context*
+get_voice_to_lyrics (Context *lyrics)
+{
+ SCM avc = lyrics->get_property ("associatedVoiceContext");
+ if (Context *c = unsmob_context (avc))
+ return c;
+
+ SCM voice = lyrics->get_property ("associatedVoice");
+ String nm = lyrics->id_string_;
+
+ if (gh_string_p (voice))
+ nm = ly_scm2string (voice);
+ else
+ {
+ int idx = nm.index_last ('-');
+ if (idx >= 0)
+ nm = nm.left_string (idx);
+ }
+
+ Context *c = lyrics->find_existing_context (ly_symbol2scm ("Voice"), nm);
+
+ if (c)
+ return c;
+
+ return lyrics->find_existing_context (ly_symbol2scm ("Voice"), "");
+}
+
+Grob *
+get_current_note_head (Context * voice)
+{
+ for (SCM s = voice->get_property ("busyGrobs");
+ gh_pair_p (s); s = gh_cdr (s))
+ {
+ Item*g = dynamic_cast<Item*> (unsmob_grob (gh_cdar (s)));
+
+ if (g && !g->get_column ()
+ && Note_head::has_interface (g))
+ return g;
+ }
+
+ return 0;
+}
+
void
Lyric_engraver::stop_translation_timestep ()
{
if (text_)
{
+ Context * voice = get_voice_to_lyrics (daddy_context_);
+
+ if (voice)
+ {
+ Grob *head = get_current_note_head (voice);
+
+ if (head)
+ {
+ text_->set_parent (head, X_AXIS);
+ if (melisma_busy (voice))
+ text_->set_property ("self-alignment-X", gh_int2scm (LEFT));
+ }
+ }
+
typeset_grob (text_);
text_ =0;
}
- req_ =0;
+ event_ =0;
}
do
{
r.item_l_drul_[d] = sp->get_bound (d);
- r.distance_ += r.item_l_drul_[d]->extent (r.item_l_drul_[d], X_AXIS)[-d];
+ if (r.item_l_drul_[d])
+ r.distance_ += r.item_l_drul_[d]->extent (r.item_l_drul_[d], X_AXIS)[-d];
}
while (flip (&d) != LEFT);
- r.add_to_cols ();
+ if (r.item_l_drul_[LEFT]
+ && r.item_l_drul_[RIGHT])
+ r.add_to_cols ();
+
return SCM_UNSPECIFIED;
}
+++ /dev/null
-/*
- new-phrasing-engraver.cc -- implement New_phrasing_engraver
-
-source file of the GNU LilyPond music typesetter
-
-(c) 2003--2004 Han-Wen Nienhuys <hanwen@xs4all.nl>
-
- */
-
-
-#include "context.hh"
-#include "engraver.hh"
-#include "note-head.hh"
-#include "lyric-extender.hh"
-#include "item.hh"
-#include "group-interface.hh"
-#include "side-position-interface.hh"
-
-struct Phrasing_association
-{
- String name_;
- Link_array<Grob> lyrics_;
- Link_array<Grob> heads_;
- Link_array<Spanner> past_extenders_;
- Link_array<Spanner> new_extenders_;
- Link_array<Grob> stanza_numbers_;
-
-
- bool melisma_;
-
- Phrasing_association()
- {
- melisma_ = false;
- }
-};
-
-class Lyric_phrasing_engraver : public Engraver
-{
-public:
- ~Lyric_phrasing_engraver ();
- TRANSLATOR_DECLARATIONS(Lyric_phrasing_engraver);
-protected:
- virtual void acknowledge_grob (Grob_info);
- virtual void process_acknowledged_grobs ();
- virtual void stop_translation_timestep ();
-
-private:
- void add_lyric_phrasing (Grob_info);
- void add_voice_phrasing (Grob_info);
- void add_lyric_extender (Grob_info);
- void add_stanza_number (Grob_info);
- Phrasing_association *get_phrasing_assoc (String nm);
- String get_voice_name_for_lyric (Context *tr);
- Link_array<Phrasing_association> assocs_;
-};
-
-Lyric_phrasing_engraver::Lyric_phrasing_engraver()
-{
-}
-
-void
-Lyric_phrasing_engraver::acknowledge_grob (Grob_info i)
-{
- Grob *h = i.grob_;
-
- if (Note_head::has_interface (h))
- add_voice_phrasing (i);
- else if (h->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface")))
- add_lyric_phrasing (i);
- else if (Lyric_extender::has_interface (h))
- add_lyric_extender (i);
- else if (h->internal_has_interface (ly_symbol2scm ("stanza-number-interface")))
- add_stanza_number (i);
-}
-
-Phrasing_association *
-Lyric_phrasing_engraver::get_phrasing_assoc (String nm)
-{
- Phrasing_association * a=0;
- for (int i=0 ; !a && i < assocs_.size (); i++)
- {
- if (assocs_[i]->name_ == nm)
- a = assocs_[i];
- }
-
- if (!a)
- {
- a = new Phrasing_association ;
- a->name_ = nm;
- assocs_.push (a);
- }
- return a;
-}
-
-
-
-String
-Lyric_phrasing_engraver::get_voice_name_for_lyric (Context *tr)
-{
- SCM voice_context = tr->get_property ("associatedVoiceContext");
- if (Context *vc = unsmob_context (voice_context))
- {
- return vc->id_string_;
- }
-
- SCM voice = tr->get_property ("associatedVoice");
- String nm = tr->id_string_;
- if (gh_string_p (voice))
- nm = ly_scm2string (voice);
- else
- {
- int idx = nm.index_last ('-');
- if (idx >= 0)
- nm = nm.left_string (idx);
- }
-
- return nm;
-}
-
-
-void
-Lyric_phrasing_engraver::add_lyric_extender (Grob_info inf)
-{
- Context * tr = inf.origin_trans_->daddy_context_;
- while (tr && !tr->is_alias (ly_symbol2scm ("Lyrics")))
- tr = tr->daddy_context_;
-
- if (!tr)
- return;
-
-
- Phrasing_association *a = get_phrasing_assoc (get_voice_name_for_lyric (tr));
- a->new_extenders_.push (dynamic_cast<Spanner*> (inf.grob_));
-}
-
-void
-Lyric_phrasing_engraver::add_stanza_number (Grob_info inf)
-{
- Context * tr = inf.origin_trans_->daddy_context_;
- while (tr && !tr->is_alias (ly_symbol2scm ("Lyrics")))
- tr = tr->daddy_context_;
-
- if (!tr)
- return;
-
- Phrasing_association *a = get_phrasing_assoc (get_voice_name_for_lyric (tr));
- a->stanza_numbers_.push (inf.grob_);
-}
-
-void
-Lyric_phrasing_engraver::add_voice_phrasing (Grob_info inf)
-{
- Context * tr = inf.origin_trans_->daddy_context_;
- while (tr && !tr->is_alias (ly_symbol2scm ("Voice")))
- tr = tr->daddy_context_;
-
- if (!tr)
- return;
-
- Phrasing_association *a = get_phrasing_assoc (tr->id_string_);
- a->heads_.push (inf.grob_);
- a->melisma_ = melisma_busy (inf.origin_trans_);
-}
-
-void
-Lyric_phrasing_engraver::add_lyric_phrasing (Grob_info inf)
-{
- Context * tr = inf.origin_trans_->daddy_context_;
- while (tr && !tr->is_alias (ly_symbol2scm ("Lyrics")))
- tr = tr->daddy_context_;
-
- if (!tr)
- return;
-
-
- Phrasing_association *a = get_phrasing_assoc (get_voice_name_for_lyric (tr));
- a->lyrics_.push (inf.grob_);
- a->past_extenders_.clear ();
-}
-
-void
-Lyric_phrasing_engraver::stop_translation_timestep ()
-{
- Link_array<Grob> stzs;
- Link_array<Grob> lyrs;
- for (int i = assocs_.size (); i--; )
- {
- Phrasing_association * a = assocs_[i];
- stzs.concat (a->stanza_numbers_);
- lyrs.concat (a->lyrics_);
- }
-
- for(int i= lyrs.size(); i--;)
- for (int j = stzs.size (); j--;)
- Side_position_interface::add_support (stzs[j], lyrs[i]);
-
- for (int i = assocs_.size (); i--; )
- {
- Phrasing_association * a = assocs_[i];
-
- a->stanza_numbers_.clear ();
- a->heads_.clear ();
- a->lyrics_.clear ();
- a->past_extenders_.concat (assocs_[i]->new_extenders_) ;
- a->new_extenders_.clear ();
- }
-}
-
-void
-Lyric_phrasing_engraver::process_acknowledged_grobs ()
-{
- for (int i = 0; i < assocs_.size (); i++)
- {
- Phrasing_association * a = assocs_[i];
- if (! (a->heads_.size() && (a->lyrics_.size () || a->past_extenders_.size ())))
- continue;
-
- Grob *h = a->heads_[0];
- Direction alignment = CENTER;
- if (a->melisma_)
- alignment = LEFT;
-
- for (int j = 0; j < a->lyrics_.size (); j++)
- {
- Grob *l = a->lyrics_[j];
- if (!l->get_parent (X_AXIS))
- {
- l->set_parent (h, X_AXIS);
- if (alignment)
- l->set_property ("self-alignment-X", gh_int2scm (alignment));
- }
- }
-
- for (int j = a->past_extenders_.size(); j--;)
- Pointer_group_interface::add_grob (a->past_extenders_[j],ly_symbol2scm ("heads"), h);
- }
-}
-
-Lyric_phrasing_engraver::~Lyric_phrasing_engraver ()
-{
- for (int i =assocs_.size(); i--;)
- delete assocs_[i];
-}
-
-ENTER_DESCRIPTION(Lyric_phrasing_engraver,
- "This engraver combines note heads and lyrics for alignment. ",
- "",
- "",
- "stanza-number-interface lyric-syllable-interface "
- "note-head-interface lyric-extender-interface",
- "associatedVoice",
- "");
-
{
if (m->is_mus_type ("melisma-playing-event"))
{
- return melisma_busy (this);
+ return melisma_busy (daddy_context_);
}
else if (m->is_mus_type ("melisma-span-event"))
{
#include "ly-smobs.icc"
-SCM ly_deep_mus_copy (SCM);
bool
Music::internal_is_music_type (SCM k)const
{"times", TIMES},
{"translator", TRANSLATOR},
{"transpose", TRANSPOSE},
+ {"transposition", TRANSPOSITION},
{"type", TYPE},
{"unset", UNSET},
{"with", WITH},
{
if (note_evs_.size ())
{
- int transposing_i = 0;
- //urg
- SCM prop = get_property ("transposing");
- if (gh_number_p (prop))
- transposing_i = gh_scm2int (prop);
+ int transposing = 0;
+
+ SCM prop = get_property ("instrumentTransposition");
+ if (unsmob_pitch (prop))
+ transposing = unsmob_pitch (prop)->semitone_pitch ();
while (note_evs_.size ())
{
if (Pitch * pitp = unsmob_pitch (pit))
{
- Audio_note* p = new Audio_note (*pitp, n->get_length (), transposing_i);
+ Audio_note* p = new Audio_note (*pitp, n->get_length (), transposing);
Audio_element_info info (p, n);
announce_element (info);
notes_.push (p);
%token TIME_T
%token TRANSLATOR
%token TRANSPOSE
+%token TRANSPOSITION
%token TYPE
%token UNSET
%token WITH
$$ = skip;
}
- | QUOTE duration_length STRING {
+ | QUOTE STRING duration_length {
SCM tab = THIS->lexer_->lookup_identifier ("musicQuotes");
SCM evs = SCM_EOL;
if (scm_hash_table_p (tab) == SCM_BOOL_T)
{
- SCM key = $3; // use symbol?
+ SCM key = $2; // use symbol?
evs = scm_hash_ref (tab, key, SCM_BOOL_F);
}
Music * quote = 0;
- if (scm_vector_p (evs) == SCM_BOOL_T)
+ if (gh_vector_p (evs))
{
quote = MY_MAKE_MUSIC("QuoteMusic");
- quote->set_property ("duration", $2);
+ quote->set_property ("duration", $3);
quote->set_property ("quoted-events", evs);
} else {
THIS->here_input ().warning (_f ("Can\'t find music"));
$$ = MY_MAKE_MUSIC("BarCheck");
$$->set_spot (THIS->here_input ());
}
+ | TRANSPOSITION pitch {
+ $$ = set_property_music (ly_symbol2scm ("instrumentTransposition"),
+ $2);
+ $$->set_spot (THIS-> here_input ());
+ $$ = context_spec_music (ly_symbol2scm ("Staff"), SCM_UNDEFINED,
+ $$ , SCM_EOL);
+ }
| BAR STRING {
Music *t = set_property_music (ly_symbol2scm ("whichBar"), $2);
int event_idx_;
int end_idx_ ;
+ SCM transposed_musics_;
+
DECLARE_SCHEME_CALLBACK(constructor, ());
protected:
+ virtual void derived_mark ();
virtual void construct_children ();
virtual Moment pending_moment () const;
virtual void process (Moment);
virtual bool ok () const;
};
+void
+Quote_iterator::derived_mark ()
+{
+ scm_gc_mark (transposed_musics_ );
+}
+
Quote_iterator::Quote_iterator ()
{
event_vector_ = SCM_EOL;
{
int cmp = (lo + hi) / 2;
- SCM when = gh_car (SCM_VECTOR_REF(vec, cmp));
+ SCM when = gh_caar (SCM_VECTOR_REF(vec, cmp));
bool result = (*is_less) (key, when);
if (result)
hi = cmp;
start_moment_ = now;
event_vector_ = get_music ()->get_property ("quoted-events");
- if (scm_vector_p (event_vector_) == SCM_BOOL_T)
+ if (gh_vector_p (event_vector_))
{
event_idx_ = binsearch_scm_vector (event_vector_, now.smobbed_copy (), &moment_less);
end_idx_ = binsearch_scm_vector (event_vector_, stop.smobbed_copy (), &moment_less);
Quote_iterator::pending_moment () const
{
SCM entry = SCM_VECTOR_REF (event_vector_, event_idx_);
- return *unsmob_moment (gh_car (entry)) - start_moment_;
+ return *unsmob_moment (gh_caar (entry)) - start_moment_;
}
{
entry = SCM_VECTOR_REF (event_vector_, event_idx_);
- Moment em = *unsmob_moment (gh_car (entry));
+ Moment em = *unsmob_moment (gh_caar (entry));
if (em > m)
return ;
if (gh_pair_p (entry))
{
+ Pitch * quote_pitch = unsmob_pitch (gh_cdar (entry));
+ Pitch * me_pitch = unsmob_pitch (get_outlet ()->get_property ("instrumentTransposition"));
+
for (SCM s = gh_cdr (entry); gh_pair_p (s); s = gh_cdr (s))
{
SCM ev_acc = gh_car (s);
Music * mus = unsmob_music (gh_car (ev_acc));
if (mus)
{
+ if (quote_pitch || me_pitch)
+ {
+ Pitch qp, mp;
+ if (quote_pitch)
+ qp = *quote_pitch;
+ if (me_pitch)
+ mp = *me_pitch;
+
+ Pitch diff = interval (mp, qp);
+
+ SCM copy = ly_deep_mus_copy (mus->self_scm ());
+ mus = unsmob_music (copy);
+ transposed_musics_ = gh_cons (copy, transposed_musics_);
+
+ mus->transpose (diff);
+ }
+
+
bool b = get_outlet ()->try_music (mus);
if (!b)
class Recording_group_engraver : public Engraver_group_engraver
{
+ void start ();
public:
TRANSLATOR_DECLARATIONS(Recording_group_engraver);
virtual bool try_music (Music *m);
Recording_group_engraver::initialize ()
{
Engraver_group_engraver::initialize ();
- accumulator_ = gh_cons (gh_cons (now_mom (). smobbed_copy (), SCM_EOL),
- SCM_EOL);
+ start ();
}
Recording_group_engraver::Recording_group_engraver()
start_translation_timestep(), since start_translation_timestep()
isn't called on the first time-step.
*/
+ start () ;
+}
+
+void
+Recording_group_engraver::start ()
+{
+ if (!gh_pair_p (accumulator_))
+ accumulator_ = gh_cons (SCM_EOL, SCM_EOL);
if (!gh_pair_p (gh_car (accumulator_)))
- scm_set_car_x (accumulator_, gh_cons (now_mom ().smobbed_copy (), SCM_EOL));
+ {
+ /*
+ Need to store transposition for every moment; transposition changes during pieces.
+ */
+ scm_set_car_x (accumulator_, gh_cons (gh_cons (now_mom ().smobbed_copy (),
+ get_property ("instrumentTransposition")),
+ SCM_EOL));
+ }
}
void
\consists "Bar_engraver"
% Bar_engraver must be first so default bars aren't overwritten
% with empty ones.
+
\consists "Font_size_engraver"
-% \consists "Repeat_engraver"
\consists "Volta_engraver"
\consists "Separating_line_group_engraver"
\consists "Dot_column_engraver"
\consists "Accidental_engraver"
\consists "Piano_pedal_engraver"
\consists "Instrument_name_engraver"
- \consists "Grob_pq_engraver"
- \consists "Forbid_line_break_engraver"
\consists "String_number_engraver"
\consistsend "Axis_group_engraver"
\consists "Multi_measure_rest_engraver"
\consists "Text_spanner_engraver"
\consists "Grob_pq_engraver"
+ \consists "Forbid_line_break_engraver"
\consists "Note_head_line_engraver"
\consists "Glissando_engraver"
\consists "Break_align_engraver"
\consists "Spacing_engraver"
\consists "Vertical_align_engraver"
- \consists "Lyric_phrasing_engraver"
+ \consists "Stanza_number_align_engraver"
\consists "Bar_number_engraver"
\consists "Span_arpeggio_engraver"
@code{instrument} property labels the staff in the first system, and
the @code{instr} property labels following lines.")
(instrumentEqualizer ,procedure? "[DOCUMENT-ME]")
+ (instrumentTransposition ,ly:pitch? "Define the transposition of the instrument. This is used to transpose the MIDI output, and @code{\\quote}s.")
(instrumentSupport ,list? "list of grobs to attach instrument name
to.")
Switch off for cadenzas.")
(tonic ,ly:pitch?
"The tonic of the current scale")
- (transposing ,integer? "Transpose the MIDI output. Set this property to the number of half-steps to transpose by.")
(tremoloFlags ,integer? "Number of tremolo flags to add if none is specified.")
(define-class <Voice-state> ()
(event-list #:init-value '() #:accessor events #:init-keyword #:events)
(when-moment #:accessor when #:init-keyword #:when)
+ (tuning #:accessor tuning #:init-keyword #:tuning)
(split-index #:accessor split-index)
(vector-index)
(state-vector)
(map
(lambda (v)
(make <Voice-state>
- #:when (car v)
+ #:when (caar v)
+ #:tuning (cdar v)
#:events (map car (cdr v))
))
evl))))
(if (null? event-list)
acc
(let*
- ((evs (map car (cdar event-list)))
- (now (caar event-list))
+ ((now-tun (caar event-list))
+ (evs (map car (cdar event-list)))
+ (now (car now-tun))
(notes (filter (lambda (x)
(equal? (ly:music-property x 'name) 'NoteEvent))
evs))
return str
conversions.append (((2,1,26), conv, """More Scheme function renaming"""))
+
+def conv (str):
+ def subst (m):
+ g = string.atoi (m.group (2))
+ o = g / 12
+ g -= o * 12
+ if g < 0:
+ g += 12
+ o -= 1
+
+
+ lower_pitches = filter (lambda x : x <= g, [0, 2, 4, 5, 7, 9, 11, 12])
+ s = len (lower_pitches) -1
+ a = g - lower_pitches [-1]
+
+
+ print s , lower_pitches, g, a, s
+ str = 'cdefgab' [s]
+ str += ['eses', 'es', '', 'is', 'isis'][a + 2]
+ if o < 0:
+ str += ',' * (-o - 1)
+ elif o >= 0:
+ str += "'" * (o + 1)
+
+ return '\\tuning %s ' % str
+
+
+ str = re.sub (r"\\set ([A-Za-z]+\s*\.\s*)?transposing\s*=\s*#([-0-9]+)",
+ subst, str)
+ return str
+
+conversions.append (((2,1,26), conv, """property transposing -> tuning"""))
################################
# END OF CONVERSIONS
################################