Nitpick: ly:spanner-bound grob name slur -> spanner.
[lilypond.git] / lily / auto-beam-engraver.cc
blob4c813a7038264d3fb0a30b14fbe29e1439a83aed
1 /*
2 auto-beam-engraver.cc -- implement Auto_beam_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2009 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 check_bar_property ();
308 if (stems_)
309 end_beam ();
312 void
313 Auto_beam_engraver::acknowledge_bar_line (Grob_info /* info */)
315 check_bar_property ();
316 if (stems_)
317 end_beam ();
320 void
321 Auto_beam_engraver::acknowledge_rest (Grob_info /* info */)
323 check_bar_property ();
324 if (stems_)
325 end_beam ();
328 void
329 Auto_beam_engraver::acknowledge_stem (Grob_info info)
331 check_bar_property ();
332 Item *stem = dynamic_cast<Item *> (info.grob ());
333 Stream_event *ev = info.ultimate_event_cause ();
334 if (!ev->in_event_class ("rhythmic-event"))
336 programming_error ("stem must have rhythmic structure");
337 return;
341 Don't (start) auto-beam over empty stems; skips or rests
343 if (!Stem::head_count (stem))
345 if (stems_)
346 end_beam ();
347 return;
350 if (Stem::get_beam (stem))
352 if (stems_)
353 junk_beam ();
354 return;
357 int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
359 if (durlog <= 2)
361 if (stems_)
362 end_beam ();
363 return;
367 ignore grace notes.
369 Moment now = now_mom ();
370 if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
371 return;
373 Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
375 consider_end (dur);
376 consider_begin (dur);
378 if (dur < shortest_mom_)
379 shortest_mom_ = dur;
381 if (!stems_)
382 return;
384 grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
385 durlog - 2,
386 Stem::is_invisible (stem));
387 stems_->push_back (stem);
388 last_add_mom_ = now;
389 extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
392 void
393 Auto_beam_engraver::process_acknowledged ()
395 if (extend_mom_ > now_mom ())
396 return;
398 if (!process_acknowledged_count_)
400 consider_end (shortest_mom_);
401 consider_begin (shortest_mom_);
403 else if (process_acknowledged_count_ > 1)
405 if (stems_)
407 Moment now = now_mom ();
408 if ((extend_mom_ < now)
409 || ((extend_mom_ == now) && (last_add_mom_ != now)))
410 end_beam ();
411 else if (!stems_->size ())
412 junk_beam ();
416 process_acknowledged_count_++;
419 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
420 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
421 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
422 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
423 ADD_TRANSLATOR (Auto_beam_engraver,
424 /* doc */
425 "Generate beams based on measure characteristics and observed"
426 " Stems. Uses @code{beatLength}, @code{measureLength}, and"
427 " @code{measurePosition} to decide when to start and stop a"
428 " beam. Overriding beaming is done through"
429 " @ref{Stem_engraver} properties @code{stemLeftBeamCount} and"
430 " @code{stemRightBeamCount}.",
432 /* create */
433 "Beam ",
435 /* read */
436 "autoBeaming "
437 "autoBeamSettings "
438 "beatLength "
439 "subdivideBeams ",
441 /* write */