]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
Merge master into nested-bookparts
[lilypond.git] / lily / completion-note-heads-engraver.cc
1 /*
2   completion-note-heads-engraver.cc -- Completion_heads_engraver
3
4   (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 */
6
7 #include <cctype>
8 using namespace std;
9
10 #include "dot-column.hh"
11 #include "dots.hh"
12 #include "duration.hh"
13 #include "global-context.hh"
14 #include "item.hh"
15 #include "output-def.hh"
16 #include "pitch.hh"
17 #include "pqueue.hh"
18 #include "rhythmic-head.hh"
19 #include "score-engraver.hh"
20 #include "spanner.hh"
21 #include "staff-symbol-referencer.hh"
22 #include "stream-event.hh"
23 #include "tie.hh"
24 #include "warn.hh"
25
26 #include "translator.icc"
27
28 /*
29   TODO: make matching rest engraver.
30 */
31 struct Pending_tie
32 {
33   Moment when_;
34   Stream_event* tie_event_;
35   Pending_tie() { tie_event_ = 0; }
36 };
37
38 int compare(Pending_tie const &a, Pending_tie const &b)
39 {
40   return compare(a.when_, b.when_);
41 }
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
64   // Must remember notes for explicit ties.
65   vector<Item*> tie_note_candidates_;
66   vector<Stream_event*> tie_note_candidate_events_;
67   vector<Grob*> ties_;
68   PQueue<Pending_tie> pending_ties_;
69   vector<Stream_event*> note_events_;
70
71   Stream_event *current_tie_event_;
72   Moment note_end_mom_;
73   bool is_first_;
74   Rational left_to_do_;
75   Rational do_nothing_until_;
76
77   Moment next_barline_moment ();
78   Item *make_note_head (Stream_event*);
79
80 public:
81   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
82
83 protected:
84   virtual void initialize ();
85   void make_tie (Grob *, Grob *);
86   void start_translation_timestep ();
87   void process_music ();
88   void stop_translation_timestep ();
89   DECLARE_TRANSLATOR_LISTENER (note);
90   DECLARE_TRANSLATOR_LISTENER (tie);
91 };
92
93 void
94 Completion_heads_engraver::initialize ()
95 {
96   is_first_ = false;
97   current_tie_event_ = 0;
98 }
99
100 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
101 void
102 Completion_heads_engraver::listen_note (Stream_event *ev)
103 {
104   note_events_.push_back (ev);
105   
106   is_first_ = true;
107   Moment now = now_mom ();
108   Moment musiclen = get_event_length (ev, now);
109
110   note_end_mom_ = max (note_end_mom_, (now + musiclen));
111   do_nothing_until_ = Rational (0, 0);
112 }
113
114 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, tie);
115 void
116 Completion_heads_engraver::listen_tie (Stream_event *ev)
117 {
118   is_first_ = true;
119   current_tie_event_ = ev;
120 }
121
122 /*
123   The duration _until_ the next barline.
124 */
125 Moment
126 Completion_heads_engraver::next_barline_moment ()
127 {
128   Moment *e = unsmob_moment (get_property ("measurePosition"));
129   Moment *l = unsmob_moment (get_property ("measureLength"));
130   if (!e || !l || !to_boolean (get_property ("timing")))
131     {
132       return Moment (0, 0);
133     }
134
135   return (*l - *e);
136 }
137
138 Item*
139 Completion_heads_engraver::make_note_head (Stream_event *ev)
140 {
141   Item *note = make_item ("NoteHead", ev->self_scm ());
142   Pitch *pit = unsmob_pitch (ev->get_property ("pitch"));
143
144   int pos = pit->steps ();
145   SCM c0 = get_property ("middleCPosition");
146   if (scm_is_number (c0))
147     pos += scm_to_int (c0);
148
149   note->set_property ("staff-position", scm_from_int (pos));
150
151   return note;
152 }
153
154 void
155 Completion_heads_engraver::process_music ()
156 {
157   if (!is_first_ && !left_to_do_)
158     return;
159
160   if (current_tie_event_)
161     {
162       Pending_tie pending;
163       pending.when_ = note_end_mom_;
164       pending.tie_event_ = current_tie_event_;
165       pending_ties_.insert (pending);
166     }
167   
168   is_first_ = false;
169
170   Moment now = now_mom ();
171   if (do_nothing_until_ > now.main_part_)
172     return;
173
174   Duration note_dur;
175   Duration *orig = 0;
176   if (left_to_do_)
177     note_dur = Duration (left_to_do_, false);
178   else
179     {
180       orig = unsmob_duration (note_events_[0]->get_property ("duration"));
181       note_dur = *orig;
182     }
183   Moment nb = next_barline_moment ();
184   if (nb.main_part_ && nb < note_dur.get_length ())
185     {
186       note_dur = Duration (nb.main_part_, false);
187
188       do_nothing_until_ = now.main_part_ + note_dur.get_length ();
189     }
190
191   if (orig)
192     left_to_do_ = orig->get_length ();
193
194   for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
195     {
196       bool need_clone = !orig || *orig != note_dur;
197       Stream_event *event = note_events_[i];
198
199       if (need_clone)
200         event = event->clone ();
201
202       SCM pits = note_events_[i]->get_property ("pitch");
203
204       event->set_property ("pitch", pits);
205       event->set_property ("duration", note_dur.smobbed_copy ());
206       event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
207       event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
208
209       Item *note = make_note_head (event);
210       if (need_clone)
211         event->unprotect ();
212       notes_.push_back (note);
213     }
214   
215   if (pending_ties_.size ()
216       && pending_ties_.front().when_ == now_mom())
217     {
218       for (vsize i = 0; i < tie_note_candidate_events_.size(); i++)
219         for (vsize j = 0; j < note_events_.size(); j++)
220           {
221             Pitch *p =  unsmob_pitch (note_events_[j]->get_property ("pitch"));
222             Pitch *p_last
223               = unsmob_pitch (tie_note_candidate_events_[j]->get_property ("pitch"));
224             if (p && p_last && *p == *p_last)
225               make_tie (tie_note_candidates_[i], notes_[j]);
226           }
227     }
228       
229   if (prev_notes_.size () == notes_.size ())
230     {
231       for (vsize i = 0; i < notes_.size (); i++)
232         make_tie (prev_notes_[i], notes_[i]);
233     }
234
235   left_to_do_ -= note_dur.get_length ();
236   if (left_to_do_)
237     get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length());
238   /*
239     don't do complicated arithmetic with grace notes.
240   */
241   if (orig && now_mom ().grace_part_)
242     left_to_do_ = Rational (0, 0);
243 }
244
245 void
246 Completion_heads_engraver::make_tie (Grob *left, Grob *right)
247 {
248   Grob *p = make_spanner ("Tie", SCM_EOL);
249   Tie::set_head (p, LEFT, left);
250   Tie::set_head (p, RIGHT, right);
251   ties_.push_back (p);
252 }
253                                      
254 void
255 Completion_heads_engraver::stop_translation_timestep ()
256 {
257   ties_.clear ();
258
259   if (notes_.size ())
260     prev_notes_ = notes_;
261   notes_.clear ();
262 }
263
264 void
265 Completion_heads_engraver::start_translation_timestep ()
266 {
267   Moment now = now_mom ();
268   while (pending_ties_.size() && pending_ties_.front().when_ < now)
269     {
270       pending_ties_.delmin();
271     }
272   current_tie_event_ = 0;
273   if (note_end_mom_.main_part_ <= now.main_part_)
274     {
275       tie_note_candidate_events_ = note_events_;
276       tie_note_candidates_ = prev_notes_;
277
278       note_events_.clear ();
279       prev_notes_.clear ();
280     }
281   context ()->set_property ("completionBusy",
282                             ly_bool2scm (note_events_.size ()));
283 }
284
285 Completion_heads_engraver::Completion_heads_engraver ()
286 {
287 }
288
289 ADD_TRANSLATOR (Completion_heads_engraver,
290                 /* doc */
291                 "This engraver replaces @code{Note_heads_engraver}.  It plays"
292                 " some trickery to break long notes and automatically tie them"
293                 " into the next measure.",
294
295                 /* create */
296                 "NoteHead "
297                 "Dots "
298                 "Tie ",
299
300                 /* read */
301                 "middleCPosition "
302                 "measurePosition "
303                 "measureLength ",
304
305                 /* write */
306                 "completionBusy "
307                 );