]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
release: 1.3.55
[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 #include "beaming.hh"
10 #include "auto-beam-engraver.hh"
11 #include "musical-request.hh"
12 #include "bar.hh"
13 #include "beam.hh"
14 #include "rest.hh"
15 #include "stem.hh"
16 #include "debug.hh"
17 #include "timing-engraver.hh"
18 #include "engraver-group-engraver.hh"
19
20 ADD_THIS_TRANSLATOR (Auto_beam_engraver);
21
22
23 /*
24   TODO: remove all references to Timing_engraver; should read properties.
25   
26  */
27 Auto_beam_engraver::Auto_beam_engraver ()
28 {
29   stem_l_arr_p_ = 0;
30   shortest_mom_ = Moment (1, 8);
31   finished_beam_p_ = 0;
32   finished_grouping_p_ = 0;
33   grouping_p_ = 0;
34   timer_l_ =0;
35 }
36
37 void
38 Auto_beam_engraver::do_creation_processing ()
39 {
40   Translator * t = daddy_grav_l  ()->get_simple_translator ("Timing_engraver");
41   timer_l_ = dynamic_cast<Timing_engraver*> (t);
42 }
43
44 bool
45 Auto_beam_engraver::do_try_music (Music*) 
46 {
47   return false;
48
49
50 void
51 Auto_beam_engraver::do_process_music ()
52 {
53   consider_end_and_begin (shortest_mom_);
54 }
55
56 void
57 Auto_beam_engraver::consider_end_and_begin (Moment test_mom)
58 {
59   if (!timer_l_)
60     return;
61   
62   int num;
63   int den;
64   timer_l_->get_time_signature (&num, &den);
65   
66   String time_str = String ("time") + to_str (num) + "_" + to_str (den);
67
68   String type_str;
69   if (test_mom.num () != 1)
70     type_str = to_str (test_mom.num ());
71   if (test_mom.den () != 1)
72     type_str = type_str + "_" + to_str (test_mom.den ());
73
74   /*
75     URG
76     
77     FIXME: SHOULD USE ALIST
78     
79    */
80
81   /*
82     Determine end moment for auto beaming (and begin, mostly 0==anywhere) 
83     In order of increasing priority:
84
85     i.   every beat <den>
86     ii.  time<num>_<den>beamAutoEnd
87     iii. time<num>_<den>beamAutoEnd<type>
88     iv.  beamAutoEnd
89     v.   beamAutoEnd<type>
90
91
92     Rationale:
93
94     [to be defined in config file]
95     i.   easy catch-all rule
96     ii.  exceptions for time signature
97     iii. exceptions for time signature, for specific duration type
98
99     [user override]
100     iv.  generic override
101     v.   override for specific duration type
102
103     The user overrides should be required for common cases.
104    */
105   
106   /*
107     first guess: begin beam at any position
108   */
109   Moment begin_mom (0);
110   /*
111     first guess: end beam at end of beat
112   */
113   SCM one (get_property ("beatLength"));
114
115   Moment end_mom;
116   if (SMOB_IS_TYPE_B(Moment, one))
117     end_mom = *SMOB_TO_TYPE (Moment, one);
118
119   /*
120     second guess: property generic time exception
121   */
122   SCM begin = get_property (time_str + "beamAutoBegin");
123   if (SMOB_IS_TYPE_B(Moment, begin))
124     begin_mom = * SMOB_TO_TYPE(Moment, begin);
125
126   SCM end = get_property (time_str + "beamAutoEnd");
127   if (SMOB_IS_TYPE_B (Moment, end))
128     end_mom = * SMOB_TO_TYPE(Moment,end);
129
130   /*
131     third guess: property time exception, specific for duration type
132   */
133   if (type_str.length_i ())
134     {
135       SCM end_mult = get_property ( time_str + "beamAutoEnd" + type_str);
136       if (SMOB_IS_TYPE_B (Moment, end_mult))
137         end_mom = * SMOB_TO_TYPE (Moment,end_mult);
138
139       SCM begin_mult = get_property (time_str + "beamAutoBegin" + type_str);
140       if (SMOB_IS_TYPE_B (Moment, begin_mult))
141         begin_mom = * SMOB_TO_TYPE (Moment,begin_mult);
142     }
143
144   /*
145     fourth guess [user override]: property plain generic
146   */
147   begin = get_property ("beamAutoBegin");
148   if (SMOB_IS_TYPE_B(Moment, begin))
149     begin_mom = * SMOB_TO_TYPE(Moment, begin);
150
151
152   
153   end = get_property ("beamAutoEnd");
154   if (SMOB_IS_TYPE_B (Moment, end))
155     end_mom = * SMOB_TO_TYPE (Moment,end);
156
157   /*
158     fifth guess [user override]: property plain, specific for duration type
159   */
160   if (type_str.length_i ())
161     {
162       SCM end_mult = get_property (String ("beamAutoEnd") + type_str);
163       if (SMOB_IS_TYPE_B (Moment, end_mult))
164         end_mom = * SMOB_TO_TYPE (Moment,end_mult);
165
166       SCM begin_mult = get_property (String ("beamAutoBegin") + type_str);
167       if (SMOB_IS_TYPE_B (Moment, begin_mult))
168         begin_mom = * SMOB_TO_TYPE (Moment,begin_mult);
169     }
170
171   Rational r;
172   if (end_mom)
173     r = timer_l_->measure_position ().mod_rat (end_mom);
174   else
175     r = Moment (1);
176
177   if (stem_l_arr_p_ && !r)
178     end_beam ();
179      
180   /*
181     Allow already started autobeam to end
182    */
183   SCM on = get_property ("noAutoBeaming");
184   if (to_boolean (on))
185     return;
186
187   if (begin_mom)
188     r = timer_l_->measure_position ().mod_rat (begin_mom);
189   if (!stem_l_arr_p_ && (!begin_mom || !r))
190     begin_beam ();
191 }
192
193       
194 void
195 Auto_beam_engraver::begin_beam ()
196 {
197   assert (!stem_l_arr_p_);
198   stem_l_arr_p_ = new Array<Stem*>;
199   assert (!grouping_p_);
200   grouping_p_ = new Beaming_info_list;
201   beam_start_moment_ = now_mom ();
202   beam_start_location_ = timer_l_->measure_position ();
203 }
204
205 Beam*
206 Auto_beam_engraver::create_beam_p ()
207 {
208   Beam* beam_p = new Beam (get_property ("basicBeamProperties"));
209
210   for (int i = 0; i < stem_l_arr_p_->size (); i++)
211     {
212       /*
213         watch out for stem tremolos and abbreviation beams
214        */
215       if ((*stem_l_arr_p_)[i]->beam_l ())
216         {
217           return 0;
218         }
219       beam_p->add_stem ((*stem_l_arr_p_)[i]);
220     }
221   
222   announce_element (Score_element_info (beam_p, 0));
223
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_BOOL_T;
297   SCM wg =get_property ("weAreGraceContext");
298   return (to_boolean (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 (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->heads_i ())
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 }
415