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 class Part_combine_iterator : public Music_iterator
21 Part_combine_iterator ();
23 DECLARE_SCHEME_CALLBACK (constructor, ());
25 virtual void derived_substitute (Context *f, Context *t);
26 virtual void derived_mark () const;
27 Part_combine_iterator (Part_combine_iterator const &);
29 virtual void construct_children ();
30 virtual Moment pending_moment () const;
31 virtual void do_quit ();
32 virtual void process (Moment);
34 virtual bool ok () const;
37 /* used by try_process */
38 DECLARE_LISTENER (set_busy);
42 bool try_process (Music_iterator *i, Moment m);
44 Music_iterator *first_iter_;
45 Music_iterator *second_iter_;
60 Status playing_state_;
63 Should be SOLO1 or SOLO2
68 TODO: this is getting of hand...
73 Context_handle shared_;
76 void substitute_both (Context *to1,
79 void kill_mmrest (Context *);
80 void chords_together ();
83 void apart (bool silent);
84 void unisono (bool silent);
88 Part_combine_iterator::do_quit ()
93 second_iter_->quit ();
95 null_.set_context (0);
98 shared_.set_context (0);
99 solo_.set_context (0);
102 Part_combine_iterator::Part_combine_iterator ()
106 split_list_ = SCM_EOL;
108 playing_state_ = APART;
112 Part_combine_iterator::derived_mark () const
115 scm_gc_mark (first_iter_->self_scm ());
117 scm_gc_mark (second_iter_->self_scm ());
121 Part_combine_iterator::derived_substitute (Context *f,
125 first_iter_->substitute_outlet (f, t);
129 Part_combine_iterator::pending_moment () const
133 if (first_iter_->ok ())
134 p = min (p, first_iter_->pending_moment ());
136 if (second_iter_->ok ())
137 p = min (p, second_iter_->pending_moment ());
142 Part_combine_iterator::ok () const
144 return first_iter_->ok () || second_iter_->ok ();
148 Part_combine_iterator::chords_together ()
150 if (state_ == TOGETHER)
154 playing_state_ = TOGETHER;
157 substitute_both (shared_.get_outlet (), shared_.get_outlet ());
162 Part_combine_iterator::kill_mmrest (Context *tg)
164 static Music *mmrest;
167 mmrest = make_music_by_name (ly_symbol2scm ("MultiMeasureRestEvent"));
168 mmrest->set_property ("duration", SCM_EOL);
171 mmrest->send_to_context (tg);
175 Part_combine_iterator::solo1 ()
182 substitute_both (solo_.get_outlet (),
183 null_.get_outlet ());
185 kill_mmrest (two_.get_outlet ());
186 kill_mmrest (shared_.get_outlet ());
188 if (playing_state_ != SOLO1)
192 event = make_music_by_name (ly_symbol2scm ("SoloOneEvent"));
194 event->send_to_context (first_iter_->get_outlet ());
196 playing_state_ = SOLO1;
201 Part_combine_iterator::substitute_both (Context *to1,
204 Context *tos[] = {to1, to2};
205 Music_iterator *mis[] = {first_iter_, second_iter_};
214 for (int i = 0; i < 2; i++)
216 for (int j = 0; hs[j]; j++)
217 if (hs[j]->get_outlet () != tos[i])
218 mis[i]->substitute_outlet (hs[j]->get_outlet (), tos[i]);
221 for (int j = 0; hs[j]; j++)
223 Context *t = hs[j]->get_outlet ();
224 if (t != to1 && t != to2)
230 Part_combine_iterator::unisono (bool silent)
232 Status newstate = (silent) ? UNISILENCE : UNISONO;
234 if (newstate == state_)
239 If we're coming from SOLO2 state, we might have kill mmrests
240 in the 1st voice, so in that case, we use the second voice
241 as a basis for events.
243 Context *c1 = (last_playing_ == SOLO2) ? null_.get_outlet () : shared_.get_outlet ();
244 Context *c2 = (last_playing_ == SOLO2) ? shared_.get_outlet () : null_.get_outlet ();
245 substitute_both (c1, c2);
246 kill_mmrest ((last_playing_ == SOLO2)
247 ? one_.get_outlet () : two_.get_outlet ());
248 kill_mmrest (shared_.get_outlet ());
250 if (playing_state_ != UNISONO
251 && newstate == UNISONO)
255 event = make_music_by_name (ly_symbol2scm ("UnisonoEvent"));
257 Context *out = (last_playing_ == SOLO2 ? second_iter_ : first_iter_)
259 event->send_to_context (out);
260 playing_state_ = UNISONO;
267 Part_combine_iterator::solo2 ()
275 substitute_both (null_.get_outlet (), solo_.get_outlet ());
277 if (playing_state_ != SOLO2)
281 event = make_music_by_name (ly_symbol2scm ("SoloTwoEvent"));
283 event->send_to_context (second_iter_->get_outlet ());
284 playing_state_ = SOLO2;
290 Part_combine_iterator::apart (bool silent)
293 playing_state_ = APART;
300 substitute_both (one_.get_outlet (), two_.get_outlet ());
305 Part_combine_iterator::construct_children ()
307 start_moment_ = get_outlet ()->now_mom ();
308 split_list_ = get_music ()->get_property ("split-list");
309 SCM lst = get_music ()->get_property ("elements");
311 SCM props = scm_list_n (/*
312 used to have tweaks here.
318 = get_outlet ()->find_create_context (ly_symbol2scm ("Voice"),
321 shared_.set_context (tr);
324 If we don't, we get a new staff for every Voice.
329 = get_outlet ()->find_create_context (ly_symbol2scm ("Voice"),
332 solo_.set_context (solo_tr);
335 = get_outlet ()->find_create_context (ly_symbol2scm ("Devnull"),
339 programming_error ("no Devnull found");
341 null_.set_context (null);
343 Context *one = tr->find_create_context (ly_symbol2scm ("Voice"),
346 one_.set_context (one);
349 first_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_car (lst))));
351 Context *two = tr->find_create_context (ly_symbol2scm ("Voice"),
353 two_.set_context (two);
355 second_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_cadr (lst))));
362 "DynamicLineSpanner",
372 // Add listeners to all contexts except Devnull.
373 Context *contexts[] = {one, two, solo_tr, tr, 0};
374 for (int i = 0; contexts[i]; i++)
376 contexts[i]->event_source ()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("OldMusicEvent"));
379 for (char const **p = syms; *p; p++)
381 SCM sym = ly_symbol2scm (*p);
382 execute_pushpop_property (one, sym,
383 ly_symbol2scm ("direction"), scm_from_int (1));
385 execute_pushpop_property (two, sym,
386 ly_symbol2scm ("direction"), scm_from_int (-1));
390 IMPLEMENT_LISTENER (Part_combine_iterator, set_busy);
392 Part_combine_iterator::set_busy (SCM se)
397 Stream_event *e = unsmob_stream_event (se);
398 SCM mus = e->get_property ("music");
399 Music *m = unsmob_music (mus);
402 if (m->is_mus_type ("note-event") || m->is_mus_type ("cluster-note-event"))
407 * Processes a moment in an iterator, and returns whether any new music was reported.
410 Part_combine_iterator::try_process (Music_iterator *i, Moment m)
417 notice_busy_ = false;
422 Part_combine_iterator::process (Moment m)
424 Moment now = get_outlet ()->now_mom ();
427 /* This is needed if construct_children was called before iteration
429 if (start_moment_.main_part_.is_infinity () && start_moment_ < 0)
432 for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
434 splitm = unsmob_moment (scm_caar (split_list_));
435 if (splitm && *splitm + start_moment_ > now)
438 SCM tag = scm_cdar (split_list_);
440 if (tag == ly_symbol2scm ("chords"))
442 else if (tag == ly_symbol2scm ("apart")
443 || tag == ly_symbol2scm ("apart-silence")
444 || tag == ly_symbol2scm ("apart-spanner"))
445 apart (tag == ly_symbol2scm ("apart-silence"));
446 else if (tag == ly_symbol2scm ("unisono"))
448 else if (tag == ly_symbol2scm ("unisilence"))
450 else if (tag == ly_symbol2scm ("solo1"))
452 else if (tag == ly_symbol2scm ("solo2"))
454 else if (scm_is_symbol (tag))
456 string s = "Unknown split directive: "
457 + (scm_is_symbol (tag) ? ly_symbol2string (tag) : string ("not a symbol"));
458 programming_error (s);
462 if (first_iter_->ok ())
464 if (try_process (first_iter_, m))
465 last_playing_ = SOLO1;
468 if (second_iter_->ok ())
470 if (try_process (second_iter_, m))
471 last_playing_ = SOLO2;
475 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);