]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-engraver.cc
Run grand-replace (issue 3765)
[lilypond.git] / lily / beam-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2014 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       Grob *stem = finished_beam_->get_bound (RIGHT);
181       if (!stem)
182         {
183           stem = finished_beam_->get_bound (LEFT);
184           if (stem)
185             finished_beam_->set_bound (RIGHT, stem);
186         }
187
188       if (stem && forced_direction_)
189         set_grob_direction (stem, forced_direction_);
190
191       forced_direction_ = CENTER;
192       finished_beam_info_->beamify (finished_beaming_options_);
193
194       Beam::set_beaming (finished_beam_, finished_beam_info_);
195
196       delete finished_beam_info_;
197       finished_beam_info_ = 0;
198       finished_beam_ = 0;
199
200     }
201 }
202
203 void
204 Beam_engraver::start_translation_timestep ()
205 {
206   start_ev_ = 0;
207
208   if (beam_)
209     set_melisma (true);
210 }
211
212 void
213 Beam_engraver::stop_translation_timestep ()
214 {
215   if (stop_ev_)
216     {
217       finished_beam_ = beam_;
218       finished_beam_info_ = beam_info_;
219       finished_beaming_options_ = beaming_options_;
220
221       stop_ev_ = 0;
222       beam_ = 0;
223       beam_info_ = 0;
224       typeset_beam ();
225       set_melisma (false);
226     }
227 }
228
229 void
230 Beam_engraver::finalize ()
231 {
232   typeset_beam ();
233   if (beam_)
234     {
235       prev_start_ev_->origin ()->warning (_ ("unterminated beam"));
236
237       /*
238         we don't typeset it, (we used to, but it was commented
239         out. Reason unknown) */
240       beam_->suicide ();
241       delete beam_info_;
242     }
243 }
244
245 void
246 Beam_engraver::acknowledge_rest (Grob_info info)
247 {
248   if (beam_
249       && !scm_is_number (info.grob ()->get_property_data ("staff-position")))
250     chain_offset_callback (info.grob (),
251                            ly_make_unpure_pure_container
252                              (Beam::rest_collision_callback_proc,
253                               Beam::pure_rest_collision_callback_proc),
254                            Y_AXIS);
255 }
256
257 void
258 Beam_engraver::acknowledge_stem (Grob_info info)
259 {
260   if (!beam_)
261     return;
262
263   Moment now = now_mom ();
264   if (!valid_start_point ())
265     return;
266
267   // It's suboptimal that we don't support callbacks returning ##f,
268   // but this makes beams have no effect on "stems" reliably in
269   // TabStaff when \tabFullNotation is switched off: the real stencil
270   // callback for beams is called quite late in the process, and we
271   // don't want to trigger it early.
272   if (scm_is_false (beam_->get_property_data ("stencil")))
273     return;
274
275   Item *stem = dynamic_cast<Item *> (info.grob ());
276   if (Stem::get_beam (stem))
277     return;
278
279   Stream_event *ev = info.ultimate_event_cause ();
280   if (!ev->in_event_class ("rhythmic-event"))
281     {
282       info.grob ()->warning (_ ("stem must have Rhythmic structure"));
283       return;
284     }
285
286   last_stem_added_at_ = now;
287
288   Duration *stem_duration = unsmob_duration (ev->get_property ("duration"));
289   int durlog = stem_duration->duration_log ();
290   //int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
291   if (durlog <= 2)
292     {
293       ev->origin ()->warning (_ ("stem does not fit in beam"));
294       prev_start_ev_->origin ()->warning (_ ("beam was started here"));
295       /*
296         don't return, since
297
298         [r4 c8] can just as well be modern notation.
299       */
300     }
301
302   if (forced_direction_)
303     set_grob_direction (stem, forced_direction_);
304
305   stem->set_property ("duration-log", scm_from_int (durlog));
306   Moment stem_location = now - beam_start_mom_ + beam_start_location_;
307   beam_info_->add_stem (stem_location,
308                         max (durlog - 2, 0),
309                         Stem::is_invisible (stem),
310                         stem_duration->factor (),
311                         (stem->get_property ("tuplet-start") == SCM_BOOL_T));
312   Beam::add_stem (beam_, stem);
313 }
314
315 ADD_ACKNOWLEDGER (Beam_engraver, stem);
316 ADD_ACKNOWLEDGER (Beam_engraver, rest);
317
318 ADD_TRANSLATOR (Beam_engraver,
319                 /* doc */
320                 "Handle @code{Beam} events by engraving beams.  If omitted,"
321                 " then notes are printed with flags instead of beams.",
322
323                 /* create */
324                 "Beam ",
325
326                 /* read */
327                 "baseMoment "
328                 "beamMelismaBusy "
329                 "beatStructure "
330                 "subdivideBeams ",
331
332                 /* write */
333                 "forbidBreak"
334                );
335
336 class Grace_beam_engraver : public Beam_engraver
337 {
338 public:
339   TRANSLATOR_DECLARATIONS (Grace_beam_engraver);
340
341   DECLARE_TRANSLATOR_LISTENER (beam);
342
343 protected:
344   virtual bool valid_start_point ();
345   virtual bool valid_end_point ();
346 };
347
348 Grace_beam_engraver::Grace_beam_engraver ()
349 {
350 }
351
352 bool
353 Grace_beam_engraver::valid_start_point ()
354 {
355   Moment n = now_mom ();
356
357   return n.grace_part_ != Rational (0);
358 }
359
360 bool
361 Grace_beam_engraver::valid_end_point ()
362 {
363   return beam_ && valid_start_point ();
364 }
365
366 /*
367   Ugh, C&P code.
368  */
369 IMPLEMENT_TRANSLATOR_LISTENER (Grace_beam_engraver, beam);
370 void
371 Grace_beam_engraver::listen_beam (Stream_event *ev)
372 {
373   Direction d = to_dir (ev->get_property ("span-direction"));
374
375   if (d == START && valid_start_point ())
376     start_ev_ = ev;
377   else if (d == STOP && valid_end_point ())
378     stop_ev_ = ev;
379 }
380
381 ADD_ACKNOWLEDGER (Grace_beam_engraver, stem);
382 ADD_ACKNOWLEDGER (Grace_beam_engraver, rest);
383
384 ADD_TRANSLATOR (Grace_beam_engraver,
385                 /* doc */
386                 "Handle @code{Beam} events by engraving beams.  If omitted,"
387                 " then notes are printed with flags instead of beams.  Only"
388                 " engraves beams when we are at grace points in time.",
389
390                 /* create */
391                 "Beam ",
392
393                 /* read */
394                 "baseMoment "
395                 "beamMelismaBusy "
396                 "beatStructure "
397                 "subdivideBeams ",
398
399                 /* write */
400                 ""
401                );
402