]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
Issue 4423: eliminate part combiner's array of context handles (4/4)
[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   Moment start_moment_;
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         (scm_call_1 (ly_lily_module_constant ("ly:make-event-class"),
206                      ly_symbol2scm ("multi-measure-rest-event")));
207       mmrest_event_->set_property ("duration", SCM_EOL);
208       mmrest_event_->unprotect ();
209     }
210
211   c->event_source ()->broadcast (mmrest_event_);
212 }
213
214 void
215 Part_combine_iterator::unisono (bool silent, int newpart)
216 {
217   Status newstate = (silent) ? UNISILENCE : UNISONO;
218
219   if ((newstate == state_) and (newpart == chosen_part_))
220     return;
221   else
222     {
223       const char *c1 = (newpart == 2) ? CONTEXT_NULL : CONTEXT_SHARED;
224       const char *c2 = (newpart == 2) ? CONTEXT_SHARED : CONTEXT_NULL;
225       substitute_both (c1, c2);
226
227       state_ = newstate;
228       chosen_part_ = newpart;
229     }
230 }
231
232 void
233 Part_combine_iterator::solo1 ()
234 {
235   if ((state_ == SOLO) && (chosen_part_ == 1))
236     return;
237   else
238     {
239       state_ = SOLO;
240       chosen_part_ = 1;
241       substitute_both (CONTEXT_SOLO, CONTEXT_NULL);
242     }
243 }
244
245 void
246 Part_combine_iterator::solo2 ()
247 {
248   if ((state_ == SOLO) and (chosen_part_ == 2))
249     return;
250   else
251     {
252       state_ = SOLO;
253       chosen_part_ = 2;
254       substitute_both (CONTEXT_NULL, CONTEXT_SOLO);
255     }
256 }
257
258 void
259 Part_combine_iterator::chords_together ()
260 {
261   if (state_ == TOGETHER)
262     return;
263   else
264     {
265       state_ = TOGETHER;
266
267       substitute_both (CONTEXT_SHARED, CONTEXT_SHARED);
268     }
269 }
270
271 void
272 Part_combine_iterator::apart ()
273 {
274   if (state_ == APART)
275     return;
276   else
277     {
278       state_ = APART;
279       substitute_both (CONTEXT_ONE, CONTEXT_TWO);
280     }
281 }
282
283 void
284 Part_combine_iterator::construct_children ()
285 {
286   start_moment_ = get_outlet ()->now_mom ();
287   split_list_ = get_music ()->get_property ("split-list");
288
289   SCM lst = get_music ()->get_property ("elements");
290   iterators_[0] = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_car (lst))));
291   iterators_[1] = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_cadr (lst))));
292 }
293
294 void
295 Part_combine_iterator::process (Moment m)
296 {
297   Moment now = get_outlet ()->now_mom ();
298   Moment *splitm = 0;
299
300   /* This is needed if construct_children was called before iteration
301      started */
302   if (start_moment_.main_part_.is_infinity () && start_moment_ < 0)
303     start_moment_ = now;
304
305   Context *prev_active_outlets[NUM_PARTS];
306   for (size_t i = 0; i < NUM_PARTS; i++)
307     prev_active_outlets[i] = iterators_[i]->get_outlet ();
308
309   for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
310     {
311       splitm = unsmob<Moment> (scm_caar (split_list_));
312       if (splitm && *splitm + start_moment_ > now)
313         break;
314
315       SCM tag = scm_cdar (split_list_);
316
317       if (scm_is_eq (tag, ly_symbol2scm ("chords")))
318         chords_together ();
319       else if (scm_is_eq (tag, ly_symbol2scm ("apart"))
320                || scm_is_eq (tag, ly_symbol2scm ("apart-silence"))
321                || scm_is_eq (tag, ly_symbol2scm ("apart-spanner")))
322         apart ();
323       else if (scm_is_eq (tag, ly_symbol2scm ("unisono")))
324         {
325           // Continue to use the most recently used part because we might have
326           // killed mmrests in the other part.
327           unisono (false, (chosen_part_ == 2) ? 2 : 1);
328         }
329       else if (scm_is_eq (tag, ly_symbol2scm ("unisilence")))
330         {
331           // as for unisono
332           unisono (true, (chosen_part_ == 2) ? 2 : 1);
333         }
334       else if (scm_is_eq (tag, ly_symbol2scm ("silence1")))
335         unisono (true, 1);
336       else if (scm_is_eq (tag, ly_symbol2scm ("silence2")))
337         unisono (true, 2);
338       else if (scm_is_eq (tag, ly_symbol2scm ("solo1")))
339         solo1 ();
340       else if (scm_is_eq (tag, ly_symbol2scm ("solo2")))
341         solo2 ();
342       else if (scm_is_symbol (tag))
343         {
344           string s = "Unknown split directive: "
345                      + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
346           programming_error (s);
347         }
348     }
349
350   bool any_outlet_changed = false;
351   for (size_t i = 0; i < NUM_PARTS; i++)
352     {
353       if (iterators_[i]->ok ())
354         iterators_[i]->process (m);
355
356       if (prev_active_outlets[i] != iterators_[i]->get_outlet ())
357           any_outlet_changed = true;
358     }
359
360   if (any_outlet_changed)
361     {
362       // Kill multi-measure rests in outlets that were previously active and
363       // are no longer active.
364       for (size_t i = 0; i < NUM_PARTS; i++)
365         {
366           Context *c = prev_active_outlets[i];
367           if (c && !is_active_outlet (c))
368               kill_mmrest (c);
369         }
370     }
371 }
372
373 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);