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