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