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