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