Add arrowed sharp signs
[lilypond.git] / lily / auto-beam-engraver.cc
blobf86edaecc88eceddb74e17dea010458a24fa826f
1 /*
2 auto-beam-engraver.cc -- implement Auto_beam_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2008 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
9 #include "bar-line.hh"
10 #include "beaming-pattern.hh"
11 #include "beam.hh"
12 #include "context.hh"
13 #include "duration.hh"
14 #include "engraver.hh"
15 #include "item.hh"
16 #include "rest.hh"
17 #include "spanner.hh"
18 #include "stream-event.hh"
19 #include "stem.hh"
20 #include "warn.hh"
22 #include "translator.icc"
24 class Auto_beam_engraver : public Engraver
26 TRANSLATOR_DECLARATIONS (Auto_beam_engraver);
28 protected:
29 void stop_translation_timestep ();
30 void process_music ();
31 virtual void finalize ();
32 virtual void derived_mark () const;
34 DECLARE_ACKNOWLEDGER (rest);
35 DECLARE_ACKNOWLEDGER (beam);
36 DECLARE_ACKNOWLEDGER (bar_line);
37 DECLARE_ACKNOWLEDGER (stem);
38 DECLARE_TRANSLATOR_LISTENER (beam_forbid);
40 void process_acknowledged ();
42 private:
43 bool test_moment (Direction, Moment);
44 void consider_begin (Moment);
45 void consider_end (Moment);
46 Spanner *create_beam ();
47 void begin_beam ();
48 void end_beam ();
49 void junk_beam ();
50 bool is_same_grace_state (Grob *e);
51 void typeset_beam ();
53 Stream_event *forbid_;
55 shortest_mom is the shortest note in the beam.
57 Moment shortest_mom_;
58 Spanner *finished_beam_;
59 vector<Item*> *stems_;
61 int process_acknowledged_count_;
62 Moment last_add_mom_;
64 Projected ending of the beam we're working on.
66 Moment extend_mom_;
67 Moment beam_start_moment_;
68 Moment beam_start_location_;
70 // We act as if beam were created, and start a grouping anyway.
71 Beaming_pattern *grouping_;
72 SCM beam_settings_;
74 Beaming_pattern *finished_grouping_;
77 Beaming_options beaming_options_;
78 Beaming_options finished_beaming_options_;
81 void check_bar_property ();
84 void
85 Auto_beam_engraver::derived_mark () const
87 scm_gc_mark (beam_settings_);
90 void
91 Auto_beam_engraver::check_bar_property ()
93 /* Duplicated from process_music (), since
94 Repeat_acknowledge_engraver::process_music () may also set whichBar. */
96 Moment now = now_mom ();
97 if (scm_is_string (get_property ("whichBar"))
98 && beam_start_moment_ < now)
100 consider_end (shortest_mom_);
101 junk_beam ();
105 void
106 Auto_beam_engraver::process_music ()
109 don't beam over skips
111 if (stems_)
113 Moment now = now_mom ();
114 if (extend_mom_ < now)
115 end_beam ();
118 if (scm_is_string (get_property ("whichBar")))
120 consider_end (shortest_mom_);
121 junk_beam ();
124 if (forbid_)
126 consider_end (shortest_mom_);
127 junk_beam ();
131 Auto_beam_engraver::Auto_beam_engraver ()
133 forbid_ = 0;
134 process_acknowledged_count_ = 0;
135 stems_ = 0;
136 shortest_mom_ = Moment (Rational (1, 8));
137 finished_beam_ = 0;
138 finished_grouping_ = 0;
139 grouping_ = 0;
140 beam_settings_ = SCM_EOL;
143 IMPLEMENT_TRANSLATOR_LISTENER (Auto_beam_engraver, beam_forbid);
144 void
145 Auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
147 ASSIGN_EVENT_ONCE (forbid_, ev);
150 bool
151 Auto_beam_engraver::test_moment (Direction dir, Moment test)
153 return scm_call_3 (get_property ("autoBeamCheck"),
154 context ()->self_scm (),
155 scm_from_int (dir),
156 test.smobbed_copy ())
157 != SCM_BOOL_F;
160 void
161 Auto_beam_engraver::consider_begin (Moment test_mom)
163 bool on = to_boolean (get_property ("autoBeaming"));
164 if (!stems_ && on
165 && !forbid_)
167 bool b = test_moment (START, test_mom);
168 if (b)
169 begin_beam ();
173 void
174 Auto_beam_engraver::consider_end (Moment test_mom)
176 if (stems_)
178 /* Allow already started autobeam to end:
179 don't check for autoBeaming */
180 bool b = test_moment (STOP, test_mom);
181 if (b)
182 end_beam ();
186 Spanner *
187 Auto_beam_engraver::create_beam ()
189 if (to_boolean (get_property ("skipTypesetting")))
190 return 0;
192 for (vsize i = 0; i < stems_->size (); i++)
193 if (Stem::get_beam ((*stems_)[i]))
194 return 0;
197 Can't use make_spanner_from_properties () because we have to use
198 beam_settings_.
200 Spanner *beam = new Spanner (beam_settings_);
202 for (vsize i = 0; i < stems_->size (); i++)
203 Beam::add_stem (beam, (*stems_)[i]);
205 announce_grob (beam, (*stems_)[0]->self_scm ());
207 return beam;
210 void
211 Auto_beam_engraver::begin_beam ()
213 if (stems_ || grouping_)
215 programming_error ("already have autobeam");
216 return;
219 stems_ = new vector<Item*>;
220 grouping_ = new Beaming_pattern ();
221 beaming_options_.from_context (context ());
222 beam_settings_ = updated_grob_properties (context (), ly_symbol2scm ("Beam"));
224 beam_start_moment_ = now_mom ();
225 beam_start_location_
226 = robust_scm2moment (get_property ("measurePosition"), Moment (0));
229 void
230 Auto_beam_engraver::junk_beam ()
232 if (!stems_)
233 return;
235 delete stems_;
236 stems_ = 0;
237 delete grouping_;
238 grouping_ = 0;
239 beam_settings_ = SCM_EOL;
241 shortest_mom_ = Moment (Rational (1, 8));
244 void
245 Auto_beam_engraver::end_beam ()
247 if (stems_->size () < 2)
248 junk_beam ();
249 else
251 finished_beam_ = create_beam ();
253 if (finished_beam_)
255 announce_end_grob (finished_beam_, SCM_EOL);
256 finished_grouping_ = grouping_;
257 finished_beaming_options_ = beaming_options_;
259 delete stems_;
260 stems_ = 0;
261 grouping_ = 0;
262 beam_settings_ = SCM_EOL;
265 shortest_mom_ = Moment (Rational (1, 8));
268 void
269 Auto_beam_engraver::typeset_beam ()
271 if (finished_beam_)
273 if (!finished_beam_->get_bound (RIGHT))
274 finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
276 finished_grouping_->beamify (finished_beaming_options_);
277 Beam::set_beaming (finished_beam_, finished_grouping_);
278 finished_beam_ = 0;
280 delete finished_grouping_;
281 finished_grouping_ = 0;
285 void
286 Auto_beam_engraver::stop_translation_timestep ()
288 typeset_beam ();
289 process_acknowledged_count_ = 0;
290 forbid_ = 0;
293 void
294 Auto_beam_engraver::finalize ()
296 /* finished beams may be typeset */
297 typeset_beam ();
298 /* but unfinished may need another announce/acknowledge pass */
299 if (stems_)
300 junk_beam ();
304 void
305 Auto_beam_engraver::acknowledge_beam (Grob_info info)
307 (void)info;
308 check_bar_property ();
309 if (stems_)
310 end_beam ();
313 void
314 Auto_beam_engraver::acknowledge_bar_line (Grob_info info)
316 (void)info;
317 check_bar_property ();
318 if (stems_)
319 end_beam ();
322 void
323 Auto_beam_engraver::acknowledge_rest (Grob_info info)
325 (void)info;
326 check_bar_property ();
327 if (stems_)
328 end_beam ();
331 void
332 Auto_beam_engraver::acknowledge_stem (Grob_info info)
334 check_bar_property ();
335 Item *stem = dynamic_cast<Item *> (info.grob ());
336 Stream_event *ev = info.ultimate_event_cause ();
337 if (!ev->in_event_class ("rhythmic-event"))
339 programming_error ("stem must have rhythmic structure");
340 return;
344 Don't (start) auto-beam over empty stems; skips or rests
346 if (!Stem::head_count (stem))
348 if (stems_)
349 end_beam ();
350 return;
353 if (Stem::get_beam (stem))
355 if (stems_)
356 junk_beam ();
357 return;
360 int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
362 if (durlog <= 2)
364 if (stems_)
365 end_beam ();
366 return;
370 ignore grace notes.
372 Moment now = now_mom ();
373 if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
374 return;
376 Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
378 consider_end (dur);
379 consider_begin (dur);
381 if (dur < shortest_mom_)
382 shortest_mom_ = dur;
384 if (!stems_)
385 return;
387 grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
388 durlog - 2,
389 Stem::is_invisible (stem));
390 stems_->push_back (stem);
391 last_add_mom_ = now;
392 extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
395 void
396 Auto_beam_engraver::process_acknowledged ()
398 if (extend_mom_ > now_mom ())
399 return;
401 if (!process_acknowledged_count_)
403 consider_end (shortest_mom_);
404 consider_begin (shortest_mom_);
406 else if (process_acknowledged_count_ > 1)
408 if (stems_)
410 Moment now = now_mom ();
411 if ((extend_mom_ < now)
412 || ((extend_mom_ == now) && (last_add_mom_ != now)))
413 end_beam ();
414 else if (!stems_->size ())
415 junk_beam ();
419 process_acknowledged_count_++;
422 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
423 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
424 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
425 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
426 ADD_TRANSLATOR (Auto_beam_engraver,
427 /* doc */
428 "Generate beams based on measure characteristics and observed"
429 " Stems. Uses @code{beatLength}, @code{measureLength}, and"
430 " @code{measurePosition} to decide when to start and stop a"
431 " beam. Overriding beaming is done through"
432 " @ref{Stem_engraver} properties @code{stemLeftBeamCount} and"
433 " @code{stemRightBeamCount}.",
435 /* create */
436 "Beam ",
438 /* read */
439 "autoBeaming "
440 "autoBeamSettings "
441 "beatLength "
442 "subdivideBeams ",
444 /* write */