2 new-part-combine-music-iterator.cc -- implement Part_combine_iterator
4 source file of the GNU LilyPond music typesetter
6 (c) 2004--2006 Han-Wen Nienhuys
10 #include "dispatcher.hh"
11 #include "lily-guile.hh"
12 #include "listener.hh"
14 #include "music-iterator.hh"
15 #include "music-sequence.hh"
18 typedef enum Outlet_type
20 CONTEXT_ONE, CONTEXT_TWO,
21 CONTEXT_SHARED, CONTEXT_SOLO,
22 CONTEXT_NULL, NUM_OUTLETS
25 static const char *outlet_names_[NUM_OUTLETS] =
26 {"one", "two", "shared", "solo", "null"};
28 class Part_combine_iterator : public Music_iterator
31 Part_combine_iterator ();
33 DECLARE_SCHEME_CALLBACK (constructor, ());
35 virtual void derived_substitute (Context *f, Context *t);
36 virtual void derived_mark () const;
38 virtual void construct_children ();
39 virtual Moment pending_moment () const;
40 virtual void do_quit ();
41 virtual void process (Moment);
43 virtual bool ok () const;
46 /* used by try_process */
47 DECLARE_LISTENER (set_busy);
51 bool try_process (Music_iterator *i, Moment m);
53 Music_iterator *first_iter_;
54 Music_iterator *second_iter_;
59 Stream_event *unisono_event_;
60 Stream_event *solo_one_event_;
61 Stream_event *solo_two_event_;
62 Stream_event *mmrest_event_;
74 Status playing_state_;
77 Should be SOLO1 or SOLO2
82 TODO: this is getting of hand...
84 Context_handle handles_[NUM_OUTLETS];
86 void substitute_both (Outlet_type to1,
89 /* parameter is really Outlet_type */
90 void kill_mmrest (int in);
91 void chords_together ();
94 void apart (bool silent);
95 void unisono (bool silent);
99 Part_combine_iterator::do_quit ()
102 first_iter_->quit ();
104 second_iter_->quit ();
106 // Add listeners to all contexts except Devnull.
107 for (int i = 0; i < NUM_OUTLETS; i++)
109 Context *c = handles_[i].get_outlet ();
110 if (c->is_alias (ly_symbol2scm ("Voice")))
111 c->event_source ()->remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
112 handles_[i].set_context (0);
116 Part_combine_iterator::Part_combine_iterator ()
125 split_list_ = SCM_EOL;
127 playing_state_ = APART;
131 Part_combine_iterator::derived_mark () const
134 scm_gc_mark (first_iter_->self_scm ());
136 scm_gc_mark (second_iter_->self_scm ());
138 Stream_event *ptrs[] = {
145 for (int i = 0; ptrs[i]; i++)
147 scm_gc_mark (ptrs[i]->self_scm ());
151 Part_combine_iterator::derived_substitute (Context *f,
155 first_iter_->substitute_outlet (f, t);
159 Part_combine_iterator::pending_moment () const
163 if (first_iter_->ok ())
164 p = min (p, first_iter_->pending_moment ());
166 if (second_iter_->ok ())
167 p = min (p, second_iter_->pending_moment ());
172 Part_combine_iterator::ok () const
174 return first_iter_->ok () || second_iter_->ok ();
178 Part_combine_iterator::chords_together ()
180 if (state_ == TOGETHER)
184 playing_state_ = TOGETHER;
187 substitute_both (CONTEXT_SHARED, CONTEXT_SHARED);
192 Part_combine_iterator::kill_mmrest (int in)
197 mmrest_event_ = new Stream_event (ly_symbol2scm ("multi-measure-rest-event"));
198 mmrest_event_->set_property ("duration", SCM_EOL);
199 mmrest_event_->unprotect ();
202 handles_[in].get_outlet ()->event_source ()->broadcast (mmrest_event_);
206 Part_combine_iterator::solo1 ()
213 substitute_both (CONTEXT_SOLO, CONTEXT_NULL);
215 kill_mmrest (CONTEXT_TWO);
216 kill_mmrest (CONTEXT_SHARED);
218 if (playing_state_ != SOLO1)
220 if (!solo_one_event_)
222 solo_one_event_ = new Stream_event (ly_symbol2scm ("solo-one-event"));
223 solo_one_event_->unprotect ();
226 first_iter_->get_outlet ()->event_source ()->broadcast (solo_one_event_);
228 playing_state_ = SOLO1;
233 Part_combine_iterator::substitute_both (Outlet_type to1,
236 Outlet_type tos[] = {to1, to2};
238 Music_iterator *mis[] = {first_iter_, second_iter_};
240 for (int i = 0; i < 2; i++)
242 for (int j = 0; j < NUM_OUTLETS; j++)
244 mis[i]->substitute_outlet (handles_[j].get_outlet (), handles_[tos[i]].get_outlet ());
247 for (int j = 0; j < NUM_OUTLETS; j++)
249 if (j != to1 && j != to2)
255 Part_combine_iterator::unisono (bool silent)
257 Status newstate = (silent) ? UNISILENCE : UNISONO;
259 if (newstate == state_)
264 If we're coming from SOLO2 state, we might have kill mmrests
265 in the 1st voice, so in that case, we use the second voice
266 as a basis for events.
268 Outlet_type c1 = (last_playing_ == SOLO2) ? CONTEXT_NULL : CONTEXT_SHARED;
269 Outlet_type c2 = (last_playing_ == SOLO2) ? CONTEXT_SHARED : CONTEXT_NULL;
270 substitute_both (c1, c2);
271 kill_mmrest ((last_playing_ == SOLO2)
272 ? CONTEXT_ONE : CONTEXT_TWO);
273 kill_mmrest (CONTEXT_SHARED);
275 if (playing_state_ != UNISONO
276 && newstate == UNISONO)
280 unisono_event_ = new Stream_event (ly_symbol2scm ("unisono-event"));
281 unisono_event_->unprotect ();
285 Context *out = (last_playing_ == SOLO2 ? second_iter_ : first_iter_)
287 out->event_source ()->broadcast (unisono_event_);
288 playing_state_ = UNISONO;
295 Part_combine_iterator::solo2 ()
303 substitute_both (CONTEXT_NULL, CONTEXT_SOLO);
305 if (playing_state_ != SOLO2)
307 if (!solo_two_event_)
309 solo_two_event_ = new Stream_event (ly_symbol2scm ("solo-two-event"));
310 solo_two_event_->unprotect ();
313 second_iter_->get_outlet ()->event_source ()->broadcast (solo_two_event_);
314 playing_state_ = SOLO2;
320 Part_combine_iterator::apart (bool silent)
323 playing_state_ = APART;
330 substitute_both (CONTEXT_ONE, CONTEXT_TWO);
335 Part_combine_iterator::construct_children ()
337 start_moment_ = get_outlet ()->now_mom ();
338 split_list_ = get_music ()->get_property ("split-list");
340 Context *c = get_outlet ();
342 for (int i = 0; i < NUM_OUTLETS; i++)
344 SCM type = (i == CONTEXT_NULL) ? ly_symbol2scm ("Devnull") : ly_symbol2scm ("Voice");
345 /* find context below c: otherwise we may create new staff for each voice */
346 c = c->find_create_context (type, outlet_names_[i], SCM_EOL);
347 handles_[i].set_context (c);
348 if (c->is_alias (ly_symbol2scm ("Voice")))
349 c->event_source ()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
352 SCM lst = get_music ()->get_property ("elements");
353 Context *one = handles_[CONTEXT_ONE].get_outlet ();
355 first_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_car (lst))));
356 Context *two = handles_[CONTEXT_TWO].get_outlet ();
358 second_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_cadr (lst))));
363 "DynamicLineSpanner",
373 for (char const **p = syms; *p; p++)
375 SCM sym = ly_symbol2scm (*p);
376 execute_pushpop_property (one, sym,
377 ly_symbol2scm ("direction"), scm_from_int (1));
379 execute_pushpop_property (two, sym,
380 ly_symbol2scm ("direction"), scm_from_int (-1));
384 IMPLEMENT_LISTENER (Part_combine_iterator, set_busy);
386 Part_combine_iterator::set_busy (SCM se)
391 Stream_event *e = unsmob_stream_event (se);
393 if (e->in_event_class ("note-event") || e->in_event_class ("cluster-note-event"))
398 Processes a moment in an iterator, and returns whether any new music
402 Part_combine_iterator::try_process (Music_iterator *i, Moment m)
409 notice_busy_ = false;
414 Part_combine_iterator::process (Moment m)
416 Moment now = get_outlet ()->now_mom ();
419 /* This is needed if construct_children was called before iteration
421 if (start_moment_.main_part_.is_infinity () && start_moment_ < 0)
424 for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
426 splitm = unsmob_moment (scm_caar (split_list_));
427 if (splitm && *splitm + start_moment_ > now)
430 SCM tag = scm_cdar (split_list_);
432 if (tag == ly_symbol2scm ("chords"))
434 else if (tag == ly_symbol2scm ("apart")
435 || tag == ly_symbol2scm ("apart-silence")
436 || tag == ly_symbol2scm ("apart-spanner"))
437 apart (tag == ly_symbol2scm ("apart-silence"));
438 else if (tag == ly_symbol2scm ("unisono"))
440 else if (tag == ly_symbol2scm ("unisilence"))
442 else if (tag == ly_symbol2scm ("solo1"))
444 else if (tag == ly_symbol2scm ("solo2"))
446 else if (scm_is_symbol (tag))
448 string s = "Unknown split directive: "
449 + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
450 programming_error (s);
454 if (first_iter_->ok ())
456 if (try_process (first_iter_, m))
457 last_playing_ = SOLO1;
460 if (second_iter_->ok ())
462 if (try_process (second_iter_, m))
463 last_playing_ = SOLO2;
467 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);