]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-engraver.cc
Web-ja: update introduction
[lilypond.git] / lily / beam-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2015 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   void acknowledge_stem (Grob_info);
42   void acknowledge_rest (Grob_info);
43   void listen_beam (Stream_event *);
44
45 protected:
46   Stream_event *start_ev_;
47
48   Spanner *finished_beam_;
49   Spanner *beam_;
50   Stream_event *prev_start_ev_;
51
52   Stream_event *stop_ev_;
53
54   Direction forced_direction_;
55
56   Beaming_pattern *beam_info_;
57   Beaming_pattern *finished_beam_info_;
58
59   /// location  within measure where beam started.
60   Moment beam_start_location_;
61
62   /// moment (global time) where beam started.
63   Moment beam_start_mom_;
64
65   Beaming_options beaming_options_;
66   Beaming_options finished_beaming_options_;
67
68   void typeset_beam ();
69   void set_melisma (bool);
70
71   Moment last_stem_added_at_;
72   void stop_translation_timestep ();
73   void start_translation_timestep ();
74   virtual void finalize ();
75
76   void process_music ();
77
78   virtual bool valid_start_point ();
79   virtual bool valid_end_point ();
80
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 (Context *c)
104   : Engraver (c)
105 {
106   beam_ = 0;
107   finished_beam_ = 0;
108   finished_beam_info_ = 0;
109   beam_info_ = 0;
110   forced_direction_ = CENTER;
111   stop_ev_ = 0;
112   start_ev_ = 0;
113   prev_start_ev_ = 0;
114 }
115
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                            Unpure_pure_container::make_smob
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                         (to_boolean (stem->get_property ("tuplet-start"))));
312   Beam::add_stem (beam_, stem);
313 }
314
315
316 void
317 Beam_engraver::boot ()
318 {
319   ADD_LISTENER (Beam_engraver, beam);
320   ADD_ACKNOWLEDGER (Beam_engraver, stem);
321   ADD_ACKNOWLEDGER (Beam_engraver, rest);
322 }
323
324 ADD_TRANSLATOR (Beam_engraver,
325                 /* doc */
326                 "Handle @code{Beam} events by engraving beams.  If omitted,"
327                 " then notes are printed with flags instead of beams.",
328
329                 /* create */
330                 "Beam ",
331
332                 /* read */
333                 "baseMoment "
334                 "beamMelismaBusy "
335                 "beatStructure "
336                 "subdivideBeams ",
337
338                 /* write */
339                 "forbidBreak"
340                );
341
342 class Grace_beam_engraver : public Beam_engraver
343 {
344 public:
345   TRANSLATOR_DECLARATIONS (Grace_beam_engraver);
346   TRANSLATOR_INHERIT (Beam_engraver);
347
348 protected:
349   virtual bool valid_start_point ();
350   virtual bool valid_end_point ();
351 };
352
353 Grace_beam_engraver::Grace_beam_engraver (Context *c)
354   : Beam_engraver (c)
355 {
356 }
357
358 bool
359 Grace_beam_engraver::valid_start_point ()
360 {
361   Moment n = now_mom ();
362
363   return n.grace_part_ != Rational (0);
364 }
365
366 bool
367 Grace_beam_engraver::valid_end_point ()
368 {
369   return beam_ && valid_start_point ();
370 }
371
372 void
373 Grace_beam_engraver::boot ()
374 {
375   ADD_LISTENER (Grace_beam_engraver, beam);
376   ADD_ACKNOWLEDGER (Grace_beam_engraver, stem);
377   ADD_ACKNOWLEDGER (Grace_beam_engraver, rest);
378 }
379
380 ADD_TRANSLATOR (Grace_beam_engraver,
381                 /* doc */
382                 "Handle @code{Beam} events by engraving beams.  If omitted,"
383                 " then notes are printed with flags instead of beams.  Only"
384                 " engraves beams when we are at grace points in time.",
385
386                 /* create */
387                 "Beam ",
388
389                 /* read */
390                 "baseMoment "
391                 "beamMelismaBusy "
392                 "beatStructure "
393                 "subdivideBeams ",
394
395                 /* write */
396                 ""
397                );
398