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