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