]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
* flower/*.cc: remove <? and >?
[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_ = max (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   The duration _until_ the next barline.
107 */
108 Moment
109 Completion_heads_engraver::next_barline_moment ()
110 {
111   Moment *e = unsmob_moment (get_property ("measurePosition"));
112   Moment *l = unsmob_moment (get_property ("measureLength"));
113   if (!e || !l)
114     {
115       programming_error ("no timing props set?");
116       return Moment (1, 1);
117     }
118
119   return (*l - *e);
120 }
121
122 Duration
123 Completion_heads_engraver::find_nearest_duration (Rational length)
124 {
125   int log_limit = 6;
126
127   Duration d (0, 0);
128
129   /*
130     this could surely be done more efficient. Left to the reader as an
131     excercise.  */
132   while (d.get_length () > length && d.duration_log () < log_limit)
133     {
134       if (d.dot_count ())
135         {
136           d = Duration (d.duration_log (), d.dot_count ()- 1);
137           continue;
138         }
139       else
140         {
141           d = Duration (d.duration_log () + 1, 2);
142         }
143     }
144
145   if (d.duration_log () >= log_limit)
146     {
147       // junk the dots.
148       d = Duration (d.duration_log (), 0);
149
150       // scale up.
151       d = d.compressed (length / d.get_length ());
152     }
153
154   return d;
155 }
156
157 void
158 Completion_heads_engraver::process_music ()
159 {
160   if (!is_first_ && !left_to_do_)
161     return;
162
163   is_first_ = false;
164
165   Moment now = now_mom ();
166   if (do_nothing_until_ > now.main_part_)
167     return;
168
169   Duration note_dur;
170   Duration *orig = 0;
171   if (left_to_do_)
172     {
173       note_dur = find_nearest_duration (left_to_do_);
174     }
175   else
176     {
177       orig = unsmob_duration (note_reqs_[0]->get_property ("duration"));
178       note_dur = *orig;
179     }
180   Moment nb = next_barline_moment ();
181   if (nb < note_dur.get_length ())
182     {
183       note_dur = find_nearest_duration (nb.main_part_);
184
185       Moment next = now;
186       next.main_part_ += note_dur.get_length ();
187
188       get_global_context ()->add_moment_to_process (next);
189       do_nothing_until_ = next.main_part_;
190     }
191
192   if (orig)
193     {
194       left_to_do_ = orig->get_length ();
195     }
196
197   if (orig && note_dur.get_length () != orig->get_length ())
198     {
199       if (!scratch_note_reqs_.size ())
200         for (int i = 0; i < note_reqs_.size (); i++)
201           {
202             Music *m = note_reqs_[i]->clone ();
203             scratch_note_reqs_.push (m);
204           }
205     }
206
207   for (int i = 0;
208        left_to_do_ && i < note_reqs_.size (); i++)
209     {
210       Music *req = note_reqs_[i];
211       if (scratch_note_reqs_.size ())
212         {
213           req = scratch_note_reqs_[i];
214           SCM pits = note_reqs_[i]->get_property ("pitch");
215           req->set_property ("pitch", pits);
216         }
217
218       req->set_property ("duration", note_dur.smobbed_copy ());
219
220       Item *note = make_item ("NoteHead", req->self_scm ());
221       note->set_property ("duration-log",
222                           scm_int2num (note_dur.duration_log ()));
223
224       int dots = note_dur.dot_count ();
225       if (dots)
226         {
227           Item *d = make_item ("Dots", SCM_EOL);
228           Rhythmic_head::set_dots (note, d);
229
230           /*
231             measly attempt to save an eeny-weenie bit of memory.
232           */
233           if (dots != scm_to_int (d->get_property ("dot-count")))
234             d->set_property ("dot-count", scm_int2num (dots));
235
236           d->set_parent (note, Y_AXIS);
237           dots_.push (d);
238         }
239
240       Pitch *pit = unsmob_pitch (req->get_property ("pitch"));
241
242       int pos = pit->steps ();
243       SCM c0 = get_property ("middleCPosition");
244       if (scm_is_number (c0))
245         pos += scm_to_int (c0);
246
247       note->set_property ("staff-position", scm_int2num (pos));
248       notes_.push (note);
249     }
250
251   if (prev_notes_.size () == notes_.size ())
252     {
253       for (int i = 0; i < notes_.size (); i++)
254         {
255           Grob *p = make_spanner ("Tie", SCM_EOL);
256           Tie::set_interface (p); // cannot remove yet!
257
258           Tie::set_head (p, LEFT, prev_notes_[i]);
259           Tie::set_head (p, RIGHT, notes_[i]);
260
261           ties_.push (p);
262         }
263     }
264
265   left_to_do_ -= note_dur.get_length ();
266
267   /*
268     don't do complicated arithmetic with grace notes.
269   */
270   if (orig
271       && now_mom ().grace_part_)
272     {
273       left_to_do_ = Rational (0, 0);
274     }
275 }
276
277 void
278 Completion_heads_engraver::stop_translation_timestep ()
279 {
280   ties_.clear ();
281
282   if (notes_.size ())
283     prev_notes_ = notes_;
284   notes_.clear ();
285
286   dots_.clear ();
287
288   for (int i = scratch_note_reqs_.size (); i--;)
289     {
290       scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm ());
291     }
292
293   scratch_note_reqs_.clear ();
294 }
295
296 void
297 Completion_heads_engraver::start_translation_timestep ()
298 {
299   Moment now = now_mom ();
300   if (note_end_mom_.main_part_ <= now.main_part_)
301     {
302       note_reqs_.clear ();
303       prev_notes_.clear ();
304     }
305 }
306
307 Completion_heads_engraver::Completion_heads_engraver ()
308 {
309 }
310
311 ADD_TRANSLATOR (Completion_heads_engraver,
312                 /* descr */ "This engraver replaces "
313                 "@code{Note_heads_engraver}. It plays some trickery to "
314                 "break long notes and automatically tie them into the next measure.",
315                 /* creats*/ "NoteHead Dots Tie",
316                 /* accepts */ "busy-playing-event note-event",
317                 /* acks  */ "",
318                 /* reads */ "middleCPosition measurePosition measureLength",
319                 /* write */ "");