Nitpick: ly:spanner-bound grob name slur -> spanner.
[lilypond.git] / lily / part-combine-iterator.cc
blob5f35a0bba4d2d18725d0b04078597fdcfd035f50
1 /*
2 new-part-combine-music-iterator.cc -- implement Part_combine_iterator
4 source file of the GNU LilyPond music typesetter
6 (c) 2004--2009 Han-Wen Nienhuys
7 */
9 #include "context.hh"
10 #include "dispatcher.hh"
11 #include "lily-guile.hh"
12 #include "music.hh"
13 #include "music-iterator.hh"
14 #include "music-sequence.hh"
15 #include "warn.hh"
17 typedef enum Outlet_type
19 CONTEXT_ONE, CONTEXT_TWO,
20 CONTEXT_SHARED, CONTEXT_SOLO,
21 CONTEXT_NULL, NUM_OUTLETS
24 static const char *outlet_names_[NUM_OUTLETS] =
25 {"one", "two", "shared", "solo", "null"};
27 class Part_combine_iterator : public Music_iterator
29 public:
30 Part_combine_iterator ();
32 DECLARE_SCHEME_CALLBACK (constructor, ());
33 protected:
34 virtual void derived_substitute (Context *f, Context *t);
35 virtual void derived_mark () const;
37 virtual void construct_children ();
38 virtual Moment pending_moment () const;
39 virtual void do_quit ();
40 virtual void process (Moment);
42 virtual bool ok () const;
44 private:
45 /* used by try_process */
46 DECLARE_LISTENER (set_busy);
47 bool busy_;
48 bool notice_busy_;
50 bool try_process (Music_iterator *i, Moment m);
52 Music_iterator *first_iter_;
53 Music_iterator *second_iter_;
54 Moment start_moment_;
56 SCM split_list_;
58 Stream_event *unisono_event_;
59 Stream_event *solo_one_event_;
60 Stream_event *solo_two_event_;
61 Stream_event *mmrest_event_;
63 enum Status
65 APART,
66 TOGETHER,
67 SOLO1,
68 SOLO2,
69 UNISONO,
70 UNISILENCE,
72 Status state_;
73 Status playing_state_;
76 Should be SOLO1 or SOLO2
78 Status last_playing_;
81 TODO: this is getting of hand...
83 Context_handle handles_[NUM_OUTLETS];
85 void substitute_both (Outlet_type to1,
86 Outlet_type to2);
88 /* parameter is really Outlet_type */
89 void kill_mmrest (int in);
90 void chords_together ();
91 void solo1 ();
92 void solo2 ();
93 void apart (bool silent);
94 void unisono (bool silent);
97 void
98 Part_combine_iterator::do_quit ()
100 if (first_iter_)
101 first_iter_->quit ();
102 if (second_iter_)
103 second_iter_->quit ();
105 // Add listeners to all contexts except Devnull.
106 for (int i = 0; i < NUM_OUTLETS; i++)
108 Context *c = handles_[i].get_outlet ();
109 if (c->is_alias (ly_symbol2scm ("Voice")))
110 c->event_source ()->remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
111 handles_[i].set_context (0);
115 Part_combine_iterator::Part_combine_iterator ()
117 mmrest_event_ = 0;
118 unisono_event_ = 0;
119 solo_two_event_ = 0;
120 solo_one_event_= 0;
122 first_iter_ = 0;
123 second_iter_ = 0;
124 split_list_ = SCM_EOL;
125 state_ = APART;
126 playing_state_ = APART;
129 void
130 Part_combine_iterator::derived_mark () const
132 if (first_iter_)
133 scm_gc_mark (first_iter_->self_scm ());
134 if (second_iter_)
135 scm_gc_mark (second_iter_->self_scm ());
137 Stream_event *ptrs[] = {
138 unisono_event_,
139 mmrest_event_,
140 solo_two_event_,
141 solo_one_event_,
144 for (int i = 0; ptrs[i]; i++)
145 if (ptrs[i])
146 scm_gc_mark (ptrs[i]->self_scm ());
149 void
150 Part_combine_iterator::derived_substitute (Context *f,
151 Context *t)
153 if (first_iter_)
154 first_iter_->substitute_outlet (f, t);
157 Moment
158 Part_combine_iterator::pending_moment () const
160 Moment p;
161 p.set_infinite (1);
162 if (first_iter_->ok ())
163 p = min (p, first_iter_->pending_moment ());
165 if (second_iter_->ok ())
166 p = min (p, second_iter_->pending_moment ());
167 return p;
170 bool
171 Part_combine_iterator::ok () const
173 return first_iter_->ok () || second_iter_->ok ();
176 void
177 Part_combine_iterator::chords_together ()
179 if (state_ == TOGETHER)
180 return;
181 else
183 playing_state_ = TOGETHER;
184 state_ = TOGETHER;
186 substitute_both (CONTEXT_SHARED, CONTEXT_SHARED);
190 void
191 Part_combine_iterator::kill_mmrest (int in)
194 if (!mmrest_event_)
196 mmrest_event_ = new Stream_event (ly_symbol2scm ("multi-measure-rest-event"));
197 mmrest_event_->set_property ("duration", SCM_EOL);
198 mmrest_event_->unprotect ();
201 handles_[in].get_outlet ()->event_source ()->broadcast (mmrest_event_);
204 void
205 Part_combine_iterator::solo1 ()
207 if (state_ == SOLO1)
208 return;
209 else
211 state_ = SOLO1;
212 substitute_both (CONTEXT_SOLO, CONTEXT_NULL);
214 kill_mmrest (CONTEXT_TWO);
215 kill_mmrest (CONTEXT_SHARED);
217 if (playing_state_ != SOLO1)
219 if (!solo_one_event_)
221 solo_one_event_ = new Stream_event (ly_symbol2scm ("solo-one-event"));
222 solo_one_event_->unprotect ();
225 first_iter_->get_outlet ()->event_source ()->broadcast (solo_one_event_);
227 playing_state_ = SOLO1;
231 void
232 Part_combine_iterator::substitute_both (Outlet_type to1,
233 Outlet_type to2)
235 Outlet_type tos[] = {to1, to2};
237 Music_iterator *mis[] = {first_iter_, second_iter_};
239 for (int i = 0; i < 2; i++)
241 for (int j = 0; j < NUM_OUTLETS; j++)
242 if (j != tos[i])
243 mis[i]->substitute_outlet (handles_[j].get_outlet (), handles_[tos[i]].get_outlet ());
246 for (int j = 0; j < NUM_OUTLETS; j++)
248 if (j != to1 && j != to2)
249 kill_mmrest (j);
253 void
254 Part_combine_iterator::unisono (bool silent)
256 Status newstate = (silent) ? UNISILENCE : UNISONO;
258 if (newstate == state_)
259 return;
260 else
263 If we're coming from SOLO2 state, we might have kill mmrests
264 in the 1st voice, so in that case, we use the second voice
265 as a basis for events.
267 Outlet_type c1 = (last_playing_ == SOLO2) ? CONTEXT_NULL : CONTEXT_SHARED;
268 Outlet_type c2 = (last_playing_ == SOLO2) ? CONTEXT_SHARED : CONTEXT_NULL;
269 substitute_both (c1, c2);
270 kill_mmrest ((last_playing_ == SOLO2)
271 ? CONTEXT_ONE : CONTEXT_TWO);
272 kill_mmrest (CONTEXT_SHARED);
274 if (playing_state_ != UNISONO
275 && newstate == UNISONO)
277 if (!unisono_event_)
279 unisono_event_ = new Stream_event (ly_symbol2scm ("unisono-event"));
280 unisono_event_->unprotect ();
284 Context *out = (last_playing_ == SOLO2 ? second_iter_ : first_iter_)
285 ->get_outlet ();
286 out->event_source ()->broadcast (unisono_event_);
287 playing_state_ = UNISONO;
289 state_ = newstate;
293 void
294 Part_combine_iterator::solo2 ()
296 if (state_ == SOLO2)
297 return;
298 else
300 state_ = SOLO2;
302 substitute_both (CONTEXT_NULL, CONTEXT_SOLO);
304 if (playing_state_ != SOLO2)
306 if (!solo_two_event_)
308 solo_two_event_ = new Stream_event (ly_symbol2scm ("solo-two-event"));
309 solo_two_event_->unprotect ();
312 second_iter_->get_outlet ()->event_source ()->broadcast (solo_two_event_);
313 playing_state_ = SOLO2;
318 void
319 Part_combine_iterator::apart (bool silent)
321 if (!silent)
322 playing_state_ = APART;
324 if (state_ == APART)
325 return;
326 else
328 state_ = APART;
329 substitute_both (CONTEXT_ONE, CONTEXT_TWO);
333 void
334 Part_combine_iterator::construct_children ()
336 start_moment_ = get_outlet ()->now_mom ();
337 split_list_ = get_music ()->get_property ("split-list");
339 Context *c = get_outlet ();
341 for (int i = 0; i < NUM_OUTLETS; i++)
343 SCM type = (i == CONTEXT_NULL) ? ly_symbol2scm ("Devnull") : ly_symbol2scm ("Voice");
344 /* find context below c: otherwise we may create new staff for each voice */
345 c = c->find_create_context (type, outlet_names_[i], SCM_EOL);
346 handles_[i].set_context (c);
347 if (c->is_alias (ly_symbol2scm ("Voice")))
348 c->event_source ()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
351 SCM lst = get_music ()->get_property ("elements");
352 Context *one = handles_[CONTEXT_ONE].get_outlet ();
353 set_context (one);
354 first_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_car (lst))));
355 Context *two = handles_[CONTEXT_TWO].get_outlet ();
356 set_context (two);
357 second_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_cadr (lst))));
359 char const *syms[]
361 "Stem",
362 "DynamicLineSpanner",
363 "Tie",
364 "Dots",
365 "Rest",
366 "Slur",
367 "TextScript",
368 "Script",
372 for (char const **p = syms; *p; p++)
374 SCM sym = ly_symbol2scm (*p);
375 execute_pushpop_property (one, sym,
376 ly_symbol2scm ("direction"), scm_from_int (1));
378 execute_pushpop_property (two, sym,
379 ly_symbol2scm ("direction"), scm_from_int (-1));
383 IMPLEMENT_LISTENER (Part_combine_iterator, set_busy);
384 void
385 Part_combine_iterator::set_busy (SCM se)
387 if (!notice_busy_)
388 return;
390 Stream_event *e = unsmob_stream_event (se);
392 if (e->in_event_class ("note-event") || e->in_event_class ("cluster-note-event"))
393 busy_ = true;
397 Processes a moment in an iterator, and returns whether any new music
398 was reported.
400 bool
401 Part_combine_iterator::try_process (Music_iterator *i, Moment m)
403 busy_ = false;
404 notice_busy_ = true;
406 i->process (m);
408 notice_busy_ = false;
409 return busy_;
412 void
413 Part_combine_iterator::process (Moment m)
415 Moment now = get_outlet ()->now_mom ();
416 Moment *splitm = 0;
418 /* This is needed if construct_children was called before iteration
419 started */
420 if (start_moment_.main_part_.is_infinity () && start_moment_ < 0)
421 start_moment_ = now;
423 for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
425 splitm = unsmob_moment (scm_caar (split_list_));
426 if (splitm && *splitm + start_moment_ > now)
427 break;
429 SCM tag = scm_cdar (split_list_);
431 if (tag == ly_symbol2scm ("chords"))
432 chords_together ();
433 else if (tag == ly_symbol2scm ("apart")
434 || tag == ly_symbol2scm ("apart-silence")
435 || tag == ly_symbol2scm ("apart-spanner"))
436 apart (tag == ly_symbol2scm ("apart-silence"));
437 else if (tag == ly_symbol2scm ("unisono"))
438 unisono (false);
439 else if (tag == ly_symbol2scm ("unisilence"))
440 unisono (true);
441 else if (tag == ly_symbol2scm ("solo1"))
442 solo1 ();
443 else if (tag == ly_symbol2scm ("solo2"))
444 solo2 ();
445 else if (scm_is_symbol (tag))
447 string s = "Unknown split directive: "
448 + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
449 programming_error (s);
453 if (first_iter_->ok ())
455 if (try_process (first_iter_, m))
456 last_playing_ = SOLO1;
459 if (second_iter_->ok ())
461 if (try_process (second_iter_, m))
462 last_playing_ = SOLO2;
466 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);