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