]> git.donarmstrong.com Git - lilypond.git/blob - lily/lyric-combine-music-iterator.cc
Merge branch 'master' of git://git.sv.gnu.org/lilypond
[lilypond.git] / lily / lyric-combine-music-iterator.cc
1 /*
2   new-lyric-combine-iterator.cc -- implement Lyric_combine_music_iterator
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2004--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "dispatcher.hh"
10 #include "global-context.hh"
11 #include "grob.hh"
12 #include "input.hh"
13 #include "international.hh"
14 #include "listener.hh"
15 #include "music-iterator.hh"
16 #include "music.hh"
17
18 /*
19   This iterator is hairy.  It tracks both lyric and melody contexts,
20   and has a complicated communication route, reading/writing
21   properties in both.
22
23   In the future, this should rather be done with
24
25      \interpretAsMelodyFor { MUSIC } { LYRICS LYRICS LYRICS }
26
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
29   the LYRICS.
30 */
31
32 class Lyric_combine_music_iterator : public Music_iterator
33 {
34 public:
35   Lyric_combine_music_iterator ();
36   Lyric_combine_music_iterator (Lyric_combine_music_iterator const &src);
37   DECLARE_SCHEME_CALLBACK (constructor, ());
38 protected:
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);
48 private:
49   bool start_new_syllable () const;
50   Context *find_voice ();
51   DECLARE_LISTENER (set_busy);
52   DECLARE_LISTENER (check_new_context);
53
54   bool pending_grace_lyric_;
55   bool music_found_;
56   Context *lyrics_context_;
57   Context *music_context_;
58   SCM lyricsto_voice_name_;
59
60   bool busy_;
61   Music_iterator *lyric_iter_;
62 };
63
64 Lyric_combine_music_iterator::Lyric_combine_music_iterator ()
65 {
66   music_found_ = false;
67   pending_grace_lyric_ = false;
68   lyric_iter_ = 0;
69   music_context_ = 0;
70   lyrics_context_ = 0;
71   busy_ = false;
72 }
73
74 IMPLEMENT_LISTENER (Lyric_combine_music_iterator, set_busy)
75 void
76 Lyric_combine_music_iterator::set_busy (SCM se)
77 {
78   Stream_event *e = unsmob_stream_event (se);
79
80   if (e->in_event_class ("note-event") || e->in_event_class ("cluster-note-event"))
81     busy_ = true;
82 }
83
84 void
85 Lyric_combine_music_iterator::set_music_context (Context *to)
86 {
87   if (music_context_)
88     {
89       music_context_->event_source()->remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
90       lyrics_context_->unset_property (ly_symbol2scm ("associatedVoiceContext"));
91     }
92
93   music_context_ = to;
94   if (to)
95     {
96       to->event_source()->add_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
97       if (lyrics_context_)
98         lyrics_context_->set_property ("associatedVoiceContext", to->self_scm ());
99     }
100 }
101
102 bool
103 Lyric_combine_music_iterator::start_new_syllable () const
104 {
105   if (!busy_)
106     return false;
107
108   if (!lyrics_context_)
109     return false;
110
111   if (!to_boolean (lyrics_context_->get_property ("ignoreMelismata")))
112     {
113       bool m = melisma_busy (music_context_);
114       if (m)
115         return false;
116     }
117
118   return true;
119 }
120
121 Moment
122 Lyric_combine_music_iterator::pending_moment () const
123 {
124   Moment m;
125
126   m.set_infinite (1);
127
128   return m;
129 }
130
131 bool
132 Lyric_combine_music_iterator::run_always () const
133 {
134   return true;
135 }
136
137 bool
138 Lyric_combine_music_iterator::ok () const
139 {
140   return lyric_iter_ && lyric_iter_->ok ();
141 }
142
143 void
144 Lyric_combine_music_iterator::derived_mark ()const
145 {
146   if (lyric_iter_)
147     scm_gc_mark (lyric_iter_->self_scm ());
148   if (lyrics_context_)
149     scm_gc_mark (lyrics_context_->self_scm ());
150   if (music_context_)
151     scm_gc_mark (music_context_->self_scm ());
152 }
153
154 void
155 Lyric_combine_music_iterator::derived_substitute (Context *f, Context *t)
156 {
157   if (lyric_iter_)
158     lyric_iter_->substitute_outlet (f, t);
159   if (lyrics_context_ && lyrics_context_ == f)
160     lyrics_context_ = t;
161   if (music_context_ && music_context_ == f)
162     set_music_context (t);
163 }
164
165 void
166 Lyric_combine_music_iterator::construct_children ()
167 {
168   Music *m = unsmob_music (get_music ()->get_property ("element"));
169   lyric_iter_ = unsmob_iterator (get_iterator (m));
170   if (!lyric_iter_)
171     return;
172   lyrics_context_ = find_context_below (lyric_iter_->get_outlet (),
173                                         ly_symbol2scm ("Lyrics"), "");
174
175   if (!lyrics_context_)
176     {
177       m->origin ()->warning ("argument of \\lyricsto should contain Lyrics context");
178     }
179   
180   lyricsto_voice_name_ = get_music ()->get_property ("associated-context");
181
182   Context *voice = find_voice ();
183   if (voice)
184     set_music_context (voice);
185
186   /*
187     Wait for a Create_context event. If this isn't done, lyrics can be 
188     delayed when voices are created implicitly.
189   */
190   Global_context *g = get_outlet ()->get_global_context ();
191   g->events_below ()->add_listener (GET_LISTENER (check_new_context), ly_symbol2scm ("CreateContext"));
192
193   /*
194     We do not create a Lyrics context, because the user might
195     create one with a different name, and then we will not find that
196     one.
197   */
198 }
199
200 IMPLEMENT_LISTENER (Lyric_combine_music_iterator, check_new_context)
201 void
202 Lyric_combine_music_iterator::check_new_context (SCM sev)
203 {
204   // TODO: Check first if type=Voice and if id matches
205   Stream_event * ev = unsmob_stream_event (sev);
206   if (ev->get_property ("type") != ly_symbol2scm ("Voice"))
207     return ;
208   
209   Context *voice = find_voice ();
210
211   if (voice)
212     {
213       set_music_context (voice);
214     }
215 }
216
217 /*
218   Look for a suitable voice to align lyrics to.
219
220   Returns 0 if nothing should change; i.e., if we already listen to the
221   right voice, or if we don't yet listen to a voice but no appropriate
222   voice could be found.
223 */
224 Context *
225 Lyric_combine_music_iterator::find_voice ()
226 {
227   SCM voice_name = lyricsto_voice_name_;
228   SCM running = lyrics_context_
229     ? lyrics_context_->get_property ("associatedVoice")
230     : SCM_EOL;
231
232   if (scm_is_string (running))
233     voice_name = running;
234
235   if (scm_is_string (voice_name)
236       && (!music_context_ || ly_scm2string (voice_name) != music_context_->id_string ()))
237     {
238       Context *t = get_outlet ();
239       while (t && t->get_parent_context ())
240         t = t->get_parent_context ();
241
242       string name = ly_scm2string (voice_name);
243       return find_context_below (t, ly_symbol2scm ("Voice"), name);
244     }
245
246   return 0;
247 }
248
249 void
250 Lyric_combine_music_iterator::process (Moment)
251 {
252   /* see if associatedVoice has been changed */
253   Context *new_voice = find_voice ();
254   if (new_voice)
255     set_music_context (new_voice);
256
257   if (!music_context_)
258     return;
259
260   if (!music_context_->get_parent_context ())
261     {
262       /*
263         The melody has died.
264         We die too.
265       */
266       if (lyrics_context_)
267         lyrics_context_->unset_property (ly_symbol2scm ("associatedVoiceContext"));
268       lyric_iter_ = 0;
269       set_music_context (0);
270     }
271
272
273   if (music_context_
274       && (start_new_syllable () || pending_grace_lyric_)
275       && lyric_iter_->ok ())
276     {
277       if (music_context_->now_mom ().grace_part_)
278         {
279           pending_grace_lyric_ = true;
280           return;
281         }
282       else
283         pending_grace_lyric_ = false;
284       
285       Moment m = lyric_iter_->pending_moment ();
286       lyric_iter_->process (m);
287
288       music_found_ = true;
289     }
290
291   busy_ = false;
292 }
293
294 void
295 Lyric_combine_music_iterator::do_quit ()
296 {
297   if (!music_found_)
298     {
299       SCM voice_name = get_music ()->get_property ("associated-context");
300
301       string name;
302       if (scm_is_string (voice_name))
303         name = ly_scm2string (voice_name);
304
305       get_music ()->origin ()->warning (_f ("cannot find Voice `%s'",
306                                             name.c_str ()) + "\n");
307     }
308
309   if (lyric_iter_)
310     lyric_iter_->quit ();
311 }
312
313 IMPLEMENT_CTOR_CALLBACK (Lyric_combine_music_iterator);