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