]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
patch::: 1.3.18.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
10 #include "beaming.hh"
11 #include "auto-beam-engraver.hh"
12 #include "musical-request.hh"
13 #include "bar.hh"
14 #include "beam.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   int num;
59   int den;
60   timer_l_->get_time_signature (&num, &den);
61   
62   String time_str = String ("time") + to_str (num) + "_" + to_str (den);
63
64   String type_str;
65   if (test_mom.num () != 1)
66     type_str = to_str (test_mom.num ());
67   if (test_mom.den () != 1)
68     type_str = type_str + "_" + to_str (test_mom.den ());
69
70   /*
71     URG
72     
73     FIXME: SHOULD USE ALIST
74     
75    */
76
77   /*
78     Determine end moment for auto beaming (and begin, mostly 0==anywhere) 
79     In order of increasing priority:
80
81     i.   every beat <den>
82     ii.  time<num>_<den>beamAutoEnd
83     iii. time<num>_<den>beamAutoEnd<type>
84     iv.  beamAutoEnd
85     v.   beamAutoEnd<type>
86
87
88     Rationale:
89
90     [to be defined in config file]
91     i.   easy catch-all rule
92     ii.  exceptions for time signature
93     iii. exceptions for time signature, for specific duration type
94
95     [user override]
96     iv.  generic override
97     v.   override for specific duration type
98
99     The user overrides should be required for common cases.
100    */
101   
102   /*
103     first guess: begin beam at any position
104   */
105   Moment begin_mom (0);
106   /*
107     first guess: end beam at end of beat
108   */
109   SCM one (get_property ("beatLength", 0));
110
111   Moment end_mom;
112   if (SMOB_IS_TYPE_B(Moment, one))
113     end_mom = *SMOB_TO_TYPE (Moment, one);
114
115   /*
116     second guess: property generic time exception
117   */
118   SCM begin = get_property (time_str + "beamAutoBegin", 0);
119   if (SMOB_IS_TYPE_B(Moment, begin))
120     begin_mom = * SMOB_TO_TYPE(Moment, begin);
121
122   SCM end = get_property (time_str + "beamAutoEnd", 0);
123   if (SMOB_IS_TYPE_B (Moment, end))
124     end_mom = * SMOB_TO_TYPE(Moment,end);
125
126   /*
127     third guess: property time exception, specific for duration type
128   */
129   if (type_str.length_i ())
130     {
131       SCM end_mult = get_property (time_str + "beamAutoEnd" + type_str, 0);
132       if (SMOB_IS_TYPE_B (Moment, end_mult))
133         end_mom = * SMOB_TO_TYPE (Moment,end_mult);
134
135       SCM begin_mult = get_property (time_str + "beamAutoBegin" + type_str, 0);
136       if (SMOB_IS_TYPE_B (Moment, begin_mult))
137         begin_mom = * SMOB_TO_TYPE (Moment,begin_mult);
138     }
139
140   /*
141     fourth guess [user override]: property plain generic
142   */
143   begin = get_property ("beamAutoBegin", 0);
144   if (SMOB_IS_TYPE_B(Moment, begin))
145     begin_mom = * SMOB_TO_TYPE(Moment, begin);
146
147
148   
149   end = get_property ("beamAutoEnd", 0);
150   if (SMOB_IS_TYPE_B (Moment, end))
151     end_mom = * SMOB_TO_TYPE (Moment,end);
152
153   /*
154     fifth guess [user override]: property plain, specific for duration type
155   */
156   if (type_str.length_i ())
157     {
158       SCM end_mult = get_property (String ("beamAutoEnd") + type_str, 0);
159       if (SMOB_IS_TYPE_B (Moment, end_mult))
160         end_mom = * SMOB_TO_TYPE (Moment,end_mult);
161
162       SCM begin_mult = get_property (String ("beamAutoBegin") + type_str, 0);
163       if (SMOB_IS_TYPE_B (Moment, begin_mult))
164         begin_mom = * SMOB_TO_TYPE (Moment,begin_mult);
165     }
166
167   Rational r;
168   if (end_mom)
169     r = timer_l_->measure_position ().mod_rat (end_mom);
170   else
171     r = Moment (1);
172
173   if (stem_l_arr_p_ && !r)
174     end_beam ();
175      
176   /*
177     Allow already started autobeam to end
178    */
179   SCM on = get_property ("noAutoBeaming", 0);
180   if (to_boolean (on))
181     return;
182
183   if (begin_mom)
184     r = timer_l_->measure_position ().mod_rat (begin_mom);
185   if (!stem_l_arr_p_ && (!begin_mom || !r))
186     begin_beam ();
187 }
188
189       
190 void
191 Auto_beam_engraver::begin_beam ()
192 {
193   assert (!stem_l_arr_p_);
194   stem_l_arr_p_ = new Array<Stem*>;
195   assert (!grouping_p_);
196   grouping_p_ = new Beaming_info_list;
197   beam_start_moment_ = now_mom ();
198   beam_start_location_ = timer_l_->measure_position ();
199 }
200
201 Beam*
202 Auto_beam_engraver::create_beam_p ()
203 {
204   Beam* beam_p = new Beam;
205
206   for (int i = 0; i < stem_l_arr_p_->size (); i++)
207     {
208       /*
209         watch out for stem tremolos and abbreviation beams
210        */
211       if ((*stem_l_arr_p_)[i]->beam_l ())
212         {
213           return 0;
214         }
215       beam_p->add_stem ((*stem_l_arr_p_)[i]);
216     }
217   
218   announce_element (Score_element_info (beam_p, 0));
219
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/acknowledge 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_BOOL_T;
293   SCM wg =get_property ("weAreGraceContext",0);
294   return (to_boolean (wg)) == gr;
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 (Bar *b = dynamic_cast<Bar *> (info.elem_l_))
310         {
311           end_beam ();
312         }
313       else if (Rest* rest_l = dynamic_cast<Rest *> (info.elem_l_))
314         {
315           end_beam ();
316         }
317     }
318   
319   if (Stem* stem_l = dynamic_cast<Stem *> (info.elem_l_))
320     {
321       Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.req_l_);
322       if (!rhythmic_req)
323         {
324           programming_error ("Stem must have rhythmic structure");
325           return;
326         }
327       
328       /*
329         Don't (start) auto-beam over empty stems; skips or rests
330         */
331       if (!stem_l->heads_i ())
332         {
333           if (stem_l_arr_p_)
334             end_beam ();
335           return;
336         }
337
338       if (stem_l->beam_l ())
339         {
340           if (stem_l_arr_p_)
341             junk_beam ();
342           return ;
343         }
344               
345       int durlog  =rhythmic_req->duration_.durlog_i_;
346       if (durlog <= 2)
347         {
348           if (stem_l_arr_p_)
349             end_beam ();
350           return;
351         }
352
353       /*
354         if shortest duration would change
355         reconsider ending/starting beam first.
356       */
357       Moment mom = rhythmic_req->duration_.length_mom ();
358       consider_end_and_begin (mom);
359       if (!stem_l_arr_p_)
360         return;
361       if (mom < shortest_mom_)
362         {
363           if (stem_l_arr_p_->size ())
364             {
365               shortest_mom_ = mom;
366               consider_end_and_begin (shortest_mom_);
367               if (!stem_l_arr_p_)
368                 return;
369             }
370           shortest_mom_ = mom;
371         }
372       Moment now = now_mom ();
373       
374       grouping_p_->add_stem (now - beam_start_moment_ + beam_start_location_,
375                              durlog - 2);
376       stem_l_arr_p_->push (stem_l);
377       last_add_mom_ = now;
378       extend_mom_ = extend_mom_ >? now + rhythmic_req->length_mom ();
379     }
380 }
381
382 void
383 Auto_beam_engraver::junk_beam () 
384 {
385   assert (stem_l_arr_p_);
386   
387   delete stem_l_arr_p_;
388   stem_l_arr_p_ = 0;
389   delete grouping_p_;
390   grouping_p_ = 0;
391   shortest_mom_ = Moment (1, 8);
392 }
393
394 void
395 Auto_beam_engraver::process_acknowledged ()
396 {
397   if (stem_l_arr_p_)
398     {
399       Moment now = now_mom ();
400       if ((extend_mom_ < now)
401           || ((extend_mom_ == now) && (last_add_mom_ != now )))
402         {
403           end_beam ();
404         }
405       else if (!stem_l_arr_p_->size ())
406         {
407           junk_beam ();
408         }
409     }
410 }
411