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