]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
release: 1.5.30
[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       Staff_symbol_referencer::set_interface (note_p);
199       
200       Music * req =  note_req_l_arr_[i];
201       if (scratch_note_reqs_.size())
202         {
203           req = scratch_note_reqs_[i];
204           SCM pits = note_req_l_arr_[i]->get_mus_property ("pitch");
205           req->set_mus_property ("pitch",pits);
206         }
207
208       note_p->set_grob_property ("duration-log",
209                                  gh_int2scm (note_dur.duration_log ()));
210       
211       int dots= note_dur.dot_count ();
212       if (dots)
213         {
214           Item * d = new Item (get_property ("Dots"));
215           Rhythmic_head::set_dots (note_p, d);
216
217           /*
218            measly attempt to save an eeny-weenie bit of memory.
219           */
220           if (dots != gh_scm2int (d->get_grob_property ("dot-count")))
221             d->set_grob_property ("dot-count", gh_int2scm (dots));
222
223           d->set_parent (note_p, Y_AXIS);
224           announce_grob (d, SCM_EOL);
225           dot_p_arr_.push (d);
226         }
227
228       Pitch *pit =unsmob_pitch (req->get_mus_property ("pitch"));
229
230       int pos = pit->steps ();
231       SCM c0 = get_property ("centralCPosition");
232       if (gh_number_p (c0))
233         pos += gh_scm2int (c0);
234
235       note_p->set_grob_property ("staff-position",   gh_int2scm (pos));
236       if (to_boolean (get_property ("easyPlay")))
237         {
238           char s[2] = "a";
239           s[0] = (pit->notename_i_ + 2)%7 + 'a';
240
241           s[0] = toupper (s[0]);
242           note_p->set_grob_property ("note-character", ly_str02scm (s));
243         }
244       
245       announce_grob (note_p,req->self_scm ());
246       note_p_arr_.push (note_p);
247     }
248
249   left_to_do_ -= note_dur.length_mom ();
250
251
252   /*
253     don't do complicated arithmetic with grace notes.
254    */
255   if (orig
256       &&  now_mom().grace_part_ )
257     {
258       left_to_do_ = Rational (0,0);
259     }
260   
261 }
262  
263 void
264 Completion_heads_engraver::stop_translation_timestep ()
265 {
266   for (int i=0; i < note_p_arr_.size (); i++)
267     {
268       typeset_grob (note_p_arr_[i]);
269     }
270   note_p_arr_.clear ();
271   
272   for (int i=0; i < dot_p_arr_.size (); i++)
273     {
274       typeset_grob (dot_p_arr_[i]);
275     }
276   dot_p_arr_.clear ();
277
278   for (int i = scratch_note_reqs_.size(); i--;)
279     {
280       scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm () );
281       
282     }
283   scratch_note_reqs_.clear();
284 }
285
286 Tie_req * tie_req = 0;
287
288 void
289 Completion_heads_engraver::start_translation_timestep ()
290 {
291   Moment now = now_mom ();
292   if (note_end_mom_.main_part_ <= now.main_part_)
293     {
294       note_req_l_arr_.clear ();
295     }
296
297   if (left_to_do_)
298     {
299       if (!tie_req)
300         tie_req = new Tie_req;
301       
302       bool succ = daddy_trans_l_->try_music (tie_req);
303       if (!succ)
304         {
305           programming_error ("Completion_heads_engraver: no-one to make tie.");
306         }
307     }
308 }
309
310 Completion_heads_engraver::Completion_heads_engraver()
311 {
312 }
313
314 ENTER_DESCRIPTION(Completion_heads_engraver,
315 /* descr */       "This engraver replaces
316 @code{Note_heads_engraver}. It plays some trickery to
317 break long notes and automatically tie them into the next measure.",
318 /* creats*/       "NoteHead Dots",
319 /* acks  */       "",
320 /* reads */       "easyPlay centralCPosition measurePosition measureLength",
321 /* write */       "");