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