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