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