]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
release: 1.3.0
[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   SCM begin = get_property (time_str + "beamAutoBegin", 0);
107   if (SMOB_IS_TYPE_B(Moment, begin))
108     begin_mom = * SMOB_TO_TYPE(Moment, begin);
109
110   SCM end = get_property (time_str + "beamAutoEnd", 0);
111   if (SMOB_IS_TYPE_B (Moment, end))
112     end_mom = * SMOB_TO_TYPE(Moment,end);
113
114   /*
115     third guess: property time exception, specific for duration type
116   */
117   if (type_str.length_i ())
118     {
119       SCM end_mult = get_property (time_str + "beamAutoEnd" + type_str, 0);
120       if (SMOB_IS_TYPE_B (Moment, end_mult))
121         end_mom = * SMOB_TO_TYPE (Moment,end_mult);
122
123       SCM begin_mult = get_property (time_str + "beamAutoBegin" + type_str, 0);
124       if (SMOB_IS_TYPE_B (Moment, begin_mult))
125         begin_mom = * SMOB_TO_TYPE (Moment,begin_mult);
126     }
127
128   /*
129     fourth guess [user override]: property plain generic
130   */
131   begin = get_property ("beamAutoBegin", 0);
132   if (SMOB_IS_TYPE_B(Moment, begin))
133     begin_mom = * SMOB_TO_TYPE(Moment, begin);
134
135
136   
137   end = get_property ("beamAutoEnd", 0);
138   if (SMOB_IS_TYPE_B (Moment, end))
139     end_mom = * SMOB_TO_TYPE (Moment,end);
140
141   /*
142     fifth guess [user override]: property plain, specific for duration type
143   */
144   if (type_str.length_i ())
145     {
146       SCM end_mult = get_property (String ("beamAutoEnd") + type_str, 0);
147       if (SMOB_IS_TYPE_B (Moment, end_mult))
148         end_mom = * SMOB_TO_TYPE (Moment,end_mult);
149
150       SCM begin_mult = get_property (String ("beamAutoBegin") + type_str, 0);
151       if (SMOB_IS_TYPE_B (Moment, begin_mult))
152         begin_mom = * SMOB_TO_TYPE (Moment,begin_mult);
153     }
154
155   Rational r;
156   if (end_mom)
157     r = time->whole_in_measure_.mod_rat (end_mom);
158   else
159     r = Moment (1);
160
161   if (stem_l_arr_p_ && !r)
162     end_beam ();
163      
164   /*
165     Allow already started autobeam to end
166    */
167   SCM on = get_property ("noAutoBeaming", 0);
168   if (gh_boolean_p (on) && gh_scm2bool (on))
169     return;
170
171   if (begin_mom)
172     r = time->whole_in_measure_.mod_rat (begin_mom);
173   if (!stem_l_arr_p_ && (!begin_mom || !r))
174     begin_beam ();
175 }
176
177       
178 void
179 Auto_beam_engraver::begin_beam ()
180 {
181   assert (!stem_l_arr_p_);
182   stem_l_arr_p_ = new Array<Stem*>;
183   assert (!grouping_p_);
184   grouping_p_ = new Beaming_info_list;
185   beam_start_moment_ = now_mom ();
186   beam_start_location_ = timer_l_->time_.whole_in_measure_;
187 }
188
189 Beam*
190 Auto_beam_engraver::create_beam_p ()
191 {
192   Beam* beam_p = new Beam;
193
194   for (int i = 0; i < stem_l_arr_p_->size (); i++)
195     {
196       /*
197         watch out for stem tremolos and abbreviation beams
198        */
199       if ((*stem_l_arr_p_)[i]->beam_l_)
200         {
201           return 0;
202         }
203       beam_p->add_stem ((*stem_l_arr_p_)[i]);
204     }
205   
206   /* urg, copied from Beam_engraver */
207   SCM prop = get_property ("beamslopedamping", 0);
208   if (SCM_NUMBERP(prop)) 
209     beam_p->set_elt_property (damping_scm_sym, prop);
210
211   prop = get_property ("autoKneeGap", 0);
212   if (SCM_NUMBERP(prop)) 
213     beam_p->set_elt_property (auto_knee_gap_scm_sym, prop);
214
215   prop = get_property ("autoInterstaffKneeGap", 0);
216   if (SCM_NUMBERP(prop)) 
217     beam_p->set_elt_property (auto_interstaff_knee_gap_scm_sym, prop);
218       
219   prop = get_property ("beamquantisation", 0);
220   if (SCM_NUMBERP(prop)) 
221     beam_p->quantisation_ = (Beam::Quantisation)(int)prop;
222  
223   announce_element (Score_element_info (beam_p, 0));
224   return beam_p;
225 }
226
227 void
228 Auto_beam_engraver::end_beam ()
229 {
230   if (stem_l_arr_p_->size () < 2)
231     {
232       junk_beam ();
233     }
234   else
235     {
236       finished_beam_p_ = create_beam_p ();
237       if (finished_beam_p_)
238         finished_grouping_p_ = grouping_p_;
239       delete stem_l_arr_p_;
240       stem_l_arr_p_ = 0;
241       grouping_p_ = 0;
242       shortest_mom_ = Moment (1, 8);
243     }
244 }
245  
246 void
247 Auto_beam_engraver::typeset_beam ()
248 {
249   if (finished_beam_p_)
250     {
251       finished_grouping_p_->beamify ();
252       finished_beam_p_->set_beaming (finished_grouping_p_);
253       typeset_element (finished_beam_p_);
254       finished_beam_p_ = 0;
255     
256       delete finished_grouping_p_;
257       finished_grouping_p_= 0;
258     }
259 }
260
261 void
262 Auto_beam_engraver::do_post_move_processing ()
263 {
264   /*
265     don't beam over skips
266    */
267   if (stem_l_arr_p_)
268     {
269       Moment now = now_mom ();
270       if (extend_mom_ < now)
271         {
272           end_beam ();
273         }
274     }
275 }
276
277 void
278 Auto_beam_engraver::do_pre_move_processing ()
279 {
280   typeset_beam ();
281 }
282
283 void
284 Auto_beam_engraver::do_removal_processing ()
285 {
286   /* finished beams may be typeset */
287   typeset_beam ();
288   /* but unfinished may need another announce/acknowledge pass */
289   if (stem_l_arr_p_)
290     junk_beam ();
291 }
292
293 bool
294 Auto_beam_engraver::same_grace_state_b (Score_element* e)
295 {
296   bool gr = e->get_elt_property (grace_scm_sym) != SCM_BOOL_F;
297   SCM wg =get_property ("weAreGraceContext",0);
298   return (gh_boolean_p (wg) && gh_scm2bool (wg)) == gr;
299 }
300
301 void
302 Auto_beam_engraver::acknowledge_element (Score_element_info info)
303 {
304   if (!same_grace_state_b (info.elem_l_) || !timer_l_)
305     return;
306   
307   if (stem_l_arr_p_)
308     {
309       if (Beam *b = dynamic_cast<Beam *> (info.elem_l_))
310         {
311           end_beam ();
312         }
313       else if (Chord_tremolo *b = dynamic_cast<Chord_tremolo*> (info.elem_l_))
314         {
315           end_beam ();
316         }
317       else if (Bar *b = dynamic_cast<Bar *> (info.elem_l_))
318         {
319           end_beam ();
320         }
321       else if (Rest* rest_l = dynamic_cast<Rest *> (info.elem_l_))
322         {
323           end_beam ();
324         }
325     }
326   
327   if (Stem* stem_l = dynamic_cast<Stem *> (info.elem_l_))
328     {
329       Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.req_l_);
330       if (!rhythmic_req)
331         {
332           programming_error ("Stem must have rhythmic structure");
333           return;
334         }
335       
336       /*
337         Don't (start) auto-beam over empty stems; skips or rests
338         */
339       if (!stem_l->head_l_arr_.size ())
340         {
341           if (stem_l_arr_p_)
342             end_beam ();
343           return;
344         }
345
346       if (stem_l->beam_l_)
347         {
348           if (stem_l_arr_p_)
349             junk_beam ();
350           return ;
351         }
352               
353       int durlog  =rhythmic_req->duration_.durlog_i_;
354       if (durlog <= 2)
355         {
356           if (stem_l_arr_p_)
357             end_beam ();
358           return;
359         }
360
361       /*
362         if shortest duration would change
363         reconsider ending/starting beam first.
364       */
365       Moment mom = rhythmic_req->duration_.length_mom ();
366       consider_end_and_begin (mom);
367       if (!stem_l_arr_p_)
368         return;
369       if (mom < shortest_mom_)
370         {
371           if (stem_l_arr_p_->size ())
372             {
373               shortest_mom_ = mom;
374               consider_end_and_begin (shortest_mom_);
375               if (!stem_l_arr_p_)
376                 return;
377             }
378           shortest_mom_ = mom;
379         }
380       Moment now = now_mom ();
381       
382       grouping_p_->add_stem (now - beam_start_moment_ + beam_start_location_,
383                              durlog - 2);
384       stem_l_arr_p_->push (stem_l);
385       last_add_mom_ = now;
386       extend_mom_ = extend_mom_ >? now + rhythmic_req->length_mom ();
387     }
388 }
389
390 void
391 Auto_beam_engraver::junk_beam () 
392 {
393   assert (stem_l_arr_p_);
394   
395   delete stem_l_arr_p_;
396   stem_l_arr_p_ = 0;
397   delete grouping_p_;
398   grouping_p_ = 0;
399   shortest_mom_ = Moment (1, 8);
400 }
401
402 void
403 Auto_beam_engraver::process_acknowledged ()
404 {
405   if (stem_l_arr_p_)
406     {
407       Moment now = now_mom ();
408       if ((extend_mom_ < now)
409           || ((extend_mom_ == now) && (last_add_mom_ != now )))
410         {
411           end_beam ();
412         }
413       else if (!stem_l_arr_p_->size ())
414         {
415           junk_beam ();
416         }
417     }
418 }