]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-engraver.cc
*** empty log message ***
[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_;
28   Spanner *beam_;
29   Span_req * prev_start_req_;
30
31   Beaming_info_list * beam_info_;
32   Beaming_info_list * finished_beam_info_;  
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   Moment beat_length_;
42
43   void typeset_beam ();
44   void set_melisma (bool);
45
46   Moment last_stem_added_at_;
47   
48
49
50   virtual void stop_translation_timestep ();
51   virtual void start_translation_timestep ();
52   virtual void finalize ();
53
54   virtual void acknowledge_grob (Grob_info);
55   virtual bool try_music (Music*);
56   virtual void process_music ();
57
58   virtual bool valid_start_moment();
59   virtual bool valid_end_moment ();
60   
61 public:
62   TRANSLATOR_DECLARATIONS(  Beam_engraver );
63 };
64
65
66 /*
67   Hmm. this isn't necessary, since grace beams and normal beams are
68   always nested.
69  */
70 bool
71 Beam_engraver::valid_start_moment()
72 {
73   Moment n = now_mom ();
74
75   return n.grace_part_ == Rational (0);
76 }
77
78 bool
79 Beam_engraver::valid_end_moment()
80 {
81   return last_stem_added_at_.grace_part_ == Rational(0);
82 }
83
84
85 Beam_engraver::Beam_engraver ()
86 {
87   beam_ = 0;
88   finished_beam_ =0;
89   finished_beam_info_=0;
90   beam_info_ =0;
91   reqs_drul_[LEFT] = reqs_drul_[RIGHT] =0;
92   prev_start_req_ =0;
93   
94 }
95
96 bool
97 Beam_engraver::try_music (Music *m)
98 {
99   if (Span_req * c = dynamic_cast<Span_req*> (m))
100     {
101       if (scm_equal_p (c->get_mus_property ("span-type"),
102                        scm_makfrom0str ("abort")) == SCM_BOOL_T)
103         {
104           reqs_drul_[START] = 0;
105           reqs_drul_[STOP] = 0;
106           if (beam_)
107             beam_->suicide ();
108           beam_ = 0;
109         }
110       else if (scm_equal_p (c->get_mus_property ("span-type"),
111                             scm_makfrom0str ("beam")) == SCM_BOOL_T)
112         {
113           Direction d =c->get_span_dir ();
114
115
116           if (d == STOP && !valid_end_moment())
117             return false;
118
119           if (d == START && !valid_start_moment ())
120             return false;
121           
122           if (d == STOP)
123             {
124               SCM m = get_property ("automaticMelismata");
125               SCM b = get_property ("autoBeaming");
126               if (to_boolean (m) && !to_boolean (b))
127                 {
128                   set_melisma (false);
129                 }
130             }
131
132           reqs_drul_[d ] = c;
133           return true;
134         }
135     }
136   return false;
137 }
138
139 void
140 Beam_engraver::set_melisma (bool m)
141 {
142   daddy_trans_->set_property ("beamMelismaBusy", m ? SCM_BOOL_T :SCM_BOOL_F);
143 }
144
145 void
146 Beam_engraver::process_music ()
147 {
148   if (reqs_drul_[STOP])
149     {
150       prev_start_req_ =0;
151       finished_beam_ = beam_;
152       finished_beam_info_ = beam_info_;
153
154       beam_info_ =0;
155       beam_ = 0;
156     }
157
158
159   if (beam_)
160     {
161       top_engraver ()->forbid_breaks ();
162     }
163   if (reqs_drul_[START])
164     {
165       if (beam_)
166         {
167           reqs_drul_[START]->origin ()->warning (_ ("already have a beam"));
168           return;
169         }
170
171       prev_start_req_ = reqs_drul_[START];
172       beam_ = new Spanner (get_property ("Beam"));
173       SCM smp = get_property ("measurePosition");
174       Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
175
176       beam_start_location_ = mp;
177       beam_start_mom_ = now_mom ();
178       
179       beam_info_ = new Beaming_info_list;
180       
181       /* urg, must copy to Auto_beam_engraver too */
182  
183       announce_grob(beam_, reqs_drul_[START]->self_scm());
184     }
185
186 }
187
188
189 void
190 Beam_engraver::typeset_beam ()
191 {
192   if (finished_beam_)
193     {
194       finished_beam_info_->beamify(beat_length_, subdivide_beams_);
195       Beam::set_beaming (finished_beam_, finished_beam_info_);
196       typeset_grob (finished_beam_);
197       delete finished_beam_info_;
198       finished_beam_info_ =0;
199       finished_beam_ = 0;
200     }
201 }
202
203 void
204 Beam_engraver::start_translation_timestep ()
205 {
206   reqs_drul_ [START] =0;
207   reqs_drul_[STOP] = 0;
208   
209   if (beam_)
210     {
211       SCM m = get_property ("automaticMelismata");
212       SCM b = get_property ("autoBeaming");
213       if (to_boolean (m) && !to_boolean (b))
214         {
215           set_melisma (true);
216         }
217       subdivide_beams_ = to_boolean(get_property("subdivideBeams"));
218       beat_length_ = *unsmob_moment (get_property ("beatLength"));
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_)
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_->suicide ();
240       delete beam_info_;
241     }
242 }
243
244 void
245 Beam_engraver::acknowledge_grob (Grob_info info)
246 {
247   if (beam_)
248     {
249       if (Rest::has_interface (info.grob_))
250         {
251           info.grob_->add_offset_callback (Beam::rest_collision_callback_proc, Y_AXIS);
252         }
253       else if (Stem::has_interface (info.grob_))
254         {
255           Moment now = now_mom();
256
257           if (!valid_start_moment ())
258             return ;
259           
260           Item *stem = dynamic_cast<Item*> (info.grob_);
261           if (Stem::get_beam (stem))
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->set_grob_property ("duration-log",
291                                     gh_int2scm (durlog));
292           Moment stem_location = now - beam_start_mom_ + beam_start_location_;
293           beam_info_->add_stem (stem_location,
294  (durlog- 2) >? 0);
295           Beam::add_stem (beam_, stem);
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 beatLength 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_ && 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 beatLength subdivideBeams",
352 /* write */       "");
353