]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
(parse_symbol_list): Bugfix.
[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   TODO: make matching rest engraver.
27 */
28
29 /*
30   How does this work?
31
32   When we catch the note, we predict the end of the note. We keep the
33   events living until we reach the predicted end-time.
34
35   Every time process_music () is called and there are note events, we
36   figure out how long the note to typeset should be. It should be no
37   longer than what's specified, than what is left to do and it should
38   not cross barlines.
39
40   We copy the events into scratch note events, to make sure that we get
41   all durations exactly right.
42 */
43
44 class Completion_heads_engraver : public Engraver
45 {
46   Link_array<Item> notes_;
47   Link_array<Item> prev_notes_;
48   Link_array<Grob> ties_;
49
50   Link_array<Item> dots_;
51   Link_array<Music> note_events_;
52   Link_array<Music> scratch_note_events_;
53
54   Moment note_end_mom_;
55   bool is_first_;
56   Rational left_to_do_;
57   Rational do_nothing_until_;
58
59   Moment next_barline_moment ();
60   Duration find_nearest_duration (Rational length);
61
62 public:
63   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
64
65 protected:
66   virtual void initialize ();
67   void start_translation_timestep ();
68   virtual bool try_music (Music *event);
69   void process_music ();
70   void stop_translation_timestep ();
71 };
72
73 void
74 Completion_heads_engraver::initialize ()
75 {
76   is_first_ = false;
77 }
78
79 bool
80 Completion_heads_engraver::try_music (Music *m)
81 {
82   if (m->is_mus_type ("note-event"))
83     {
84       note_events_.push (m);
85
86       is_first_ = true;
87       Moment musiclen = m->get_length ();
88       Moment now = now_mom ();
89
90       if (now_mom ().grace_part_)
91         {
92           musiclen.grace_part_ = musiclen.main_part_;
93           musiclen.main_part_ = Rational (0, 1);
94         }
95       note_end_mom_ = max (note_end_mom_, (now + musiclen));
96       do_nothing_until_ = Rational (0, 0);
97
98       return true;
99     }
100   else if (m->is_mus_type ("busy-playing-event"))
101     {
102       return note_events_.size () && is_first_;
103     }
104
105   return false;
106 }
107
108 /*
109   The duration _until_ the next barline.
110 */
111 Moment
112 Completion_heads_engraver::next_barline_moment ()
113 {
114   Moment *e = unsmob_moment (get_property ("measurePosition"));
115   Moment *l = unsmob_moment (get_property ("measureLength"));
116   if (!e || !l)
117     {
118       programming_error ("no timing props set?");
119       return Moment (1, 1);
120     }
121
122   return (*l - *e);
123 }
124
125 Duration
126 Completion_heads_engraver::find_nearest_duration (Rational length)
127 {
128   int log_limit = 6;
129
130   Duration d (0, 0);
131
132   /*
133     this could surely be done more efficient. Left to the reader as an
134     excercise.  */
135   while (d.get_length () > length && d.duration_log () < log_limit)
136     {
137       if (d.dot_count ())
138         {
139           d = Duration (d.duration_log (), d.dot_count ()- 1);
140           continue;
141         }
142       else
143         d = Duration (d.duration_log () + 1, 2);
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_events_[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_events_.size ())
201         for (int i = 0; i < note_events_.size (); i++)
202           {
203             Music *m = note_events_[i]->clone ();
204             scratch_note_events_.push (m);
205           }
206     }
207
208   for (int i = 0;
209        left_to_do_ && i < note_events_.size (); i++)
210     {
211       Music *event = note_events_[i];
212       if (scratch_note_events_.size ())
213         {
214           event = scratch_note_events_[i];
215           SCM pits = note_events_[i]->get_property ("pitch");
216           event->set_property ("pitch", pits);
217         }
218
219       event->set_property ("duration", note_dur.smobbed_copy ());
220
221       Item *note = make_item ("NoteHead", event->self_scm ());
222       note->set_property ("duration-log",
223                           scm_from_int (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_from_int (dots));
236
237           d->set_parent (note, Y_AXIS);
238           dots_.push (d);
239         }
240
241       Pitch *pit = unsmob_pitch (event->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_from_int (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   left_to_do_ -= note_dur.get_length ();
267
268   /*
269     don't do complicated arithmetic with grace notes.
270   */
271   if (orig
272       && now_mom ().grace_part_)
273     {
274       left_to_do_ = Rational (0, 0);
275     }
276 }
277
278 void
279 Completion_heads_engraver::stop_translation_timestep ()
280 {
281   ties_.clear ();
282
283   if (notes_.size ())
284     prev_notes_ = notes_;
285   notes_.clear ();
286
287   dots_.clear ();
288
289   for (int i = scratch_note_events_.size (); i--;)
290     {
291       scratch_note_events_[i]->unprotect ();
292     }
293
294   scratch_note_events_.clear ();
295 }
296
297 void
298 Completion_heads_engraver::start_translation_timestep ()
299 {
300   Moment now = now_mom ();
301   if (note_end_mom_.main_part_ <= now.main_part_)
302     {
303       note_events_.clear ();
304       prev_notes_.clear ();
305     }
306 }
307
308 Completion_heads_engraver::Completion_heads_engraver ()
309 {
310 }
311
312 #include "translator.icc"
313
314 ADD_TRANSLATOR (Completion_heads_engraver,
315                 /* doc */ "This engraver replaces "
316                 "@code{Note_heads_engraver}. It plays some trickery to "
317                 "break long notes and automatically tie them into the next measure.",
318                 /* create */ "NoteHead Dots Tie",
319                 /* accept */ "busy-playing-event note-event",
320                 /* read */ "middleCPosition measurePosition measureLength",
321                 /* write */ "");