]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-engraver.cc
637a73ef32f3d36348823d34f014af687d5e6306
[lilypond.git] / lily / tie-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "engraver.hh"
21
22 #include "context.hh"
23 #include "international.hh"
24 #include "item.hh"
25 #include "note-head.hh"
26 #include "protected-scm.hh"
27 #include "spanner.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "stream-event.hh"
30 #include "tie-column.hh"
31 #include "tie.hh"
32 #include "warn.hh"
33
34 #include "translator.icc"
35
36 /**
37    Manufacture ties.  Acknowledge note heads, and put them into a
38    priority queue. If we have a TieEvent, connect the notes that finish
39    just at this time, and note that start at this time.
40
41    TODO: Remove the dependency on musical info. We should tie on the
42    basis of position and duration-log of the heads (not of the events).
43 */
44
45 struct Head_event_tuple
46 {
47   Grob *head_;
48   Moment end_moment_;
49   Stream_event *tie_stream_event_;
50   Stream_event *tie_event_;
51   Spanner *tie_;
52   // Indicate whether a tie from the same moment has been processed successfully
53   // This is needed for tied chords, e.g. <c e g>~ g, because otherwise the c
54   // and e will trigger a warning for an unterminated tie!
55   bool tie_from_chord_created;
56
57   Head_event_tuple ()
58   {
59     head_ = 0;
60     tie_event_ = 0;
61     tie_stream_event_ = 0;
62     tie_from_chord_created = false;
63     tie_ = 0;
64   }
65 };
66
67 class Tie_engraver : public Engraver
68 {
69   /*
70     Whether tie event has been processed and can be deleted or should
71     be kept for later portions of a split note.
72   */
73   bool event_processed_;
74   Stream_event *event_;
75   vector<Grob *> now_heads_;
76   vector<Head_event_tuple> heads_to_tie_;
77   vector<Grob *> ties_;
78
79   Spanner *tie_column_;
80
81 protected:
82   void process_acknowledged ();
83   void stop_translation_timestep ();
84   void start_translation_timestep ();
85   DECLARE_ACKNOWLEDGER (note_head);
86   DECLARE_TRANSLATOR_LISTENER (tie);
87   void process_music ();
88   void typeset_tie (Grob *);
89   void report_unterminated_tie (Head_event_tuple const &);
90   bool has_autosplit_end (Stream_event *event);
91 public:
92   TRANSLATOR_DECLARATIONS (Tie_engraver);
93 };
94
95 Tie_engraver::Tie_engraver ()
96 {
97   event_ = 0;
98   tie_column_ = 0;
99   event_processed_ = false;
100 }
101
102 IMPLEMENT_TRANSLATOR_LISTENER (Tie_engraver, tie);
103 void
104 Tie_engraver::listen_tie (Stream_event *ev)
105 {
106   if (!to_boolean (get_property ("skipTypesetting")))
107     {
108       ASSIGN_EVENT_ONCE (event_, ev);
109     }
110 }
111
112 void Tie_engraver::report_unterminated_tie (Head_event_tuple const &tie_start)
113 {
114   // If tie_from_chord_created is set, we have another note at the same
115   // moment that created a tie, so this is not necessarily an unterminated
116   // tie. Happens e.g. for <c e g>~ g
117   if (!tie_start.tie_from_chord_created)
118     {
119       tie_start.tie_->warning (_ ("unterminated tie"));
120       tie_start.tie_->suicide ();
121     }
122 }
123
124 /*
125   Determines whether the end of an event was created by
126   a split in Completion_heads_engraver or by user input.
127 */
128 bool
129 Tie_engraver::has_autosplit_end (Stream_event *event)
130 {
131   if (event)
132     return to_boolean (event->get_property ("autosplit-end"));
133   return false;
134 }
135
136 void
137 Tie_engraver::process_music ()
138 {
139   bool busy = event_;
140   for (vsize i = 0; !busy && i < heads_to_tie_.size (); i++)
141     busy |= (heads_to_tie_[i].tie_event_
142              || heads_to_tie_[i].tie_stream_event_);
143
144   if (busy)
145     context ()->set_property ("tieMelismaBusy", SCM_BOOL_T);
146 }
147
148 void
149 Tie_engraver::acknowledge_note_head (Grob_info i)
150 {
151   Grob *h = i.grob ();
152
153   now_heads_.push_back (h);
154   for (vsize i = heads_to_tie_.size (); i--;)
155     {
156       Grob *th = heads_to_tie_[i].head_;
157       Stream_event *right_ev = unsmob_stream_event (h->get_property ("cause"));
158       Stream_event *left_ev = unsmob_stream_event (th->get_property ("cause"));
159
160       /*
161         maybe should check positions too.
162       */
163       if (!right_ev || !left_ev)
164         continue;
165
166       /*
167         Make a tie only if pitches are equal or if event end was not generated by
168         Completion_heads_engraver.
169       */
170       if (ly_is_equal (right_ev->get_property ("pitch"), left_ev->get_property ("pitch"))
171           && (!Tie_engraver::has_autosplit_end (left_ev)))
172         {
173           Grob *p = heads_to_tie_[i].tie_;
174           Moment end = heads_to_tie_[i].end_moment_;
175
176           Stream_event *cause = heads_to_tie_[i].tie_event_
177                                     ? heads_to_tie_[i].tie_event_
178                                     : heads_to_tie_[i].tie_stream_event_;
179
180           announce_end_grob (p, cause->self_scm ());
181
182           Tie::set_head (p, LEFT, th);
183           Tie::set_head (p, RIGHT, h);
184
185           if (is_direction (cause->get_property ("direction")))
186             {
187               Direction d = to_dir (cause->get_property ("direction"));
188               p->set_property ("direction", scm_from_int (d));
189             }
190
191           ties_.push_back (p);
192           heads_to_tie_.erase (heads_to_tie_.begin () + i);
193
194           /*
195             Prevent all other tied notes ending at the same moment (assume
196             implicitly the notes have also started at the same moment!)
197             from triggering an "unterminated tie" warning. Needed e.g. for
198             <c e g>~ g
199           */
200           for (vsize j = heads_to_tie_.size (); j--;)
201             {
202               if (heads_to_tie_[j].end_moment_ == end)
203                 heads_to_tie_[j].tie_from_chord_created = true;
204             }
205         }
206     }
207
208   if (ties_.size () && ! tie_column_)
209     tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
210
211   if (tie_column_)
212     for (vsize i = ties_.size (); i--;)
213       Tie_column::add_tie (tie_column_, ties_[i]);
214 }
215
216 void
217 Tie_engraver::start_translation_timestep ()
218 {
219   if (heads_to_tie_.size () && !to_boolean (get_property ("tieWaitForNote")))
220     {
221       Moment now = now_mom ();
222       for (vsize i = heads_to_tie_.size (); i--;)
223         {
224           if (now > heads_to_tie_[i].end_moment_)
225             {
226               report_unterminated_tie (heads_to_tie_[i]);
227               heads_to_tie_.erase (heads_to_tie_.begin () + i);
228             }
229         }
230     }
231
232   context ()->set_property ("tieMelismaBusy",
233                             ly_bool2scm (heads_to_tie_.size ()));
234 }
235
236 void
237 Tie_engraver::process_acknowledged ()
238 {
239   bool wait = to_boolean (get_property ("tieWaitForNote"));
240   if (ties_.size ())
241     {
242       if (!wait)
243         {
244           vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
245           for (; it < heads_to_tie_.end (); it++)
246             report_unterminated_tie (*it);
247           heads_to_tie_.clear ();
248         }
249
250       for (vsize i = 0; i < ties_.size (); i++)
251         typeset_tie (ties_[i]);
252
253       ties_.clear ();
254       tie_column_ = 0;
255     }
256
257   vector<Head_event_tuple> new_heads_to_tie;
258
259
260   for (vsize i = 0; i < now_heads_.size (); i++)
261     {
262       Grob *head = now_heads_[i];
263       Stream_event *left_ev
264         = unsmob_stream_event (head->get_property ("cause"));
265
266       if (!left_ev)
267         {
268           // may happen for ambitus
269           continue;
270         }
271
272       // We only want real notes to cause ties, not e.g. pitched trills
273       if (!left_ev->in_event_class ("note-event"))
274         continue;
275
276       SCM left_articulations = left_ev->get_property ("articulations");
277
278       Stream_event *tie_event = 0;
279       Stream_event *tie_stream_event = event_;
280       for (SCM s = left_articulations;
281            !tie_event && !tie_stream_event && scm_is_pair (s);
282            s = scm_cdr (s))
283         {
284           Stream_event *ev = unsmob_stream_event (scm_car (s));
285           if (!ev)
286             continue;
287
288           if (ev->in_event_class ("tie-event"))
289             tie_event = ev;
290         }
291
292       if (left_ev && (tie_event || tie_stream_event)
293           && (!Tie_engraver::has_autosplit_end (left_ev)))
294         {
295           event_processed_ = true;
296
297           Head_event_tuple event_tup;
298
299           event_tup.head_ = head;
300           event_tup.tie_event_ = tie_event;
301           event_tup.tie_stream_event_ = tie_stream_event;
302           event_tup.tie_ = make_spanner ("Tie", tie_event
303                                     ? tie_event->self_scm ()
304                                     : tie_stream_event->self_scm ());
305
306           Moment end = now_mom ();
307           if (end.grace_part_)
308             {
309               end.grace_part_ += get_event_length (left_ev).main_part_;
310             }
311           else
312             {
313               end += get_event_length (left_ev);
314             }
315           event_tup.end_moment_ = end;
316
317           new_heads_to_tie.push_back (event_tup);
318         }
319     }
320
321   if (!wait && new_heads_to_tie.size ())
322     {
323       vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
324       for (; it < heads_to_tie_.end (); it++)
325         report_unterminated_tie (*it);
326       heads_to_tie_.clear ();
327     }
328
329   // hmmm, how to do with copy () ?
330   for (vsize i = 0; i < new_heads_to_tie.size (); i++)
331     heads_to_tie_.push_back (new_heads_to_tie[i]);
332
333   now_heads_.clear ();
334 }
335
336 void
337 Tie_engraver::stop_translation_timestep ()
338 {
339   /*
340     Discard event only if it has been processed with at least one
341     appropriate note.
342   */
343   if (event_processed_)
344     event_ = 0;
345
346   event_processed_ = false;
347 }
348
349 void
350 Tie_engraver::typeset_tie (Grob *her)
351 {
352   if (! (Tie::head (her, LEFT) && Tie::head (her, RIGHT)))
353     warning (_ ("lonely tie"));
354
355   Drul_array<Grob *> new_head_drul;
356   new_head_drul[LEFT] = Tie::head (her, LEFT);
357   new_head_drul[RIGHT] = Tie::head (her, RIGHT);
358   for (LEFT_and_RIGHT (d))
359     {
360       if (!Tie::head (her, d))
361         new_head_drul[d] = Tie::head (her, (Direction) - d);
362     }
363
364   Spanner *sp = dynamic_cast<Spanner *> (her);
365   sp->set_bound (LEFT, new_head_drul[LEFT]);
366   sp->set_bound (RIGHT, new_head_drul[RIGHT]);
367 }
368
369 ADD_ACKNOWLEDGER (Tie_engraver, note_head);
370 ADD_TRANSLATOR (Tie_engraver,
371                 /* doc */
372                 "Generate ties between note heads of equal pitch.",
373
374                 /* create */
375                 "Tie "
376                 "TieColumn ",
377
378                 /* read */
379                 "skipTypesetting "
380                 "tieWaitForNote ",
381
382                 /* write */
383                 "tieMelismaBusy "
384                );