]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
Run grand-replace for 2009.
[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--2009 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   check_bar_property ();
308   if (stems_)
309     end_beam ();
310 }
311
312 void
313 Auto_beam_engraver::acknowledge_bar_line (Grob_info /* info */)
314 {
315   check_bar_property ();
316   if (stems_)
317     end_beam ();
318 }
319
320 void
321 Auto_beam_engraver::acknowledge_rest (Grob_info /* info */)
322 {
323   check_bar_property ();
324   if (stems_)
325     end_beam ();
326 }
327
328 void
329 Auto_beam_engraver::acknowledge_stem (Grob_info info)
330 {
331   check_bar_property ();
332   Item *stem = dynamic_cast<Item *> (info.grob ());
333   Stream_event *ev = info.ultimate_event_cause ();
334   if (!ev->in_event_class ("rhythmic-event"))
335     {
336       programming_error ("stem must have rhythmic structure");
337       return;
338     }
339
340   /*
341     Don't (start) auto-beam over empty stems; skips or rests
342   */
343   if (!Stem::head_count (stem))
344     {
345       if (stems_)
346         end_beam ();
347       return;
348     }
349
350   if (Stem::get_beam (stem))
351     {
352       if (stems_)
353         junk_beam ();
354       return;
355     }
356
357   int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
358
359   if (durlog <= 2)
360     {
361       if (stems_)
362         end_beam ();
363       return;
364     }
365
366   /*
367     ignore grace notes.
368   */
369   Moment now = now_mom ();
370   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
371     return;
372
373   Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
374
375   consider_end (dur);
376   consider_begin (dur);
377
378   if (dur < shortest_mom_)
379     shortest_mom_ = dur;
380
381   if (!stems_)
382     return;
383
384   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
385                        durlog - 2,
386                        Stem::is_invisible (stem));
387   stems_->push_back (stem);
388   last_add_mom_ = now;
389   extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
390 }
391
392 void
393 Auto_beam_engraver::process_acknowledged ()
394 {
395   if (extend_mom_ > now_mom ())
396     return;
397
398   if (!process_acknowledged_count_)
399     {
400       consider_end (shortest_mom_);
401       consider_begin (shortest_mom_);
402     }
403   else if (process_acknowledged_count_ > 1)
404     {
405       if (stems_)
406         {
407           Moment now = now_mom ();
408           if ((extend_mom_ < now)
409               || ((extend_mom_ == now) && (last_add_mom_ != now)))
410             end_beam ();
411           else if (!stems_->size ())
412             junk_beam ();
413         }
414     }
415
416   process_acknowledged_count_++;
417 }
418
419 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
420 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
421 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
422 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
423 ADD_TRANSLATOR (Auto_beam_engraver,
424                 /* doc */
425                 "Generate beams based on measure characteristics and observed"
426                 " Stems.  Uses @code{beatLength}, @code{measureLength}, and"
427                 " @code{measurePosition} to decide when to start and stop a"
428                 " beam.  Overriding beaming is done through"
429                 " @ref{Stem_engraver} properties @code{stemLeftBeamCount} and"
430                 " @code{stemRightBeamCount}.",
431
432                 /* create */
433                 "Beam ",
434
435                 /* read */
436                 "autoBeaming "
437                 "autoBeamSettings "
438                 "beatLength "
439                 "subdivideBeams ",
440                 
441                 /* write */
442                 ""
443                 );