]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
8754effd295eef87581097a558f97a0626a7f967
[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 #include "lily-imports.hh"
28
29 static const char *const CONTEXT_ONE = "one";
30 static const char *const CONTEXT_TWO = "two";
31 static const char *const CONTEXT_SHARED = "shared";
32 static const char *const CONTEXT_SOLO = "solo";
33 static const char *const CONTEXT_NULL = "null";
34
35 class Part_combine_iterator : public Music_iterator
36 {
37 public:
38   Part_combine_iterator ();
39
40   DECLARE_SCHEME_CALLBACK (constructor, ());
41 protected:
42   virtual void derived_substitute (Context *f, Context *t);
43   virtual void derived_mark () const;
44
45   virtual void construct_children ();
46   virtual Moment pending_moment () const;
47   virtual void do_quit ();
48   virtual void process (Moment);
49
50   virtual bool ok () const;
51
52 private:
53   static const size_t NUM_PARTS = 2;
54   Music_iterator *iterators_[NUM_PARTS];
55
56   SCM split_list_;
57
58   Stream_event *mmrest_event_;
59
60   enum Status
61   {
62     INITIAL,
63     APART,
64     TOGETHER,
65     SOLO,
66     UNISONO,
67     UNISILENCE,
68   };
69   Status state_;
70
71   // For states in which it matters, this is the relevant part,
72   // e.g. 1 for Solo I, 2 for Solo II.
73   int chosen_part_;
74
75   void substitute_one (Music_iterator *iter, const char *voice_id);
76   void substitute_both (const char *part1_voice_id, const char *part2_voice_id);
77   bool is_active_outlet (const Context *c) const;
78   void kill_mmrest (Context *c);
79   void chords_together ();
80   void solo1 ();
81   void solo2 ();
82   void apart ();
83   void unisono (bool silent, int newpart);
84 };
85
86 const size_t Part_combine_iterator::NUM_PARTS;
87
88 void
89 Part_combine_iterator::do_quit ()
90 {
91   for (size_t i = 0; i < NUM_PARTS; i++)
92     if (iterators_[i])
93       iterators_[i]->quit ();
94 }
95
96 Part_combine_iterator::Part_combine_iterator ()
97 {
98   mmrest_event_ = 0;
99
100   for (size_t i = 0; i < NUM_PARTS; i++)
101     iterators_[i] = 0;
102   split_list_ = SCM_EOL;
103   state_ = INITIAL;
104   chosen_part_ = 1;
105 }
106
107 void
108 Part_combine_iterator::derived_mark () const
109 {
110   for (size_t i = 0; i < NUM_PARTS; i++)
111     if (iterators_[i])
112       scm_gc_mark (iterators_[i]->self_scm ());
113
114   if (mmrest_event_)
115     scm_gc_mark (mmrest_event_->self_scm ());
116 }
117
118 void
119 Part_combine_iterator::derived_substitute (Context *f,
120                                            Context *t)
121 {
122   // (Explain why just iterators_[0].)
123   if (iterators_[0])
124     iterators_[0]->substitute_outlet (f, t);
125 }
126
127 Moment
128 Part_combine_iterator::pending_moment () const
129 {
130   Moment p;
131   p.set_infinite (1);
132
133   for (size_t i = 0; i < NUM_PARTS; i++)
134     if (iterators_[i]->ok ())
135       p = min (p, iterators_[i]->pending_moment ());
136
137   return p;
138 }
139
140 bool
141 Part_combine_iterator::ok () const
142 {
143   for (size_t i = 0; i < NUM_PARTS; i++)
144     if (iterators_[i]->ok ())
145       return true;
146
147   return false;
148 }
149
150 void
151 Part_combine_iterator::substitute_one (Music_iterator *iter,
152                                        const char *voice_id)
153 {
154   Context *c = iter->get_outlet ();
155   if (!c)
156     {
157       programming_error ("no context");
158       return;
159     }
160   c = c->get_parent_context ();
161   if (!c)
162     {
163       programming_error ("no parent context");
164       return;
165     }
166   c = find_context_below (c, ly_symbol2scm("Voice"), voice_id);
167   if (!c)
168     {
169       string s = "can not find Voice context: ";
170       s += voice_id;
171       programming_error (s);
172       return;
173     }
174   iter->substitute_outlet (iter->get_outlet (), c);
175 }
176
177 void
178 Part_combine_iterator::substitute_both (const char *part1_voice_id,
179                                         const char *part2_voice_id)
180 {
181   // TODO: There is no good reason to tie the parts together here.
182   // Factor out per-part stuff into a new class of iterator which
183   // reads a part-specific list similar to the existing combined
184   // "split-list".
185   substitute_one(iterators_[0], part1_voice_id);
186   substitute_one(iterators_[1], part2_voice_id);
187 }
188
189 bool Part_combine_iterator::is_active_outlet (const Context *c) const
190 {
191   for (size_t i = 0; i < NUM_PARTS; i++)
192     if (iterators_[i] && (iterators_[i]->get_outlet () == c))
193       return true;
194
195   return false;
196 }
197
198 void
199 Part_combine_iterator::kill_mmrest (Context *c)
200 {
201
202   if (!mmrest_event_)
203     {
204       mmrest_event_ = new Stream_event
205         (Lily::ly_make_event_class (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);