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