]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
Issue 4423: eliminate part combiner's array of context handles (1/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 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   static const size_t NUM_PARTS = 2;
57   Music_iterator *iterators_[NUM_PARTS];
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   bool is_active_outlet (const Context *c) const;
86   void kill_mmrest (Context *c);
87   void chords_together ();
88   void solo1 ();
89   void solo2 ();
90   void apart ();
91   void unisono (bool silent, int newpart);
92 };
93
94 const size_t Part_combine_iterator::NUM_PARTS;
95
96 void
97 Part_combine_iterator::do_quit ()
98 {
99   for (size_t i = 0; i < NUM_PARTS; i++)
100     if (iterators_[i])
101       iterators_[i]->quit ();
102 }
103
104 Part_combine_iterator::Part_combine_iterator ()
105 {
106   mmrest_event_ = 0;
107
108   for (size_t i = 0; i < NUM_PARTS; i++)
109     iterators_[i] = 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   for (size_t i = 0; i < NUM_PARTS; i++)
119     if (iterators_[i])
120       scm_gc_mark (iterators_[i]->self_scm ());
121
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   // (Explain why just iterators_[0].)
131   if (iterators_[0])
132     iterators_[0]->substitute_outlet (f, t);
133 }
134
135 Moment
136 Part_combine_iterator::pending_moment () const
137 {
138   Moment p;
139   p.set_infinite (1);
140
141   for (size_t i = 0; i < NUM_PARTS; i++)
142     if (iterators_[i]->ok ())
143       p = min (p, iterators_[i]->pending_moment ());
144
145   return p;
146 }
147
148 bool
149 Part_combine_iterator::ok () const
150 {
151   for (size_t i = 0; i < NUM_PARTS; i++)
152     if (iterators_[i]->ok ())
153       return true;
154
155   return false;
156 }
157
158 void
159 Part_combine_iterator::substitute_both (Outlet_type to1,
160                                         Outlet_type to2)
161 {
162   // TODO: There is no good reason to tie the parts together here.
163   // Factor out per-part stuff into a new class of iterator which
164   // reads a part-specific list similar to the existing combined
165   // "split-list".
166   iterators_[0]->substitute_outlet (iterators_[0]->get_outlet (),
167                                     handles_[to1].get_context ());
168   iterators_[1]->substitute_outlet (iterators_[1]->get_outlet (),
169                                     handles_[to2].get_context ());
170 }
171
172 bool Part_combine_iterator::is_active_outlet (const Context *c) const
173 {
174   for (size_t i = 0; i < NUM_PARTS; i++)
175     if (iterators_[i] && (iterators_[i]->get_outlet () == c))
176       return true;
177
178   return false;
179 }
180
181 void
182 Part_combine_iterator::kill_mmrest (Context *c)
183 {
184
185   if (!mmrest_event_)
186     {
187       mmrest_event_ = new Stream_event
188         (scm_call_1 (ly_lily_module_constant ("ly:make-event-class"),
189                      ly_symbol2scm ("multi-measure-rest-event")));
190       mmrest_event_->set_property ("duration", SCM_EOL);
191       mmrest_event_->unprotect ();
192     }
193
194   c->event_source ()->broadcast (mmrest_event_);
195 }
196
197 void
198 Part_combine_iterator::unisono (bool silent, int newpart)
199 {
200   Status newstate = (silent) ? UNISILENCE : UNISONO;
201
202   if ((newstate == state_) and (newpart == chosen_part_))
203     return;
204   else
205     {
206       Outlet_type c1 = (newpart == 2) ? CONTEXT_NULL : CONTEXT_SHARED;
207       Outlet_type c2 = (newpart == 2) ? CONTEXT_SHARED : CONTEXT_NULL;
208       substitute_both (c1, c2);
209
210       state_ = newstate;
211       chosen_part_ = newpart;
212     }
213 }
214
215 void
216 Part_combine_iterator::solo1 ()
217 {
218   if ((state_ == SOLO) && (chosen_part_ == 1))
219     return;
220   else
221     {
222       state_ = SOLO;
223       chosen_part_ = 1;
224       substitute_both (CONTEXT_SOLO, CONTEXT_NULL);
225     }
226 }
227
228 void
229 Part_combine_iterator::solo2 ()
230 {
231   if ((state_ == SOLO) and (chosen_part_ == 2))
232     return;
233   else
234     {
235       state_ = SOLO;
236       chosen_part_ = 2;
237       substitute_both (CONTEXT_NULL, CONTEXT_SOLO);
238     }
239 }
240
241 void
242 Part_combine_iterator::chords_together ()
243 {
244   if (state_ == TOGETHER)
245     return;
246   else
247     {
248       state_ = TOGETHER;
249
250       substitute_both (CONTEXT_SHARED, CONTEXT_SHARED);
251     }
252 }
253
254 void
255 Part_combine_iterator::apart ()
256 {
257   if (state_ == APART)
258     return;
259   else
260     {
261       state_ = APART;
262       substitute_both (CONTEXT_ONE, CONTEXT_TWO);
263     }
264 }
265
266 void
267 Part_combine_iterator::construct_children ()
268 {
269   start_moment_ = get_outlet ()->now_mom ();
270   split_list_ = get_music ()->get_property ("split-list");
271
272   Context *c = get_outlet ();
273
274   for (int i = 0; i < NUM_OUTLETS; i++)
275     {
276       SCM type = (i == CONTEXT_NULL) ? ly_symbol2scm ("Devnull") : ly_symbol2scm ("Voice");
277       /* find context below c: otherwise we may create new staff for each voice */
278       c = c->find_create_context (type, outlet_names_[i], SCM_EOL);
279       handles_[i].set_context (c);
280     }
281
282   SCM lst = get_music ()->get_property ("elements");
283
284   Context *one = handles_[CONTEXT_ONE].get_context ();
285   set_context (one);
286   iterators_[0] = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_car (lst))));
287
288   Context *two = handles_[CONTEXT_TWO].get_context ();
289   set_context (two);
290   iterators_[1] = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_cadr (lst))));
291
292   // (Explain the purpose of switching to the shared context.)
293   Context *shared = handles_[CONTEXT_SHARED].get_context ();
294   set_context (shared);
295 }
296
297 void
298 Part_combine_iterator::process (Moment m)
299 {
300   Moment now = get_outlet ()->now_mom ();
301   Moment *splitm = 0;
302
303   /* This is needed if construct_children was called before iteration
304      started */
305   if (start_moment_.main_part_.is_infinity () && start_moment_ < 0)
306     start_moment_ = now;
307
308   Context *prev_active_outlets[NUM_PARTS];
309   for (size_t i = 0; i < NUM_PARTS; i++)
310     prev_active_outlets[i] = iterators_[i]->get_outlet ();
311
312   for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
313     {
314       splitm = unsmob<Moment> (scm_caar (split_list_));
315       if (splitm && *splitm + start_moment_ > now)
316         break;
317
318       SCM tag = scm_cdar (split_list_);
319
320       if (scm_is_eq (tag, ly_symbol2scm ("chords")))
321         chords_together ();
322       else if (scm_is_eq (tag, ly_symbol2scm ("apart"))
323                || scm_is_eq (tag, ly_symbol2scm ("apart-silence"))
324                || scm_is_eq (tag, ly_symbol2scm ("apart-spanner")))
325         apart ();
326       else if (scm_is_eq (tag, ly_symbol2scm ("unisono")))
327         {
328           // Continue to use the most recently used part because we might have
329           // killed mmrests in the other part.
330           unisono (false, (chosen_part_ == 2) ? 2 : 1);
331         }
332       else if (scm_is_eq (tag, ly_symbol2scm ("unisilence")))
333         {
334           // as for unisono
335           unisono (true, (chosen_part_ == 2) ? 2 : 1);
336         }
337       else if (scm_is_eq (tag, ly_symbol2scm ("silence1")))
338         unisono (true, 1);
339       else if (scm_is_eq (tag, ly_symbol2scm ("silence2")))
340         unisono (true, 2);
341       else if (scm_is_eq (tag, ly_symbol2scm ("solo1")))
342         solo1 ();
343       else if (scm_is_eq (tag, ly_symbol2scm ("solo2")))
344         solo2 ();
345       else if (scm_is_symbol (tag))
346         {
347           string s = "Unknown split directive: "
348                      + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
349           programming_error (s);
350         }
351     }
352
353   bool any_outlet_changed = false;
354   for (size_t i = 0; i < NUM_PARTS; i++)
355     {
356       if (iterators_[i]->ok ())
357         iterators_[i]->process (m);
358
359       if (prev_active_outlets[i] != iterators_[i]->get_outlet ())
360           any_outlet_changed = true;
361     }
362
363   if (any_outlet_changed)
364     {
365       // Kill multi-measure rests in outlets that were previously active and
366       // are no longer active.
367       for (size_t i = 0; i < NUM_PARTS; i++)
368         {
369           Context *c = prev_active_outlets[i];
370           if (c && !is_active_outlet (c))
371               kill_mmrest (c);
372         }
373     }
374 }
375
376 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);