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