2 new-lyric-combine-iterator.cc -- implement Lyric_combine_music_iterator
4 source file of the GNU LilyPond music typesetter
6 (c) 2004--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "dispatcher.hh"
10 #include "global-context.hh"
13 #include "international.hh"
14 #include "listener.hh"
15 #include "music-iterator.hh"
19 This iterator is hairy. It tracks both lyric and melody contexts,
20 and has a complicated communication route, reading/writing
23 In the future, this should rather be done with
25 \interpretAsMelodyFor { MUSIC } { LYRICS LYRICS LYRICS }
27 This can run an interpret step on MUSIC, generating a stream. Then
28 the stream can be perused at leisure to apply durations to all of
32 class Lyric_combine_music_iterator
: public Music_iterator
35 Lyric_combine_music_iterator ();
36 Lyric_combine_music_iterator (Lyric_combine_music_iterator
const &src
);
37 DECLARE_SCHEME_CALLBACK (constructor
, ());
39 virtual void construct_children ();
40 virtual Moment
pending_moment () const;
41 virtual void do_quit ();
42 virtual void process (Moment
);
43 virtual bool run_always ()const;
44 virtual bool ok () const;
45 virtual void derived_mark () const;
46 virtual void derived_substitute (Context
*, Context
*);
47 void set_music_context (Context
*to
);
49 bool start_new_syllable () const;
50 Context
*find_voice ();
51 DECLARE_LISTENER (set_busy
);
52 DECLARE_LISTENER (check_new_context
);
55 Context
*lyrics_context_
;
56 Context
*music_context_
;
57 SCM lyricsto_voice_name_
;
60 Moment pending_grace_moment_
;
62 Music_iterator
*lyric_iter_
;
65 Lyric_combine_music_iterator::Lyric_combine_music_iterator ()
68 pending_grace_moment_
.set_infinite (1);
72 busy_moment_
.set_infinite (-1);
77 It's dubious whether we can ever make this fully work. Due to
78 associatedVoice switching, this routine may be triggered for
79 the wrong music_context_
81 IMPLEMENT_LISTENER (Lyric_combine_music_iterator
, set_busy
)
83 Lyric_combine_music_iterator::set_busy (SCM se
)
85 Stream_event
*e
= unsmob_stream_event (se
);
87 if ((e
->in_event_class ("note-event") || e
->in_event_class ("cluster-note-event"))
90 busy_moment_
= max (music_context_
->now_mom (),
96 Lyric_combine_music_iterator::set_music_context (Context
*to
)
100 music_context_
->event_source ()->
101 remove_listener (GET_LISTENER (set_busy
), ly_symbol2scm ("music-event"));
107 to
->event_source ()->add_listener (GET_LISTENER (set_busy
),
108 ly_symbol2scm ("music-event"));
113 Lyric_combine_music_iterator::start_new_syllable () const
115 if (busy_moment_
< music_context_
->now_mom ())
118 if (!lyrics_context_
)
121 if (!to_boolean (lyrics_context_
->get_property ("ignoreMelismata")))
123 bool m
= melisma_busy (music_context_
);
132 Lyric_combine_music_iterator::pending_moment () const
142 Lyric_combine_music_iterator::run_always () const
148 Lyric_combine_music_iterator::ok () const
150 return lyric_iter_
&& lyric_iter_
->ok ();
154 Lyric_combine_music_iterator::derived_mark ()const
157 scm_gc_mark (lyric_iter_
->self_scm ());
159 scm_gc_mark (lyrics_context_
->self_scm ());
161 scm_gc_mark (music_context_
->self_scm ());
165 Lyric_combine_music_iterator::derived_substitute (Context
*f
, Context
*t
)
168 lyric_iter_
->substitute_outlet (f
, t
);
169 if (lyrics_context_
&& lyrics_context_
== f
)
171 if (music_context_
&& music_context_
== f
)
172 set_music_context (t
);
176 Lyric_combine_music_iterator::construct_children ()
178 Music
*m
= unsmob_music (get_music ()->get_property ("element"));
179 lyric_iter_
= unsmob_iterator (get_iterator (m
));
182 lyrics_context_
= find_context_below (lyric_iter_
->get_outlet (),
183 ly_symbol2scm ("Lyrics"), "");
185 if (!lyrics_context_
)
187 m
->origin ()->warning ("argument of \\lyricsto should contain Lyrics context");
190 lyricsto_voice_name_
= get_music ()->get_property ("associated-context");
192 Context
*voice
= find_voice ();
194 set_music_context (voice
);
197 Wait for a Create_context event. If this isn't done, lyrics can be
198 delayed when voices are created implicitly.
200 Global_context
*g
= get_outlet ()->get_global_context ();
201 g
->events_below ()->add_listener (GET_LISTENER (check_new_context
), ly_symbol2scm ("CreateContext"));
204 We do not create a Lyrics context, because the user might
205 create one with a different name, and then we will not find that
210 IMPLEMENT_LISTENER (Lyric_combine_music_iterator
, check_new_context
)
212 Lyric_combine_music_iterator::check_new_context (SCM sev
)
217 // TODO: Check first if type=Voice and if id matches
218 Stream_event
* ev
= unsmob_stream_event (sev
);
219 if (ev
->get_property ("type") != ly_symbol2scm ("Voice"))
222 Context
*voice
= find_voice ();
226 set_music_context (voice
);
231 Look for a suitable voice to align lyrics to.
233 Returns 0 if nothing should change; i.e., if we already listen to the
234 right voice, or if we don't yet listen to a voice but no appropriate
235 voice could be found.
238 Lyric_combine_music_iterator::find_voice ()
240 SCM voice_name
= lyricsto_voice_name_
;
241 SCM running
= lyrics_context_
242 ? lyrics_context_
->get_property ("associatedVoice")
245 if (scm_is_string (running
))
246 voice_name
= running
;
248 if (scm_is_string (voice_name
)
249 && (!music_context_
|| ly_scm2string (voice_name
) != music_context_
->id_string ()))
251 Context
*t
= get_outlet ();
252 while (t
&& t
->get_parent_context ())
253 t
= t
->get_parent_context ();
255 string name
= ly_scm2string (voice_name
);
256 return find_context_below (t
, ly_symbol2scm ("Voice"), name
);
263 Lyric_combine_music_iterator::process (Moment
/* when */)
265 /* see if associatedVoice has been changed */
266 Context
*new_voice
= find_voice ();
268 set_music_context (new_voice
);
273 if (!music_context_
->get_parent_context ())
280 lyrics_context_
->unset_property (ly_symbol2scm ("associatedVoiceContext"));
282 set_music_context (0);
287 && (start_new_syllable () ||
288 (busy_moment_
>= pending_grace_moment_
))
289 && lyric_iter_
->ok ())
291 Moment now
= music_context_
->now_mom ();
294 pending_grace_moment_
= now
;
295 pending_grace_moment_
.grace_part_
= Rational (0);
300 pending_grace_moment_
.set_infinite (1);
303 Moment m
= lyric_iter_
->pending_moment ();
304 lyrics_context_
->set_property (ly_symbol2scm ("associatedVoiceContext"),
305 music_context_
->self_scm ());
306 lyric_iter_
->process (m
);
311 new_voice
= find_voice ();
313 set_music_context (new_voice
);
317 Lyric_combine_music_iterator::do_quit ()
321 SCM voice_name
= get_music ()->get_property ("associated-context");
324 if (scm_is_string (voice_name
))
325 name
= ly_scm2string (voice_name
);
327 get_music ()->origin ()->warning (_f ("cannot find Voice `%s'",
328 name
.c_str ()) + "\n");
332 lyric_iter_
->quit ();
335 IMPLEMENT_CTOR_CALLBACK (Lyric_combine_music_iterator
);