]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
patch::: 1.5.1.jcn2
[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--2001 Jan Nieuwenhuizen <janneke@gnu.org>
7   
8  */
9
10 #include "beaming.hh"
11 #include "musical-request.hh"
12 #include "beam.hh"
13 #include "stem.hh"
14 #include "debug.hh"
15 #include "engraver-group-engraver.hh"
16 #include "bar.hh"
17 #include "rest.hh"
18 #include "engraver.hh"
19 #include "item.hh"
20 #include "spanner.hh"
21
22 /*
23   TODO: figure what to do in grace?
24
25   TODO: documentme.
26  */
27 class Auto_beam_engraver : public Engraver
28 {
29 public:
30   Auto_beam_engraver ();
31   VIRTUAL_COPY_CONS (Translator);
32
33 protected:
34   virtual void stop_translation_timestep ();
35   virtual void start_translation_timestep ();
36   virtual void finalize ();
37   virtual void acknowledge_grob (Grob_info);
38   virtual void create_grobs ();
39
40 private:
41   bool test_moment (Direction, Moment);
42   void consider_begin (Moment);
43   void consider_end (Moment);
44   Spanner* create_beam_p ();
45   void begin_beam ();
46   void end_beam ();
47   void junk_beam ();
48   bool same_grace_state_b (Grob* e);
49   void typeset_beam ();
50
51   /*
52     shortest_mom is the shortest note in the beam.
53    */
54   Moment shortest_mom_;
55   Spanner *finished_beam_p_;
56   Link_array<Item>* stem_l_arr_p_;
57
58
59   int count_i_;
60   Moment last_add_mom_;
61
62   /*
63     Projected ending of the  beam we're working on.
64    */
65   Moment extend_mom_;
66   Moment beam_start_moment_;
67   Moment beam_start_location_;
68   
69   // We act as if beam were created, and start a grouping anyway.
70   Beaming_info_list*grouping_p_;  
71   Beaming_info_list*finished_grouping_p_;
72 };
73
74 ADD_THIS_TRANSLATOR (Auto_beam_engraver);
75
76 Auto_beam_engraver::Auto_beam_engraver ()
77 {
78   count_i_ = 0;
79   stem_l_arr_p_ = 0;
80   shortest_mom_ = Moment (1, 8);
81   finished_beam_p_ = 0;
82   finished_grouping_p_ = 0;
83   grouping_p_ = 0;
84 }
85
86 /*
87   Determine end moment for auto beaming (or begin moment, but mostly
88   0==anywhere) In order of increasing priority:
89   
90   i.   begin anywhere, end at every beat
91   ii.  end   *    <num> <den>
92   iii. end <type> <num> <den>
93   
94   iv.  end   *      *     *
95   v.   end <type>   *     *
96   
97   
98   Rationale:
99   
100   [to be defined in config file]
101   i.   easy catch-all rule
102   ii.  exceptions for time signature
103   iii. exceptions for time signature, for specific duration type
104   
105   [user override]
106   iv.  generic override
107   v.   override for specific duration type
108   
109 */
110 bool
111 Auto_beam_engraver::test_moment (Direction dir, Moment test_mom)
112 {
113   SCM wild = gh_list (ly_symbol2scm ("*"), ly_symbol2scm ("*"), SCM_UNDEFINED);
114   SCM function;
115   if (dir == START)
116     function = gh_list (ly_symbol2scm ("begin"), SCM_UNDEFINED);
117   else
118     function = gh_list (ly_symbol2scm ("end"), SCM_UNDEFINED);
119
120   Moment one_beat = *unsmob_moment (get_property ("beatLength"));
121   int num = *unsmob_moment (get_property ("measureLength")) / one_beat;
122   int den = one_beat.den ();
123   SCM time = gh_list (gh_int2scm (num), gh_int2scm (den), SCM_UNDEFINED);
124
125   SCM type = gh_list (gh_int2scm (test_mom.num ()),
126                       gh_int2scm (test_mom.den ()), SCM_UNDEFINED);
127
128   SCM settings = get_property ("autoBeamSettings");
129   
130   /* first guess */
131   
132   /* begin beam at any position
133  (and fallback for end) */
134   Moment moment (0);
135   
136   /* end beam at end of beat */
137   if (dir == STOP)
138     {
139       SCM beat (get_property ("beatLength"));
140       
141       if (unsmob_moment (beat))
142         moment = *unsmob_moment (beat);
143     }
144
145   /* second guess: property generic time exception */
146   SCM m = gh_assoc (gh_append3 (function, wild, time), settings);
147   
148   if (m != SCM_BOOL_F && unsmob_moment (gh_cdr (m)))
149     moment = * unsmob_moment (gh_cdr (m));
150
151   /* third guess: property time exception, specific for duration type */
152   m = gh_assoc (gh_append3 (function, type, time), settings);
153   if (m != SCM_BOOL_F && unsmob_moment (gh_cdr (m)))
154     moment = * unsmob_moment (gh_cdr (m));
155
156   /* fourth guess [user override]: property plain generic */
157   m = gh_assoc (gh_append3 (function, wild, wild), settings);
158   if (m != SCM_BOOL_F && unsmob_moment (gh_cdr (m)))
159     moment = * unsmob_moment (gh_cdr (m));
160
161   /* fifth guess [user override]: property plain, specific for duration type */
162   m = gh_assoc (gh_append3 (function, type, wild), settings);
163   if (m != SCM_BOOL_F && unsmob_moment (gh_cdr (m)))
164     moment = * unsmob_moment (gh_cdr (m));
165   
166   Rational r;
167   if (moment)
168     {
169       /* Ugh? measurePosition can be negative, when \partial
170          We may have to fix this elsewhere (timing translator)
171         r = unsmob_moment (get_property ("measurePosition"))->mod_rat (moment);
172       */
173       Moment pos = * unsmob_moment (get_property ("measurePosition"));
174       if (pos < Moment (0))
175         {
176           Moment length = * unsmob_moment (get_property ("measureLength"));
177           pos = length - pos;
178         }
179       r = pos.main_part_.mod_rat (moment.main_part_);
180     }
181   else
182     {
183       if (dir == START)
184         /* if undefined, starting is ok */
185         r = 0;
186       else
187         /* but ending is not */
188         r = 1;
189     }
190   return !r;
191 }
192
193 void
194 Auto_beam_engraver::consider_begin (Moment test_mom)
195 {
196   bool off = to_boolean (get_property ("noAutoBeaming"));
197   if (!stem_l_arr_p_ && ! off)
198     {
199       bool b = test_moment (START, test_mom);
200       if (b)
201         begin_beam ();
202     }
203 }
204
205 void
206 Auto_beam_engraver::consider_end (Moment test_mom)
207 {
208   if (stem_l_arr_p_)
209     {
210       /* Allow already started autobeam to end:
211          don't check for noAutoBeaming */
212       bool b = test_moment (STOP, test_mom);
213       if (b)
214         end_beam ();
215     }
216 }
217
218 Spanner*
219 Auto_beam_engraver::create_beam_p ()
220 {
221   if (to_boolean (get_property ("skipTypesetting")))
222     {
223      return 0;
224     }
225   
226   Spanner* beam_p = new Spanner (get_property ("Beam"));
227   for (int i = 0; i < stem_l_arr_p_->size (); i++)
228     {
229       /*
230         watch out for stem tremolos and abbreviation beams
231        */
232       if (Stem::beam_l ((*stem_l_arr_p_)[i]))
233         {
234           scm_gc_unprotect_object (beam_p->self_scm ());
235           return 0;
236         }
237       Beam::add_stem (beam_p, (*stem_l_arr_p_)[i]);
238     }
239   
240   announce_grob (beam_p, 0);
241
242   return beam_p;
243 }
244
245 void
246 Auto_beam_engraver::begin_beam ()
247 {
248   assert (!stem_l_arr_p_);
249   stem_l_arr_p_ = new Link_array<Item>;
250   assert (!grouping_p_);
251   grouping_p_ = new Beaming_info_list;
252   beam_start_moment_ = now_mom ();
253   beam_start_location_ = *unsmob_moment (get_property ("measurePosition"));
254 }
255
256
257 void
258 Auto_beam_engraver::junk_beam () 
259 {
260   assert (stem_l_arr_p_);
261   
262   delete stem_l_arr_p_;
263   stem_l_arr_p_ = 0;
264   delete grouping_p_;
265   grouping_p_ = 0;
266
267   shortest_mom_ = Moment (1, 8);
268 }
269
270 void
271 Auto_beam_engraver::end_beam ()
272 {
273   if (stem_l_arr_p_->size () < 2)
274     {
275       junk_beam ();
276     }
277   else
278     
279     {
280       finished_beam_p_ = create_beam_p ();
281       if (finished_beam_p_)
282         finished_grouping_p_ = grouping_p_;
283       delete stem_l_arr_p_;
284       stem_l_arr_p_ = 0;
285       grouping_p_ = 0;
286     }
287
288   shortest_mom_ = Moment (1, 8);
289 }
290
291 void
292 Auto_beam_engraver::typeset_beam ()
293 {
294   if (finished_beam_p_)
295     {
296       finished_grouping_p_->beamify ();
297       Beam::set_beaming (finished_beam_p_, finished_grouping_p_);
298       typeset_grob (finished_beam_p_);
299       finished_beam_p_ = 0;
300     
301       delete finished_grouping_p_;
302       finished_grouping_p_= 0;
303     }
304 }
305
306 void
307 Auto_beam_engraver::start_translation_timestep ()
308 {
309   count_i_ = 0;
310   /*
311     don't beam over skips
312    */
313   if (stem_l_arr_p_)
314     {
315       Moment now = now_mom ();
316       if (extend_mom_ < now)
317         {
318           end_beam ();
319         }
320     }
321 }
322
323 void
324 Auto_beam_engraver::stop_translation_timestep ()
325 {
326   
327   typeset_beam ();
328 }
329
330 void
331 Auto_beam_engraver::finalize ()
332 {
333   /* finished beams may be typeset */
334   typeset_beam ();
335   /* but unfinished may need another announce/acknowledge pass */
336   if (stem_l_arr_p_)
337     junk_beam ();
338 }
339
340
341 void
342 Auto_beam_engraver::acknowledge_grob (Grob_info info)
343 {
344   if (stem_l_arr_p_)
345     {
346       if (Beam::has_interface (info.elem_l_))
347         {
348           end_beam ();
349         }
350       else if (Bar::has_interface (info.elem_l_))
351         {
352           end_beam ();
353         }
354       else if (Rest::has_interface (info.elem_l_))
355         {
356           end_beam ();
357         }
358     }
359   
360   if (Stem::has_interface (info.elem_l_))
361     {
362       Item* stem_l = dynamic_cast<Item *> (info.elem_l_);
363                                        
364       Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.req_l_);
365       if (!rhythmic_req)
366         {
367           programming_error ("Stem must have rhythmic structure");
368           return;
369         }
370       
371       /*
372         Don't (start) auto-beam over empty stems; skips or rests
373         */
374       if (!Stem::heads_i (stem_l))
375         {
376           if (stem_l_arr_p_)
377             end_beam ();
378           return;
379         }
380
381       if (Stem::beam_l (stem_l))
382         {
383           if (stem_l_arr_p_)
384             junk_beam ();
385           return ;
386         }
387               
388       int durlog  = unsmob_duration (rhythmic_req->get_mus_property ("duration"))->duration_log ();
389       
390       if (durlog <= 2)
391         {
392           if (stem_l_arr_p_)
393             end_beam ();
394           return;
395         }
396
397       Moment dur = unsmob_duration (rhythmic_req->get_mus_property ("duration"))->length_mom ();
398       /* FIXME:
399
400         This comment has been here since long:
401
402            if shortest duration would change
403             consider ending and beginning beam first. 
404
405         but the code didn't match: */
406 #if 1
407       consider_end (dur);
408       consider_begin (dur);
409
410       if (dur < shortest_mom_)
411         shortest_mom_ = dur;
412 #else
413       /* I very much suspect that we wanted: */
414
415       consider_end (shortest_mom_);
416       if (dur < shortest_mom_)
417         {
418           shortest_mom_ = dur;
419           consider_end (shortest_mom_);
420         }
421       consider_begin (shortest_mom_);
422 #endif
423
424       if (!stem_l_arr_p_)
425         return;
426       
427       Moment now = now_mom ();
428       
429       grouping_p_->add_stem (now - beam_start_moment_ + beam_start_location_,
430                              durlog - 2);
431       stem_l_arr_p_->push (stem_l);
432       last_add_mom_ = now;
433       extend_mom_ = (extend_mom_ >? now) + rhythmic_req->length_mom ();
434     }
435 }
436
437 void
438 Auto_beam_engraver::create_grobs ()
439 {
440   if (!count_i_)
441     {
442       consider_end (shortest_mom_);
443       consider_begin (shortest_mom_);
444     }
445   else if (count_i_ > 1)
446     {
447       if (stem_l_arr_p_)
448         {
449           Moment now = now_mom ();
450           if ((extend_mom_ < now)
451               || ((extend_mom_ == now) && (last_add_mom_ != now)))
452             {
453               end_beam ();
454             }
455           else if (!stem_l_arr_p_->size ())
456             {
457               junk_beam ();
458             }
459         }    
460     }
461
462   /*
463     count_i_++ -> 
464
465         auto-beam-engraver.cc:459: warning: value computed is not used (gcc: 2.96) */
466   count_i_ = count_i_ + 1;
467 }