]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
* lily/*-performer.cc: Converted try_music to listen_*
[lilypond.git] / lily / completion-note-heads-engraver.cc
1 /*
2   completion-note-heads-engraver.cc -- Completion_heads_engraver
3
4   (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 */
6
7 #include <cctype>
8 using namespace std;
9
10 #include "rhythmic-head.hh"
11 #include "output-def.hh"
12 #include "music.hh"
13 #include "dots.hh"
14 #include "dot-column.hh"
15 #include "staff-symbol-referencer.hh"
16 #include "item.hh"
17 #include "score-engraver.hh"
18 #include "warn.hh"
19 #include "spanner.hh"
20 #include "tie.hh"
21 #include "global-context.hh"
22 #include "duration.hh"
23 #include "pitch.hh"
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   vector<Item*> notes_;
47   vector<Item*> prev_notes_;
48   vector<Grob*> ties_;
49
50   vector<Item*> dots_;
51   vector<Music*> note_events_;
52   vector<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_back (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
101   return false;
102 }
103
104 /*
105   The duration _until_ the next barline.
106 */
107 Moment
108 Completion_heads_engraver::next_barline_moment ()
109 {
110   Moment *e = unsmob_moment (get_property ("measurePosition"));
111   Moment *l = unsmob_moment (get_property ("measureLength"));
112   if (!e || !l)
113     {
114       programming_error ("no timing props set?");
115       return Moment (1, 1);
116     }
117
118   return (*l - *e);
119 }
120
121 Duration
122 Completion_heads_engraver::find_nearest_duration (Rational length)
123 {
124   int log_limit = 6;
125
126   Duration d (0, 0);
127
128   /*
129     this could surely be done more efficient. Left to the reader as an
130     excercise.  */
131   while (d.get_length () > length && d.duration_log () < log_limit)
132     {
133       if (d.dot_count ())
134         {
135           d = Duration (d.duration_log (), d.dot_count ()- 1);
136           continue;
137         }
138       else
139         d = Duration (d.duration_log () + 1, 2);
140     }
141
142   if (d.duration_log () >= log_limit)
143     {
144       // junk the dots.
145       d = Duration (d.duration_log (), 0);
146
147       // scale up.
148       d = d.compressed (length / d.get_length ());
149     }
150
151   return d;
152 }
153
154 void
155 Completion_heads_engraver::process_music ()
156 {
157   if (!is_first_ && !left_to_do_)
158     return;
159
160   is_first_ = false;
161
162   Moment now = now_mom ();
163   if (do_nothing_until_ > now.main_part_)
164     return;
165
166   Duration note_dur;
167   Duration *orig = 0;
168   if (left_to_do_)
169     note_dur = find_nearest_duration (left_to_do_);
170   else
171     {
172       orig = unsmob_duration (note_events_[0]->get_property ("duration"));
173       note_dur = *orig;
174     }
175   Moment nb = next_barline_moment ();
176   if (nb < note_dur.get_length ())
177     {
178       note_dur = find_nearest_duration (nb.main_part_);
179
180       Moment next = now;
181       next.main_part_ += note_dur.get_length ();
182
183       get_global_context ()->add_moment_to_process (next);
184       do_nothing_until_ = next.main_part_;
185     }
186
187   if (orig)
188     left_to_do_ = orig->get_length ();
189
190   if (orig && note_dur.get_length () != orig->get_length ())
191     {
192       if (!scratch_note_events_.size ())
193         for (vsize i = 0; i < note_events_.size (); i++)
194           {
195             Music *m = note_events_[i]->clone ();
196             scratch_note_events_.push_back (m);
197           }
198     }
199
200   for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
201     {
202       Music *event = note_events_[i];
203       if (scratch_note_events_.size ())
204         {
205           event = scratch_note_events_[i];
206           SCM pits = note_events_[i]->get_property ("pitch");
207           event->set_property ("pitch", pits);
208         }
209
210       event->set_property ("duration", note_dur.smobbed_copy ());
211
212       Item *note = make_item ("NoteHead", event->self_scm ());
213       note->set_property ("duration-log",
214                           scm_from_int (note_dur.duration_log ()));
215
216       int dots = note_dur.dot_count ();
217       if (dots)
218         {
219           Item *d = make_item ("Dots", SCM_EOL);
220           Rhythmic_head::set_dots (note, d);
221
222           /*
223             measly attempt to save an eeny-weenie bit of memory.
224           */
225           if (dots != scm_to_int (d->get_property ("dot-count")))
226             d->set_property ("dot-count", scm_from_int (dots));
227
228           d->set_parent (note, Y_AXIS);
229           dots_.push_back (d);
230         }
231
232       Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
233
234       int pos = pit->steps ();
235       SCM c0 = get_property ("middleCPosition");
236       if (scm_is_number (c0))
237         pos += scm_to_int (c0);
238
239       note->set_property ("staff-position", scm_from_int (pos));
240       notes_.push_back (note);
241     }
242
243   if (prev_notes_.size () == notes_.size ())
244     {
245       for (vsize i = 0; i < notes_.size (); i++)
246         {
247           Grob *p = make_spanner ("Tie", SCM_EOL);
248           Tie::set_head (p, LEFT, prev_notes_[i]);
249           Tie::set_head (p, RIGHT, notes_[i]);
250
251           ties_.push_back (p);
252         }
253     }
254
255   left_to_do_ -= note_dur.get_length ();
256
257   /*
258     don't do complicated arithmetic with grace notes.
259   */
260   if (orig
261       && now_mom ().grace_part_)
262     left_to_do_ = Rational (0, 0);
263 }
264
265 void
266 Completion_heads_engraver::stop_translation_timestep ()
267 {
268   ties_.clear ();
269
270   if (notes_.size ())
271     prev_notes_ = notes_;
272   notes_.clear ();
273
274   dots_.clear ();
275
276   for (vsize i = scratch_note_events_.size (); i--;)
277     scratch_note_events_[i]->unprotect ();
278
279   scratch_note_events_.clear ();
280 }
281
282 void
283 Completion_heads_engraver::start_translation_timestep ()
284 {
285   Moment now = now_mom ();
286   if (note_end_mom_.main_part_ <= now.main_part_)
287     {
288       note_events_.clear ();
289       prev_notes_.clear ();
290     }
291 }
292
293 Completion_heads_engraver::Completion_heads_engraver ()
294 {
295 }
296
297 #include "translator.icc"
298
299 ADD_TRANSLATOR (Completion_heads_engraver,
300                 /* doc */ "This engraver replaces "
301                 "@code{Note_heads_engraver}. It plays some trickery to "
302                 "break long notes and automatically tie them into the next measure.",
303                 /* create */ "NoteHead Dots Tie",
304                 /* accept */ "note-event",
305                 /* read */ "middleCPosition measurePosition measureLength",
306                 /* write */ "");