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