]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
Issue 4403: Part_combine_iterator: stop tracking the last-playing part
[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 enum Outlet_type
29 {
30   CONTEXT_ONE, CONTEXT_TWO,
31   CONTEXT_SHARED, CONTEXT_SOLO,
32   CONTEXT_NULL, NUM_OUTLETS
33 };
34
35 static const char *outlet_names_[NUM_OUTLETS]
36   = {"one", "two", "shared", "solo", "null"};
37
38 class Part_combine_iterator : public Music_iterator
39 {
40 public:
41   Part_combine_iterator ();
42
43   DECLARE_SCHEME_CALLBACK (constructor, ());
44 protected:
45   virtual void derived_substitute (Context *f, Context *t);
46   virtual void derived_mark () const;
47
48   virtual void construct_children ();
49   virtual Moment pending_moment () const;
50   virtual void do_quit ();
51   virtual void process (Moment);
52
53   virtual bool ok () const;
54
55 private:
56   Music_iterator *first_iter_;
57   Music_iterator *second_iter_;
58   Moment start_moment_;
59
60   SCM split_list_;
61
62   Stream_event *mmrest_event_;
63
64   enum Status
65   {
66     APART,
67     TOGETHER,
68     SOLO,
69     UNISONO,
70     UNISILENCE,
71   };
72   Status state_;
73
74   // For states in which it matters, this is the relevant part,
75   // e.g. 1 for Solo I, 2 for Solo II.
76   int chosen_part_;
77
78   /*
79     TODO: this is getting off hand...
80   */
81   Context_handle handles_[NUM_OUTLETS];
82
83   void substitute_both (Outlet_type to1,
84                         Outlet_type to2);
85   void kill_mmrest_in_inactive_outlets ();
86   /* parameter is really Outlet_type */
87   void kill_mmrest (int in);
88   void chords_together ();
89   void solo1 ();
90   void solo2 ();
91   void apart ();
92   void unisono (bool silent, int newpart);
93 };
94
95 void
96 Part_combine_iterator::do_quit ()
97 {
98   if (first_iter_)
99     first_iter_->quit ();
100   if (second_iter_)
101     second_iter_->quit ();
102 }
103
104 Part_combine_iterator::Part_combine_iterator ()
105 {
106   mmrest_event_ = 0;
107
108   first_iter_ = 0;
109   second_iter_ = 0;
110   split_list_ = SCM_EOL;
111   state_ = APART;
112   chosen_part_ = 1;
113 }
114
115 void
116 Part_combine_iterator::derived_mark () const
117 {
118   if (first_iter_)
119     scm_gc_mark (first_iter_->self_scm ());
120   if (second_iter_)
121     scm_gc_mark (second_iter_->self_scm ());
122   if (mmrest_event_)
123     scm_gc_mark (mmrest_event_->self_scm ());
124 }
125
126 void
127 Part_combine_iterator::derived_substitute (Context *f,
128                                            Context *t)
129 {
130   if (first_iter_)
131     first_iter_->substitute_outlet (f, t);
132 }
133
134 Moment
135 Part_combine_iterator::pending_moment () const
136 {
137   Moment p;
138   p.set_infinite (1);
139   if (first_iter_->ok ())
140     p = min (p, first_iter_->pending_moment ());
141
142   if (second_iter_->ok ())
143     p = min (p, second_iter_->pending_moment ());
144   return p;
145 }
146
147 bool
148 Part_combine_iterator::ok () const
149 {
150   return first_iter_->ok () || second_iter_->ok ();
151 }
152
153 void
154 Part_combine_iterator::substitute_both (Outlet_type to1,
155                                         Outlet_type to2)
156 {
157   first_iter_->substitute_outlet (first_iter_->get_outlet (),
158                                   handles_[to1].get_context ());
159   second_iter_->substitute_outlet (second_iter_->get_outlet (),
160                                    handles_[to2].get_context ());
161 }
162
163 void Part_combine_iterator::kill_mmrest_in_inactive_outlets ()
164 {
165   for (int j = 0; j < NUM_OUTLETS; j++)
166     {
167       Context *c = handles_[j].get_context ();
168
169       if (first_iter_->get_outlet () == c)
170         continue;
171
172       if (second_iter_->get_outlet () == c)
173         continue;
174
175       kill_mmrest (j);
176     }
177 }
178
179 void
180 Part_combine_iterator::kill_mmrest (int in)
181 {
182
183   if (!mmrest_event_)
184     {
185       mmrest_event_ = new Stream_event
186         (scm_call_1 (ly_lily_module_constant ("ly:make-event-class"),
187                      ly_symbol2scm ("multi-measure-rest-event")));
188       mmrest_event_->set_property ("duration", SCM_EOL);
189       mmrest_event_->unprotect ();
190     }
191
192   handles_[in].get_context ()->event_source ()->broadcast (mmrest_event_);
193 }
194
195 void
196 Part_combine_iterator::unisono (bool silent, int newpart)
197 {
198   Status newstate = (silent) ? UNISILENCE : UNISONO;
199
200   if ((newstate == state_) and (newpart == chosen_part_))
201     return;
202   else
203     {
204       Outlet_type c1 = (newpart == 2) ? CONTEXT_NULL : CONTEXT_SHARED;
205       Outlet_type c2 = (newpart == 2) ? CONTEXT_SHARED : CONTEXT_NULL;
206       substitute_both (c1, c2);
207
208       state_ = newstate;
209       chosen_part_ = newpart;
210     }
211 }
212
213 void
214 Part_combine_iterator::solo1 ()
215 {
216   if ((state_ == SOLO) && (chosen_part_ == 1))
217     return;
218   else
219     {
220       state_ = SOLO;
221       chosen_part_ = 1;
222       substitute_both (CONTEXT_SOLO, CONTEXT_NULL);
223     }
224 }
225
226 void
227 Part_combine_iterator::solo2 ()
228 {
229   if ((state_ == SOLO) and (chosen_part_ == 2))
230     return;
231   else
232     {
233       state_ = SOLO;
234       chosen_part_ = 2;
235       substitute_both (CONTEXT_NULL, CONTEXT_SOLO);
236     }
237 }
238
239 void
240 Part_combine_iterator::chords_together ()
241 {
242   if (state_ == TOGETHER)
243     return;
244   else
245     {
246       state_ = TOGETHER;
247
248       substitute_both (CONTEXT_SHARED, CONTEXT_SHARED);
249     }
250 }
251
252 void
253 Part_combine_iterator::apart ()
254 {
255   if (state_ == APART)
256     return;
257   else
258     {
259       state_ = APART;
260       substitute_both (CONTEXT_ONE, CONTEXT_TWO);
261     }
262 }
263
264 void
265 Part_combine_iterator::construct_children ()
266 {
267   start_moment_ = get_outlet ()->now_mom ();
268   split_list_ = get_music ()->get_property ("split-list");
269
270   Context *c = get_outlet ();
271
272   for (int i = 0; i < NUM_OUTLETS; i++)
273     {
274       SCM type = (i == CONTEXT_NULL) ? ly_symbol2scm ("Devnull") : ly_symbol2scm ("Voice");
275       /* find context below c: otherwise we may create new staff for each voice */
276       c = c->find_create_context (type, outlet_names_[i], SCM_EOL);
277       handles_[i].set_context (c);
278     }
279
280   SCM lst = get_music ()->get_property ("elements");
281   Context *one = handles_[CONTEXT_ONE].get_context ();
282   set_context (one);
283   first_iter_ = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_car (lst))));
284   Context *two = handles_[CONTEXT_TWO].get_context ();
285   set_context (two);
286   second_iter_ = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_cadr (lst))));
287   Context *shared = handles_[CONTEXT_SHARED].get_context ();
288   set_context (shared);
289 }
290
291 void
292 Part_combine_iterator::process (Moment m)
293 {
294   Moment now = get_outlet ()->now_mom ();
295   Moment *splitm = 0;
296
297   /* This is needed if construct_children was called before iteration
298      started */
299   if (start_moment_.main_part_.is_infinity () && start_moment_ < 0)
300     start_moment_ = now;
301
302   for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
303     {
304       splitm = unsmob<Moment> (scm_caar (split_list_));
305       if (splitm && *splitm + start_moment_ > now)
306         break;
307
308       SCM tag = scm_cdar (split_list_);
309
310       Context *outletsBefore[] = { first_iter_->get_outlet (),
311                                    second_iter_->get_outlet () };
312
313       if (scm_is_eq (tag, ly_symbol2scm ("chords")))
314         chords_together ();
315       else if (scm_is_eq (tag, ly_symbol2scm ("apart"))
316                || scm_is_eq (tag, ly_symbol2scm ("apart-silence"))
317                || scm_is_eq (tag, ly_symbol2scm ("apart-spanner")))
318         apart ();
319       else if (scm_is_eq (tag, ly_symbol2scm ("unisono")))
320         {
321           // Continue to use the most recently used part because we might have
322           // killed mmrests in the other part.
323           unisono (false, (chosen_part_ == 2) ? 2 : 1);
324         }
325       else if (scm_is_eq (tag, ly_symbol2scm ("unisilence")))
326         {
327           // as for unisono
328           unisono (true, (chosen_part_ == 2) ? 2 : 1);
329         }
330       else if (scm_is_eq (tag, ly_symbol2scm ("silence1")))
331         unisono (true, 1);
332       else if (scm_is_eq (tag, ly_symbol2scm ("silence2")))
333         unisono (true, 2);
334       else if (scm_is_eq (tag, ly_symbol2scm ("solo1")))
335         solo1 ();
336       else if (scm_is_eq (tag, ly_symbol2scm ("solo2")))
337         solo2 ();
338       else if (scm_is_symbol (tag))
339         {
340           string s = "Unknown split directive: "
341                      + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
342           programming_error (s);
343         }
344
345       if ((first_iter_->get_outlet () != outletsBefore[0])
346           || (second_iter_->get_outlet () != outletsBefore[1]))
347         kill_mmrest_in_inactive_outlets ();
348     }
349
350   if (first_iter_->ok ())
351     first_iter_->process (m);
352
353   if (second_iter_->ok ())
354     second_iter_->process (m);
355 }
356
357 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);