]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem-engraver.cc
Fix some bugs in the dynamic engraver and PostScript backend
[lilypond.git] / lily / stem-engraver.cc
1 /*
2   stem-engraver.cc -- implement Stem_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "engraver.hh"
10
11 #include "context.hh"
12 #include "directional-element-interface.hh"
13 #include "duration.hh"
14 #include "international.hh"
15 #include "item.hh"
16 #include "misc.hh"
17 #include "rhythmic-head.hh"
18 #include "script-interface.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "stem-tremolo.hh"
21 #include "stem.hh"
22
23 /**
24    Make stems upon receiving noteheads.
25 */
26 class Stem_engraver : public Engraver
27 {
28   Grob *stem_;
29   Grob *tremolo_;
30   Music *rhythmic_ev_;
31   Music *tremolo_ev_;
32
33   TRANSLATOR_DECLARATIONS (Stem_engraver);
34
35 protected:
36   void make_stem (Grob_info);
37
38   DECLARE_ACKNOWLEDGER (rhythmic_head);
39   void stop_translation_timestep ();
40   virtual bool try_music (Music *);
41 };
42
43 Stem_engraver::Stem_engraver ()
44 {
45   tremolo_ev_ = 0;
46   stem_ = 0;
47   tremolo_ = 0;
48   rhythmic_ev_ = 0;
49 }
50
51 void
52 Stem_engraver::make_stem (Grob_info gi)
53 {
54   /* Announce the cause of the head as cause of the stem.  The
55      stem needs a rhythmic structure to fit it into a beam.  */
56   stem_ = make_item ("Stem", gi.grob ()->self_scm ());
57
58   /*
59     we take the duration log from the Event, since the duration-log
60     for a note head is always <= 2.
61   */
62   Music *music = gi.music_cause ();
63   Duration *dur = unsmob_duration (music->get_property ("duration"));
64
65   stem_->set_property ("duration-log", dur ? scm_from_int (dur->duration_log ()) : 0);
66
67   if (tremolo_ev_)
68     {
69       /* Stem tremolo is never applied to a note by default,
70          is must me requested.  But there is a default for the
71          tremolo value:
72
73          c4:8 c c:
74
75          the first and last (quarter) note bothe get one tremolo flag.  */
76       int requested_type
77         = scm_to_int (tremolo_ev_->get_property ("tremolo-type"));
78       SCM f = get_property ("tremoloFlags");
79       if (!requested_type)
80         {
81           if (scm_is_number (f))
82             requested_type = scm_to_int (f);
83           else
84             requested_type = 8;
85         }
86       else
87         context ()->set_property ("tremoloFlags", scm_from_int (requested_type));
88
89       int tremolo_flags = intlog2 (requested_type) - 2
90         - (dur->duration_log () > 2 ? dur->duration_log () - 2 : 0);
91       if (tremolo_flags <= 0)
92         {
93           tremolo_ev_->origin ()->warning (_ ("tremolo duration is too long"));
94           tremolo_flags = 0;
95         }
96
97       if (tremolo_flags)
98         {
99           tremolo_ = make_item ("StemTremolo", tremolo_ev_->self_scm ());
100
101           /* The number of tremolo flags is the number of flags of the
102              tremolo-type minus the number of flags of the note itself.  */
103           tremolo_->set_property ("flag-count", scm_from_int (tremolo_flags));
104           tremolo_->set_parent (stem_, X_AXIS);
105           stem_->set_object ("tremolo-flag", tremolo_->self_scm ());
106           tremolo_->set_object ("stem", stem_->self_scm ());
107         }
108     }
109 }
110
111 void
112 Stem_engraver::acknowledge_rhythmic_head (Grob_info gi)
113 {
114   if (Rhythmic_head::get_stem (gi.grob ()))
115     return;
116
117   Music *cause = gi.music_cause ();
118   if (!cause)
119     return;
120   Duration *d = unsmob_duration (cause->get_property ("duration"));
121   if (!d)
122     return;
123
124   if (!stem_)
125     make_stem (gi);
126
127   if (Stem::duration_log (stem_) != d->duration_log ())
128     {
129       // FIXME: 
130       gi.music_cause ()->origin ()->warning (_f ("adding note head to incompatible stem (type = %d)",
131                                                  1 << Stem::duration_log (stem_)));
132       gi.music_cause ()->origin ()->warning (_f ("maybe input should specify polyphonic voices"));
133     }
134
135   Stem::add_head (stem_, gi.grob ());
136 }
137
138 void
139 Stem_engraver::stop_translation_timestep ()
140 {
141   tremolo_ = 0;
142   if (stem_)
143     {
144       /* FIXME: junk these properties.  */
145       SCM prop = get_property ("stemLeftBeamCount");
146       if (scm_is_number (prop))
147         {
148           Stem::set_beaming (stem_, scm_to_int (prop), LEFT);
149           context ()->unset_property (ly_symbol2scm ("stemLeftBeamCount"));
150         }
151       prop = get_property ("stemRightBeamCount");
152       if (scm_is_number (prop))
153         {
154           Stem::set_beaming (stem_, scm_to_int (prop), RIGHT);
155           context ()->unset_property (ly_symbol2scm ("stemRightBeamCount"));
156         }
157       stem_ = 0;
158     }
159   tremolo_ev_ = 0;
160 }
161
162 bool
163 Stem_engraver::try_music (Music *m)
164 {
165   if (m->is_mus_type ("tremolo-event"))
166     {
167       tremolo_ev_ = m;
168       return true;
169     }
170   return false;
171 }
172
173 #include "translator.icc"
174
175 ADD_ACKNOWLEDGER (Stem_engraver, rhythmic_head);
176
177 ADD_TRANSLATOR (Stem_engraver,
178                 /* doc */ "Create stems and single-stem tremolos.  It also works together with "
179                 "the beam engraver for overriding beaming.",
180                 /* create */
181                 "Stem "
182                 "StemTremolo ",
183                 /* accept */ "tremolo-event",
184                 /* read */
185                 "tremoloFlags "
186                 "stemLeftBeamCount "
187                 "stemRightBeamCount ",
188                 /* write */ "");