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