1 /*
2 This file is part of LilyPond, the GNU music typesetter.
3
4 Copyright (C) 1997--2021 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "engraver.hh"
21
22 #include "context.hh"
23 #include "directional-element-interface.hh"
24 #include "duration.hh"
25 #include "international.hh"
26 #include "item.hh"
27 #include "misc.hh"
28 #include "rhythmic-head.hh"
29 #include "script-interface.hh"
30 #include "staff-symbol-referencer.hh"
31 #include "stem-tremolo.hh"
32 #include "stem.hh"
33 #include "stream-event.hh"
34
35 #include "translator.icc"
36
37 using std::vector;
38
39 class Stem_engraver : public Engraver
40 {
41 Grob *stem_;
42 Grob *tremolo_;
43 vector <Grob *> maybe_flags_;
44 Stream_event *rhythmic_ev_;
45 Stream_event *tremolo_ev_;
46 bool tuplet_start_;
47
48 TRANSLATOR_DECLARATIONS (Stem_engraver);
49
50 protected:
51 void make_stem (Grob_info, bool);
52
53 void listen_tremolo (Stream_event *);
54 void listen_tuplet_span (Stream_event *);
55 void acknowledge_rhythmic_head (Grob_info);
56 void stop_translation_timestep ();
57 void finalize () override;
58 void kill_unused_flags ();
59 };
60
Stem_engraver(Context * c)61 Stem_engraver::Stem_engraver (Context *c)
62 : Engraver (c)
63 {
64 tremolo_ev_ = 0;
65 stem_ = 0;
66 tremolo_ = 0;
67 rhythmic_ev_ = 0;
68 tuplet_start_ = false;
69 }
70
71 void
make_stem(Grob_info gi,bool tuplet_start)72 Stem_engraver::make_stem (Grob_info gi, bool tuplet_start)
73 {
74 /* Announce the cause of the head as cause of the stem. The
75 stem needs a rhythmic structure to fit it into a beam. */
76 stem_ = make_item ("Stem", gi.grob ()->self_scm ());
77 if (tuplet_start)
78 set_property (stem_, "tuplet-start", SCM_BOOL_T);
79 (void) make_item ("StemStub", gi.grob ()->self_scm ());
80 if (tremolo_ev_)
81 {
82 /* Stem tremolo is never applied to a note by default,
83 it must be requested. But there is a default for the
84 tremolo value:
85
86 c4:8 c c:
87
88 the first and last (quarter) note both get one tremolo flag. */
89 int requested_type
90 = from_scm (get_property (tremolo_ev_, "tremolo-type"), 8);
91
92 /*
93 we take the duration log from the Event, since the duration-log
94 for a note head is always <= 2.
95 */
96 Stream_event *ev = gi.event_cause ();
97 Duration *dur = unsmob<Duration> (get_property (ev, "duration"));
98
99 int tremolo_flags = intlog2 (requested_type) - 2
100 - (dur->duration_log () > 2 ? dur->duration_log () - 2 : 0);
101 if (tremolo_flags <= 0)
102 {
103 tremolo_ev_->warning (_ ("tremolo duration is too long"));
104 tremolo_flags = 0;
105 }
106
107 if (tremolo_flags)
108 {
109 tremolo_ = make_item ("StemTremolo", tremolo_ev_->self_scm ());
110
111 /* The number of tremolo flags is the number of flags of the
112 tremolo-type minus the number of flags of the note itself. */
113 set_property (tremolo_, "flag-count", to_scm (tremolo_flags));
114 tremolo_->set_x_parent (stem_);
115 set_object (stem_, "tremolo-flag", tremolo_->self_scm ());
116 set_object (tremolo_, "stem", stem_->self_scm ());
117 }
118 }
119 }
120
121 void
acknowledge_rhythmic_head(Grob_info gi)122 Stem_engraver::acknowledge_rhythmic_head (Grob_info gi)
123 {
124 if (Rhythmic_head::get_stem (gi.grob ()))
125 return;
126
127 Stream_event *cause = gi.event_cause ();
128 if (!cause)
129 return;
130 Duration *d = unsmob<Duration> (get_property (cause, "duration"));
131 if (!d)
132 return;
133
134 if (!stem_)
135 make_stem (gi, tuplet_start_);
136
137 int ds = Stem::duration_log (stem_);
138 int dc = d->duration_log ();
139
140 // half notes and quarter notes all have compatible stems.
141 // Longas are done differently (oops?), so we can't unify
142 // them with the other stemmed notes.
143 if (ds == 1)
144 ds = 2;
145 if (dc == 1)
146 dc = 2;
147 // whole notes and brevis both have no stems
148 if (ds == -1)
149 ds = 0;
150 if (dc == -1)
151 dc = 0;
152
153 if (ds != dc)
154 {
155 cause->warning (_f ("adding note head to incompatible stem (type = %d/%d)",
156 ds < 0 ? 1 << -ds : 1,
157 ds > 0 ? 1 << ds : 1));
158 cause->warning (_ ("maybe input should specify polyphonic voices"));
159 }
160
161 Stem::add_head (stem_, gi.grob ());
162
163 if (Stem::is_normal_stem (stem_)
164 && Stem::duration_log (stem_) > 2
165 && !(unsmob<Grob> (get_object (stem_, "flag"))))
166 {
167 Item *flag = make_item ("Flag", stem_->self_scm ());
168 flag->set_x_parent (stem_);
169 set_object (stem_, "flag", flag->self_scm ());
170 maybe_flags_.push_back (flag);
171 }
172 if (tuplet_start_)
173 set_property (stem_, "tuplet-start", SCM_BOOL_T);
174 }
175
176 void
kill_unused_flags()177 Stem_engraver::kill_unused_flags ()
178 {
179 for (vsize i = 0; i < maybe_flags_.size (); i++)
180 if (unsmob<Grob> (get_object (maybe_flags_[i]->get_x_parent (), "beam")))
181 maybe_flags_[i]->suicide ();
182 }
183
184 void
finalize()185 Stem_engraver::finalize ()
186 {
187 kill_unused_flags ();
188 }
189
190 void
stop_translation_timestep()191 Stem_engraver::stop_translation_timestep ()
192 {
193 if (scm_is_string (get_property (this, "whichBar")))
194 kill_unused_flags ();
195
196 tremolo_ = 0;
197 if (stem_)
198 {
199 /* FIXME: junk these properties. */
200 SCM prop = get_property (this, "stemLeftBeamCount");
201 if (scm_is_number (prop))
202 {
203 Stem::set_beaming (stem_, scm_to_int (prop), LEFT);
204 context ()->unset_property (ly_symbol2scm ("stemLeftBeamCount"));
205 }
206 prop = get_property (this, "stemRightBeamCount");
207 if (scm_is_number (prop))
208 {
209 Stem::set_beaming (stem_, scm_to_int (prop), RIGHT);
210 context ()->unset_property (ly_symbol2scm ("stemRightBeamCount"));
211 }
212 stem_ = 0;
213 }
214 tuplet_start_ = false;
215 tremolo_ev_ = 0;
216 }
217
218 void
listen_tuplet_span(Stream_event * ev)219 Stem_engraver::listen_tuplet_span (Stream_event *ev)
220 {
221 Direction dir = from_scm<Direction> (get_property (ev, "span-direction"));
222 if (dir == START)
223 {
224 // set stem property if stem already exists
225 if (stem_)
226 set_property (stem_, "tuplet-start", SCM_BOOL_T);
227 tuplet_start_ = true; // stash the value for use in later creation
228 }
229 }
230
231 void
listen_tremolo(Stream_event * ev)232 Stem_engraver::listen_tremolo (Stream_event *ev)
233 {
234 ASSIGN_EVENT_ONCE (tremolo_ev_, ev);
235 }
236
237 void
boot()238 Stem_engraver::boot ()
239 {
240 ADD_LISTENER (Stem_engraver, tuplet_span);
241 ADD_LISTENER (Stem_engraver, tremolo);
242 ADD_ACKNOWLEDGER (Stem_engraver, rhythmic_head);
243 }
244
245 ADD_TRANSLATOR (Stem_engraver,
246 /* doc */
247 "Create stems, flags and single-stem tremolos. It also works"
248 " together with the beam engraver for overriding beaming.",
249
250 /* create */
251 "Flag "
252 "Stem "
253 "StemStub "
254 "StemTremolo ",
255
256 /* read */
257 "stemLeftBeamCount "
258 "stemRightBeamCount "
259 "whichBar ",
260
261 /* write */
262 ""
263 );
264