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