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
10 #include "dispatcher.hh"
11 #include "lily-guile.hh"
13 #include "music-iterator.hh"
14 #include "music-sequence.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
30 Part_combine_iterator ();
32 DECLARE_SCHEME_CALLBACK (constructor
, ());
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;
45 /* used by try_process */
46 DECLARE_LISTENER (set_busy
);
50 bool try_process (Music_iterator
*i
, Moment m
);
52 Music_iterator
*first_iter_
;
53 Music_iterator
*second_iter_
;
58 Stream_event
*unisono_event_
;
59 Stream_event
*solo_one_event_
;
60 Stream_event
*solo_two_event_
;
61 Stream_event
*mmrest_event_
;
73 Status playing_state_
;
76 Should be SOLO1 or SOLO2
81 TODO: this is getting of hand...
83 Context_handle handles_
[NUM_OUTLETS
];
85 void substitute_both (Outlet_type to1
,
88 /* parameter is really Outlet_type */
89 void kill_mmrest (int in
);
90 void chords_together ();
93 void apart (bool silent
);
94 void unisono (bool silent
);
98 Part_combine_iterator::do_quit ()
101 first_iter_
->quit ();
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 ()
124 split_list_
= SCM_EOL
;
126 playing_state_
= APART
;
130 Part_combine_iterator::derived_mark () const
133 scm_gc_mark (first_iter_
->self_scm ());
135 scm_gc_mark (second_iter_
->self_scm ());
137 Stream_event
*ptrs
[] = {
144 for (int i
= 0; ptrs
[i
]; i
++)
146 scm_gc_mark (ptrs
[i
]->self_scm ());
150 Part_combine_iterator::derived_substitute (Context
*f
,
154 first_iter_
->substitute_outlet (f
, t
);
158 Part_combine_iterator::pending_moment () const
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 ());
171 Part_combine_iterator::ok () const
173 return first_iter_
->ok () || second_iter_
->ok ();
177 Part_combine_iterator::chords_together ()
179 if (state_
== TOGETHER
)
183 playing_state_
= TOGETHER
;
186 substitute_both (CONTEXT_SHARED
, CONTEXT_SHARED
);
191 Part_combine_iterator::kill_mmrest (int in
)
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_
);
205 Part_combine_iterator::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
;
232 Part_combine_iterator::substitute_both (Outlet_type to1
,
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
++)
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
)
254 Part_combine_iterator::unisono (bool silent
)
256 Status newstate
= (silent
) ? UNISILENCE
: UNISONO
;
258 if (newstate
== state_
)
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
)
279 unisono_event_
= new Stream_event (ly_symbol2scm ("unisono-event"));
280 unisono_event_
->unprotect ();
284 Context
*out
= (last_playing_
== SOLO2
? second_iter_
: first_iter_
)
286 out
->event_source ()->broadcast (unisono_event_
);
287 playing_state_
= UNISONO
;
294 Part_combine_iterator::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
;
319 Part_combine_iterator::apart (bool silent
)
322 playing_state_
= APART
;
329 substitute_both (CONTEXT_ONE
, CONTEXT_TWO
);
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 ();
354 first_iter_
= unsmob_iterator (get_iterator (unsmob_music (scm_car (lst
))));
355 Context
*two
= handles_
[CONTEXT_TWO
].get_outlet ();
357 second_iter_
= unsmob_iterator (get_iterator (unsmob_music (scm_cadr (lst
))));
362 "DynamicLineSpanner",
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
);
385 Part_combine_iterator::set_busy (SCM se
)
390 Stream_event
*e
= unsmob_stream_event (se
);
392 if (e
->in_event_class ("note-event") || e
->in_event_class ("cluster-note-event"))
397 Processes a moment in an iterator, and returns whether any new music
401 Part_combine_iterator::try_process (Music_iterator
*i
, Moment m
)
408 notice_busy_
= false;
413 Part_combine_iterator::process (Moment m
)
415 Moment now
= get_outlet ()->now_mom ();
418 /* This is needed if construct_children was called before iteration
420 if (start_moment_
.main_part_
.is_infinity () && start_moment_
< 0)
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
)
429 SCM tag
= scm_cdar (split_list_
);
431 if (tag
== ly_symbol2scm ("chords"))
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"))
439 else if (tag
== ly_symbol2scm ("unisilence"))
441 else if (tag
== ly_symbol2scm ("solo1"))
443 else if (tag
== ly_symbol2scm ("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
);