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