]> git.donarmstrong.com Git - lilypond.git/blob - lily/completion-note-heads-engraver.cc
release: 1.5.32
[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       announce_grob (note_p,req->self_scm ());
237       note_p_arr_.push (note_p);
238     }
239
240   left_to_do_ -= note_dur.length_mom ();
241
242
243   /*
244     don't do complicated arithmetic with grace notes.
245    */
246   if (orig
247       &&  now_mom().grace_part_ )
248     {
249       left_to_do_ = Rational (0,0);
250     }
251   
252 }
253  
254 void
255 Completion_heads_engraver::stop_translation_timestep ()
256 {
257   for (int i=0; i < note_p_arr_.size (); i++)
258     {
259       typeset_grob (note_p_arr_[i]);
260     }
261   note_p_arr_.clear ();
262   
263   for (int i=0; i < dot_p_arr_.size (); i++)
264     {
265       typeset_grob (dot_p_arr_[i]);
266     }
267   dot_p_arr_.clear ();
268
269   for (int i = scratch_note_reqs_.size(); i--;)
270     {
271       scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm () );
272       
273     }
274   scratch_note_reqs_.clear();
275 }
276
277 Tie_req * tie_req = 0;
278
279 void
280 Completion_heads_engraver::start_translation_timestep ()
281 {
282   Moment now = now_mom ();
283   if (note_end_mom_.main_part_ <= now.main_part_)
284     {
285       note_req_l_arr_.clear ();
286     }
287
288   if (left_to_do_)
289     {
290       if (!tie_req)
291         tie_req = new Tie_req;
292       
293       bool succ = daddy_trans_l_->try_music (tie_req);
294       if (!succ)
295         {
296           programming_error ("Completion_heads_engraver: no-one to make tie.");
297         }
298     }
299 }
300
301 Completion_heads_engraver::Completion_heads_engraver()
302 {
303 }
304
305 ENTER_DESCRIPTION(Completion_heads_engraver,
306 /* descr */       "This engraver replaces
307 @code{Note_heads_engraver}. It plays some trickery to
308 break long notes and automatically tie them into the next measure.",
309 /* creats*/       "NoteHead Dots",
310 /* acks  */       "",
311 /* reads */       "centralCPosition measurePosition measureLength",
312 /* write */       "");