]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-engraver.cc
* scripts/convert-ly.py: add tuplet-X-visibility rules.
[lilypond.git] / lily / beam-engraver.cc
1 /*   
2   beam-engraver.cc --  implement Beam_engraver
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 1998--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9 #include "engraver-group-engraver.hh"
10 #include "engraver.hh"
11 #include "musical-request.hh"
12 #include "beam.hh"
13 #include "stem.hh"
14 #include "warn.hh"
15 #include "beaming.hh"
16 #include "score-engraver.hh"
17 #include "rest.hh"
18 #include "drul-array.hh"
19 #include "item.hh"
20 #include "spanner.hh"
21
22 class Beam_engraver : public Engraver
23 {
24 protected:  
25   Drul_array<Span_req*> reqs_drul_;
26   
27   Spanner *finished_beam_p_;
28   Spanner *beam_p_;
29   Span_req * prev_start_req_;
30
31   Beaming_info_list * beam_info_p_;
32   Beaming_info_list * finished_beam_info_p_;  
33
34   /// location  within measure where beam started.
35   Moment beam_start_location_;
36
37   /// moment (global time) where beam started.
38   Moment beam_start_mom_;
39
40   bool subdivide_beams_;
41
42   void typeset_beam ();
43   void set_melisma (bool);
44
45   Moment last_stem_added_at_;
46   
47
48
49   virtual void stop_translation_timestep ();
50   virtual void start_translation_timestep ();
51   virtual void finalize ();
52
53   virtual void acknowledge_grob (Grob_info);
54   virtual bool try_music (Music*);
55   virtual void process_music ();
56
57   virtual bool valid_start_moment();
58   virtual bool valid_end_moment ();
59   
60 public:
61   TRANSLATOR_DECLARATIONS(  Beam_engraver );
62 };
63
64
65 /*
66   Hmm. this isn't necessary, since grace beams and normal beams are
67   always nested.
68  */
69 bool
70 Beam_engraver::valid_start_moment()
71 {
72   Moment n = now_mom ();
73
74   return n.grace_part_ == Rational (0);
75 }
76
77 bool
78 Beam_engraver::valid_end_moment()
79 {
80   return last_stem_added_at_.grace_part_ == Rational(0);
81 }
82
83
84 Beam_engraver::Beam_engraver ()
85 {
86   beam_p_ = 0;
87   finished_beam_p_ =0;
88   finished_beam_info_p_=0;
89   beam_info_p_ =0;
90   reqs_drul_[LEFT] = reqs_drul_[RIGHT] =0;
91   prev_start_req_ =0;
92   
93 }
94
95 bool
96 Beam_engraver::try_music (Music *m)
97 {
98   if (Span_req * c = dynamic_cast<Span_req*> (m))
99     {
100       if (scm_equal_p (c->get_mus_property ("span-type"),
101                        ly_str02scm ("abort")) == SCM_BOOL_T)
102         {
103           reqs_drul_[START] = 0;
104           reqs_drul_[STOP] = 0;
105           if (beam_p_)
106             beam_p_->suicide ();
107           beam_p_ = 0;
108         }
109       else if (scm_equal_p (c->get_mus_property ("span-type"),
110                             ly_str02scm ("beam")) == SCM_BOOL_T)
111         {
112           Direction d =c->get_span_dir ();
113
114
115           if (d == STOP && !valid_end_moment())
116             return false;
117
118           if (d == START && !valid_start_moment ())
119             return false;
120           
121           if (d == STOP)
122             {
123               SCM m = get_property ("automaticMelismata");
124               SCM b = get_property ("autoBeaming");
125               if (to_boolean (m) && !to_boolean (b))
126                 {
127                   set_melisma (false);
128                 }
129             }
130
131           reqs_drul_[d ] = c;
132           return true;
133         }
134     }
135   return false;
136 }
137
138 void
139 Beam_engraver::set_melisma (bool m)
140 {
141   daddy_trans_l_->set_property ("beamMelismaBusy", m ? SCM_BOOL_T :SCM_BOOL_F);
142 }
143
144 void
145 Beam_engraver::process_music ()
146 {
147   if (reqs_drul_[STOP])
148     {
149       prev_start_req_ =0;
150       finished_beam_p_ = beam_p_;
151       finished_beam_info_p_ = beam_info_p_;
152
153       beam_info_p_ =0;
154       beam_p_ = 0;
155     }
156
157
158   if (beam_p_)
159     {
160       top_engraver ()->forbid_breaks ();
161     }
162   if (reqs_drul_[START])
163     {
164       if (beam_p_)
165         {
166           reqs_drul_[START]->origin ()->warning (_ ("already have a beam"));
167           return;
168         }
169
170       prev_start_req_ = reqs_drul_[START];
171       beam_p_ = new Spanner (get_property ("Beam"));
172       SCM smp = get_property ("measurePosition");
173       Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
174
175       beam_start_location_ = mp;
176       beam_start_mom_ = now_mom ();
177       
178       beam_info_p_ = new Beaming_info_list;
179       
180       /* urg, must copy to Auto_beam_engraver too */
181  
182       announce_grob(beam_p_, reqs_drul_[START]->self_scm());
183     }
184
185 }
186
187
188 void
189 Beam_engraver::typeset_beam ()
190 {
191   if (finished_beam_p_)
192     {
193       finished_beam_info_p_->beamify(*unsmob_moment (get_property ("beatLength")),
194                                      subdivide_beams_);
195
196       Beam::set_beaming (finished_beam_p_, finished_beam_info_p_);
197       typeset_grob (finished_beam_p_);
198       delete finished_beam_info_p_;
199       finished_beam_info_p_ =0;
200       finished_beam_p_ = 0;
201     }
202 }
203
204 void
205 Beam_engraver::start_translation_timestep ()
206 {
207   reqs_drul_ [START] =0;
208   reqs_drul_[STOP] = 0;
209   
210   if (beam_p_)
211     {
212       SCM m = get_property ("automaticMelismata");
213       SCM b = get_property ("autoBeaming");
214       if (to_boolean (m) && !to_boolean (b))
215         {
216           set_melisma (true);
217         }
218       subdivide_beams_ = to_boolean(get_property("subdivideBeams")); 
219     }
220 }
221
222 void
223 Beam_engraver::stop_translation_timestep ()
224 {
225   typeset_beam ();
226 }
227
228 void
229 Beam_engraver::finalize ()
230 {
231   typeset_beam ();
232   if (beam_p_)
233     {
234       prev_start_req_->origin ()->warning (_ ("unterminated beam"));
235
236       /*
237         we don't typeset it, (we used to, but it was commented
238         out. Reason unknown) */
239       beam_p_->suicide ();
240       delete beam_info_p_;
241     }
242 }
243
244 void
245 Beam_engraver::acknowledge_grob (Grob_info info)
246 {
247   if (beam_p_)
248     {
249       if (Rest::has_interface (info.grob_l_))
250         {
251           info.grob_l_->add_offset_callback (Beam::rest_collision_callback_proc, Y_AXIS);
252         }
253       else if (Stem::has_interface (info.grob_l_))
254         {
255           Moment now = now_mom();
256
257           if (!valid_start_moment ())
258             return ;
259           
260           Item *stem_l = dynamic_cast<Item*> (info.grob_l_);
261           if (Stem::beam_l (stem_l))
262             return;
263
264           Rhythmic_req *rhythmic_req = dynamic_cast <Rhythmic_req *> (info.music_cause ());
265           if (!rhythmic_req)
266             {
267               String s = _ ("stem must have Rhythmic structure");
268               if (info.music_cause ())
269                 info.music_cause ()->origin ()->warning (s);
270               else
271                 ::warning (s);
272           
273               return;
274             }
275
276
277           last_stem_added_at_ = now;
278           int durlog  = unsmob_duration (rhythmic_req->get_mus_property ("duration"))-> duration_log ();
279           if (durlog <= 2)
280             {
281               rhythmic_req->origin ()->warning (_ ("stem doesn't fit in beam"));
282               prev_start_req_->origin ()->warning (_ ("beam was started here"));
283               /*
284                 don't return, since
285
286                 [r4 c8] can just as well be modern notation.
287               */
288             }
289
290           stem_l->set_grob_property ("duration-log",
291                                     gh_int2scm (durlog));
292           Moment stem_location = now - beam_start_mom_ + beam_start_location_;
293           beam_info_p_->add_stem (stem_location,
294  (durlog- 2) >? 0);
295           Beam::add_stem (beam_p_, stem_l);
296         }
297     }
298 }
299
300
301
302
303
304 ENTER_DESCRIPTION(Beam_engraver,
305 /* descr */       "Handles Beam_requests by engraving Beams.    If omitted, then notes will be
306 printed with flags instead of beams.",
307 /* creats*/       "Beam",
308 /* acks  */       "stem-interface rest-interface",
309 /* reads */       "beamMelismaBusy subdivideBeams",
310 /* write */       "");
311
312
313 class Grace_beam_engraver : public Beam_engraver
314 {
315 public:
316   TRANSLATOR_DECLARATIONS(Grace_beam_engraver);  
317
318 protected:
319   virtual bool valid_start_moment();
320   virtual bool valid_end_moment ();
321 };
322
323 Grace_beam_engraver::Grace_beam_engraver()
324 {
325 }
326
327 bool
328 Grace_beam_engraver::valid_start_moment()
329 {
330   Moment n = now_mom ();
331
332   return n.grace_part_ != Rational (0);
333 }
334
335
336 bool
337 Grace_beam_engraver::valid_end_moment ()
338 {
339   return beam_p_ && last_stem_added_at_.grace_part_ != Rational(0);
340 }
341
342
343
344 ENTER_DESCRIPTION(Grace_beam_engraver,
345 /* descr */       "Handles Beam_requests by engraving Beams.  If omitted, then notes will
346 be printed with flags instead of beams. Only engraves beams when we
347 are at grace points in time.
348 ",
349 /* creats*/       "Beam",
350 /* acks  */       "stem-interface rest-interface",
351 /* reads */       "beamMelismaBusy subdivideBeams",
352 /* write */       "");
353