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