]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-engraver.cc
restore regression tests for time-signature styles; issue 4176
[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<Grob *> 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   DECLARE_ACKNOWLEDGER (note_head);
88   DECLARE_TRANSLATOR_LISTENER (tie);
89   void process_music ();
90   void typeset_tie (Grob *);
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 IMPLEMENT_TRANSLATOR_LISTENER (Tie_engraver, tie);
105 void
106 Tie_engraver::listen_tie (Stream_event *ev)
107 {
108   if (!to_boolean (get_property ("skipTypesetting")))
109     {
110       ASSIGN_EVENT_ONCE (event_, ev);
111     }
112 }
113
114 void Tie_engraver::report_unterminated_tie (Head_event_tuple const &tie_start)
115 {
116   // If tie_from_chord_created is set, we have another note at the same
117   // moment that created a tie, so this is not necessarily an unterminated
118   // tie. Happens e.g. for <c e g>~ g
119   if (!tie_start.tie_from_chord_created)
120     {
121       tie_start.tie_->warning (_ ("unterminated tie"));
122       tie_start.tie_->suicide ();
123     }
124 }
125
126 /*
127   Determines whether the end of an event was created by
128   a split in Completion_heads_engraver or by user input.
129 */
130 bool
131 Tie_engraver::has_autosplit_end (Stream_event *event)
132 {
133   if (event)
134     return to_boolean (event->get_property ("autosplit-end"));
135   return false;
136 }
137
138 void
139 Tie_engraver::process_music ()
140 {
141   bool busy = event_;
142   for (vsize i = 0; !busy && i < heads_to_tie_.size (); i++)
143     busy |= (heads_to_tie_[i].tie_event_
144              || heads_to_tie_[i].tie_stream_event_);
145
146   if (busy)
147     context ()->set_property ("tieMelismaBusy", SCM_BOOL_T);
148 }
149
150 bool
151 Tie_engraver::tie_notehead (Grob *h, bool enharmonic)
152 {
153   bool found = false;
154
155   for (vsize i = 0; i < heads_to_tie_.size (); i++)
156     {
157       Grob *th = heads_to_tie_[i].head_;
158       Stream_event *right_ev = Stream_event::unsmob (h->get_property ("cause"));
159       Stream_event *left_ev = Stream_event::unsmob (th->get_property ("cause"));
160
161       /*
162         maybe should check positions too.
163       */
164       if (!right_ev || !left_ev)
165         continue;
166
167       /*
168         Make a tie only if pitches are equal or if event end was not generated by
169         Completion_heads_engraver.
170       */
171       SCM p1 = left_ev->get_property ("pitch");
172       SCM p2 = right_ev->get_property ("pitch");
173       if ((enharmonic
174            ? (Pitch::is_smob (p1) && Pitch::is_smob (p2) &&
175               Pitch::unsmob (p1)->tone_pitch () == Pitch::unsmob (p2)->tone_pitch ())
176            : ly_is_equal (p1, p2))
177           && (!Tie_engraver::has_autosplit_end (left_ev)))
178         {
179           Grob *p = heads_to_tie_[i].tie_;
180           Moment end = heads_to_tie_[i].end_moment_;
181
182           Stream_event *cause = heads_to_tie_[i].tie_event_
183                                     ? heads_to_tie_[i].tie_event_
184                                     : heads_to_tie_[i].tie_stream_event_;
185
186           announce_end_grob (p, cause->self_scm ());
187
188           Tie::set_head (p, LEFT, th);
189           Tie::set_head (p, RIGHT, h);
190
191           if (is_direction (cause->get_property ("direction")))
192             {
193               Direction d = to_dir (cause->get_property ("direction"));
194               p->set_property ("direction", scm_from_int (d));
195             }
196
197           ties_.push_back (p);
198           heads_to_tie_.erase (heads_to_tie_.begin () + i);
199
200           found = true;
201           /*
202             Prevent all other tied notes ending at the same moment (assume
203             implicitly the notes have also started at the same moment!)
204             from triggering an "unterminated tie" warning. Needed e.g. for
205             <c e g>~ g
206           */
207           for (vsize j = heads_to_tie_.size (); j--;)
208             {
209               if (heads_to_tie_[j].end_moment_ == end)
210                 heads_to_tie_[j].tie_from_chord_created = true;
211             }
212           break;
213         }
214     }
215   return found;
216 }
217
218 void
219 Tie_engraver::acknowledge_note_head (Grob_info i)
220 {
221   Grob *h = i.grob ();
222
223   now_heads_.push_back (h);
224
225   if (!tie_notehead (h, false))
226     tie_notehead (h, true);
227
228   if (ties_.size () && ! tie_column_)
229     tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
230
231   if (tie_column_)
232     for (vsize i = 0; i < ties_.size (); i++)
233       Tie_column::add_tie (tie_column_, ties_[i]);
234 }
235
236 void
237 Tie_engraver::start_translation_timestep ()
238 {
239   if (heads_to_tie_.size () && !to_boolean (get_property ("tieWaitForNote")))
240     {
241       Moment now = now_mom ();
242       for (vsize i = heads_to_tie_.size (); i--;)
243         {
244           if (now > heads_to_tie_[i].end_moment_)
245             {
246               report_unterminated_tie (heads_to_tie_[i]);
247               heads_to_tie_.erase (heads_to_tie_.begin () + i);
248             }
249         }
250     }
251
252   context ()->set_property ("tieMelismaBusy",
253                             ly_bool2scm (heads_to_tie_.size ()));
254 }
255
256 void
257 Tie_engraver::process_acknowledged ()
258 {
259   bool wait = to_boolean (get_property ("tieWaitForNote"));
260   if (ties_.size ())
261     {
262       if (!wait)
263         {
264           vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
265           for (; it < heads_to_tie_.end (); it++)
266             report_unterminated_tie (*it);
267           heads_to_tie_.clear ();
268         }
269
270       for (vsize i = 0; i < ties_.size (); i++)
271         typeset_tie (ties_[i]);
272
273       ties_.clear ();
274       tie_column_ = 0;
275     }
276
277   vector<Head_event_tuple> new_heads_to_tie;
278
279
280   for (vsize i = 0; i < now_heads_.size (); i++)
281     {
282       Grob *head = now_heads_[i];
283       Stream_event *left_ev
284         = Stream_event::unsmob (head->get_property ("cause"));
285
286       if (!left_ev)
287         {
288           // may happen for ambitus
289           continue;
290         }
291
292       // We only want real notes to cause ties, not e.g. pitched trills
293       if (!left_ev->in_event_class ("note-event"))
294         continue;
295
296       SCM left_articulations = left_ev->get_property ("articulations");
297
298       Stream_event *tie_event = 0;
299       Stream_event *tie_stream_event = event_;
300       for (SCM s = left_articulations;
301            !tie_event && !tie_stream_event && scm_is_pair (s);
302            s = scm_cdr (s))
303         {
304           Stream_event *ev = Stream_event::unsmob (scm_car (s));
305           if (!ev)
306             continue;
307
308           if (ev->in_event_class ("tie-event"))
309             tie_event = ev;
310         }
311
312       if (left_ev && (tie_event || tie_stream_event)
313           && (!Tie_engraver::has_autosplit_end (left_ev)))
314         {
315           event_processed_ = true;
316
317           Head_event_tuple event_tup;
318
319           event_tup.head_ = head;
320           event_tup.tie_event_ = tie_event;
321           event_tup.tie_stream_event_ = tie_stream_event;
322           event_tup.tie_ = make_spanner ("Tie", tie_event
323                                     ? tie_event->self_scm ()
324                                     : tie_stream_event->self_scm ());
325
326           Moment end = now_mom ();
327           if (end.grace_part_)
328             {
329               end.grace_part_ += get_event_length (left_ev).main_part_;
330             }
331           else
332             {
333               end += get_event_length (left_ev);
334             }
335           event_tup.end_moment_ = end;
336
337           new_heads_to_tie.push_back (event_tup);
338         }
339     }
340
341   if (!wait && new_heads_to_tie.size ())
342     {
343       vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
344       for (; it < heads_to_tie_.end (); it++)
345         report_unterminated_tie (*it);
346       heads_to_tie_.clear ();
347     }
348
349   // hmmm, how to do with copy () ?
350   for (vsize i = 0; i < new_heads_to_tie.size (); i++)
351     heads_to_tie_.push_back (new_heads_to_tie[i]);
352
353   now_heads_.clear ();
354 }
355
356 void
357 Tie_engraver::stop_translation_timestep ()
358 {
359   /*
360     Discard event only if it has been processed with at least one
361     appropriate note.
362   */
363   if (event_processed_)
364     event_ = 0;
365
366   event_processed_ = false;
367 }
368
369 void
370 Tie_engraver::typeset_tie (Grob *her)
371 {
372   if (! (Tie::head (her, LEFT) && Tie::head (her, RIGHT)))
373     warning (_ ("lonely tie"));
374
375   Drul_array<Grob *> new_head_drul;
376   new_head_drul[LEFT] = Tie::head (her, LEFT);
377   new_head_drul[RIGHT] = Tie::head (her, RIGHT);
378   for (LEFT_and_RIGHT (d))
379     {
380       if (!Tie::head (her, d))
381         new_head_drul[d] = Tie::head (her, (Direction) - d);
382     }
383
384   Spanner *sp = dynamic_cast<Spanner *> (her);
385   sp->set_bound (LEFT, new_head_drul[LEFT]);
386   sp->set_bound (RIGHT, new_head_drul[RIGHT]);
387 }
388
389 ADD_ACKNOWLEDGER (Tie_engraver, note_head);
390 ADD_TRANSLATOR (Tie_engraver,
391                 /* doc */
392                 "Generate ties between note heads of equal pitch.",
393
394                 /* create */
395                 "Tie "
396                 "TieColumn ",
397
398                 /* read */
399                 "skipTypesetting "
400                 "tieWaitForNote ",
401
402                 /* write */
403                 "tieMelismaBusy "
404                );