]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
unsmob_pitch -> Pitch::unsmob and related
[lilypond.git] / lily / completion-note-heads-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--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 <cctype>
21 using namespace std;
22
23 #include "dot-column.hh"
24 #include "dots.hh"
25 #include "duration.hh"
26 #include "global-context.hh"
27 #include "item.hh"
28 #include "output-def.hh"
29 #include "pitch.hh"
30 #include "rhythmic-head.hh"
31 #include "score-engraver.hh"
32 #include "spanner.hh"
33 #include "staff-symbol-referencer.hh"
34 #include "stream-event.hh"
35 #include "tie.hh"
36 #include "tie-column.hh"
37 #include "warn.hh"
38 #include "misc.hh"
39
40 #include "translator.icc"
41
42 /*
43   How does this work?
44
45   When we catch the note, we predict the end of the note. We keep the
46   events living until we reach the predicted end-time.
47
48   Every time process_music () is called and there are note events, we
49   figure out how long the note to typeset should be. It should be no
50   longer than what's specified, than what is left to do and it should
51   not cross barlines or sub-bar units.
52
53   We copy the events into scratch note events, to make sure that we get
54   all durations exactly right.
55 */
56
57 class Completion_heads_engraver : public Engraver
58 {
59   vector<Item *> notes_;
60   vector<Item *> prev_notes_;
61   // Must remember notes for explicit ties.
62   vector<Grob *> ties_;
63   vector<Stream_event *> note_events_;
64   Spanner *tie_column_;
65   Moment note_end_mom_;
66   bool is_first_;
67   Rational left_to_do_;
68   Rational do_nothing_until_;
69   Rational factor_;
70
71   Moment next_moment (Rational const &);
72   Item *make_note_head (Stream_event *);
73
74 public:
75   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
76
77 protected:
78   virtual void initialize ();
79   void make_tie (Grob *, Grob *);
80   void start_translation_timestep ();
81   void process_music ();
82   void stop_translation_timestep ();
83   DECLARE_TRANSLATOR_LISTENER (note);
84 };
85
86 void
87 Completion_heads_engraver::initialize ()
88 {
89   is_first_ = false;
90 }
91
92 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
93 void
94 Completion_heads_engraver::listen_note (Stream_event *ev)
95 {
96   note_events_.push_back (ev);
97
98   is_first_ = true;
99   Moment now = now_mom ();
100   Moment musiclen = get_event_length (ev, now);
101
102   note_end_mom_ = max (note_end_mom_, (now + musiclen));
103   do_nothing_until_ = Rational (0, 0);
104 }
105
106 /*
107   The duration _until_ the next bar line or completion unit
108 */
109 Moment
110 Completion_heads_engraver::next_moment (Rational const &note_len)
111 {
112   Moment *e = Moment::unsmob (get_property ("measurePosition"));
113   Moment *l = Moment::unsmob (get_property ("measureLength"));
114   if (!e || !l || !to_boolean (get_property ("timing")))
115     {
116       return Moment (0, 0);
117     }
118
119   Moment result = *l - *e;
120   if (result < 0)
121     {
122       programming_error ("invalid measure position: "
123                          + e->to_string () + " of " + l->to_string ());
124       return 0;
125     }
126   Moment const *unit = Moment::unsmob (get_property ("completionUnit"));
127
128   if (unit)
129     {
130       Rational const now_unit = e->main_part_ / unit->main_part_;
131       if (now_unit.den () > 1)
132         {
133           /*
134             within a unit - go to the end of that
135           */
136           result = unit->main_part_
137                    * (Rational (1) - (now_unit - now_unit.trunc_rat ()));
138         }
139       else
140         {
141           /*
142             at the beginning of a unit:
143             take a power-of-two number of units, but not more than required,
144             since then the Duration constructor destroys the unit structure
145           */
146           if (note_len < result.main_part_)
147             result.main_part_ = note_len;
148           Rational const step_unit = result.main_part_ / unit->main_part_;
149           if (step_unit.den () < step_unit.num ())
150             {
151               int const log2
152                 = intlog2 (int (step_unit.num () / step_unit.den ()));
153               result.main_part_ = unit->main_part_ * Rational (1 << log2);
154             }
155         }
156     }
157
158   return result;
159 }
160
161 Item *
162 Completion_heads_engraver::make_note_head (Stream_event *ev)
163 {
164   Item *note = make_item ("NoteHead", ev->self_scm ());
165   Pitch *pit = Pitch::unsmob (ev->get_property ("pitch"));
166
167   int pos = pit->steps ();
168   SCM c0 = get_property ("middleCPosition");
169   if (scm_is_number (c0))
170     pos += scm_to_int (c0);
171
172   note->set_property ("staff-position", scm_from_int (pos));
173
174   return note;
175 }
176
177 void
178 Completion_heads_engraver::process_music ()
179 {
180   if (!is_first_ && !left_to_do_)
181     return;
182
183   is_first_ = false;
184
185   Moment now = now_mom ();
186   if (do_nothing_until_ > now.main_part_)
187     return;
188
189   Duration note_dur;
190   Duration *orig = 0;
191   if (left_to_do_)
192     {
193       /*
194         note that note_dur may be strictly less than left_to_do_
195         (say, if left_to_do_ == 5/8)
196       */
197       note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_);
198     }
199   else
200     {
201       orig = Duration::unsmob (note_events_[0]->get_property ("duration"));
202       note_dur = *orig;
203       SCM factor = get_property ("completionFactor");
204       if (ly_is_procedure (factor))
205         factor = scm_call_2 (factor,
206                              context ()->self_scm (),
207                              note_dur.smobbed_copy ());
208       factor_ = robust_scm2rational (factor, note_dur.factor ());
209       left_to_do_ = orig->get_length ();
210     }
211   Moment nb = next_moment (note_dur.get_length ());
212   if (nb.main_part_ && nb < note_dur.get_length ())
213     {
214       note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
215     }
216
217   do_nothing_until_ = now.main_part_ + note_dur.get_length ();
218
219   for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
220     {
221       bool need_clone = !orig || *orig != note_dur;
222       Stream_event *event = note_events_[i];
223
224       if (need_clone)
225         event = event->clone ();
226
227       SCM pits = note_events_[i]->get_property ("pitch");
228       event->set_property ("pitch", pits);
229       event->set_property ("duration", note_dur.smobbed_copy ());
230       event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
231       event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
232
233       /*
234         The Completion_heads_engraver splits an event into a group of consecutive events.
235         For each event in the group, the property "autosplit-end" denotes whether the current event
236         was truncated during splitting. Based on "autosplit-end", the Tie_engraver decides whether a
237         tie event should be processed.
238       */
239       event->set_property ("autosplit-end",
240                            ly_bool2scm (left_to_do_ - note_dur.get_length () > Rational (0)));
241
242       Item *note = make_note_head (event);
243       if (need_clone)
244         event->unprotect ();
245       notes_.push_back (note);
246     }
247
248   if (prev_notes_.size () == notes_.size ())
249     {
250       for (vsize i = 0; i < notes_.size (); i++)
251         make_tie (prev_notes_[i], notes_[i]);
252     }
253
254   if (ties_.size () && !tie_column_)
255     tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
256
257   if (tie_column_)
258     for (vsize i = ties_.size (); i--;)
259       Tie_column::add_tie (tie_column_, ties_[i]);
260
261   left_to_do_ -= note_dur.get_length ();
262   if (left_to_do_)
263     get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length ());
264   /*
265     don't do complicated arithmetic with grace notes.
266   */
267   if (orig && now_mom ().grace_part_)
268     left_to_do_ = Rational (0, 0);
269 }
270
271 void
272 Completion_heads_engraver::make_tie (Grob *left, Grob *right)
273 {
274   Grob *p = make_spanner ("Tie", SCM_EOL);
275   Tie::set_head (p, LEFT, left);
276   Tie::set_head (p, RIGHT, right);
277   announce_end_grob (p, SCM_EOL);
278   ties_.push_back (p);
279 }
280
281 void
282 Completion_heads_engraver::stop_translation_timestep ()
283 {
284   ties_.clear ();
285   tie_column_ = 0;
286
287   if (notes_.size ())
288     prev_notes_ = notes_;
289   notes_.clear ();
290 }
291
292 void
293 Completion_heads_engraver::start_translation_timestep ()
294 {
295   Moment now = now_mom ();
296   if (note_end_mom_.main_part_ <= now.main_part_)
297     {
298       note_events_.clear ();
299       prev_notes_.clear ();
300     }
301   context ()->set_property ("completionBusy",
302                             ly_bool2scm (note_events_.size ()));
303 }
304
305 Completion_heads_engraver::Completion_heads_engraver ()
306 {
307   tie_column_ = 0;
308 }
309
310 ADD_TRANSLATOR (Completion_heads_engraver,
311                 /* doc */
312                 "This engraver replaces @code{Note_heads_engraver}.  It plays"
313                 " some trickery to break long notes and automatically tie them"
314                 " into the next measure.",
315
316                 /* create */
317                 "NoteHead "
318                 "Tie "
319                 "TieColumn ",
320
321                 /* read */
322                 "completionFactor "
323                 "completionUnit "
324                 "measureLength "
325                 "measurePosition "
326                 "middleCPosition "
327                 "timing ",
328
329                 /* write */
330                 "completionBusy "
331                );