]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
patch::: 1.2.12.jcn1
[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           delete beam_p;
198           return 0;
199         }
200       beam_p->add_stem ((*stem_l_arr_p_)[i]);
201     }
202   
203   /* urg, copied from Beam_engraver */
204   Scalar prop = get_property ("beamslopedamping", 0);
205   if (prop.isnum_b ()) 
206     beam_p->set_elt_property (damping_scm_sym, gh_int2scm(prop));
207
208   prop = get_property ("autoKneeGap", 0);
209   if (prop.isnum_b ()) 
210     beam_p->set_elt_property (auto_knee_gap_scm_sym, gh_int2scm(prop));
211
212   prop = get_property ("autoInterstaffKneeGap", 0);
213   if (prop.isnum_b ()) 
214     beam_p->set_elt_property (auto_interstaff_knee_gap_scm_sym, gh_int2scm( prop));
215       
216   prop = get_property ("beamquantisation", 0);
217   if (prop.isnum_b ()) 
218     beam_p->quantisation_ = (Beam::Quantisation)(int)prop;
219  
220   announce_element (Score_element_info (beam_p, 0));
221   return beam_p;
222 }
223
224 void
225 Auto_beam_engraver::end_beam ()
226 {
227   if (stem_l_arr_p_->size () < 2)
228     {
229       junk_beam ();
230     }
231   else
232     {
233       finished_beam_p_ = create_beam_p ();
234       if (finished_beam_p_)
235         finished_grouping_p_ = grouping_p_;
236       delete stem_l_arr_p_;
237       stem_l_arr_p_ = 0;
238       grouping_p_ = 0;
239       shortest_mom_ = Moment (1, 8);
240     }
241 }
242  
243 void
244 Auto_beam_engraver::typeset_beam ()
245 {
246   if (finished_beam_p_)
247     {
248       finished_grouping_p_->beamify ();
249       finished_beam_p_->set_beaming (finished_grouping_p_);
250       typeset_element (finished_beam_p_);
251       finished_beam_p_ = 0;
252     
253       delete finished_grouping_p_;
254       finished_grouping_p_= 0;
255     }
256 }
257
258 void
259 Auto_beam_engraver::do_post_move_processing ()
260 {
261   /*
262     don't beam over skips
263    */
264   if (stem_l_arr_p_)
265     {
266       Moment now = now_mom ();
267       if (extend_mom_ < now)
268         {
269           end_beam ();
270         }
271     }
272 }
273
274 void
275 Auto_beam_engraver::do_pre_move_processing ()
276 {
277   typeset_beam ();
278 }
279
280 void
281 Auto_beam_engraver::do_removal_processing ()
282 {
283   /* finished beams may be typeset */
284   typeset_beam ();
285   /* but unfinished may need another announce/acknoledge pass */
286   if (stem_l_arr_p_)
287     junk_beam ();
288 }
289
290 bool
291 Auto_beam_engraver::same_grace_state_b (Score_element* e)
292 {
293   bool gr = (e->get_elt_property (grace_scm_sym) != SCM_BOOL_F) ;
294
295   return gr == get_property ("weAreGraceContext",0).to_bool ();
296 }
297
298 void
299 Auto_beam_engraver::acknowledge_element (Score_element_info info)
300 {
301   if (!same_grace_state_b (info.elem_l_) || !timer_l_)
302     return;
303   
304   if (stem_l_arr_p_)
305     {
306       if (Beam *b = dynamic_cast<Beam *> (info.elem_l_))
307         {
308           end_beam ();
309         }
310       else if (Chord_tremolo *b = dynamic_cast<Chord_tremolo*> (info.elem_l_))
311         {
312           end_beam ();
313         }
314       else if (Bar *b = dynamic_cast<Bar *> (info.elem_l_))
315         {
316           end_beam ();
317         }
318       else if (Rest* rest_l = dynamic_cast<Rest *> (info.elem_l_))
319         {
320           end_beam ();
321         }
322     }
323   
324   if (Stem* stem_l = dynamic_cast<Stem *> (info.elem_l_))
325     {
326       Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.req_l_);
327       if (!rhythmic_req)
328         {
329           programming_error ("Stem must have rhythmic structure");
330           return;
331         }
332       
333       /*
334         Don't (start) auto-beam over empty stems; skips or rests
335         */
336       if (!stem_l->head_l_arr_.size ())
337         {
338           if (stem_l_arr_p_)
339             end_beam ();
340           return;
341         }
342
343       if (stem_l->beam_l_)
344         {
345           if (stem_l_arr_p_)
346             junk_beam ();
347           return ;
348         }
349               
350       int durlog  =rhythmic_req->duration_.durlog_i_;
351       if (durlog <= 2)
352         {
353           if (stem_l_arr_p_)
354             end_beam ();
355           return;
356         }
357
358       /*
359         if shortest duration would change
360         reconsider ending/starting beam first.
361       */
362       Moment mom = rhythmic_req->duration_.length_mom ();
363       consider_end_and_begin (mom);
364       if (!stem_l_arr_p_)
365         return;
366       if (mom < shortest_mom_)
367         {
368           if (stem_l_arr_p_->size ())
369             {
370               shortest_mom_ = mom;
371               consider_end_and_begin (shortest_mom_);
372               if (!stem_l_arr_p_)
373                 return;
374             }
375           shortest_mom_ = mom;
376         }
377       Moment now = now_mom ();
378       
379       grouping_p_->add_stem (now - beam_start_moment_ + beam_start_location_,
380                              durlog - 2);
381       stem_l_arr_p_->push (stem_l);
382       last_add_mom_ = now;
383       extend_mom_ = extend_mom_ >? now + rhythmic_req->length_mom ();
384     }
385 }
386
387 void
388 Auto_beam_engraver::junk_beam () 
389 {
390   assert (stem_l_arr_p_);
391   
392   delete stem_l_arr_p_;
393   stem_l_arr_p_ = 0;
394   delete grouping_p_;
395   grouping_p_ = 0;
396   shortest_mom_ = Moment (1, 8);
397 }
398
399 void
400 Auto_beam_engraver::process_acknowledged ()
401 {
402   if (stem_l_arr_p_)
403     {
404       Moment now = now_mom ();
405       if ((extend_mom_ < now)
406           || ((extend_mom_ == now) && (last_add_mom_ != now )))
407         {
408           end_beam ();
409         }
410       else if (!stem_l_arr_p_->size ())
411         {
412           junk_beam ();
413         }
414     }
415 }