]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
Doc-it: chapter 1 completed
[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--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 <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<Item *> tie_note_candidates_;
63   vector<Stream_event *> tie_note_candidate_events_;
64   vector<Grob *> ties_;
65   vector<Stream_event *> note_events_;
66   Spanner *tie_column_;
67   Moment note_end_mom_;
68   bool is_first_;
69   Rational left_to_do_;
70   Rational do_nothing_until_;
71   Rational factor_;
72
73   Moment next_moment (Rational const &);
74   Item *make_note_head (Stream_event *);
75
76 public:
77   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
78
79 protected:
80   virtual void initialize ();
81   void make_tie (Grob *, Grob *);
82   void start_translation_timestep ();
83   void process_music ();
84   void stop_translation_timestep ();
85   DECLARE_TRANSLATOR_LISTENER (note);
86 };
87
88 void
89 Completion_heads_engraver::initialize ()
90 {
91   is_first_ = false;
92 }
93
94 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
95 void
96 Completion_heads_engraver::listen_note (Stream_event *ev)
97 {
98   note_events_.push_back (ev);
99
100   is_first_ = true;
101   Moment now = now_mom ();
102   Moment musiclen = get_event_length (ev, now);
103
104   note_end_mom_ = max (note_end_mom_, (now + musiclen));
105   do_nothing_until_ = Rational (0, 0);
106 }
107
108 /*
109   The duration _until_ the next bar line or completion unit
110 */
111 Moment
112 Completion_heads_engraver::next_moment (Rational const &note_len)
113 {
114   Moment *e = unsmob_moment (get_property ("measurePosition"));
115   Moment *l = unsmob_moment (get_property ("measureLength"));
116   if (!e || !l || !to_boolean (get_property ("timing")))
117     {
118       return Moment (0, 0);
119     }
120
121   Moment result = *l - *e;
122   Moment const *unit = unsmob_moment (get_property ("completionUnit"));
123
124   if (unit)
125     {
126       Rational const now_unit = e->main_part_ / unit->main_part_;
127       if (now_unit.den () > 1)
128         {
129           /*
130             within a unit - go to the end of that
131           */
132           result = unit->main_part_
133                    * (Rational (1) - (now_unit - now_unit.trunc_rat ()));
134         }
135       else
136         {
137           /*
138             at the beginning of a unit:
139             take a power-of-two number of units, but not more than required,
140             since then the Duration constructor destroys the unit structure
141           */
142           if (note_len < result.main_part_)
143             result.main_part_ = note_len;
144           Rational const step_unit = result.main_part_ / unit->main_part_;
145           if (step_unit.den () < step_unit.num ())
146             {
147               int const log2
148                 = intlog2 (int (step_unit.num () / step_unit.den ()));
149               result.main_part_ = unit->main_part_ * Rational (1 << log2);
150             }
151         }
152     }
153
154   return result;
155 }
156
157 Item *
158 Completion_heads_engraver::make_note_head (Stream_event *ev)
159 {
160   Item *note = make_item ("NoteHead", ev->self_scm ());
161   Pitch *pit = unsmob_pitch (ev->get_property ("pitch"));
162
163   int pos = pit->steps ();
164   SCM c0 = get_property ("middleCPosition");
165   if (scm_is_number (c0))
166     pos += scm_to_int (c0);
167
168   note->set_property ("staff-position", scm_from_int (pos));
169
170   return note;
171 }
172
173 void
174 Completion_heads_engraver::process_music ()
175 {
176   if (!is_first_ && !left_to_do_)
177     return;
178
179   is_first_ = false;
180
181   Moment now = now_mom ();
182   if (do_nothing_until_ > now.main_part_)
183     return;
184
185   Duration note_dur;
186   Duration *orig = 0;
187   if (left_to_do_)
188     {
189       /*
190         note that note_dur may be strictly less than left_to_do_
191         (say, if left_to_do_ == 5/8)
192       */
193       if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
194         note_dur = Duration (left_to_do_, false);
195       else
196         note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_);
197     }
198   else
199     {
200       orig = unsmob_duration (note_events_[0]->get_property ("duration"));
201       note_dur = *orig;
202       factor_ = note_dur.factor ();
203       left_to_do_ = orig->get_length ();
204     }
205   Moment nb = next_moment (note_dur.get_length ());
206   if (nb.main_part_ && nb < note_dur.get_length ())
207     {
208       if (factor_.denominator () == 1 && factor_.numerator () > 1)
209         note_dur = Duration (nb.main_part_, false);
210       else
211         note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
212     }
213
214   do_nothing_until_ = now.main_part_ + note_dur.get_length ();
215
216   for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
217     {
218       bool need_clone = !orig || *orig != note_dur;
219       Stream_event *event = note_events_[i];
220
221       if (need_clone)
222         event = event->clone ();
223
224       SCM pits = note_events_[i]->get_property ("pitch");
225       event->set_property ("pitch", pits);
226       event->set_property ("duration", note_dur.smobbed_copy ());
227       event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
228       event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
229
230       /*
231         The Completion_heads_engraver splits an event into a group of consecutive events.
232         For each event in the group, the property "autosplit-end" denotes whether the current event
233         was truncated during splitting. Based on "autosplit-end", the Tie_engraver decides whether a
234         tie event should be processed.
235       */
236       event->set_property ("autosplit-end",
237                            ly_bool2scm (left_to_do_ - note_dur.get_length () > Rational (0)));
238
239       Item *note = make_note_head (event);
240       if (need_clone)
241         event->unprotect ();
242       notes_.push_back (note);
243     }
244
245   if (prev_notes_.size () == notes_.size ())
246     {
247       for (vsize i = 0; i < notes_.size (); i++)
248         make_tie (prev_notes_[i], notes_[i]);
249     }
250
251   if (ties_.size () && !tie_column_)
252     tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
253
254   if (tie_column_)
255     for (vsize i = ties_.size (); i--;)
256       Tie_column::add_tie (tie_column_, ties_[i]);
257
258   left_to_do_ -= note_dur.get_length ();
259   if (left_to_do_)
260     get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length ());
261   /*
262     don't do complicated arithmetic with grace notes.
263   */
264   if (orig && now_mom ().grace_part_)
265     left_to_do_ = Rational (0, 0);
266 }
267
268 void
269 Completion_heads_engraver::make_tie (Grob *left, Grob *right)
270 {
271   Grob *p = make_spanner ("Tie", SCM_EOL);
272   Tie::set_head (p, LEFT, left);
273   Tie::set_head (p, RIGHT, right);
274   ties_.push_back (p);
275 }
276
277 void
278 Completion_heads_engraver::stop_translation_timestep ()
279 {
280   ties_.clear ();
281   tie_column_ = 0;
282
283   if (notes_.size ())
284     prev_notes_ = notes_;
285   notes_.clear ();
286 }
287
288 void
289 Completion_heads_engraver::start_translation_timestep ()
290 {
291   Moment now = now_mom ();
292   if (note_end_mom_.main_part_ <= now.main_part_)
293     {
294       tie_note_candidate_events_ = note_events_;
295       tie_note_candidates_ = prev_notes_;
296
297       note_events_.clear ();
298       prev_notes_.clear ();
299     }
300   context ()->set_property ("completionBusy",
301                             ly_bool2scm (note_events_.size ()));
302 }
303
304 Completion_heads_engraver::Completion_heads_engraver ()
305 {
306   tie_column_ = 0;
307 }
308
309 ADD_TRANSLATOR (Completion_heads_engraver,
310                 /* doc */
311                 "This engraver replaces @code{Note_heads_engraver}.  It plays"
312                 " some trickery to break long notes and automatically tie them"
313                 " into the next measure.",
314
315                 /* create */
316                 "NoteHead "
317                 "Tie "
318                 "TieColumn ",
319
320                 /* read */
321                 "completionUnit "
322                 "measureLength "
323                 "measurePosition "
324                 "middleCPosition "
325                 "timing ",
326
327                 /* write */
328                 "completionBusy "
329                );