]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-engraver.cc
release: 1.5.24
[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 #include "grob-pitch-tuple.hh"
21
22
23 /**
24    Manufacture ties.  Acknowledge noteheads, and put them into a
25    priority queue. If we have a Tie_req, connect the notes that finish
26    just at this time, and note that start at this time.
27
28    TODO: junk the pq; the PQ is overkill if we assume that no
29    different durations occur in parallel.
30
31    TODO: Remove the dependency on musical info. We should tie on the
32    basis of position and duration-log of the heads (not of the reqs).
33
34 */
35 class Tie_engraver : public Engraver
36 {
37   PQueue<Grob_pitch_tuple> past_notes_pq_;
38   Moment end_mom_;
39   Moment next_end_mom_;
40
41   Tie_req *req_l_;
42   Array<Grob_pitch_tuple> now_heads_;
43   Array<Grob_pitch_tuple> stopped_heads_;
44   Link_array<Grob> tie_p_arr_;
45
46   Spanner * tie_column_p_;
47   
48   void set_melisma (bool);
49   
50 protected:
51   virtual void start_translation_timestep ();
52   virtual void stop_translation_timestep ();
53   virtual void acknowledge_grob (Grob_info);
54   virtual bool try_music (Music*);
55   virtual void create_grobs ();
56   void typeset_tie (Grob*);
57 public:
58   TRANSLATOR_DECLARATIONS(Tie_engraver);
59 };
60
61
62
63 Tie_engraver::Tie_engraver ()
64 {
65   req_l_ = 0;
66   tie_column_p_ = 0;
67 }
68
69
70 bool
71 Tie_engraver::try_music (Music *m)
72 {
73   if (Tie_req * c = dynamic_cast<Tie_req*> (m))
74     {
75       /*      if (end_mom_ > now_mom ())
76        return false;
77       */
78       req_l_ = c;
79       SCM m = get_property ("automaticMelismata");
80       bool am = gh_boolean_p (m) &&gh_scm2bool (m);
81       if (am)
82         {
83           set_melisma (true);
84         }
85       return true;
86     }
87   return false;
88 }
89
90 void
91 Tie_engraver::set_melisma (bool m)
92 {
93   daddy_trans_l_->set_property ("tieMelismaBusy", m ? SCM_BOOL_T : SCM_BOOL_F);
94 }
95
96 void
97 Tie_engraver::acknowledge_grob (Grob_info i)
98 {
99   if (Rhythmic_head::has_interface (i.grob_l_))
100     {
101       Note_req * m = dynamic_cast<Note_req* > (i.music_cause ());
102       if (!m)
103         return;
104       now_heads_.push (Grob_pitch_tuple (i.grob_l_, m, now_mom () + m->length_mom ()));
105     }
106 }
107
108
109 void
110 Tie_engraver::create_grobs ()
111 {
112   if (req_l_)
113     {
114       now_heads_.sort (Grob_pitch_tuple::pitch_compare);
115       stopped_heads_.sort (Grob_pitch_tuple::pitch_compare);
116
117       SCM head_list = SCM_EOL;
118       
119       int j = stopped_heads_.size ()-1;
120       int i = now_heads_.size ()-1;
121
122       while (i >= 0 && j >=0)
123         {
124           int comp
125             = Pitch::compare (now_heads_[i].pitch_,
126                               stopped_heads_[j].pitch_);
127
128           if (comp)
129             {
130  (comp < 0) ? j -- : i--;
131               continue;
132             }
133           else
134             {
135               head_list  = gh_cons (gh_cons (stopped_heads_[j].head_l_->self_scm (),
136                                              now_heads_[i].head_l_->self_scm ()),
137                                     head_list);
138
139               past_notes_pq_. insert (now_heads_[i]);
140               now_heads_.del (i);
141               stopped_heads_.del (j);
142               i--;
143               j--;
144             }
145         }
146
147       SCM basic = get_property ("Tie");
148       SCM sparse = get_property ("sparseTies");
149       if (to_boolean (sparse))
150         {
151           int i = scm_ilength (head_list);
152
153           if (!i)
154             return;
155           
156           SCM pair = scm_list_ref (head_list, gh_int2scm (i/2));
157           
158           Spanner * p = new Spanner (basic);
159
160           Tie::set_interface (p);
161           Tie::set_head (p,LEFT, dynamic_cast<Item*> (unsmob_grob (ly_car (pair))));
162           Tie::set_head (p,RIGHT, dynamic_cast<Item*> (unsmob_grob (ly_cdr (pair))));
163           
164           tie_p_arr_.push (p);
165           announce_grob (p, req_l_);
166         }
167       else for (SCM s = head_list; gh_pair_p (s); s = ly_cdr (s))
168         {
169           Grob * p = new Spanner (basic);
170           Tie::set_interface (p);
171           
172           Tie::set_head (p, LEFT, dynamic_cast<Item*> (unsmob_grob (ly_caar (s))));
173           Tie::set_head (p, RIGHT, dynamic_cast<Item*> (unsmob_grob (ly_cdar (s))));
174           
175           tie_p_arr_.push (p);
176           announce_grob (p, req_l_);
177         }
178
179       if (tie_p_arr_.size () > 1 && !tie_column_p_)
180         {
181           tie_column_p_ = new Spanner (get_property ("TieColumn"));
182           Tie_column::set_interface (tie_column_p_);
183           for (int i = tie_p_arr_.size (); i--;)
184             Tie_column::add_tie (tie_column_p_,tie_p_arr_ [i]);
185           announce_grob (tie_column_p_, 0);
186         }
187     }
188 }
189
190
191 void
192 Tie_engraver::stop_translation_timestep ()
193 {
194   req_l_ = 0;
195   for (int i=0; i < now_heads_.size (); i++)
196     {
197       past_notes_pq_.insert (now_heads_[i]);
198     }
199   now_heads_.clear ();
200
201   /*
202     we don't warn for no ties, since this happens naturally when you
203     use skipTypesetting.  */
204   
205   for (int i=0; i<  tie_p_arr_.size (); i++)
206    {
207       typeset_tie (tie_p_arr_[i]);
208     }
209   tie_p_arr_.clear ();
210   if (tie_column_p_)
211     {
212       typeset_grob (tie_column_p_);
213       tie_column_p_ =0;
214     }
215 }
216
217 void
218 Tie_engraver::typeset_tie (Grob *her)
219 {
220   if (! (Tie::head (her,LEFT) && Tie::head (her,RIGHT)))
221     warning (_ ("lonely tie"));
222
223   Direction d = LEFT;
224   Drul_array<Grob *> new_head_drul;
225   new_head_drul[LEFT] = Tie::head (her,LEFT);
226   new_head_drul[RIGHT] = Tie::head (her,RIGHT);  
227   do {
228     if (!Tie::head (her,d))
229       new_head_drul[d] = Tie::head (her, (Direction)-d);
230   } while (flip (&d) != LEFT);
231
232   index_set_cell (her->get_grob_property ("heads"), LEFT, new_head_drul[LEFT]->self_scm ());
233   index_set_cell (her->get_grob_property ("heads"), RIGHT, new_head_drul[RIGHT]->self_scm ());
234
235   typeset_grob (her);
236 }
237
238 void
239 Tie_engraver::start_translation_timestep ()
240 {
241   SCM m = get_property ("automaticMelismata");
242   if (to_boolean (m))
243     {
244       set_melisma (false);
245     }
246
247   Moment now = now_mom ();
248   while (past_notes_pq_.size () && past_notes_pq_.front ().end_ < now)
249     past_notes_pq_.delmin ();
250
251
252   stopped_heads_.clear ();
253   while (past_notes_pq_.size ()
254          && past_notes_pq_.front ().end_ == now)
255     stopped_heads_.push (past_notes_pq_.get ());
256
257 }
258
259
260 ENTER_DESCRIPTION(Tie_engraver,
261 /* descr */       "Generate ties between noteheads of equal pitch.",
262 /* creats*/       "Tie TieColumn",
263 /* acks  */       "rhythmic-head-interface",
264 /* reads */       "sparseTies tieMelismaBusy",
265 /* write */       "");