]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
Remove duplicate ties for chords autosplit (1630)
[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--2011 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 "warn.hh"
37
38 #include "translator.icc"
39
40 /*
41   TODO: make matching rest engraver.
42 */
43
44 /*
45   How does this work?
46
47   When we catch the note, we predict the end of the note. We keep the
48   events living until we reach the predicted end-time.
49
50   Every time process_music () is called and there are note events, we
51   figure out how long the note to typeset should be. It should be no
52   longer than what's specified, than what is left to do and it should
53   not cross barlines.
54
55   We copy the events into scratch note events, to make sure that we get
56   all durations exactly right.
57 */
58
59 class Completion_heads_engraver : public Engraver
60 {
61   vector<Item*> notes_;
62   vector<Item*> prev_notes_;
63   // Must remember notes for explicit ties.
64   vector<Item*> tie_note_candidates_;
65   vector<Stream_event*> tie_note_candidate_events_;
66   vector<Grob*> ties_;
67   vector<Stream_event*> note_events_;
68   Moment note_end_mom_;
69   bool is_first_;
70   Rational left_to_do_;
71   Rational do_nothing_until_;
72   Rational factor_;
73
74   Moment next_barline_moment ();
75   Item *make_note_head (Stream_event*);
76
77 public:
78   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
79
80 protected:
81   virtual void initialize ();
82   void make_tie (Grob *, Grob *);
83   void start_translation_timestep ();
84   void process_music ();
85   void stop_translation_timestep ();
86   DECLARE_TRANSLATOR_LISTENER (note);
87 };
88
89 void
90 Completion_heads_engraver::initialize ()
91 {
92   is_first_ = false;
93 }
94
95 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
96 void
97 Completion_heads_engraver::listen_note (Stream_event *ev)
98 {
99   note_events_.push_back (ev);
100   
101   is_first_ = true;
102   Moment now = now_mom ();
103   Moment musiclen = get_event_length (ev, now);
104
105   note_end_mom_ = max (note_end_mom_, (now + musiclen));
106   do_nothing_until_ = Rational (0, 0);
107 }
108
109 /*
110   The duration _until_ the next barline.
111 */
112 Moment
113 Completion_heads_engraver::next_barline_moment ()
114 {
115   Moment *e = unsmob_moment (get_property ("measurePosition"));
116   Moment *l = unsmob_moment (get_property ("measureLength"));
117   if (!e || !l || !to_boolean (get_property ("timing")))
118     {
119       return Moment (0, 0);
120     }
121
122   return (*l - *e);
123 }
124
125 Item*
126 Completion_heads_engraver::make_note_head (Stream_event *ev)
127 {
128   Item *note = make_item ("NoteHead", ev->self_scm ());
129   Pitch *pit = unsmob_pitch (ev->get_property ("pitch"));
130
131   int pos = pit->steps ();
132   SCM c0 = get_property ("middleCPosition");
133   if (scm_is_number (c0))
134     pos += scm_to_int (c0);
135
136   note->set_property ("staff-position", scm_from_int (pos));
137
138   return note;
139 }
140
141 void
142 Completion_heads_engraver::process_music ()
143 {
144   if (!is_first_ && !left_to_do_)
145     return;
146   
147   is_first_ = false;
148
149   Moment now = now_mom ();
150   if (do_nothing_until_ > now.main_part_)
151     return;
152
153   Duration note_dur;
154   Duration *orig = 0;
155   if (left_to_do_)
156     {
157       /*
158         note that note_dur may be strictly less than left_to_do_
159         (say, if left_to_do_ == 5/8)
160       */
161       if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
162         note_dur = Duration (left_to_do_, false);
163       else
164         note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_);
165     }
166   else
167     {
168       orig = unsmob_duration (note_events_[0]->get_property ("duration"));
169       note_dur = *orig;
170       factor_ = note_dur.factor ();
171       left_to_do_ = orig->get_length ();
172     }
173   Moment nb = next_barline_moment ();
174   if (nb.main_part_ && nb < note_dur.get_length ())
175     {
176       if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
177         note_dur = Duration (nb.main_part_, false);
178       else
179         note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
180     }
181
182   do_nothing_until_ = now.main_part_ + note_dur.get_length ();
183
184   for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
185     {
186       bool need_clone = !orig || *orig != note_dur;
187       Stream_event *event = note_events_[i];
188
189       if (need_clone)
190         event = event->clone ();
191
192       SCM pits = note_events_[i]->get_property ("pitch");
193       event->set_property ("pitch", pits);
194       event->set_property ("duration", note_dur.smobbed_copy ());
195       event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
196       event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
197
198       /*
199         The Completion_heads_engraver splits an event into a group of consecutive events.
200         For each event in the group, the property "autosplit-end" denotes whether the current event
201         was truncated during splitting. Based on "autosplit-end", the Tie_engraver decides whether a
202         tie event should be processed.
203       */
204       event->set_property ("autosplit-end",
205                            ly_bool2scm (left_to_do_ - note_dur.get_length () > Rational (0)));
206
207       Item *note = make_note_head (event);
208       if (need_clone)
209         event->unprotect ();
210       notes_.push_back (note);
211     }
212   
213   if (prev_notes_.size () == notes_.size ())
214     {
215       for (vsize i = 0; i < notes_.size (); i++)
216         make_tie (prev_notes_[i], notes_[i]);
217     }
218
219   left_to_do_ -= note_dur.get_length ();
220   if (left_to_do_)
221     get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length());
222   /*
223     don't do complicated arithmetic with grace notes.
224   */
225   if (orig && now_mom ().grace_part_)
226     left_to_do_ = Rational (0, 0);
227 }
228
229 void
230 Completion_heads_engraver::make_tie (Grob *left, Grob *right)
231 {
232   Grob *p = make_spanner ("Tie", SCM_EOL);
233   Tie::set_head (p, LEFT, left);
234   Tie::set_head (p, RIGHT, right);
235   ties_.push_back (p);
236 }
237                                      
238 void
239 Completion_heads_engraver::stop_translation_timestep ()
240 {
241   ties_.clear ();
242
243   if (notes_.size ())
244     prev_notes_ = notes_;
245   notes_.clear ();
246 }
247
248 void
249 Completion_heads_engraver::start_translation_timestep ()
250 {
251   Moment now = now_mom ();
252   if (note_end_mom_.main_part_ <= now.main_part_)
253     {
254       tie_note_candidate_events_ = note_events_;
255       tie_note_candidates_ = prev_notes_;
256
257       note_events_.clear ();
258       prev_notes_.clear ();
259     }
260   context ()->set_property ("completionBusy",
261                             ly_bool2scm (note_events_.size ()));
262 }
263
264 Completion_heads_engraver::Completion_heads_engraver ()
265 {
266 }
267
268 ADD_TRANSLATOR (Completion_heads_engraver,
269                 /* doc */
270                 "This engraver replaces @code{Note_heads_engraver}.  It plays"
271                 " some trickery to break long notes and automatically tie them"
272                 " into the next measure."
273                 ,
274                 /* create */
275                 "NoteHead "
276                 "Tie "
277                 ,
278                 /* read */
279                 "middleCPosition "
280                 "measurePosition "
281                 "measureLength "
282                 ,
283                 /* write */
284                 "completionBusy "
285                 );