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