]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
release: 1.5.47
[lilypond.git] / lily / completion-note-heads-engraver.cc
1 /*
2   head-grav.cc -- part of GNU LilyPond
3
4   (c)  1997--2002 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 "musical-request.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
19 /*
20
21   How does this work?
22
23   When we catch the note, we predict the end of the note. We keep the
24   requests living until we reach the predicted end-time.
25
26   Every time process_music() is called and there are note requests, we
27   figure out how long the note to typeset should be. It should be no
28   longer than what's specified, than what is left to do and it should
29   not cross barlines.
30   
31   We copy the reqs into scratch note reqs, to make sure that we get
32   all durations exactly right.
33  */
34
35 class Completion_heads_engraver : public Engraver
36 {
37   Link_array<Item> note_p_arr_;
38   
39   Link_array<Item> dot_p_arr_;
40   Link_array<Music> note_req_l_arr_;
41   Link_array<Music> scratch_note_reqs_;
42
43   Moment note_end_mom_;
44   bool first_b_;
45   Rational left_to_do_;
46
47   Moment next_barline_moment ();
48   Duration find_nearest_duration (Rational length);
49   
50 public:
51   TRANSLATOR_DECLARATIONS(Completion_heads_engraver);
52
53 protected:
54   virtual void initialize ();
55   virtual void start_translation_timestep ();
56   virtual bool try_music (Music *req_l) ;
57   virtual void process_music ();
58   virtual void stop_translation_timestep ();
59 };
60
61 void
62 Completion_heads_engraver::initialize ()
63 {
64   first_b_ = false;
65 }
66
67 bool
68 Completion_heads_engraver::try_music (Music *m) 
69 {
70   if (Note_req * n =dynamic_cast <Note_req *> (m))
71     {
72       note_req_l_arr_.push (n);
73
74       first_b_ = true;
75       Moment musiclen = m->length_mom ();
76       Moment now = now_mom();
77
78       if (now_mom ().grace_part_)
79         {
80           musiclen.grace_part_ = musiclen.main_part_ ;
81           musiclen.main_part_ = Rational (0,1);
82         }
83       note_end_mom_  = note_end_mom_ >? (now + musiclen);
84       return true;
85     }
86   else if (dynamic_cast<Busy_playing_req*> (m))
87     {
88       return note_req_l_arr_.size ();
89     }
90   
91   return false;
92   
93 }
94
95 Moment
96 Completion_heads_engraver::next_barline_moment ( )
97 {
98   Moment *e = unsmob_moment (get_property ("measurePosition"));
99   Moment *l = unsmob_moment (get_property ("measureLength"));
100   if (!e || !l)
101     {
102       programming_error ("No timing props set?");
103       return Moment (1,1);
104     }
105
106   return (*l - *e);
107 }
108
109 Duration  
110 Completion_heads_engraver::find_nearest_duration (Rational length)
111 {
112   int log_limit= 6;
113
114   Duration d(0,0);
115
116   /*
117     this could surely be done more efficient. Left to the reader as an
118     excercise.  */
119   while (d.length_mom () > length && d.duration_log () < log_limit)
120     {
121       if (d.dot_count ())
122         {
123           d = Duration (d.duration_log (), d.dot_count ()- 1);
124           continue;
125         }
126       else
127         {
128           d = Duration (d.duration_log () + 1, 2);
129         }
130     }
131
132   if (d.duration_log () >= log_limit)
133     {
134       // junk the dots.
135       d = Duration (d.duration_log (), 0);
136
137       // scale up.
138       d = d.compressed (length / d.length_mom ());
139     }
140   
141   return d;
142 }
143
144 void
145 Completion_heads_engraver::process_music ()
146 {
147   if (!first_b_ && !left_to_do_)
148     return ;
149
150   first_b_ = false;
151   
152   Duration note_dur;
153   Duration *orig = 0;
154   if (left_to_do_)
155     {
156       note_dur = find_nearest_duration (left_to_do_);
157     }
158   else
159     {
160       orig = unsmob_duration (note_req_l_arr_[0]->get_mus_property ("duration"));
161       note_dur = *orig;
162     }
163
164   Moment nb = next_barline_moment ();
165   if (nb < note_dur.length_mom ())
166     {
167       note_dur = find_nearest_duration (nb.main_part_);
168
169       Moment next = now_mom();
170       next.main_part_ += note_dur.length_mom ();
171       top_engraver ()->add_moment_to_process (next);
172     }
173
174   if (orig)
175     {
176       left_to_do_ = orig->length_mom ();
177     }
178
179   if (orig && note_dur.length_mom() != orig->length_mom())
180     {
181       if (!scratch_note_reqs_.size ())
182         for (int i = 0; i < note_req_l_arr_.size (); i++)
183           {
184             Music * m = note_req_l_arr_[i]->clone ();
185             scratch_note_reqs_.push (m);
186           }
187
188       for (int i =0; i < scratch_note_reqs_.size (); i++)
189         scratch_note_reqs_[i]->set_mus_property ("duration", note_dur.smobbed_copy ());
190     }
191
192   
193   for (int i = 0;
194        left_to_do_ && i < note_req_l_arr_.size (); i++)
195     {
196       Item *note_p  = new Item (get_property ("NoteHead"));
197       
198       Music * req =  note_req_l_arr_[i];
199       if (scratch_note_reqs_.size())
200         {
201           req = scratch_note_reqs_[i];
202           SCM pits = note_req_l_arr_[i]->get_mus_property ("pitch");
203           req->set_mus_property ("pitch",pits);
204         }
205
206       note_p->set_grob_property ("duration-log",
207                                  gh_int2scm (note_dur.duration_log ()));
208       
209       int dots= note_dur.dot_count ();
210       if (dots)
211         {
212           Item * d = new Item (get_property ("Dots"));
213           Rhythmic_head::set_dots (note_p, d);
214
215           /*
216            measly attempt to save an eeny-weenie bit of memory.
217           */
218           if (dots != gh_scm2int (d->get_grob_property ("dot-count")))
219             d->set_grob_property ("dot-count", gh_int2scm (dots));
220
221           d->set_parent (note_p, Y_AXIS);
222           announce_grob (d, SCM_EOL);
223           dot_p_arr_.push (d);
224         }
225
226       Pitch *pit =unsmob_pitch (req->get_mus_property ("pitch"));
227
228       int pos = pit->steps ();
229       SCM c0 = get_property ("centralCPosition");
230       if (gh_number_p (c0))
231         pos += gh_scm2int (c0);
232
233       note_p->set_grob_property ("staff-position",   gh_int2scm (pos));
234       announce_grob (note_p,req->self_scm ());
235       note_p_arr_.push (note_p);
236     }
237
238   left_to_do_ -= note_dur.length_mom ();
239
240
241   /*
242     don't do complicated arithmetic with grace notes.
243    */
244   if (orig
245       &&  now_mom().grace_part_ )
246     {
247       left_to_do_ = Rational (0,0);
248     }
249   
250 }
251  
252 void
253 Completion_heads_engraver::stop_translation_timestep ()
254 {
255   for (int i=0; i < note_p_arr_.size (); i++)
256     {
257       typeset_grob (note_p_arr_[i]);
258     }
259   note_p_arr_.clear ();
260   
261   for (int i=0; i < dot_p_arr_.size (); i++)
262     {
263       typeset_grob (dot_p_arr_[i]);
264     }
265   dot_p_arr_.clear ();
266
267   for (int i = scratch_note_reqs_.size(); i--;)
268     {
269       scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm () );
270       
271     }
272   scratch_note_reqs_.clear();
273 }
274
275 Tie_req * tie_req = 0;
276
277 void
278 Completion_heads_engraver::start_translation_timestep ()
279 {
280   Moment now = now_mom ();
281   if (note_end_mom_.main_part_ <= now.main_part_)
282     {
283       note_req_l_arr_.clear ();
284     }
285
286   if (left_to_do_)
287     {
288       if (!tie_req)
289         tie_req = new Tie_req;
290       
291       bool succ = daddy_trans_l_->try_music (tie_req);
292       if (!succ)
293         {
294           programming_error ("Completion_heads_engraver: no-one to make tie.");
295         }
296     }
297 }
298
299 Completion_heads_engraver::Completion_heads_engraver()
300 {
301 }
302
303 ENTER_DESCRIPTION(Completion_heads_engraver,
304 /* descr */       "This engraver replaces
305 @code{Note_heads_engraver}. It plays some trickery to
306 break long notes and automatically tie them into the next measure.",
307 /* creats*/       "NoteHead Dots",
308 /* acks  */       "",
309 /* reads */       "centralCPosition measurePosition measureLength",
310 /* write */       "");