]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
(class Phrasing_slur_engraver):
[lilypond.git] / lily / completion-note-heads-engraver.cc
1 /*
2   completion-note-heads-engraver.cc -- Completion_heads_engraver
3
4   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
6
7 #include <cctype>
8
9 #include "rhythmic-head.hh"
10 #include "output-def.hh"
11 #include "music.hh"
12 #include "dots.hh"
13 #include "dot-column.hh"
14 #include "staff-symbol-referencer.hh"
15 #include "item.hh"
16 #include "score-engraver.hh"
17 #include "warn.hh"
18 #include "spanner.hh"
19 #include "tie.hh"
20 #include "global-context.hh"
21 #include "duration.hh"
22 #include "pitch.hh"
23
24
25
26 /*
27   TODO: make matching rest engraver.
28 */
29
30 /*
31   How does this work?
32
33   When we catch the note, we predict the end of the note. We keep the
34   events living until we reach the predicted end-time.
35
36   Every time process_music () is called and there are note events, we
37   figure out how long the note to typeset should be. It should be no
38   longer than what's specified, than what is left to do and it should
39   not cross barlines.
40
41   We copy the events into scratch note events, to make sure that we get
42   all durations exactly right.
43 */
44
45 class Completion_heads_engraver : public Engraver
46 {
47   Link_array<Item> notes_;
48   Link_array<Item> prev_notes_;
49   Link_array<Grob> ties_;
50
51   Link_array<Item> dots_;
52   Link_array<Music> note_events_;
53   Link_array<Music> scratch_note_events_;
54
55   Moment note_end_mom_;
56   bool is_first_;
57   Rational left_to_do_;
58   Rational do_nothing_until_;
59
60   Moment next_barline_moment ();
61   Duration find_nearest_duration (Rational length);
62
63 public:
64   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
65
66 protected:
67   virtual void initialize ();
68   void start_translation_timestep ();
69   virtual bool try_music (Music *event);
70   void process_music ();
71   void stop_translation_timestep ();
72 };
73
74 void
75 Completion_heads_engraver::initialize ()
76 {
77   is_first_ = false;
78 }
79
80 bool
81 Completion_heads_engraver::try_music (Music *m)
82 {
83   if (m->is_mus_type ("note-event"))
84     {
85       note_events_.push (m);
86
87       is_first_ = true;
88       Moment musiclen = m->get_length ();
89       Moment now = now_mom ();
90
91       if (now_mom ().grace_part_)
92         {
93           musiclen.grace_part_ = musiclen.main_part_;
94           musiclen.main_part_ = Rational (0, 1);
95         }
96       note_end_mom_ = max (note_end_mom_, (now + musiclen));
97       do_nothing_until_ = Rational (0, 0);
98
99       return true;
100     }
101   else if (m->is_mus_type ("busy-playing-event"))
102     {
103       return note_events_.size () && is_first_;
104     }
105
106   return false;
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)
118     {
119       programming_error ("no timing props set?");
120       return Moment (1, 1);
121     }
122
123   return (*l - *e);
124 }
125
126 Duration
127 Completion_heads_engraver::find_nearest_duration (Rational length)
128 {
129   int log_limit = 6;
130
131   Duration d (0, 0);
132
133   /*
134     this could surely be done more efficient. Left to the reader as an
135     excercise.  */
136   while (d.get_length () > length && d.duration_log () < log_limit)
137     {
138       if (d.dot_count ())
139         {
140           d = Duration (d.duration_log (), d.dot_count ()- 1);
141           continue;
142         }
143       else
144         {
145           d = Duration (d.duration_log () + 1, 2);
146         }
147     }
148
149   if (d.duration_log () >= log_limit)
150     {
151       // junk the dots.
152       d = Duration (d.duration_log (), 0);
153
154       // scale up.
155       d = d.compressed (length / d.get_length ());
156     }
157
158   return d;
159 }
160
161 void
162 Completion_heads_engraver::process_music ()
163 {
164   if (!is_first_ && !left_to_do_)
165     return;
166
167   is_first_ = false;
168
169   Moment now = now_mom ();
170   if (do_nothing_until_ > now.main_part_)
171     return;
172
173   Duration note_dur;
174   Duration *orig = 0;
175   if (left_to_do_)
176     {
177       note_dur = find_nearest_duration (left_to_do_);
178     }
179   else
180     {
181       orig = unsmob_duration (note_events_[0]->get_property ("duration"));
182       note_dur = *orig;
183     }
184   Moment nb = next_barline_moment ();
185   if (nb < note_dur.get_length ())
186     {
187       note_dur = find_nearest_duration (nb.main_part_);
188
189       Moment next = now;
190       next.main_part_ += note_dur.get_length ();
191
192       get_global_context ()->add_moment_to_process (next);
193       do_nothing_until_ = next.main_part_;
194     }
195
196   if (orig)
197     {
198       left_to_do_ = orig->get_length ();
199     }
200
201   if (orig && note_dur.get_length () != orig->get_length ())
202     {
203       if (!scratch_note_events_.size ())
204         for (int i = 0; i < note_events_.size (); i++)
205           {
206             Music *m = note_events_[i]->clone ();
207             scratch_note_events_.push (m);
208           }
209     }
210
211   for (int i = 0;
212        left_to_do_ && i < note_events_.size (); i++)
213     {
214       Music *event = note_events_[i];
215       if (scratch_note_events_.size ())
216         {
217           event = scratch_note_events_[i];
218           SCM pits = note_events_[i]->get_property ("pitch");
219           event->set_property ("pitch", pits);
220         }
221
222       event->set_property ("duration", note_dur.smobbed_copy ());
223
224       Item *note = make_item ("NoteHead", event->self_scm ());
225       note->set_property ("duration-log",
226                           scm_from_int (note_dur.duration_log ()));
227
228       int dots = note_dur.dot_count ();
229       if (dots)
230         {
231           Item *d = make_item ("Dots", SCM_EOL);
232           Rhythmic_head::set_dots (note, d);
233
234           /*
235             measly attempt to save an eeny-weenie bit of memory.
236           */
237           if (dots != scm_to_int (d->get_property ("dot-count")))
238             d->set_property ("dot-count", scm_from_int (dots));
239
240           d->set_parent (note, Y_AXIS);
241           dots_.push (d);
242         }
243
244       Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
245
246       int pos = pit->steps ();
247       SCM c0 = get_property ("middleCPosition");
248       if (scm_is_number (c0))
249         pos += scm_to_int (c0);
250
251       note->set_property ("staff-position", scm_from_int (pos));
252       notes_.push (note);
253     }
254
255   if (prev_notes_.size () == notes_.size ())
256     {
257       for (int i = 0; i < notes_.size (); i++)
258         {
259           Grob *p = make_spanner ("Tie", SCM_EOL);
260           Tie::set_interface (p); // cannot remove yet!
261
262           Tie::set_head (p, LEFT, prev_notes_[i]);
263           Tie::set_head (p, RIGHT, notes_[i]);
264
265           ties_.push (p);
266         }
267     }
268
269   left_to_do_ -= note_dur.get_length ();
270
271   /*
272     don't do complicated arithmetic with grace notes.
273   */
274   if (orig
275       && now_mom ().grace_part_)
276     {
277       left_to_do_ = Rational (0, 0);
278     }
279 }
280
281 void
282 Completion_heads_engraver::stop_translation_timestep ()
283 {
284   ties_.clear ();
285
286   if (notes_.size ())
287     prev_notes_ = notes_;
288   notes_.clear ();
289
290   dots_.clear ();
291
292   for (int i = scratch_note_events_.size (); i--;)
293     {
294       scratch_note_events_[i]->unprotect ();
295     }
296
297   scratch_note_events_.clear ();
298 }
299
300 void
301 Completion_heads_engraver::start_translation_timestep ()
302 {
303   Moment now = now_mom ();
304   if (note_end_mom_.main_part_ <= now.main_part_)
305     {
306       note_events_.clear ();
307       prev_notes_.clear ();
308     }
309 }
310
311 Completion_heads_engraver::Completion_heads_engraver ()
312 {
313 }
314
315 #include "translator.icc"
316
317 ADD_TRANSLATOR (Completion_heads_engraver,
318                 /* doc */ "This engraver replaces "
319                 "@code{Note_heads_engraver}. It plays some trickery to "
320                 "break long notes and automatically tie them into the next measure.",
321                 /* create */ "NoteHead Dots Tie",
322                 /* accept */ "busy-playing-event note-event",
323                 /* read */ "middleCPosition measurePosition measureLength",
324                 /* write */ "");