]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
Run `make grand-replace'.
[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--2008 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 process_music ();
31   virtual void finalize ();
32   virtual void derived_mark () const;
33
34   DECLARE_ACKNOWLEDGER (rest);
35   DECLARE_ACKNOWLEDGER (beam);
36   DECLARE_ACKNOWLEDGER (bar_line);
37   DECLARE_ACKNOWLEDGER (stem);
38   DECLARE_TRANSLATOR_LISTENER (beam_forbid);
39
40   void process_acknowledged ();
41
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 ();
52
53   Stream_event *forbid_;
54   /*
55     shortest_mom is the shortest note in the beam.
56   */
57   Moment shortest_mom_;
58   Spanner *finished_beam_;
59   vector<Item*> *stems_;
60
61   int process_acknowledged_count_;
62   Moment last_add_mom_;
63   /*
64     Projected ending of the  beam we're working on.
65   */
66   Moment extend_mom_;
67   Moment beam_start_moment_;
68   Moment beam_start_location_;
69
70   // We act as if beam were created, and start a grouping anyway.
71   Beaming_pattern *grouping_;
72   SCM beam_settings_;
73
74   Beaming_pattern *finished_grouping_;
75
76
77   Beaming_options beaming_options_;
78   Beaming_options finished_beaming_options_;
79   
80   
81   void check_bar_property ();
82 };
83
84 void
85 Auto_beam_engraver::derived_mark () const
86 {
87   scm_gc_mark (beam_settings_);
88 }
89
90 void
91 Auto_beam_engraver::check_bar_property ()
92 {
93   /* Duplicated from process_music (), since
94      Repeat_acknowledge_engraver::process_music () may also set whichBar.  */
95
96   Moment now = now_mom ();
97   if (scm_is_string (get_property ("whichBar"))
98       && beam_start_moment_ < now)
99     {
100       consider_end (shortest_mom_);
101       junk_beam ();
102     }
103 }
104
105 void
106 Auto_beam_engraver::process_music ()
107 {
108   /*
109     don't beam over skips
110   */
111   if (stems_)
112     {
113       Moment now = now_mom ();
114       if (extend_mom_ < now)
115         end_beam ();
116     }
117
118   if (scm_is_string (get_property ("whichBar")))
119     {
120       consider_end (shortest_mom_);
121       junk_beam ();
122     }
123
124   if (forbid_)
125     {
126       consider_end (shortest_mom_);
127       junk_beam ();
128     }
129 }
130
131 Auto_beam_engraver::Auto_beam_engraver ()
132 {
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;
141 }
142
143 IMPLEMENT_TRANSLATOR_LISTENER (Auto_beam_engraver, beam_forbid);
144 void
145 Auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
146 {
147   ASSIGN_EVENT_ONCE (forbid_, ev);
148 }
149
150 bool
151 Auto_beam_engraver::test_moment (Direction dir, Moment test)
152 {
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;
158 }
159
160 void
161 Auto_beam_engraver::consider_begin (Moment test_mom)
162 {
163   bool on = to_boolean (get_property ("autoBeaming"));
164   if (!stems_ && on
165       && !forbid_)
166     {
167       bool b = test_moment (START, test_mom);
168       if (b)
169         begin_beam ();
170     }
171 }
172
173 void
174 Auto_beam_engraver::consider_end (Moment test_mom)
175 {
176   if (stems_)
177     {
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 ();
183     }
184 }
185
186 Spanner *
187 Auto_beam_engraver::create_beam ()
188 {
189   if (to_boolean (get_property ("skipTypesetting")))
190     return 0;
191
192   for (vsize i = 0; i < stems_->size (); i++)
193     if (Stem::get_beam ((*stems_)[i]))
194       return 0;
195
196   /*
197     Can't use make_spanner_from_properties () because we have to use
198     beam_settings_.
199   */
200   Spanner *beam = new Spanner (beam_settings_);
201
202   for (vsize i = 0; i < stems_->size (); i++)
203     Beam::add_stem (beam, (*stems_)[i]);
204
205   announce_grob (beam, (*stems_)[0]->self_scm ());
206
207   return beam;
208 }
209
210 void
211 Auto_beam_engraver::begin_beam ()
212 {
213   if (stems_ || grouping_)
214     {
215       programming_error ("already have autobeam");
216       return;
217     }
218
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"));
223
224   beam_start_moment_ = now_mom ();
225   beam_start_location_
226     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
227 }
228
229 void
230 Auto_beam_engraver::junk_beam ()
231 {
232   if (!stems_)
233     return;
234
235   delete stems_;
236   stems_ = 0;
237   delete grouping_;
238   grouping_ = 0;
239   beam_settings_ = SCM_EOL;
240
241   shortest_mom_ = Moment (Rational (1, 8));
242 }
243
244 void
245 Auto_beam_engraver::end_beam ()
246 {
247   if (stems_->size () < 2)
248     junk_beam ();
249   else
250     {
251       finished_beam_ = create_beam ();
252       
253       if (finished_beam_)
254         {
255           announce_end_grob (finished_beam_, SCM_EOL);
256           finished_grouping_ = grouping_;
257           finished_beaming_options_ = beaming_options_;
258         }
259       delete stems_;
260       stems_ = 0;
261       grouping_ = 0;
262       beam_settings_ = SCM_EOL;
263     }
264
265   shortest_mom_ = Moment (Rational (1, 8));
266 }
267
268 void
269 Auto_beam_engraver::typeset_beam ()
270 {
271   if (finished_beam_)
272     {
273       if (!finished_beam_->get_bound (RIGHT))
274         finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
275       
276       finished_grouping_->beamify (finished_beaming_options_);
277       Beam::set_beaming (finished_beam_, finished_grouping_);
278       finished_beam_ = 0;
279
280       delete finished_grouping_;
281       finished_grouping_ = 0;
282     }
283 }
284
285 void
286 Auto_beam_engraver::stop_translation_timestep ()
287 {
288   typeset_beam ();
289   process_acknowledged_count_ = 0;
290   forbid_ = 0;
291 }
292
293 void
294 Auto_beam_engraver::finalize ()
295 {
296   /* finished beams may be typeset */
297   typeset_beam ();
298   /* but unfinished may need another announce/acknowledge pass */
299   if (stems_)
300     junk_beam ();
301 }
302
303
304 void
305 Auto_beam_engraver::acknowledge_beam (Grob_info info)
306 {
307   (void)info;
308   check_bar_property ();
309   if (stems_)
310     end_beam ();
311 }
312
313 void
314 Auto_beam_engraver::acknowledge_bar_line (Grob_info info)
315 {
316   (void)info;
317   check_bar_property ();
318   if (stems_)
319     end_beam ();
320 }
321
322 void
323 Auto_beam_engraver::acknowledge_rest (Grob_info info)
324 {
325   (void)info;
326   check_bar_property ();
327   if (stems_)
328     end_beam ();
329 }
330
331 void
332 Auto_beam_engraver::acknowledge_stem (Grob_info info)
333 {
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"))
338     {
339       programming_error ("stem must have rhythmic structure");
340       return;
341     }
342
343   /*
344     Don't (start) auto-beam over empty stems; skips or rests
345   */
346   if (!Stem::head_count (stem))
347     {
348       if (stems_)
349         end_beam ();
350       return;
351     }
352
353   if (Stem::get_beam (stem))
354     {
355       if (stems_)
356         junk_beam ();
357       return;
358     }
359
360   int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
361
362   if (durlog <= 2)
363     {
364       if (stems_)
365         end_beam ();
366       return;
367     }
368
369   /*
370     ignore grace notes.
371   */
372   Moment now = now_mom ();
373   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
374     return;
375
376   Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
377
378   consider_end (dur);
379   consider_begin (dur);
380
381   if (dur < shortest_mom_)
382     shortest_mom_ = dur;
383
384   if (!stems_)
385     return;
386
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);
393 }
394
395 void
396 Auto_beam_engraver::process_acknowledged ()
397 {
398   if (extend_mom_ > now_mom ())
399     return;
400
401   if (!process_acknowledged_count_)
402     {
403       consider_end (shortest_mom_);
404       consider_begin (shortest_mom_);
405     }
406   else if (process_acknowledged_count_ > 1)
407     {
408       if (stems_)
409         {
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 ();
416         }
417     }
418
419   process_acknowledged_count_++;
420 }
421
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}.",
434
435                 /* create */
436                 "Beam ",
437
438                 /* read */
439                 "autoBeaming "
440                 "autoBeamSettings "
441                 "beatLength "
442                 "subdivideBeams ",
443                 
444                 /* write */
445                 ""
446                 );