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