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