]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
*** empty log message ***
[lilypond.git] / lily / completion-note-heads-engraver.cc
1 /*
2   head-grav.cc -- part of GNU LilyPond
3
4   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
6
7 #include <ctype.h>
8
9 #include "rhythmic-head.hh"
10 #include "paper-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
24   How does this work?
25
26   When we catch the note, we predict the end of the note. We keep the
27   events living until we reach the predicted end-time.
28
29   Every time process_music () is called and there are note events, we
30   figure out how long the note to typeset should be. It should be no
31   longer than what's specified, than what is left to do and it should
32   not cross barlines.
33   
34   We copy the reqs into scratch note reqs, to make sure that we get
35   all durations exactly right.
36 */
37
38 class Completion_heads_engraver : public Engraver
39 {
40   Link_array<Item> notes_;
41   Link_array<Item> prev_notes_;
42   Link_array<Grob> ties_;
43   
44   Link_array<Item> dots_;
45   Link_array<Music> note_reqs_;
46   Link_array<Music> scratch_note_reqs_;
47
48   Moment note_end_mom_;
49   bool first_b_;
50   Rational left_to_do_;
51   Rational do_nothing_until_;
52   
53   Moment next_barline_moment ();
54   Duration find_nearest_duration (Rational length);
55   
56 public:
57   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
58
59 protected:
60   virtual void initialize ();
61   virtual void start_translation_timestep ();
62   virtual bool try_music (Music *req) ;
63   virtual void process_music ();
64   virtual void stop_translation_timestep ();
65 };
66
67 void
68 Completion_heads_engraver::initialize ()
69 {
70   first_b_ = false;
71 }
72
73 bool
74 Completion_heads_engraver::try_music (Music *m) 
75 {
76   if (m->is_mus_type ("note-event"))
77     {
78       note_reqs_.push (m);
79
80       first_b_ = true;
81       Moment musiclen = m->get_length ();
82       Moment now = now_mom ();
83
84       if (now_mom ().grace_part_)
85         {
86           musiclen.grace_part_ = musiclen.main_part_ ;
87           musiclen.main_part_ = Rational (0,1);
88         }
89       note_end_mom_  = note_end_mom_ >? (now + musiclen);
90       do_nothing_until_ = Rational (0,0);
91       
92       return true;
93     }
94   else if  (m->is_mus_type ("busy-playing-event"))
95     {
96       return note_reqs_.size ();
97     }
98   
99   return false;
100   
101 }
102
103 /*
104   The duration _until_ the next barline.
105  */
106 Moment
107 Completion_heads_engraver::next_barline_moment ( )
108 {
109   Moment *e = unsmob_moment (get_property ("measurePosition"));
110   Moment *l = unsmob_moment (get_property ("measureLength"));
111   if (!e || !l)
112     {
113       programming_error ("No timing props set?");
114       return Moment (1,1);
115     }
116
117   return (*l - *e);
118 }
119
120 Duration  
121 Completion_heads_engraver::find_nearest_duration (Rational length)
122 {
123   int log_limit= 6;
124
125   Duration d (0,0);
126
127   /*
128     this could surely be done more efficient. Left to the reader as an
129     excercise.  */
130   while (d.get_length () > length && d.duration_log () < log_limit)
131     {
132       if (d.dot_count ())
133         {
134           d = Duration (d.duration_log (), d.dot_count ()- 1);
135           continue;
136         }
137       else
138         {
139           d = Duration (d.duration_log () + 1, 2);
140         }
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 (!first_b_ && !left_to_do_)
159     return ;
160   
161   first_b_ = 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     {
171       note_dur = find_nearest_duration (left_to_do_);
172     }
173   else
174     {
175       orig = unsmob_duration (note_reqs_[0]->get_property ("duration"));
176       note_dur = *orig;
177     }
178   Moment nb = next_barline_moment ();
179   if (nb < note_dur.get_length ())
180     {
181       note_dur = find_nearest_duration (nb.main_part_);
182
183       Moment next = now;
184       next.main_part_ += note_dur.get_length ();
185       
186       get_global_context ()->add_moment_to_process (next);
187       do_nothing_until_ = next.main_part_;
188     }
189
190   if (orig)
191     {
192       left_to_do_ = orig->get_length ();
193     }
194
195   if (orig && note_dur.get_length () != orig->get_length ())
196     {
197       if (!scratch_note_reqs_.size ())
198         for (int i = 0; i < note_reqs_.size (); i++)
199           {
200             Music * m = note_reqs_[i]->clone ();
201             scratch_note_reqs_.push (m);
202           }
203     }
204
205   
206   for (int i = 0;
207        left_to_do_ && i < note_reqs_.size (); i++)
208     {
209       Item *note  = make_item ("NoteHead");
210       
211       Music * req =  note_reqs_[i];
212       if (scratch_note_reqs_.size ())
213         {
214           req = scratch_note_reqs_[i];
215           SCM pits = note_reqs_[i]->get_property ("pitch");
216           req->set_property ("pitch",pits);
217         }
218       
219       req->set_property ("duration", note_dur.smobbed_copy ());
220       note->set_property ("duration-log",
221                                  gh_int2scm (note_dur.duration_log ()));
222       
223       int dots= note_dur.dot_count ();
224       if (dots)
225         {
226           Item * d = make_item ("Dots");
227           Rhythmic_head::set_dots (note, d);
228
229           /*
230            measly attempt to save an eeny-weenie bit of memory.
231           */
232           if (dots != gh_scm2int (d->get_property ("dot-count")))
233             d->set_property ("dot-count", gh_int2scm (dots));
234
235           d->set_parent (note, Y_AXIS);
236           announce_grob (d, SCM_EOL);
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 ("centralCPosition");
244       if (gh_number_p (c0))
245         pos += gh_scm2int (c0);
246
247       note->set_property ("staff-position",   gh_int2scm (pos));
248       announce_grob (note,req->self_scm ());
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");
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           announce_grob (p, SCM_EOL);
264         }
265     }
266
267   left_to_do_ -= note_dur.get_length ();
268
269   /*
270     don't do complicated arithmetic with grace notes.
271    */
272   if (orig
273       &&  now_mom ().grace_part_ )
274     {
275       left_to_do_ = Rational (0,0);
276     }
277 }
278  
279 void
280 Completion_heads_engraver::stop_translation_timestep ()
281 {
282   for (int i = ties_.size (); i--;)
283     typeset_grob (ties_[i]); 
284   ties_.clear ();
285   
286   for (int i=0; i < notes_.size (); i++)
287     {
288       typeset_grob (notes_[i]);
289     }
290   if (notes_.size ())
291     prev_notes_ = notes_;
292   notes_.clear ();
293   
294   for (int i=0; i < dots_.size (); i++)
295     {
296       typeset_grob (dots_[i]);
297     }
298   dots_.clear ();
299
300   for (int i = scratch_note_reqs_.size (); i--;)
301     {
302       scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm () );
303     }
304   
305   scratch_note_reqs_.clear ();
306 }
307
308 void
309 Completion_heads_engraver::start_translation_timestep ()
310 {
311   Moment now = now_mom ();
312   if (note_end_mom_.main_part_ <= now.main_part_)
313     {
314       note_reqs_.clear ();
315       prev_notes_.clear ();
316     }
317 }
318
319 Completion_heads_engraver::Completion_heads_engraver ()
320 {
321 }
322
323 ENTER_DESCRIPTION (Completion_heads_engraver,
324 /* descr */       "This engraver replaces "
325 "@code{Note_heads_engraver}. It plays some trickery to "
326 "break long notes and automatically tie them into the next measure.",
327 /* creats*/       "NoteHead Dots Tie",
328 /* accepts */     "busy-playing-event note-event",
329 /* acks  */      "",
330 /* reads */       "centralCPosition measurePosition measureLength",
331 /* write */       "");