]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
release: 1.1.31
[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 "auto-beam-engraver.hh"
11 #include "musical-request.hh"
12 #include "bar.hh"
13 #include "beam.hh"
14 #include "rhythmic-grouping.hh"
15 #include "rest.hh"
16 #include "stem.hh"
17 #include "debug.hh"
18 #include "time-description.hh"
19
20 ADD_THIS_TRANSLATOR (Auto_beam_engraver);
21
22 Auto_beam_engraver::Auto_beam_engraver ()
23 {
24   beam_p_ = 0;
25   mult_i_ = 0;
26   finished_beam_p_ = 0;
27   finished_grouping_p_ = 0;
28   grouping_p_ = 0;
29 }
30
31 void
32 Auto_beam_engraver::do_process_requests ()
33 {
34   consider_end_and_begin ();
35 }
36
37 void
38 Auto_beam_engraver::consider_end_and_begin ()
39 {
40   Time_description const *time = get_staff_info().time_C_;
41   int num = time->whole_per_measure_ / time->one_beat_;
42   int den = time->one_beat_.den_i ();
43   String time_str = String ("time") + to_str (num) + "_" + to_str (den);
44   int type = 1 << (mult_i_ + 2);
45   String type_str = to_str (type);
46
47   /*
48     Determine end moment for auto beaming (and begin, mostly 0==anywhere) 
49     In order of increasing priority:
50
51     i.   every beat <den>
52     ii.  time<num>_<den>beamAutoEnd
53     iii. time<num>_<den>beamAutoEnd<type>
54     iv.  beamAutoEnd
55     v.   beamAutoEnd<type>
56
57
58     Rationale:
59
60     [to be defined in config file]
61     i.   easy catch-all rule
62     ii.  exceptions for time signature
63     iii. exceptions for time signature, for specific duration type
64
65     [user override]
66     iv.  generic override
67     v.   override for specific duration type
68
69     The user overrides should be required for common cases.
70    */
71   
72   /*
73     first guess: begin beam at any position
74   */
75   Moment begin_mom (0);
76   /*
77     first guess: end beam at end of beat
78   */
79   Moment end_mom = time->one_beat_;
80
81   /*
82     second guess: property generic time exception
83   */
84   Scalar begin = get_property (time_str + "beamAutoBegin", 0);
85   if (begin.length_i ())
86     begin_mom = begin.to_rat ();
87
88   Scalar end = get_property (time_str + "beamAutoEnd", 0);
89   if (end.length_i ())
90     end_mom = end.to_rat ();
91
92   /*
93     third guess: property time exception, specific for duration type
94   */
95   if (mult_i_)
96     {
97       Scalar end_mult = get_property (time_str + "beamAutoEnd" + type_str, 0);
98       if (end_mult.length_i ())
99         end_mom = end_mult.to_rat ();
100       Scalar begin_mult = get_property (time_str + "beamAutoBegin" + type_str, 0);
101       if (begin_mult.length_i ())
102         begin_mom = begin_mult.to_rat ();
103     }
104
105   /*
106     fourth guess [user override]: property plain generic
107   */
108   begin = get_property ("beamAutoBegin", 0);
109   if (begin.length_i ())
110     begin_mom = begin.to_rat ();
111   
112   end = get_property ("beamAutoEnd", 0);
113   if (end.length_i ())
114     end_mom = end.to_rat ();
115
116   /*
117     fifth guess [user override]: property plain, specific for duration type
118   */
119   if (mult_i_)
120     {
121       Scalar end_mult = get_property (String ("beamAutoEnd") + type_str, 0);
122       if (end_mult.length_i ())
123         end_mom = end_mult.to_rat ();
124       Scalar begin_mult = get_property (String ("beamAutoBegin") + type_str, 0);
125       if (begin_mult.length_i ())
126         begin_mom = begin_mult.to_rat ();
127     }
128
129   /* UGH
130      Rational != Float
131   */
132   Real f;
133   if (end_mom)
134     f = fmod (time->whole_in_measure_, end_mom);
135   else
136     f = Moment (1);
137
138   // enge floots
139   Real epsilon_f = Moment (1, 512);
140   if (beam_p_ && (abs (f) < epsilon_f))
141     end_beam ();
142      
143   /*
144     Allow already started autobeam to end
145    */
146   Scalar on = get_property ("beamAuto", 0);
147   if (!on.to_bool ())
148     return;
149
150   if (begin_mom)
151     f = fmod (time->whole_in_measure_, begin_mom);
152   if (!beam_p_ && (!begin_mom || (abs (f) < epsilon_f)))
153     begin_beam ();
154 }
155
156       
157 void
158 Auto_beam_engraver::begin_beam ()
159 {
160   DOUT << String ("starting autobeam at: ") + now_mom ().str () + "\n";
161   beam_p_ = new Beam;
162   grouping_p_ = new Rhythmic_grouping;
163
164       /* urg, copied from Beam_engraver */
165   Scalar prop = get_property ("beamslopedamping", 0);
166   if (prop.isnum_b ()) 
167     beam_p_->damping_i_ = prop;
168
169   prop = get_property ("beamquantisation", 0);
170   if (prop.isnum_b ()) 
171     beam_p_->quantisation_ = (Beam::Quantisation)(int)prop;
172  
173       // must set minVerticalAlign = = maxVerticalAlign to get sane results
174       // see input/test/beam-interstaff.ly
175   prop = get_property ("minVerticalAlign", 0);
176   if (prop.isnum_b ())
177     beam_p_->vertical_align_drul_[MIN] = prop;
178
179   prop = get_property ("maxVerticalAlign", 0);
180   if (prop.isnum_b ())
181     beam_p_->vertical_align_drul_[MAX] = prop;
182
183   announce_element (Score_element_info (beam_p_, 0));
184 }
185
186 void
187 Auto_beam_engraver::end_beam ()
188 {
189   DOUT << String ("ending autobeam at: ") + now_mom ().str () + "\n";
190   if (beam_p_->stems_.size () < 2)
191     {
192       DOUT << "junking autombeam: less than two stems\n";
193       junk_beam ();
194     }
195   else
196     {
197       finished_beam_p_ = beam_p_;
198       finished_grouping_p_ = grouping_p_;
199       beam_p_ = 0;
200       grouping_p_ = 0;
201       mult_i_ = 0;
202     }
203 }
204  
205 void
206 Auto_beam_engraver::typeset_beam ()
207 {
208   if (finished_beam_p_)
209     {
210       Rhythmic_grouping const * rg_C = get_staff_info().rhythmic_C_;
211       rg_C->extend (finished_grouping_p_->interval());
212       finished_beam_p_->set_grouping (*rg_C, *finished_grouping_p_);
213       typeset_element (finished_beam_p_);
214       finished_beam_p_ = 0;
215     
216       delete finished_grouping_p_;
217       finished_grouping_p_= 0;
218     }
219 }
220
221 void
222 Auto_beam_engraver::do_post_move_processing ()
223 {
224 }
225
226 void
227 Auto_beam_engraver::do_pre_move_processing ()
228 {
229   typeset_beam ();
230 }
231
232 void
233 Auto_beam_engraver::do_removal_processing ()
234 {
235   typeset_beam ();
236   if (beam_p_)
237     {
238       DOUT << "Unfinished beam\n";
239       junk_beam ();
240     }
241 }
242
243 void
244 Auto_beam_engraver::acknowledge_element (Score_element_info info)
245 {
246   if (Beam *b = dynamic_cast<Beam *> (info.elem_l_))
247     {
248       if (beam_p_)
249         {
250           DOUT << "junking autobeam: beam encountered\n";
251           junk_beam ();
252         }
253     }
254   if (Bar *b = dynamic_cast<Bar *> (info.elem_l_))
255     {
256       if (beam_p_)
257         {
258           DOUT << "junking autobeam: bar encountered\n";
259           junk_beam ();
260         }
261     }
262
263   if (beam_p_)
264     {
265       Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.req_l_);
266       if (!rhythmic_req)
267         return;
268
269       if (dynamic_cast<Rest *> (info.elem_l_))
270         {
271           DOUT << "junking autobeam: rest encountered\n";
272           end_beam ();
273           return;
274         }
275
276       Stem* stem_l = dynamic_cast<Stem *> (info.elem_l_);
277       if (!stem_l)
278         return;
279
280       if (stem_l->beam_l_ && (stem_l->beam_l_ != beam_p_))
281         {
282           DOUT << "junking autobeam: beamed stem encountered\n";
283           junk_beam ();
284           return;
285         }
286         
287
288       /*
289         now that we have last_add_mom_, perhaps we can (should) do away
290         with these individual junk_beams
291        */
292       if (rhythmic_req->duration_.durlog_i_<= 2)
293         {
294           DOUT << "ending autobeam: stem doesn't fit in beam\n";
295           end_beam ();
296           return;
297         }
298
299       Moment start = get_staff_info().time_C_->whole_in_measure_;
300       if (!grouping_p_->child_fit_b (start))
301         {
302           DOUT << "ending autobeam: stem doesn't fit in group\n";
303           end_beam ();
304         }
305       else
306         {
307           int m = (rhythmic_req->duration_.durlog_i_ - 2);
308           /*
309             if multiplicity would become greater,
310             reconsider ending/starting beam first.
311            */
312           if (m > mult_i_)
313             {
314               mult_i_ = m;
315               consider_end_and_begin ();
316             }
317           mult_i_ = m;
318           grouping_p_->add_child (start, rhythmic_req->length_mom ());
319           stem_l->flag_i_ = rhythmic_req->duration_.durlog_i_;
320           beam_p_->add_stem (stem_l);
321           Moment now = now_mom ();
322           last_add_mom_ = now;
323           extend_mom_ = extend_mom_ >? now + rhythmic_req->length_mom ();
324         }
325     }
326 }
327
328 void
329 Auto_beam_engraver::junk_beam () 
330 {
331   assert (beam_p_);
332   for (int i=0; i < beam_p_->stems_.size (); i++)
333     {
334       Stem* s = beam_p_->stems_[i];
335       s->beams_i_drul_[LEFT] = 0;
336       s->beams_i_drul_[RIGHT] = 0;
337       s->mult_i_ = 0;
338       s->beam_l_ = 0;
339     }
340   
341   beam_p_->unlink ();
342   beam_p_ = 0;
343   delete grouping_p_;
344   grouping_p_ = 0;
345   mult_i_ = 0;
346 }
347
348 void
349 Auto_beam_engraver::process_acknowledged ()
350 {
351   if (beam_p_)
352     {
353       Moment now = now_mom ();
354       if ((extend_mom_ < now)
355           || ((extend_mom_ == now) && (last_add_mom_ != now )))
356         {
357           DOUT << String ("junking autobeam: no stem added since: ")
358             + last_add_mom_.str () + "\n";
359           end_beam ();
360         }
361       else if (!beam_p_->stems_.size ())
362         {
363           DOUT << "junking started autobeam: no stems\n";
364           junk_beam ();
365         }
366     }
367 }