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