]> 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           if (is_direction (unsmob_stream_event (cause)->get_property ("direction")))
181             {
182               Direction d = to_dir (unsmob_stream_event (cause)->get_property ("direction"));
183               p->set_property ("direction", scm_from_int (d));
184             }
185
186           ties_.push_back (p);
187           heads_to_tie_.erase (heads_to_tie_.begin () + i);
188
189           /*
190             Prevent all other tied notes ending at the same moment (assume
191             implicitly the notes have also started at the same moment!)
192             from triggering an "unterminated tie" warning. Needed e.g. for
193             <c e g>~ g
194           */
195           for (vsize j = heads_to_tie_.size (); j--;)
196             {
197               if (heads_to_tie_[j].end_moment_ == end)
198                 heads_to_tie_[j].tie_from_chord_created = true;
199             }
200         }
201     }
202
203   if (ties_.size () && ! tie_column_)
204     tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
205
206   if (tie_column_)
207     for (vsize i = ties_.size (); i--;)
208       Tie_column::add_tie (tie_column_, ties_[i]);
209 }
210
211 void
212 Tie_engraver::start_translation_timestep ()
213 {
214   if (heads_to_tie_.size () && !to_boolean (get_property ("tieWaitForNote")))
215     {
216       Moment now = now_mom ();
217       for (vsize i = heads_to_tie_.size (); i--;)
218         {
219           if (now > heads_to_tie_[i].end_moment_)
220             {
221               report_unterminated_tie (heads_to_tie_[i]);
222               heads_to_tie_.erase (heads_to_tie_.begin () + i);
223             }
224         }
225     }
226
227   context ()->set_property ("tieMelismaBusy",
228                             ly_bool2scm (heads_to_tie_.size ()));
229 }
230
231 void
232 Tie_engraver::stop_translation_timestep ()
233 {
234   bool wait = to_boolean (get_property ("tieWaitForNote"));
235   if (ties_.size ())
236     {
237       if (!wait)
238         {
239           vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
240           for (; it < heads_to_tie_.end (); it++)
241             report_unterminated_tie (*it);
242           heads_to_tie_.clear ();
243         }
244
245       for (vsize i = 0; i < ties_.size (); i++)
246         typeset_tie (ties_[i]);
247
248       ties_.clear ();
249       tie_column_ = 0;
250     }
251
252   vector<Head_event_tuple> new_heads_to_tie;
253
254   /*
255     Whether tie event has been processed and can be deleted or should
256     be kept for later portions of a split note.
257   */
258   bool event_processed = false;
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           SCM start_definition
300             = updated_grob_properties (context (), ly_symbol2scm ("Tie"));
301
302           event_tup.head_ = head;
303           event_tup.tie_definition_ = start_definition;
304           event_tup.tie_event_ = tie_event;
305           event_tup.tie_stream_event_ = tie_stream_event;
306
307           Moment end = now_mom ();
308           if (end.grace_part_)
309             {
310               end.grace_part_ += get_event_length (left_ev).main_part_;
311             }
312           else
313             {
314               end += get_event_length (left_ev);
315             }
316           event_tup.end_moment_ = end;
317
318           new_heads_to_tie.push_back (event_tup);
319         }
320     }
321
322   if (!wait && new_heads_to_tie.size ())
323     {
324       vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
325       for (; it < heads_to_tie_.end (); it++)
326         report_unterminated_tie (*it);
327       heads_to_tie_.clear ();
328     }
329
330   // hmmm, how to do with copy () ?
331   for (vsize i = 0; i < new_heads_to_tie.size (); i++)
332     heads_to_tie_.push_back (new_heads_to_tie[i]);
333
334   /*
335     Discard event only if it has been processed with at least one
336     appropriate note.
337   */
338   if (event_processed)
339     event_ = 0;
340
341   now_heads_.clear ();
342 }
343
344 void
345 Tie_engraver::typeset_tie (Grob *her)
346 {
347   if (! (Tie::head (her, LEFT) && Tie::head (her, RIGHT)))
348     warning (_ ("lonely tie"));
349
350   Direction d = LEFT;
351   Drul_array<Grob *> new_head_drul;
352   new_head_drul[LEFT] = Tie::head (her, LEFT);
353   new_head_drul[RIGHT] = Tie::head (her, RIGHT);
354   do
355     {
356       if (!Tie::head (her, d))
357         new_head_drul[d] = Tie::head (her, (Direction) - d);
358     }
359   while (flip (&d) != LEFT);
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                );