]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
patch::: 1.3.108.jcn4
[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--2000 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 class Auto_beam_engraver : public Engraver
26 {
27 public:
28   Auto_beam_engraver ();
29   VIRTUAL_COPY_CONS (Translator);
30
31 protected:
32   virtual void do_pre_move_processing ();
33   virtual void do_post_move_processing ();
34   virtual void do_removal_processing ();
35   virtual void acknowledge_element (Score_element_info);
36   virtual void process_acknowledged ();
37
38 private:
39   void begin_beam ();
40   void consider_end_and_begin (Moment test_mom);
41   Spanner* create_beam_p ();
42   void end_beam ();
43   void junk_beam ();
44   bool same_grace_state_b (Score_element* e);
45   void typeset_beam ();
46
47   Moment shortest_mom_;
48   Spanner *finished_beam_p_;
49   Link_array<Item>* stem_l_arr_p_;
50   
51   Moment last_add_mom_;
52   Moment extend_mom_;
53   Moment beam_start_moment_;
54   Moment beam_start_location_;
55   
56   // We act as if beam were created, and start a grouping anyway.
57   Beaming_info_list*grouping_p_;  
58   Beaming_info_list*finished_grouping_p_;
59 };
60
61 ADD_THIS_TRANSLATOR (Auto_beam_engraver);
62
63 Auto_beam_engraver::Auto_beam_engraver ()
64 {
65   stem_l_arr_p_ = 0;
66   shortest_mom_ = Moment (1, 8);
67   finished_beam_p_ = 0;
68   finished_grouping_p_ = 0;
69   grouping_p_ = 0;
70 }
71
72 void
73 Auto_beam_engraver::consider_end_and_begin (Moment test_mom)
74 {
75   SCM wild = gh_list (ly_symbol2scm ("*"), ly_symbol2scm ("*"), SCM_UNDEFINED);
76   SCM b = gh_list (ly_symbol2scm ("begin"), SCM_UNDEFINED);
77   SCM e = gh_list (ly_symbol2scm ("end"), SCM_UNDEFINED);
78
79   Moment one_beat = *unsmob_moment( get_property ("beatLength"));
80   int num = *unsmob_moment (get_property("measureLength")) / one_beat;
81   int den = one_beat.den_i ();
82   SCM time = gh_list (gh_int2scm (num), gh_int2scm (den), SCM_UNDEFINED);
83
84   SCM type = gh_list (gh_int2scm (test_mom.num_i ()),
85                       gh_int2scm (test_mom.den_i ()), SCM_UNDEFINED);
86
87   SCM settings = get_property ("autoBeamSettings");
88   
89   /*
90     Determine end moment for auto beaming (and begin, mostly 0==anywhere) 
91     In order of increasing priority:
92
93     i.   every beat
94     ii.  end   *    <num> <den>
95     iii. end <type> <num> <den>
96
97     iv.  end   *      *     *
98     v.   end <type>   *     *
99
100
101     Rationale:
102
103     [to be defined in config file]
104     i.   easy catch-all rule
105     ii.  exceptions for time signature
106     iii. exceptions for time signature, for specific duration type
107
108     [user override]
109     iv.  generic override
110     v.   override for specific duration type
111
112   */
113   
114
115   /*
116     first guess: begin beam at any position
117   */
118   Moment begin_mom (0);
119   /*
120     first guess: end beam at end of beat
121   */
122   SCM one (get_property ("beatLength"));
123
124   Moment end_mom;
125   if (unsmob_moment (one))
126     end_mom = *unsmob_moment (one);
127
128   /*
129     second guess: property generic time exception
130   */
131   SCM begin = gh_assoc (gh_append3 (b, wild, time), settings);
132   
133   if (begin != SCM_BOOL_F && unsmob_moment (gh_cdr (begin)))
134     begin_mom = * unsmob_moment (gh_cdr (begin));
135
136   SCM end = gh_assoc (gh_append3 (e, wild, time), settings);
137   if (end != SCM_BOOL_F && unsmob_moment (gh_cdr (end)))
138     end_mom = * unsmob_moment (gh_cdr (end));
139
140   /*
141     third guess: property time exception, specific for duration type
142   */
143   SCM begin_mult = gh_assoc (gh_append3 (b, type, time), settings);
144   if (begin_mult != SCM_BOOL_F && unsmob_moment (gh_cdr (begin_mult)))
145     begin_mom = * unsmob_moment (gh_cdr (begin_mult));
146   
147   SCM end_mult = gh_assoc (gh_append3 (e, type, time), settings);
148   if (end_mult != SCM_BOOL_F && unsmob_moment (gh_cdr (end_mult)))
149     end_mom = * unsmob_moment (gh_cdr (end_mult));
150
151   /*
152     fourth guess [user override]: property plain generic
153   */
154   begin = gh_assoc (gh_append3 (b, wild, wild), settings);
155   if (begin != SCM_BOOL_F && unsmob_moment (gh_cdr (begin)))
156     begin_mom = * unsmob_moment (gh_cdr (begin));
157
158   end = gh_assoc (gh_append3 (e, wild, wild), settings);
159   if (end != SCM_BOOL_F && unsmob_moment (gh_cdr (end)))
160     end_mom = * unsmob_moment (gh_cdr (end));
161
162   /*
163     fifth guess [user override]: property plain, specific for duration type
164   */
165   begin_mult = gh_assoc (gh_append3 (b, type, wild), settings);
166   if (begin_mult != SCM_BOOL_F && unsmob_moment (gh_cdr (begin_mult)))
167     begin_mom = * unsmob_moment (gh_cdr (begin_mult));
168   
169   end_mult = gh_assoc (gh_append3 (e, type, wild), settings);
170   if (end_mult != SCM_BOOL_F && unsmob_moment (gh_cdr (end_mult)))
171     end_mom = * unsmob_moment (gh_cdr (end_mult));
172
173   Rational r;
174   if (end_mom)
175     r = unsmob_moment (get_property ("measurePosition"))->mod_rat (end_mom);
176   else
177     r = Moment (1);
178
179   if (stem_l_arr_p_ && stem_l_arr_p_->size () > 1 && !r)
180     end_beam ();
181
182   /*
183     Allow already started autobeam to end
184    */
185   SCM on = get_property ("noAutoBeaming");
186   if (to_boolean (on))
187     return;
188
189   if (begin_mom)
190     r = unsmob_moment (get_property ("measurePosition"))->mod_rat (begin_mom);
191   if (!stem_l_arr_p_ && (!begin_mom || !r))
192     begin_beam ();
193 }
194       
195 Spanner*
196 Auto_beam_engraver::create_beam_p ()
197 {
198   Spanner* beam_p = new Spanner (get_property ("Beam"));
199   Beam::set_interface (beam_p);
200
201   for (int i = 0; i < stem_l_arr_p_->size (); i++)
202     {
203       /*
204         watch out for stem tremolos and abbreviation beams
205        */
206       if (Stem::beam_l ((*stem_l_arr_p_)[i]))
207         {
208           return 0;
209         }
210       Beam::add_stem (beam_p,(*stem_l_arr_p_)[i]);
211     }
212   
213   announce_element (beam_p, 0);
214
215   return beam_p;
216 }
217
218 void
219 Auto_beam_engraver::begin_beam ()
220 {
221   assert (!stem_l_arr_p_);
222   stem_l_arr_p_ = new Link_array<Item>;
223   assert (!grouping_p_);
224   grouping_p_ = new Beaming_info_list;
225   beam_start_moment_ = now_mom ();
226   beam_start_location_ = *unsmob_moment (get_property ("measurePosition"));
227 }
228
229
230 void
231 Auto_beam_engraver::junk_beam () 
232 {
233   assert (stem_l_arr_p_);
234   
235   delete stem_l_arr_p_;
236   stem_l_arr_p_ = 0;
237   delete grouping_p_;
238   grouping_p_ = 0;
239   shortest_mom_ = Moment (1, 8);
240 }
241
242 void
243 Auto_beam_engraver::end_beam ()
244 {
245   if (stem_l_arr_p_->size () < 2)
246     {
247       junk_beam ();
248     }
249   else
250     {
251       finished_beam_p_ = create_beam_p ();
252       if (finished_beam_p_)
253         finished_grouping_p_ = grouping_p_;
254       delete stem_l_arr_p_;
255       stem_l_arr_p_ = 0;
256       grouping_p_ = 0;
257       shortest_mom_ = Moment (1, 8);
258     }
259 }
260
261 void
262 Auto_beam_engraver::typeset_beam ()
263 {
264   if (finished_beam_p_)
265     {
266       finished_grouping_p_->beamify ();
267       Beam::set_beaming (finished_beam_p_, finished_grouping_p_);
268       typeset_element (finished_beam_p_);
269       finished_beam_p_ = 0;
270     
271       delete finished_grouping_p_;
272       finished_grouping_p_= 0;
273     }
274 }
275
276 void
277 Auto_beam_engraver::do_post_move_processing ()
278 {
279   /*
280     don't beam over skips
281    */
282   if (stem_l_arr_p_)
283     {
284       Moment now = now_mom ();
285       if (extend_mom_ < now)
286         {
287           end_beam ();
288         }
289     }
290 }
291
292 void
293 Auto_beam_engraver::do_pre_move_processing ()
294 {
295   if (stem_l_arr_p_)
296     {
297       Moment now = now_mom ();
298       if ((extend_mom_ < now)
299           || ((extend_mom_ == now) && (last_add_mom_ != now )))
300         {
301           end_beam ();
302         }
303       else if (!stem_l_arr_p_->size ())
304         {
305           junk_beam ();
306         }
307     }
308   typeset_beam ();
309 }
310
311 void
312 Auto_beam_engraver::do_removal_processing ()
313 {
314   /* finished beams may be typeset */
315   typeset_beam ();
316   /* but unfinished may need another announce/acknowledge pass */
317   if (stem_l_arr_p_)
318     junk_beam ();
319 }
320
321 bool
322 Auto_beam_engraver::same_grace_state_b (Score_element* e)
323 {
324   bool gr = e->get_elt_property ("grace") == SCM_BOOL_T;
325   SCM wg =get_property ("weAreGraceContext");
326   return (to_boolean (wg)) == gr;
327 }
328
329 void
330 Auto_beam_engraver::acknowledge_element (Score_element_info info)
331 {
332   if (!same_grace_state_b (info.elem_l_))
333     return;
334   
335   if (stem_l_arr_p_)
336     {
337       if (Beam::has_interface (info.elem_l_))
338         {
339           end_beam ();
340         }
341       else if (Bar::has_interface (info.elem_l_))
342         {
343           end_beam ();
344         }
345       else if (Rest::has_interface (info.elem_l_))
346         {
347           end_beam ();
348         }
349     }
350   
351   if (Stem::has_interface (info.elem_l_))
352     {
353       Item* stem_l = dynamic_cast<Item *> (info.elem_l_);
354                                        
355       Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.req_l_);
356       if (!rhythmic_req)
357         {
358           programming_error ("Stem must have rhythmic structure");
359           return;
360         }
361       
362       /*
363         Don't (start) auto-beam over empty stems; skips or rests
364         */
365       if (!Stem::heads_i (stem_l))
366         {
367           if (stem_l_arr_p_)
368             end_beam ();
369           return;
370         }
371
372       if (Stem::beam_l (stem_l))
373         {
374           if (stem_l_arr_p_)
375             junk_beam ();
376           return ;
377         }
378               
379       int durlog  = unsmob_duration (rhythmic_req->get_mus_property ("duration"))->duration_log ();
380       
381       if (durlog <= 2)
382         {
383           if (stem_l_arr_p_)
384             end_beam ();
385           return;
386         }
387
388       /*
389         if shortest duration would change
390         reconsider ending/starting beam first.
391       */
392       Moment mom = unsmob_duration (rhythmic_req->get_mus_property ("duration"))->length_mom ();
393       consider_end_and_begin (mom);
394       if (!stem_l_arr_p_)
395         return;
396       if (mom < shortest_mom_)
397         {
398           if (stem_l_arr_p_->size ())
399             {
400               shortest_mom_ = mom;
401               consider_end_and_begin (shortest_mom_);
402               if (!stem_l_arr_p_)
403                 return;
404             }
405           shortest_mom_ = mom;
406         }
407       Moment now = now_mom ();
408       
409       grouping_p_->add_stem (now - beam_start_moment_ + beam_start_location_,
410                              durlog - 2);
411       stem_l_arr_p_->push (stem_l);
412       last_add_mom_ = now;
413       extend_mom_ = extend_mom_ >? now + rhythmic_req->length_mom ();
414     }
415 }
416
417 void
418 Auto_beam_engraver::process_acknowledged ()
419 {
420    if (!stem_l_arr_p_)
421      consider_end_and_begin (shortest_mom_);
422 }