]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
use unbreakable-spanner-interface for Beam too.
[lilypond.git] / lily / auto-beam-engraver.cc
1 /*
2   auto-beam-engraver.cc -- implement Auto_beam_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2006 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
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"
21
22 #include "translator.icc"
23
24 class Auto_beam_engraver : public Engraver
25 {
26   TRANSLATOR_DECLARATIONS (Auto_beam_engraver);
27
28 protected:
29   void stop_translation_timestep ();
30   void start_translation_timestep ();
31   void process_music ();
32   virtual void finalize ();
33   virtual void derived_mark () const;
34
35   DECLARE_ACKNOWLEDGER (rest);
36   DECLARE_ACKNOWLEDGER (beam);
37   DECLARE_ACKNOWLEDGER (bar_line);
38   DECLARE_ACKNOWLEDGER (stem);
39   DECLARE_TRANSLATOR_LISTENER (beam_forbid);
40
41   void process_acknowledged ();
42
43 private:
44   bool test_moment (Direction, Moment);
45   void consider_begin (Moment);
46   void consider_end (Moment);
47   Spanner *create_beam ();
48   void begin_beam ();
49   void end_beam ();
50   void junk_beam ();
51   bool is_same_grace_state (Grob *e);
52   void typeset_beam ();
53
54   Stream_event *forbid_;
55   /*
56     shortest_mom is the shortest note in the beam.
57   */
58   Moment shortest_mom_;
59   Spanner *finished_beam_;
60   vector<Item*> *stems_;
61
62   int process_acknowledged_count_;
63   Moment last_add_mom_;
64   /*
65     Projected ending of the  beam we're working on.
66   */
67   Moment extend_mom_;
68   Moment beam_start_moment_;
69   Moment beam_start_location_;
70
71   bool subdivide_beams_;
72   Moment beat_length_;
73
74   // We act as if beam were created, and start a grouping anyway.
75   Beaming_pattern *grouping_;
76   SCM beam_settings_;
77
78   Beaming_pattern *finished_grouping_;
79
80   void check_bar_property ();
81 };
82
83 void
84 Auto_beam_engraver::derived_mark () const
85 {
86   scm_gc_mark (beam_settings_);
87 }
88
89 void
90 Auto_beam_engraver::check_bar_property ()
91 {
92   /* Duplicated from process_music (), since
93      Repeat_acknowledge_engraver::process_music () may also set whichBar.  */
94
95   Moment now = now_mom ();
96   if (scm_is_string (get_property ("whichBar"))
97       && beam_start_moment_ < now)
98     {
99       consider_end (shortest_mom_);
100       junk_beam ();
101     }
102 }
103
104 void
105 Auto_beam_engraver::process_music ()
106 {
107   if (scm_is_string (get_property ("whichBar")))
108     {
109       consider_end (shortest_mom_);
110       junk_beam ();
111     }
112
113   if (forbid_)
114     {
115       consider_end (shortest_mom_);
116       junk_beam ();
117     }
118 }
119
120 Auto_beam_engraver::Auto_beam_engraver ()
121 {
122   forbid_ = 0;
123   process_acknowledged_count_ = 0;
124   stems_ = 0;
125   shortest_mom_ = Moment (Rational (1, 8));
126   finished_beam_ = 0;
127   finished_grouping_ = 0;
128   grouping_ = 0;
129   beam_settings_ = SCM_EOL;
130 }
131
132 IMPLEMENT_TRANSLATOR_LISTENER (Auto_beam_engraver, beam_forbid);
133 void
134 Auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
135 {
136   ASSIGN_EVENT_ONCE (forbid_, ev);
137 }
138
139 bool
140 Auto_beam_engraver::test_moment (Direction dir, Moment test)
141 {
142   return scm_call_3 (get_property ("autoBeamCheck"),
143                      context ()->self_scm (),
144                      scm_from_int (dir),
145                      test.smobbed_copy ())
146     != SCM_BOOL_F;
147 }
148
149 void
150 Auto_beam_engraver::consider_begin (Moment test_mom)
151 {
152   bool on = to_boolean (get_property ("autoBeaming"));
153   if (!stems_ && on
154       && !forbid_)
155     {
156       bool b = test_moment (START, test_mom);
157       if (b)
158         begin_beam ();
159     }
160 }
161
162 void
163 Auto_beam_engraver::consider_end (Moment test_mom)
164 {
165   if (stems_)
166     {
167       /* Allow already started autobeam to end:
168          don't check for autoBeaming */
169       bool b = test_moment (STOP, test_mom);
170       if (b)
171         end_beam ();
172     }
173 }
174
175 Spanner *
176 Auto_beam_engraver::create_beam ()
177 {
178   if (to_boolean (get_property ("skipTypesetting")))
179     return 0;
180
181   for (vsize i = 0; i < stems_->size (); i++)
182     if (Stem::get_beam ((*stems_)[i]))
183       return 0;
184
185   /*
186     Can't use make_spanner_from_properties() because we have to use
187     beam_settings_.
188   */
189   Spanner *beam = new Spanner (beam_settings_,
190                                context ()->get_grob_key ("Beam"));
191
192   for (vsize i = 0; i < stems_->size (); i++)
193     Beam::add_stem (beam, (*stems_)[i]);
194
195   announce_grob (beam, (*stems_)[0]->self_scm ());
196
197   return beam;
198 }
199
200 void
201 Auto_beam_engraver::begin_beam ()
202 {
203   if (stems_ || grouping_)
204     {
205       programming_error ("already have autobeam");
206       return;
207     }
208
209   stems_ = new vector<Item*>;
210   grouping_ = new Beaming_pattern ();
211   beam_settings_ = updated_grob_properties (context (), ly_symbol2scm ("Beam"));
212
213   beam_start_moment_ = now_mom ();
214   beam_start_location_
215     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
216 }
217
218 void
219 Auto_beam_engraver::junk_beam ()
220 {
221   if (!stems_)
222     return;
223
224   delete stems_;
225   stems_ = 0;
226   delete grouping_;
227   grouping_ = 0;
228   beam_settings_ = SCM_EOL;
229
230   shortest_mom_ = Moment (Rational (1, 8));
231 }
232
233 void
234 Auto_beam_engraver::end_beam ()
235 {
236   if (stems_->size () < 2)
237     junk_beam ();
238   else
239     {
240       finished_beam_ = create_beam ();
241       
242       if (finished_beam_)
243         {
244           announce_end_grob (finished_beam_, SCM_EOL);
245           finished_grouping_ = grouping_;
246         }
247       delete stems_;
248       stems_ = 0;
249       grouping_ = 0;
250       beam_settings_ = SCM_EOL;
251     }
252
253   shortest_mom_ = Moment (Rational (1, 8));
254 }
255
256 void
257 Auto_beam_engraver::typeset_beam ()
258 {
259   if (finished_beam_)
260     {
261       if (!finished_beam_->get_bound (RIGHT))
262         finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
263       
264       finished_grouping_->beamify (context ());
265       Beam::set_beaming (finished_beam_, finished_grouping_);
266       finished_beam_ = 0;
267
268       delete finished_grouping_;
269       finished_grouping_ = 0;
270     }
271 }
272
273 void
274 Auto_beam_engraver::start_translation_timestep ()
275 {
276   process_acknowledged_count_ = 0;
277   /*
278     don't beam over skips
279   */
280   if (stems_)
281     {
282       Moment now = now_mom ();
283       if (extend_mom_ < now)
284         end_beam ();
285     }
286   forbid_ = 0;
287 }
288
289 void
290 Auto_beam_engraver::stop_translation_timestep ()
291 {
292   typeset_beam ();
293 }
294
295 void
296 Auto_beam_engraver::finalize ()
297 {
298   /* finished beams may be typeset */
299   typeset_beam ();
300   /* but unfinished may need another announce/acknowledge pass */
301   if (stems_)
302     junk_beam ();
303 }
304
305
306 void
307 Auto_beam_engraver::acknowledge_beam (Grob_info info)
308 {
309   (void)info;
310   check_bar_property ();
311   if (stems_)
312     end_beam ();
313 }
314
315 void
316 Auto_beam_engraver::acknowledge_bar_line (Grob_info info)
317 {
318   (void)info;
319   check_bar_property ();
320   if (stems_)
321     end_beam ();
322 }
323
324 void
325 Auto_beam_engraver::acknowledge_rest (Grob_info info)
326 {
327   (void)info;
328   check_bar_property ();
329   if (stems_)
330     end_beam ();
331 }
332
333 void
334 Auto_beam_engraver::acknowledge_stem (Grob_info info)
335 {
336   check_bar_property ();
337   Item *stem = dynamic_cast<Item *> (info.grob ());
338   Stream_event *ev = info.ultimate_event_cause ();
339   if (!ev->in_event_class ("rhythmic-event"))
340     {
341       programming_error ("stem must have rhythmic structure");
342       return;
343     }
344
345   /*
346     Don't (start) auto-beam over empty stems; skips or rests
347   */
348   if (!Stem::head_count (stem))
349     {
350       if (stems_)
351         end_beam ();
352       return;
353     }
354
355   if (Stem::get_beam (stem))
356     {
357       if (stems_)
358         junk_beam ();
359       return;
360     }
361
362   int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
363
364   if (durlog <= 2)
365     {
366       if (stems_)
367         end_beam ();
368       return;
369     }
370
371   /*
372     ignore grace notes.
373   */
374   Moment now = now_mom ();
375   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
376     return;
377
378   Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
379
380   consider_end (dur);
381   consider_begin (dur);
382
383   if (dur < shortest_mom_)
384     shortest_mom_ = dur;
385
386   if (!stems_)
387     return;
388
389   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
390                        durlog - 2);
391   stems_->push_back (stem);
392   last_add_mom_ = now;
393   extend_mom_ = max (extend_mom_, now) + get_event_length (ev);
394 }
395
396 void
397 Auto_beam_engraver::process_acknowledged ()
398 {
399   if (extend_mom_ > now_mom ())
400     return;
401
402   if (!process_acknowledged_count_)
403     {
404       consider_end (shortest_mom_);
405       consider_begin (shortest_mom_);
406     }
407   else if (process_acknowledged_count_ > 1)
408     {
409       if (stems_)
410         {
411           Moment now = now_mom ();
412           if ((extend_mom_ < now)
413               || ((extend_mom_ == now) && (last_add_mom_ != now)))
414             end_beam ();
415           else if (!stems_->size ())
416             junk_beam ();
417         }
418     }
419
420   process_acknowledged_count_++;
421 }
422
423 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
424 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
425 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
426 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
427 ADD_TRANSLATOR (Auto_beam_engraver,
428                 /* doc */ "Generate beams based on measure characteristics and observed "
429                 "Stems.  Uses beatLength, measureLength and measurePosition to decide "
430                 "when to start and stop a beam.  Overriding beaming is done through "
431                 "@ref{Stem_engraver} properties @code{stemLeftBeamCount} and "
432                 "@code{stemRightBeamCount}. ",
433                 /* create */ "Beam",
434
435                 /* read */
436                 "autoBeaming "
437                 "autoBeamSettings "
438                 "beatLength "
439                 "subdivideBeams ",
440                 
441                 /* write */ "");