]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
(derived_mark): new method. Yes. We
[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     {
201       Beam::add_stem (beam, (*stems_)[i]);
202     }
203
204   announce_grob (beam, (*stems_)[0]->self_scm ());
205
206   return beam;
207 }
208
209 void
210 Auto_beam_engraver::begin_beam ()
211 {
212   if (stems_ || grouping_)
213     {
214       programming_error ("already have autobeam");
215       return;
216     }
217
218   stems_ = new Link_array<Item>;
219   grouping_ = new Beaming_info_list;
220   beam_settings_ = updated_grob_properties (context (), ly_symbol2scm ("Beam"));
221
222   beam_start_moment_ = now_mom ();
223   beam_start_location_
224     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
225   subdivide_beams_ = ly_scm2bool (get_property ("subdivideBeams"));
226   beat_length_ = robust_scm2moment (get_property ("beatLength"), Moment (1, 4));
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     {
249       junk_beam ();
250     }
251   else
252     {
253       finished_beam_ = create_beam ();
254       if (finished_beam_)
255         finished_grouping_ = grouping_;
256       delete stems_;
257       stems_ = 0;
258       grouping_ = 0;
259       beam_settings_ = SCM_EOL;
260     }
261
262   shortest_mom_ = Moment (Rational (1, 8));
263 }
264
265 void
266 Auto_beam_engraver::typeset_beam ()
267 {
268   if (finished_beam_)
269     {
270       finished_grouping_->beamify (beat_length_, subdivide_beams_);
271       Beam::set_beaming (finished_beam_, finished_grouping_);
272       finished_beam_ = 0;
273
274       delete finished_grouping_;
275       finished_grouping_ = 0;
276     }
277 }
278
279 void
280 Auto_beam_engraver::start_translation_timestep ()
281 {
282   process_acknowledged_count_ = 0;
283   /*
284     don't beam over skips
285   */
286   if (stems_)
287     {
288       Moment now = now_mom ();
289       if (extend_mom_ < now)
290         {
291           end_beam ();
292         }
293     }
294   forbid_ = 0;
295 }
296
297 void
298 Auto_beam_engraver::stop_translation_timestep ()
299 {
300   typeset_beam ();
301 }
302
303 void
304 Auto_beam_engraver::finalize ()
305 {
306   /* finished beams may be typeset */
307   typeset_beam ();
308   /* but unfinished may need another announce/acknowledge pass */
309   if (stems_)
310     junk_beam ();
311 }
312
313
314
315
316 void
317 Auto_beam_engraver::acknowledge_beam (Grob_info info)
318 {
319   (void)info;
320   check_bar_property ();
321   if (stems_)
322     {
323       end_beam ();
324     }
325 }
326  
327 void
328 Auto_beam_engraver::acknowledge_bar_line (Grob_info info)
329 {
330   (void)info;
331   check_bar_property ();
332   if (stems_)
333     end_beam ();
334 }
335
336 void
337 Auto_beam_engraver::acknowledge_rest (Grob_info info)
338 {
339   (void)info;
340   check_bar_property ();
341   if (stems_)
342     end_beam ();
343 }
344
345 void
346 Auto_beam_engraver::acknowledge_stem (Grob_info info)
347 {
348   check_bar_property ();
349   Item *stem = dynamic_cast<Item *> (info.grob ());
350   Music *m = info.music_cause ();
351   if (!m->is_mus_type ("rhythmic-event"))
352     {
353       programming_error ("stem must have rhythmic structure");
354       return;
355     }
356
357   /*
358     Don't (start) auto-beam over empty stems; skips or rests
359   */
360   if (!Stem::head_count (stem))
361     {
362       if (stems_)
363         end_beam ();
364       return;
365     }
366
367   if (Stem::get_beam (stem))
368     {
369       if (stems_)
370         junk_beam ();
371       return;
372     }
373
374   int durlog = unsmob_duration (m->get_property ("duration"))->duration_log ();
375
376   if (durlog <= 2)
377     {
378       if (stems_)
379         end_beam ();
380       return;
381     }
382
383   /*
384     ignore grace notes.
385   */
386   Moment now = now_mom ();
387   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
388     return;
389
390   Moment dur = unsmob_duration (m->get_property ("duration"))->get_length ();
391
392   consider_end (dur);
393   consider_begin (dur);
394
395   if (dur < shortest_mom_)
396     shortest_mom_ = dur;
397
398   if (!stems_)
399     return;
400
401   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
402                        durlog - 2);
403   stems_->push (stem);
404   last_add_mom_ = now;
405   extend_mom_ = max (extend_mom_, now) + m->get_length ();
406 }
407
408 void
409 Auto_beam_engraver::process_acknowledged ()
410 {
411   if (extend_mom_ > now_mom ())
412     return ; 
413
414   if (!process_acknowledged_count_)
415     {
416       consider_end (shortest_mom_);
417       consider_begin (shortest_mom_);
418     }
419   else if (process_acknowledged_count_ > 1)
420     {
421       if (stems_)
422         {
423           Moment now = now_mom ();
424           if ((extend_mom_ < now)
425               || ((extend_mom_ == now) && (last_add_mom_ != now)))
426             {
427               end_beam ();
428             }
429           else if (!stems_->size ())
430             {
431               junk_beam ();
432             }
433         }
434     }
435
436   process_acknowledged_count_++;
437 }
438
439 ADD_ACKNOWLEDGER (Auto_beam_engraver,stem);
440 ADD_ACKNOWLEDGER (Auto_beam_engraver,bar_line);
441 ADD_ACKNOWLEDGER (Auto_beam_engraver,beam);
442 ADD_ACKNOWLEDGER (Auto_beam_engraver,rest);
443 ADD_TRANSLATOR (Auto_beam_engraver,
444                 /* descr */ "Generate beams based on measure characteristics and observed "
445                 "Stems.  Uses beatLength, measureLength and measurePosition to decide "
446                 "when to start and stop a beam.  Overriding beaming is done through "
447                 "@ref{Stem_engraver} properties @code{stemLeftBeamCount} and "
448                 "@code{stemRightBeamCount}. ",
449                 /* creats*/ "Beam",
450                 /* accepts */ "beam-forbid-event",
451                 /* reads */ "autoBeaming autoBeamSettings beatLength subdivideBeams",
452                 /* write */ "");