]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-engraver.cc
Merge branch 'master' into lilypond/translation
[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--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "beam.hh"
10 #include "beaming-pattern.hh"
11 #include "context.hh"
12 #include "directional-element-interface.hh"
13 #include "drul-array.hh"
14 #include "duration.hh"
15 #include "engraver.hh"
16 #include "international.hh"
17 #include "item.hh"
18 #include "rest.hh"
19 #include "spanner.hh"
20 #include "stream-event.hh"
21 #include "stem.hh"
22 #include "warn.hh"
23
24 #include "translator.icc"
25
26 class Beam_engraver : public Engraver
27 {
28 public:
29   DECLARE_ACKNOWLEDGER (stem);
30   DECLARE_ACKNOWLEDGER (rest);
31
32 protected:
33   Stream_event *start_ev_;
34
35   Spanner *finished_beam_;
36   Spanner *beam_;
37   Stream_event *prev_start_ev_;
38
39   Stream_event *stop_ev_;
40
41   Direction forced_direction_;
42
43   Beaming_pattern *beam_info_;
44   Beaming_pattern *finished_beam_info_;
45
46   /// location  within measure where beam started.
47   Moment beam_start_location_;
48
49   /// moment (global time) where beam started.
50   Moment beam_start_mom_;
51
52   Beaming_options beaming_options_;
53   Beaming_options finished_beaming_options_;
54   
55   void typeset_beam ();
56   void set_melisma (bool);
57
58   Moment last_stem_added_at_;
59   void stop_translation_timestep ();
60   void start_translation_timestep ();
61   virtual void finalize ();
62
63   void process_music ();
64
65   virtual bool valid_start_point ();
66   virtual bool valid_end_point ();
67
68   DECLARE_TRANSLATOR_LISTENER (beam);
69 public:
70   TRANSLATOR_DECLARATIONS (Beam_engraver);
71 };
72
73 /*
74   Hmm. this isn't necessary, since grace beams and normal beams are
75   always nested.
76 */
77 bool
78 Beam_engraver::valid_start_point ()
79 {
80   Moment n = now_mom ();
81
82   return n.grace_part_ == Rational (0);
83 }
84
85 bool
86 Beam_engraver::valid_end_point ()
87 {
88   return valid_start_point ();
89 }
90
91 Beam_engraver::Beam_engraver ()
92 {
93   beam_ = 0;
94   finished_beam_ = 0;
95   finished_beam_info_ = 0;
96   beam_info_ = 0;
97   forced_direction_ = CENTER;
98   stop_ev_ = 0;
99   start_ev_ = 0;
100   prev_start_ev_ = 0;
101 }
102
103 IMPLEMENT_TRANSLATOR_LISTENER (Beam_engraver, beam);
104 void
105 Beam_engraver::listen_beam (Stream_event *ev)
106 {
107   Direction d = to_dir (ev->get_property ("span-direction"));
108
109   if (d == START && valid_start_point ())
110     {
111       ASSIGN_EVENT_ONCE (start_ev_, ev);
112
113       Direction updown = to_dir (ev->get_property ("direction"));
114       if (updown)
115         forced_direction_ = updown;
116     }
117   else if (d == STOP && valid_end_point ())
118     ASSIGN_EVENT_ONCE (stop_ev_, ev);
119 }
120
121 void
122 Beam_engraver::set_melisma (bool ml)
123 {
124   SCM b = get_property ("autoBeaming");
125   if (!to_boolean (b))
126     context ()->set_property ("beamMelismaBusy", ml ? SCM_BOOL_T : SCM_BOOL_F);
127 }
128
129 void
130 Beam_engraver::process_music ()
131 {
132   if (start_ev_)
133     {
134       if (beam_)
135         {
136           start_ev_->origin ()->warning (_ ("already have a beam"));
137           return;
138         }
139
140       set_melisma (true);
141       prev_start_ev_ = start_ev_;
142       beam_ = make_spanner ("Beam", start_ev_->self_scm ());
143
144       Moment mp (robust_scm2moment (get_property ("measurePosition"),
145                                     Moment (0)));
146
147       beam_start_location_ = mp;
148       beam_start_mom_ = now_mom ();
149
150       beaming_options_.from_context (context ());
151       beam_info_ = new Beaming_pattern;
152       /* urg, must copy to Auto_beam_engraver too */
153     }
154
155   typeset_beam ();
156   if (stop_ev_ && beam_)
157     {
158       announce_end_grob (beam_, stop_ev_->self_scm ());
159       
160     }
161 }
162
163 void
164 Beam_engraver::typeset_beam ()
165 {
166   if (finished_beam_)
167     {
168       if (!finished_beam_->get_bound (RIGHT))
169         finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
170       if (forced_direction_)
171         {
172           Grob *stem = finished_beam_->get_bound (RIGHT);
173           set_grob_direction (stem, forced_direction_);
174           forced_direction_ = CENTER;
175         }
176       finished_beam_info_->beamify (finished_beaming_options_);
177
178       Beam::set_beaming (finished_beam_, finished_beam_info_);
179
180       delete finished_beam_info_;
181       finished_beam_info_ = 0;
182       finished_beam_ = 0;
183
184     }
185 }
186
187 void
188 Beam_engraver::start_translation_timestep ()
189 {
190   start_ev_ = 0;
191
192   if (beam_)
193     set_melisma (true);
194 }
195
196 void
197 Beam_engraver::stop_translation_timestep ()
198 {
199   if (stop_ev_)
200     {
201       finished_beam_ = beam_;
202       finished_beam_info_ = beam_info_;
203       finished_beaming_options_ = beaming_options_;
204       
205       stop_ev_ = 0;
206       beam_ = 0;
207       beam_info_ = 0;
208       typeset_beam ();
209       set_melisma (false);
210     }
211 }
212
213 void
214 Beam_engraver::finalize ()
215 {
216   typeset_beam ();
217   if (beam_)
218     {
219       prev_start_ev_->origin ()->warning (_ ("unterminated beam"));
220
221       /*
222         we don't typeset it, (we used to, but it was commented
223         out. Reason unknown) */
224       beam_->suicide ();
225       delete beam_info_;
226     }
227 }
228
229 void
230 Beam_engraver::acknowledge_rest (Grob_info info)
231 {
232   if (beam_
233       && !scm_is_number (info.grob ()->get_property_data ("staff-position")))
234     chain_offset_callback (info.grob (),
235                            Beam::rest_collision_callback_proc, Y_AXIS);
236 }
237
238 void
239 Beam_engraver::acknowledge_stem (Grob_info info)
240 {
241   if (!beam_)
242     return;
243   
244   Moment now = now_mom ();
245   if (!valid_start_point ())
246     return;
247
248   Item *stem = dynamic_cast<Item *> (info.grob ());
249   if (Stem::get_beam (stem))
250     return;
251   
252   Stream_event *ev = info.ultimate_event_cause ();
253   if (!ev->in_event_class ("rhythmic-event"))
254     {
255       info.grob ()->warning (_ ("stem must have Rhythmic structure"));
256       return;
257     }
258
259   last_stem_added_at_ = now;
260   int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
261   if (durlog <= 2)
262     {
263       ev->origin ()->warning (_ ("stem does not fit in beam"));
264       prev_start_ev_->origin ()->warning (_ ("beam was started here"));
265       /*
266         don't return, since
267
268         [r4 c8] can just as well be modern notation.
269       */
270     }
271
272   if (forced_direction_)
273     set_grob_direction (stem, forced_direction_);
274
275   stem->set_property ("duration-log", scm_from_int (durlog));
276   Moment stem_location = now - beam_start_mom_ + beam_start_location_;
277   beam_info_->add_stem (stem_location,
278                         max (durlog- 2, 0),
279                         Stem::is_invisible (stem));
280   Beam::add_stem (beam_, stem);
281 }
282
283 ADD_ACKNOWLEDGER (Beam_engraver, stem);
284 ADD_ACKNOWLEDGER (Beam_engraver, rest);
285
286 ADD_TRANSLATOR (Beam_engraver,
287                 /* doc */
288                 "Handle @code{Beam} events by engraving beams.  If omitted,"
289                 " then notes are printed with flags instead of beams.",
290                 
291                 /* create */
292                 "Beam ",
293
294                 /* read */
295                 "beamMelismaBusy "
296                 "beatLength "
297                 "subdivideBeams ",
298
299                 /* write */
300                 "forbidBreak"
301                 );
302
303 class Grace_beam_engraver : public Beam_engraver
304 {
305 public:
306   TRANSLATOR_DECLARATIONS (Grace_beam_engraver);
307
308   DECLARE_TRANSLATOR_LISTENER (beam);
309   
310 protected:
311   virtual bool valid_start_point ();
312   virtual bool valid_end_point ();
313 };
314
315 Grace_beam_engraver::Grace_beam_engraver ()
316 {
317 }
318
319 bool
320 Grace_beam_engraver::valid_start_point ()
321 {
322   Moment n = now_mom ();
323
324   return n.grace_part_ != Rational (0);
325 }
326
327 bool
328 Grace_beam_engraver::valid_end_point ()
329 {
330   return beam_ && valid_start_point ();
331 }
332
333 /*
334   Ugh, C&P code.
335  */
336 IMPLEMENT_TRANSLATOR_LISTENER (Grace_beam_engraver, beam);
337 void
338 Grace_beam_engraver::listen_beam (Stream_event *ev)
339 {
340   Direction d = to_dir (ev->get_property ("span-direction"));
341
342   if (d == START && valid_start_point ())
343     start_ev_ = ev;
344   else if (d == STOP && valid_end_point ())
345     stop_ev_ = ev;
346 }
347
348
349 ADD_ACKNOWLEDGER (Grace_beam_engraver, stem);
350 ADD_ACKNOWLEDGER (Grace_beam_engraver, rest);
351
352 ADD_TRANSLATOR (Grace_beam_engraver,
353                 /* doc */
354                 "Handle @code{Beam} events by engraving beams.  If omitted,"
355                 " then notes are printed with flags instead of beams.  Only"
356                 " engraves beams when we are at grace points in time.",
357                 
358                 /* create */
359                 "Beam ",
360
361                 /* read */
362                 "beamMelismaBusy "
363                 "beatLength "
364                 "subdivideBeams ",
365
366                 /* write */
367                 ""
368                 );
369