]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
release: 1.3.98
[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 bool do_try_music (Music*);
33   virtual void do_pre_move_processing ();
34   virtual void do_post_move_processing ();
35   virtual void do_removal_processing ();
36   virtual void acknowledge_element (Score_element_info);
37   virtual void do_process_music ();
38   virtual void process_acknowledged ();
39 private:
40   void begin_beam ();
41   void consider_end_and_begin (Moment test_mom);
42   Spanner* create_beam_p ();
43   void end_beam ();
44   void junk_beam ();
45   bool same_grace_state_b (Score_element* e);
46   void typeset_beam ();
47
48   Moment shortest_mom_;
49   Spanner *finished_beam_p_;
50   Link_array<Item>* stem_l_arr_p_;
51   
52   Moment last_add_mom_;
53   Moment extend_mom_;
54   Moment beam_start_moment_;
55   Moment beam_start_location_;
56   
57   // We act as if beam were created, and start a grouping anyway.
58   Beaming_info_list*grouping_p_;  
59   Beaming_info_list*finished_grouping_p_;
60 };
61
62 ADD_THIS_TRANSLATOR (Auto_beam_engraver);
63
64 Auto_beam_engraver::Auto_beam_engraver ()
65 {
66   stem_l_arr_p_ = 0;
67   shortest_mom_ = Moment (1, 8);
68   finished_beam_p_ = 0;
69   finished_grouping_p_ = 0;
70   grouping_p_ = 0;
71 }
72
73 bool
74 Auto_beam_engraver::do_try_music (Music*) 
75 {
76   return false;
77
78
79 void
80 Auto_beam_engraver::do_process_music ()
81 {
82   consider_end_and_begin (shortest_mom_);
83 }
84
85 void
86 Auto_beam_engraver::consider_end_and_begin (Moment test_mom)
87 {
88   Moment one_beat = *unsmob_moment( get_property ("beatLength"));
89
90   int num = *unsmob_moment (get_property("measureLength")) / one_beat;
91   int den = one_beat.den_i ();
92   
93   String time_str = String ("time") + to_str (num) + "_" + to_str (den);
94
95   String type_str;
96   if (test_mom.num () != 1)
97     type_str = to_str (test_mom.num ());
98   if (test_mom.den () != 1)
99     type_str = type_str + "_" + to_str (test_mom.den ());
100
101   /*
102     URG
103     
104     FIXME: SHOULD USE ALIST
105     
106    */
107
108   /*
109     Determine end moment for auto beaming (and begin, mostly 0==anywhere) 
110     In order of increasing priority:
111
112     i.   every beat <den>
113     ii.  time<num>_<den>beamAutoEnd
114     iii. time<num>_<den>beamAutoEnd<type>
115     iv.  beamAutoEnd
116     v.   beamAutoEnd<type>
117
118
119     Rationale:
120
121     [to be defined in config file]
122     i.   easy catch-all rule
123     ii.  exceptions for time signature
124     iii. exceptions for time signature, for specific duration type
125
126     [user override]
127     iv.  generic override
128     v.   override for specific duration type
129
130     The user overrides should be required for common cases.
131    */
132   
133   /*
134     first guess: begin beam at any position
135   */
136   Moment begin_mom (0);
137   /*
138     first guess: end beam at end of beat
139   */
140   SCM one (get_property ("beatLength"));
141
142   Moment end_mom;
143   if (unsmob_moment (one))
144     end_mom = *unsmob_moment (one);
145
146   /*
147     second guess: property generic time exception
148   */
149   SCM begin = get_property ((time_str + "beamAutoBegin").ch_C());
150   if (unsmob_moment (begin))
151     begin_mom = * unsmob_moment (begin);
152
153   SCM end = get_property ((time_str + "beamAutoEnd").ch_C());
154   if (unsmob_moment (end))
155     end_mom = * unsmob_moment (end);
156
157   /*
158     third guess: property time exception, specific for duration type
159   */
160   if (type_str.length_i ())
161     {
162       SCM end_mult = get_property ((time_str + "beamAutoEnd" + type_str).ch_C());
163       if (unsmob_moment (end_mult))
164         end_mom = * unsmob_moment (end_mult);
165
166       SCM begin_mult = get_property ((time_str + "beamAutoBegin" + type_str).ch_C());
167       if (unsmob_moment (begin_mult))
168         begin_mom = * unsmob_moment (begin_mult);
169     }
170
171   /*
172     fourth guess [user override]: property plain generic
173   */
174   begin = get_property ("beamAutoBegin");
175   if (unsmob_moment (begin))
176     begin_mom = * unsmob_moment (begin);
177
178
179   
180   end = get_property ("beamAutoEnd");
181   if (unsmob_moment (end))
182     end_mom = * unsmob_moment (end);
183
184   /*
185     fifth guess [user override]: property plain, specific for duration type
186   */
187   if (type_str.length_i ())
188     {
189       SCM end_mult = get_property ((String ("beamAutoEnd") + type_str).ch_C());
190       if (unsmob_moment (end_mult))
191         end_mom = * unsmob_moment (end_mult);
192
193       SCM begin_mult = get_property ((String ("beamAutoBegin") + type_str).ch_C());
194       if (unsmob_moment (begin_mult))
195         begin_mom = * unsmob_moment (begin_mult);
196     }
197
198   Rational r;
199   if (end_mom)
200     r = unsmob_moment (get_property ("measurePosition"))->mod_rat (end_mom);
201   else
202     r = Moment (1);
203
204   if (stem_l_arr_p_ && !r)
205     end_beam ();
206      
207   /*
208     Allow already started autobeam to end
209    */
210   SCM on = get_property ("noAutoBeaming");
211   if (to_boolean (on))
212     return;
213
214   if (begin_mom)
215     r = unsmob_moment (get_property ("measurePosition"))->mod_rat (begin_mom);
216   if (!stem_l_arr_p_ && (!begin_mom || !r))
217     begin_beam ();
218 }
219
220       
221 void
222 Auto_beam_engraver::begin_beam ()
223 {
224   assert (!stem_l_arr_p_);
225   stem_l_arr_p_ = new Link_array<Item>;
226   assert (!grouping_p_);
227   grouping_p_ = new Beaming_info_list;
228   beam_start_moment_ = now_mom ();
229   beam_start_location_ = *unsmob_moment (get_property ("measurePosition"));
230 }
231
232 Spanner*
233 Auto_beam_engraver::create_beam_p ()
234 {
235   Spanner* beam_p = new Spanner (get_property ("Beam"));
236   Beam::set_interface (beam_p);
237
238   for (int i = 0; i < stem_l_arr_p_->size (); i++)
239     {
240       /*
241         watch out for stem tremolos and abbreviation beams
242        */
243       if (Stem::beam_l ((*stem_l_arr_p_)[i]))
244         {
245           return 0;
246         }
247       Beam::add_stem (beam_p,(*stem_l_arr_p_)[i]);
248     }
249   
250   announce_element (beam_p, 0);
251
252   return beam_p;
253 }
254
255 void
256 Auto_beam_engraver::end_beam ()
257 {
258   if (stem_l_arr_p_->size () < 2)
259     {
260       junk_beam ();
261     }
262   else
263     {
264       finished_beam_p_ = create_beam_p ();
265       if (finished_beam_p_)
266         finished_grouping_p_ = grouping_p_;
267       delete stem_l_arr_p_;
268       stem_l_arr_p_ = 0;
269       grouping_p_ = 0;
270       shortest_mom_ = Moment (1, 8);
271     }
272 }
273  
274 void
275 Auto_beam_engraver::typeset_beam ()
276 {
277   if (finished_beam_p_)
278     {
279       finished_grouping_p_->beamify ();
280       Beam::set_beaming (finished_beam_p_, finished_grouping_p_);
281       typeset_element (finished_beam_p_);
282       finished_beam_p_ = 0;
283     
284       delete finished_grouping_p_;
285       finished_grouping_p_= 0;
286     }
287 }
288
289 void
290 Auto_beam_engraver::do_post_move_processing ()
291 {
292   /*
293     don't beam over skips
294    */
295   if (stem_l_arr_p_)
296     {
297       Moment now = now_mom ();
298       if (extend_mom_ < now)
299         {
300           end_beam ();
301         }
302     }
303 }
304
305 void
306 Auto_beam_engraver::do_pre_move_processing ()
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  =rhythmic_req->duration_.durlog_i_;
380       if (durlog <= 2)
381         {
382           if (stem_l_arr_p_)
383             end_beam ();
384           return;
385         }
386
387       /*
388         if shortest duration would change
389         reconsider ending/starting beam first.
390       */
391       Moment mom = rhythmic_req->duration_.length_mom ();
392       consider_end_and_begin (mom);
393       if (!stem_l_arr_p_)
394         return;
395       if (mom < shortest_mom_)
396         {
397           if (stem_l_arr_p_->size ())
398             {
399               shortest_mom_ = mom;
400               consider_end_and_begin (shortest_mom_);
401               if (!stem_l_arr_p_)
402                 return;
403             }
404           shortest_mom_ = mom;
405         }
406       Moment now = now_mom ();
407       
408       grouping_p_->add_stem (now - beam_start_moment_ + beam_start_location_,
409                              durlog - 2);
410       stem_l_arr_p_->push (stem_l);
411       last_add_mom_ = now;
412       extend_mom_ = extend_mom_ >? now + rhythmic_req->length_mom ();
413     }
414 }
415
416 void
417 Auto_beam_engraver::junk_beam () 
418 {
419   assert (stem_l_arr_p_);
420   
421   delete stem_l_arr_p_;
422   stem_l_arr_p_ = 0;
423   delete grouping_p_;
424   grouping_p_ = 0;
425   shortest_mom_ = Moment (1, 8);
426 }
427
428 void
429 Auto_beam_engraver::process_acknowledged ()
430 {
431   if (stem_l_arr_p_)
432     {
433       Moment now = now_mom ();
434       if ((extend_mom_ < now)
435           || ((extend_mom_ == now) && (last_add_mom_ != now )))
436         {
437           end_beam ();
438         }
439       else if (!stem_l_arr_p_->size ())
440         {
441           junk_beam ();
442         }
443     }
444 }