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