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