]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
Issue 4429: fix timekeeping in Auto_change_iterator and Part_combine_iterator
[lilypond.git] / lily / part-combine-iterator.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2004--2015 Han-Wen Nienhuys
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 "context.hh"
21 #include "dispatcher.hh"
22 #include "lily-guile.hh"
23 #include "music.hh"
24 #include "music-iterator.hh"
25 #include "music-sequence.hh"
26 #include "warn.hh"
27
28 static const char *const CONTEXT_ONE = "one";
29 static const char *const CONTEXT_TWO = "two";
30 static const char *const CONTEXT_SHARED = "shared";
31 static const char *const CONTEXT_SOLO = "solo";
32 static const char *const CONTEXT_NULL = "null";
33
34 class Part_combine_iterator : public Music_iterator
35 {
36 public:
37   Part_combine_iterator ();
38
39   DECLARE_SCHEME_CALLBACK (constructor, ());
40 protected:
41   virtual void derived_substitute (Context *f, Context *t);
42   virtual void derived_mark () const;
43
44   virtual void construct_children ();
45   virtual Moment pending_moment () const;
46   virtual void do_quit ();
47   virtual void process (Moment);
48
49   virtual bool ok () const;
50
51 private:
52   static const size_t NUM_PARTS = 2;
53   Music_iterator *iterators_[NUM_PARTS];
54
55   SCM split_list_;
56
57   Stream_event *mmrest_event_;
58
59   enum Status
60   {
61     INITIAL,
62     APART,
63     TOGETHER,
64     SOLO,
65     UNISONO,
66     UNISILENCE,
67   };
68   Status state_;
69
70   // For states in which it matters, this is the relevant part,
71   // e.g. 1 for Solo I, 2 for Solo II.
72   int chosen_part_;
73
74   void substitute_one (Music_iterator *iter, const char *voice_id);
75   void substitute_both (const char *part1_voice_id, const char *part2_voice_id);
76   bool is_active_outlet (const Context *c) const;
77   void kill_mmrest (Context *c);
78   void chords_together ();
79   void solo1 ();
80   void solo2 ();
81   void apart ();
82   void unisono (bool silent, int newpart);
83 };
84
85 const size_t Part_combine_iterator::NUM_PARTS;
86
87 void
88 Part_combine_iterator::do_quit ()
89 {
90   for (size_t i = 0; i < NUM_PARTS; i++)
91     if (iterators_[i])
92       iterators_[i]->quit ();
93 }
94
95 Part_combine_iterator::Part_combine_iterator ()
96 {
97   mmrest_event_ = 0;
98
99   for (size_t i = 0; i < NUM_PARTS; i++)
100     iterators_[i] = 0;
101   split_list_ = SCM_EOL;
102   state_ = INITIAL;
103   chosen_part_ = 1;
104 }
105
106 void
107 Part_combine_iterator::derived_mark () const
108 {
109   for (size_t i = 0; i < NUM_PARTS; i++)
110     if (iterators_[i])
111       scm_gc_mark (iterators_[i]->self_scm ());
112
113   if (mmrest_event_)
114     scm_gc_mark (mmrest_event_->self_scm ());
115 }
116
117 void
118 Part_combine_iterator::derived_substitute (Context *f,
119                                            Context *t)
120 {
121   // (Explain why just iterators_[0].)
122   if (iterators_[0])
123     iterators_[0]->substitute_outlet (f, t);
124 }
125
126 Moment
127 Part_combine_iterator::pending_moment () const
128 {
129   Moment p;
130   p.set_infinite (1);
131
132   for (size_t i = 0; i < NUM_PARTS; i++)
133     if (iterators_[i]->ok ())
134       p = min (p, iterators_[i]->pending_moment ());
135
136   return p;
137 }
138
139 bool
140 Part_combine_iterator::ok () const
141 {
142   for (size_t i = 0; i < NUM_PARTS; i++)
143     if (iterators_[i]->ok ())
144       return true;
145
146   return false;
147 }
148
149 void
150 Part_combine_iterator::substitute_one (Music_iterator *iter,
151                                        const char *voice_id)
152 {
153   Context *c = iter->get_outlet ();
154   if (!c)
155     {
156       programming_error ("no context");
157       return;
158     }
159   c = c->get_parent_context ();
160   if (!c)
161     {
162       programming_error ("no parent context");
163       return;
164     }
165   c = find_context_below (c, ly_symbol2scm("Voice"), voice_id);
166   if (!c)
167     {
168       string s = "can not find Voice context: ";
169       s += voice_id;
170       programming_error (s);
171       return;
172     }
173   iter->substitute_outlet (iter->get_outlet (), c);
174 }
175
176 void
177 Part_combine_iterator::substitute_both (const char *part1_voice_id,
178                                         const char *part2_voice_id)
179 {
180   // TODO: There is no good reason to tie the parts together here.
181   // Factor out per-part stuff into a new class of iterator which
182   // reads a part-specific list similar to the existing combined
183   // "split-list".
184   substitute_one(iterators_[0], part1_voice_id);
185   substitute_one(iterators_[1], part2_voice_id);
186 }
187
188 bool Part_combine_iterator::is_active_outlet (const Context *c) const
189 {
190   for (size_t i = 0; i < NUM_PARTS; i++)
191     if (iterators_[i] && (iterators_[i]->get_outlet () == c))
192       return true;
193
194   return false;
195 }
196
197 void
198 Part_combine_iterator::kill_mmrest (Context *c)
199 {
200
201   if (!mmrest_event_)
202     {
203       mmrest_event_ = new Stream_event
204         (scm_call_1 (ly_lily_module_constant ("ly:make-event-class"),
205                      ly_symbol2scm ("multi-measure-rest-event")));
206       mmrest_event_->set_property ("duration", SCM_EOL);
207       mmrest_event_->unprotect ();
208     }
209
210   c->event_source ()->broadcast (mmrest_event_);
211 }
212
213 void
214 Part_combine_iterator::unisono (bool silent, int newpart)
215 {
216   Status newstate = (silent) ? UNISILENCE : UNISONO;
217
218   if ((newstate == state_) and (newpart == chosen_part_))
219     return;
220   else
221     {
222       const char *c1 = (newpart == 2) ? CONTEXT_NULL : CONTEXT_SHARED;
223       const char *c2 = (newpart == 2) ? CONTEXT_SHARED : CONTEXT_NULL;
224       substitute_both (c1, c2);
225
226       state_ = newstate;
227       chosen_part_ = newpart;
228     }
229 }
230
231 void
232 Part_combine_iterator::solo1 ()
233 {
234   if ((state_ == SOLO) && (chosen_part_ == 1))
235     return;
236   else
237     {
238       state_ = SOLO;
239       chosen_part_ = 1;
240       substitute_both (CONTEXT_SOLO, CONTEXT_NULL);
241     }
242 }
243
244 void
245 Part_combine_iterator::solo2 ()
246 {
247   if ((state_ == SOLO) and (chosen_part_ == 2))
248     return;
249   else
250     {
251       state_ = SOLO;
252       chosen_part_ = 2;
253       substitute_both (CONTEXT_NULL, CONTEXT_SOLO);
254     }
255 }
256
257 void
258 Part_combine_iterator::chords_together ()
259 {
260   if (state_ == TOGETHER)
261     return;
262   else
263     {
264       state_ = TOGETHER;
265
266       substitute_both (CONTEXT_SHARED, CONTEXT_SHARED);
267     }
268 }
269
270 void
271 Part_combine_iterator::apart ()
272 {
273   if (state_ == APART)
274     return;
275   else
276     {
277       state_ = APART;
278       substitute_both (CONTEXT_ONE, CONTEXT_TWO);
279     }
280 }
281
282 void
283 Part_combine_iterator::construct_children ()
284 {
285   split_list_ = get_music ()->get_property ("split-list");
286
287   SCM lst = get_music ()->get_property ("elements");
288   iterators_[0] = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_car (lst))));
289   iterators_[1] = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_cadr (lst))));
290 }
291
292 void
293 Part_combine_iterator::process (Moment m)
294 {
295   Moment *splitm = 0;
296
297   Context *prev_active_outlets[NUM_PARTS];
298   for (size_t i = 0; i < NUM_PARTS; i++)
299     prev_active_outlets[i] = iterators_[i]->get_outlet ();
300
301   for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
302     {
303       splitm = unsmob<Moment> (scm_caar (split_list_));
304       if (splitm && *splitm > m)
305         break;
306
307       SCM tag = scm_cdar (split_list_);
308
309       if (scm_is_eq (tag, ly_symbol2scm ("chords")))
310         chords_together ();
311       else if (scm_is_eq (tag, ly_symbol2scm ("apart"))
312                || scm_is_eq (tag, ly_symbol2scm ("apart-silence"))
313                || scm_is_eq (tag, ly_symbol2scm ("apart-spanner")))
314         apart ();
315       else if (scm_is_eq (tag, ly_symbol2scm ("unisono")))
316         {
317           // Continue to use the most recently used part because we might have
318           // killed mmrests in the other part.
319           unisono (false, (chosen_part_ == 2) ? 2 : 1);
320         }
321       else if (scm_is_eq (tag, ly_symbol2scm ("unisilence")))
322         {
323           // as for unisono
324           unisono (true, (chosen_part_ == 2) ? 2 : 1);
325         }
326       else if (scm_is_eq (tag, ly_symbol2scm ("silence1")))
327         unisono (true, 1);
328       else if (scm_is_eq (tag, ly_symbol2scm ("silence2")))
329         unisono (true, 2);
330       else if (scm_is_eq (tag, ly_symbol2scm ("solo1")))
331         solo1 ();
332       else if (scm_is_eq (tag, ly_symbol2scm ("solo2")))
333         solo2 ();
334       else if (scm_is_symbol (tag))
335         {
336           string s = "Unknown split directive: "
337                      + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
338           programming_error (s);
339         }
340     }
341
342   bool any_outlet_changed = false;
343   for (size_t i = 0; i < NUM_PARTS; i++)
344     {
345       if (iterators_[i]->ok ())
346         iterators_[i]->process (m);
347
348       if (prev_active_outlets[i] != iterators_[i]->get_outlet ())
349           any_outlet_changed = true;
350     }
351
352   if (any_outlet_changed)
353     {
354       // Kill multi-measure rests in outlets that were previously active and
355       // are no longer active.
356       for (size_t i = 0; i < NUM_PARTS; i++)
357         {
358           Context *c = prev_active_outlets[i];
359           if (c && !is_active_outlet (c))
360               kill_mmrest (c);
361         }
362     }
363 }
364
365 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);