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