]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
Merge branch 'master' of ssh+git://hanwen@git.sv.gnu.org/srv/git/lilypond
[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--2007 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 start_translation_timestep ();
31   void process_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   DECLARE_TRANSLATOR_LISTENER (beam_forbid);
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   Stream_event *forbid_;
55   /*
56     shortest_mom is the shortest note in the beam.
57   */
58   Moment shortest_mom_;
59   Spanner *finished_beam_;
60   vector<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   // We act as if beam were created, and start a grouping anyway.
72   Beaming_pattern *grouping_;
73   SCM beam_settings_;
74
75   Beaming_pattern *finished_grouping_;
76
77
78   Beaming_options beaming_options_;
79   Beaming_options finished_beaming_options_;
80   
81   
82   void check_bar_property ();
83 };
84
85 void
86 Auto_beam_engraver::derived_mark () const
87 {
88   scm_gc_mark (beam_settings_);
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 IMPLEMENT_TRANSLATOR_LISTENER (Auto_beam_engraver, beam_forbid);
135 void
136 Auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
137 {
138   ASSIGN_EVENT_ONCE (forbid_, ev);
139 }
140
141 bool
142 Auto_beam_engraver::test_moment (Direction dir, Moment test)
143 {
144   return scm_call_3 (get_property ("autoBeamCheck"),
145                      context ()->self_scm (),
146                      scm_from_int (dir),
147                      test.smobbed_copy ())
148     != SCM_BOOL_F;
149 }
150
151 void
152 Auto_beam_engraver::consider_begin (Moment test_mom)
153 {
154   bool on = to_boolean (get_property ("autoBeaming"));
155   if (!stems_ && on
156       && !forbid_)
157     {
158       bool b = test_moment (START, test_mom);
159       if (b)
160         begin_beam ();
161     }
162 }
163
164 void
165 Auto_beam_engraver::consider_end (Moment test_mom)
166 {
167   if (stems_)
168     {
169       /* Allow already started autobeam to end:
170          don't check for autoBeaming */
171       bool b = test_moment (STOP, test_mom);
172       if (b)
173         end_beam ();
174     }
175 }
176
177 Spanner *
178 Auto_beam_engraver::create_beam ()
179 {
180   if (to_boolean (get_property ("skipTypesetting")))
181     return 0;
182
183   for (vsize i = 0; i < stems_->size (); i++)
184     if (Stem::get_beam ((*stems_)[i]))
185       return 0;
186
187   /*
188     Can't use make_spanner_from_properties () because we have to use
189     beam_settings_.
190   */
191   Spanner *beam = new Spanner (beam_settings_);
192
193   for (vsize i = 0; i < stems_->size (); i++)
194     Beam::add_stem (beam, (*stems_)[i]);
195
196   announce_grob (beam, (*stems_)[0]->self_scm ());
197
198   return beam;
199 }
200
201 void
202 Auto_beam_engraver::begin_beam ()
203 {
204   if (stems_ || grouping_)
205     {
206       programming_error ("already have autobeam");
207       return;
208     }
209
210   stems_ = new vector<Item*>;
211   grouping_ = new Beaming_pattern ();
212   beaming_options_.from_context (context ());
213   beam_settings_ = updated_grob_properties (context (), ly_symbol2scm ("Beam"));
214
215   beam_start_moment_ = now_mom ();
216   beam_start_location_
217     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
218 }
219
220 void
221 Auto_beam_engraver::junk_beam ()
222 {
223   if (!stems_)
224     return;
225
226   delete stems_;
227   stems_ = 0;
228   delete grouping_;
229   grouping_ = 0;
230   beam_settings_ = SCM_EOL;
231
232   shortest_mom_ = Moment (Rational (1, 8));
233 }
234
235 void
236 Auto_beam_engraver::end_beam ()
237 {
238   if (stems_->size () < 2)
239     junk_beam ();
240   else
241     {
242       finished_beam_ = create_beam ();
243       
244       if (finished_beam_)
245         {
246           announce_end_grob (finished_beam_, SCM_EOL);
247           finished_grouping_ = grouping_;
248           finished_beaming_options_ = beaming_options_;
249         }
250       delete stems_;
251       stems_ = 0;
252       grouping_ = 0;
253       beam_settings_ = SCM_EOL;
254     }
255
256   shortest_mom_ = Moment (Rational (1, 8));
257 }
258
259 void
260 Auto_beam_engraver::typeset_beam ()
261 {
262   if (finished_beam_)
263     {
264       if (!finished_beam_->get_bound (RIGHT))
265         finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
266       
267       finished_grouping_->beamify (finished_beaming_options_);
268       Beam::set_beaming (finished_beam_, finished_grouping_);
269       finished_beam_ = 0;
270
271       delete finished_grouping_;
272       finished_grouping_ = 0;
273     }
274 }
275
276 void
277 Auto_beam_engraver::start_translation_timestep ()
278 {
279   process_acknowledged_count_ = 0;
280   /*
281     don't beam over skips
282   */
283   if (stems_)
284     {
285       Moment now = now_mom ();
286       if (extend_mom_ < now)
287         end_beam ();
288     }
289   forbid_ = 0;
290 }
291
292 void
293 Auto_beam_engraver::stop_translation_timestep ()
294 {
295   typeset_beam ();
296 }
297
298 void
299 Auto_beam_engraver::finalize ()
300 {
301   /* finished beams may be typeset */
302   typeset_beam ();
303   /* but unfinished may need another announce/acknowledge pass */
304   if (stems_)
305     junk_beam ();
306 }
307
308
309 void
310 Auto_beam_engraver::acknowledge_beam (Grob_info info)
311 {
312   (void)info;
313   check_bar_property ();
314   if (stems_)
315     end_beam ();
316 }
317
318 void
319 Auto_beam_engraver::acknowledge_bar_line (Grob_info info)
320 {
321   (void)info;
322   check_bar_property ();
323   if (stems_)
324     end_beam ();
325 }
326
327 void
328 Auto_beam_engraver::acknowledge_rest (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_stem (Grob_info info)
338 {
339   check_bar_property ();
340   Item *stem = dynamic_cast<Item *> (info.grob ());
341   Stream_event *ev = info.ultimate_event_cause ();
342   if (!ev->in_event_class ("rhythmic-event"))
343     {
344       programming_error ("stem must have rhythmic structure");
345       return;
346     }
347
348   /*
349     Don't (start) auto-beam over empty stems; skips or rests
350   */
351   if (!Stem::head_count (stem))
352     {
353       if (stems_)
354         end_beam ();
355       return;
356     }
357
358   if (Stem::get_beam (stem))
359     {
360       if (stems_)
361         junk_beam ();
362       return;
363     }
364
365   int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
366
367   if (durlog <= 2)
368     {
369       if (stems_)
370         end_beam ();
371       return;
372     }
373
374   /*
375     ignore grace notes.
376   */
377   Moment now = now_mom ();
378   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
379     return;
380
381   Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
382
383   consider_end (dur);
384   consider_begin (dur);
385
386   if (dur < shortest_mom_)
387     shortest_mom_ = dur;
388
389   if (!stems_)
390     return;
391
392   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
393                        durlog - 2);
394   stems_->push_back (stem);
395   last_add_mom_ = now;
396   extend_mom_ = max (extend_mom_, now) + get_event_length (ev);
397 }
398
399 void
400 Auto_beam_engraver::process_acknowledged ()
401 {
402   if (extend_mom_ > now_mom ())
403     return;
404
405   if (!process_acknowledged_count_)
406     {
407       consider_end (shortest_mom_);
408       consider_begin (shortest_mom_);
409     }
410   else if (process_acknowledged_count_ > 1)
411     {
412       if (stems_)
413         {
414           Moment now = now_mom ();
415           if ((extend_mom_ < now)
416               || ((extend_mom_ == now) && (last_add_mom_ != now)))
417             end_beam ();
418           else if (!stems_->size ())
419             junk_beam ();
420         }
421     }
422
423   process_acknowledged_count_++;
424 }
425
426 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
427 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
428 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
429 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
430 ADD_TRANSLATOR (Auto_beam_engraver,
431                 /* doc */ "Generate beams based on measure characteristics and observed "
432                 "Stems.  Uses beatLength, measureLength and measurePosition to decide "
433                 "when to start and stop a beam.  Overriding beaming is done through "
434                 "@ref{Stem_engraver} properties @code{stemLeftBeamCount} and "
435                 "@code{stemRightBeamCount}. ",
436                 /* create */ "Beam",
437
438                 /* read */
439                 "autoBeaming "
440                 "autoBeamSettings "
441                 "beatLength "
442                 "subdivideBeams ",
443                 
444                 /* write */ "");