]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-engraver.cc
patch::: 1.5.14.jcn1
[lilypond.git] / lily / tie-engraver.cc
1 /*   
2   tie-engraver.cc --  implement Tie_engraver
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 1998--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include "command-request.hh"
11 #include "rhythmic-head.hh"
12 #include "musical-request.hh"
13 #include "tie.hh"
14 #include "translator-group.hh"
15 #include "spanner.hh"
16 #include "tie-column.hh"
17 #include "pqueue.hh"
18 #include "engraver.hh"
19 #include "item.hh"
20
21 struct CHead_melodic_tuple {
22   Melodic_req *req_l_ ;
23   Grob *head_l_;
24   Moment end_;
25   CHead_melodic_tuple ();
26   CHead_melodic_tuple (Grob*, Melodic_req*, Moment);
27   static int pitch_compare (CHead_melodic_tuple const &, CHead_melodic_tuple const &);
28   static int time_compare (CHead_melodic_tuple const &, CHead_melodic_tuple const &);  
29 };
30
31 inline int compare (CHead_melodic_tuple const &a, CHead_melodic_tuple const &b)
32 {
33   return CHead_melodic_tuple::time_compare (a,b);
34 }
35
36
37 /**
38    Manufacture ties.  Acknowledge noteheads, and put them into a
39    priority queue. If we have a Tie_req, connect the notes that finish
40    just at this time, and note that start at this time.
41
42    TODO: junk the pq; the PQ is overkill if we assume that no
43    different durations occur in parallel.
44 */
45 class Tie_engraver : public Engraver
46 {
47   PQueue<CHead_melodic_tuple> past_notes_pq_;
48   Moment end_mom_;
49   Moment next_end_mom_;
50
51   Tie_req *req_l_;
52   Array<CHead_melodic_tuple> now_heads_;
53   Array<CHead_melodic_tuple> stopped_heads_;
54   Link_array<Grob> tie_p_arr_;
55
56   Spanner * tie_column_p_;
57   
58   void set_melisma (bool);
59   
60 protected:
61   virtual void start_translation_timestep ();
62   virtual void stop_translation_timestep ();
63   virtual void acknowledge_grob (Grob_info);
64   virtual bool try_music (Music*);
65   virtual void create_grobs ();
66   void typeset_tie (Grob*);
67 public:
68   TRANSLATOR_DECLARATIONS(Tie_engraver);
69 };
70
71
72
73 Tie_engraver::Tie_engraver ()
74 {
75   req_l_ = 0;
76   tie_column_p_ = 0;
77 }
78
79
80 bool
81 Tie_engraver::try_music (Music *m)
82 {
83   if (Tie_req * c = dynamic_cast<Tie_req*> (m))
84     {
85       /*      if (end_mom_ > now_mom ())
86        return false;
87       */
88       req_l_ = c;
89       SCM m = get_property ("automaticMelismata");
90       bool am = gh_boolean_p (m) &&gh_scm2bool (m);
91       if (am)
92         {
93           set_melisma (true);
94         }
95       return true;
96     }
97   return false;
98 }
99
100 void
101 Tie_engraver::set_melisma (bool m)
102 {
103   daddy_trans_l_->set_property ("tieMelismaBusy", m ? SCM_BOOL_T : SCM_BOOL_F);
104 }
105
106 void
107 Tie_engraver::acknowledge_grob (Grob_info i)
108 {
109   if (Rhythmic_head::has_interface (i.grob_l_))
110     {
111       Note_req * m = dynamic_cast<Note_req* > (i.req_l_);
112       if (!m)
113         return;
114       now_heads_.push (CHead_melodic_tuple (i.grob_l_, m, now_mom ()+ m->length_mom ()));
115     }
116 }
117
118
119 void
120 Tie_engraver::create_grobs ()
121 {
122   if (req_l_)
123     {
124       now_heads_.sort (CHead_melodic_tuple::pitch_compare);
125       stopped_heads_.sort (CHead_melodic_tuple::pitch_compare);
126
127       SCM head_list = SCM_EOL;
128       
129       int j = stopped_heads_.size ()-1;
130       int i = now_heads_.size ()-1;
131
132       while (i >= 0 && j >=0)
133         {
134           int comp
135             = Pitch::compare (*unsmob_pitch (now_heads_[i].req_l_->get_mus_property ("pitch")),
136                                       *unsmob_pitch (stopped_heads_[j].req_l_->get_mus_property ("pitch")));
137
138           if (comp)
139             {
140  (comp < 0) ? j -- : i--;
141               continue;
142             }
143           else
144             {
145               head_list  = gh_cons (gh_cons (stopped_heads_[j].head_l_->self_scm (),
146                                              now_heads_[i].head_l_->self_scm ()),
147                                     head_list);
148
149               past_notes_pq_. insert (now_heads_[i]);
150               now_heads_.del (i);
151               stopped_heads_.del (j);
152               i--;
153               j--;
154             }
155         }
156
157       SCM basic = get_property ("Tie");
158       SCM sparse = get_property ("sparseTies");
159       if (to_boolean (sparse))
160         {
161           int i = scm_ilength (head_list);
162
163           if (!i)
164             return;
165           
166           SCM pair = scm_list_ref (head_list, gh_int2scm (i/2));
167           
168           Spanner * p = new Spanner (basic);
169
170           Tie::set_interface (p);
171           Tie::set_head (p,LEFT, dynamic_cast<Item*> (unsmob_grob (ly_car (pair))));
172           Tie::set_head (p,RIGHT, dynamic_cast<Item*> (unsmob_grob (ly_cdr (pair))));
173           
174           tie_p_arr_.push (p);
175           announce_grob (p, req_l_);
176         }
177       else for (SCM s = head_list; gh_pair_p (s); s = ly_cdr (s))
178         {
179           Grob * p = new Spanner (basic);
180           Tie::set_interface (p);
181           
182           Tie::set_head (p, LEFT, dynamic_cast<Item*> (unsmob_grob (ly_caar (s))));
183           Tie::set_head (p, RIGHT, dynamic_cast<Item*> (unsmob_grob (ly_cdar (s))));
184           
185           tie_p_arr_.push (p);
186           announce_grob (p, req_l_);
187         }
188
189       if (tie_p_arr_.size () > 1 && !tie_column_p_)
190         {
191           tie_column_p_ = new Spanner (get_property ("TieColumn"));
192           Tie_column::set_interface (tie_column_p_);
193           for (int i = tie_p_arr_.size (); i--;)
194             Tie_column::add_tie (tie_column_p_,tie_p_arr_ [i]);
195           announce_grob (tie_column_p_, 0);
196         }
197     }
198 }
199
200
201 void
202 Tie_engraver::stop_translation_timestep ()
203 {
204   for (int i=0; i < now_heads_.size (); i++)
205     {
206       past_notes_pq_.insert (now_heads_[i]);
207     }
208   now_heads_.clear ();
209
210   /*
211     we don't warn for no ties, since this happens naturally when you
212     use skipTypesetting.  */
213   
214 #if 0
215   if (req_l_ && !tie_p_arr_.size ())
216     {
217       /* How to shut up this warning, when no notes appeared because
218          they were suicided by Thread_devnull_engraver? */
219       req_l_->origin ()->warning (_ ("No ties were created!"));
220     }
221 #endif
222   
223   for (int i=0; i<  tie_p_arr_.size (); i++)
224    {
225       typeset_tie (tie_p_arr_[i]);
226     }
227   tie_p_arr_.clear ();
228   if (tie_column_p_)
229     {
230       typeset_grob (tie_column_p_);
231       tie_column_p_ =0;
232     }
233 }
234
235 void
236 Tie_engraver::typeset_tie (Grob *her)
237 {
238   if (! (Tie::head (her,LEFT) && Tie::head (her,RIGHT)))
239     warning (_ ("lonely tie"));
240
241   Direction d = LEFT;
242   Drul_array<Grob *> new_head_drul;
243   new_head_drul[LEFT] = Tie::head (her,LEFT);
244   new_head_drul[RIGHT] = Tie::head (her,RIGHT);  
245   do {
246     if (!Tie::head (her,d))
247       new_head_drul[d] = Tie::head (her, (Direction)-d);
248   } while (flip (&d) != LEFT);
249
250   index_set_cell (her->get_grob_property ("heads"), LEFT, new_head_drul[LEFT]->self_scm ());
251   index_set_cell (her->get_grob_property ("heads"), RIGHT, new_head_drul[RIGHT]->self_scm ());
252
253   typeset_grob (her);
254 }
255
256 void
257 Tie_engraver::start_translation_timestep ()
258 {
259   SCM m = get_property ("automaticMelismata");
260   if (to_boolean (m))
261     {
262       set_melisma (false);
263     }
264   req_l_ = 0;
265   Moment now = now_mom ();
266   while (past_notes_pq_.size () && past_notes_pq_.front ().end_ < now)
267     past_notes_pq_.delmin ();
268
269
270   stopped_heads_.clear ();
271   while (past_notes_pq_.size ()
272          && past_notes_pq_.front ().end_ == now)
273     stopped_heads_.push (past_notes_pq_.get ());
274
275 }
276
277
278
279
280 CHead_melodic_tuple::CHead_melodic_tuple ()
281 {
282   head_l_ =0;
283   req_l_ =0;
284   end_ = 0;
285 }
286
287 CHead_melodic_tuple::CHead_melodic_tuple (Grob *h, Melodic_req*m, Moment mom)
288 {
289   head_l_ = h;
290   req_l_ = m;
291   end_ = mom;
292 }
293
294 /*
295   signed compare, should use pitch<? 
296  */
297 int
298 CHead_melodic_tuple::pitch_compare (CHead_melodic_tuple const&h1,
299                              CHead_melodic_tuple const &h2)
300 {
301   SCM p1  = h1.req_l_->get_mus_property ("pitch");
302   SCM p2  = h2.req_l_->get_mus_property ("pitch");
303   
304   return Pitch::compare (*unsmob_pitch (p1),
305                          *unsmob_pitch (p2));
306 }
307
308 int
309 CHead_melodic_tuple::time_compare (CHead_melodic_tuple const&h1,
310                                    CHead_melodic_tuple const &h2)
311 {
312   return Moment::compare(h1.end_,  h2.end_);
313 }
314 ENTER_DESCRIPTION(Tie_engraver,
315 /* descr */       "Generate ties between noteheads of equal pitch.",
316 /* creats*/       "Tie TieColumn",
317 /* acks  */       "rhythmic-head-interface",
318 /* reads */       "sparseTies tieMelismaBusy",
319 /* write */       "");