]> git.donarmstrong.com Git - lilypond.git/blob - lily/lyric-combine-music-iterator.cc
Update source file headers. Fixes using standard GNU package conventions.
[lilypond.git] / lily / lyric-combine-music-iterator.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2004--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "dispatcher.hh"
21 #include "global-context.hh"
22 #include "grob.hh"
23 #include "input.hh"
24 #include "international.hh"
25 #include "listener.hh"
26 #include "music-iterator.hh"
27 #include "music.hh"
28
29 /*
30   This iterator is hairy.  It tracks both lyric and melody contexts,
31   and has a complicated communication route, reading/writing
32   properties in both.
33
34   In the future, this should rather be done with
35
36      \interpretAsMelodyFor { MUSIC } { LYRICS LYRICS LYRICS }
37
38   This can run an interpret step on MUSIC, generating a stream.  Then
39   the stream can be perused at leisure to apply durations to all of
40   the LYRICS.
41 */
42
43 class Lyric_combine_music_iterator : public Music_iterator
44 {
45 public:
46   Lyric_combine_music_iterator ();
47   Lyric_combine_music_iterator (Lyric_combine_music_iterator const &src);
48   DECLARE_SCHEME_CALLBACK (constructor, ());
49 protected:
50   virtual void construct_children ();
51   virtual Moment pending_moment () const;
52   virtual void do_quit ();
53   virtual void process (Moment);
54   virtual bool run_always ()const;
55   virtual bool ok () const;
56   virtual void derived_mark () const;
57   virtual void derived_substitute (Context *, Context *);
58   void set_music_context (Context *to);
59 private:
60   bool start_new_syllable () const;
61   Context *find_voice ();
62   DECLARE_LISTENER (set_busy);
63   DECLARE_LISTENER (check_new_context);
64
65   bool music_found_;
66   Context *lyrics_context_;
67   Context *music_context_;
68   SCM lyricsto_voice_name_;
69
70   Moment busy_moment_;
71   Moment pending_grace_moment_;
72   
73   Music_iterator *lyric_iter_;
74 };
75
76 Lyric_combine_music_iterator::Lyric_combine_music_iterator ()
77 {
78   music_found_ = false;
79   pending_grace_moment_.set_infinite (1);
80   lyric_iter_ = 0;
81   music_context_ = 0;
82   lyrics_context_ = 0;
83   busy_moment_.set_infinite (-1);
84 }
85
86
87 /*
88   It's dubious whether we can ever make this fully work.  Due to
89   associatedVoice switching, this routine may be triggered for 
90   the wrong music_context_ 
91  */
92 IMPLEMENT_LISTENER (Lyric_combine_music_iterator, set_busy)
93 void
94 Lyric_combine_music_iterator::set_busy (SCM se)
95 {
96   Stream_event *e = unsmob_stream_event (se);
97
98   if ((e->in_event_class ("note-event") || e->in_event_class ("cluster-note-event"))
99       && music_context_)
100     
101     busy_moment_ = max (music_context_->now_mom (),
102                         busy_moment_);
103   
104 }
105
106 void
107 Lyric_combine_music_iterator::set_music_context (Context *to)
108 {
109   if (music_context_)
110     {
111       music_context_->event_source ()->
112         remove_listener (GET_LISTENER (set_busy), ly_symbol2scm ("music-event"));
113     }
114
115   music_context_ = to;
116   if (to)
117     {
118       to->event_source ()->add_listener (GET_LISTENER (set_busy),
119                                          ly_symbol2scm ("music-event"));
120     }
121 }
122
123 bool
124 Lyric_combine_music_iterator::start_new_syllable () const
125 {
126   if (busy_moment_ < music_context_->now_mom ())
127     return false;
128
129   if (!lyrics_context_)
130     return false;
131
132   if (!to_boolean (lyrics_context_->get_property ("ignoreMelismata")))
133     {
134       bool m = melisma_busy (music_context_);
135       if (m)
136         return false;
137     }
138
139   return true;
140 }
141
142 Moment
143 Lyric_combine_music_iterator::pending_moment () const
144 {
145   Moment m;
146
147   m.set_infinite (1);
148
149   return m;
150 }
151
152 bool
153 Lyric_combine_music_iterator::run_always () const
154 {
155   return true;
156 }
157
158 bool
159 Lyric_combine_music_iterator::ok () const
160 {
161   return lyric_iter_ && lyric_iter_->ok ();
162 }
163
164 void
165 Lyric_combine_music_iterator::derived_mark ()const
166 {
167   if (lyric_iter_)
168     scm_gc_mark (lyric_iter_->self_scm ());
169   if (lyrics_context_)
170     scm_gc_mark (lyrics_context_->self_scm ());
171   if (music_context_)
172     scm_gc_mark (music_context_->self_scm ());
173 }
174
175 void
176 Lyric_combine_music_iterator::derived_substitute (Context *f, Context *t)
177 {
178   if (lyric_iter_)
179     lyric_iter_->substitute_outlet (f, t);
180   if (lyrics_context_ && lyrics_context_ == f)
181     lyrics_context_ = t;
182   if (music_context_ && music_context_ == f)
183     set_music_context (t);
184 }
185
186 void
187 Lyric_combine_music_iterator::construct_children ()
188 {
189   Music *m = unsmob_music (get_music ()->get_property ("element"));
190   lyric_iter_ = unsmob_iterator (get_iterator (m));
191   if (!lyric_iter_)
192     return;
193   lyrics_context_ = find_context_below (lyric_iter_->get_outlet (),
194                                         ly_symbol2scm ("Lyrics"), "");
195
196   if (!lyrics_context_)
197     {
198       m->origin ()->warning ("argument of \\lyricsto should contain Lyrics context");
199     }
200   
201   lyricsto_voice_name_ = get_music ()->get_property ("associated-context");
202
203   Context *voice = find_voice ();
204   if (voice)
205     set_music_context (voice);
206
207   /*
208     Wait for a Create_context event. If this isn't done, lyrics can be 
209     delayed when voices are created implicitly.
210   */
211   Global_context *g = get_outlet ()->get_global_context ();
212   g->events_below ()->add_listener (GET_LISTENER (check_new_context), ly_symbol2scm ("CreateContext"));
213
214   /*
215     We do not create a Lyrics context, because the user might
216     create one with a different name, and then we will not find that
217     one.
218   */
219 }
220
221 IMPLEMENT_LISTENER (Lyric_combine_music_iterator, check_new_context)
222 void
223 Lyric_combine_music_iterator::check_new_context (SCM sev)
224 {
225   if (!ok ())
226     return ;
227   
228   // TODO: Check first if type=Voice and if id matches
229   Stream_event * ev = unsmob_stream_event (sev);
230   if (ev->get_property ("type") != ly_symbol2scm ("Voice"))
231     return ;
232   
233   Context *voice = find_voice ();
234
235   if (voice)
236     {
237       set_music_context (voice);
238     }
239 }
240
241 /*
242   Look for a suitable voice to align lyrics to.
243
244   Returns 0 if nothing should change; i.e., if we already listen to the
245   right voice, or if we don't yet listen to a voice but no appropriate
246   voice could be found.
247 */
248 Context *
249 Lyric_combine_music_iterator::find_voice ()
250 {
251   SCM voice_name = lyricsto_voice_name_;
252   SCM running = lyrics_context_
253     ? lyrics_context_->get_property ("associatedVoice")
254     : SCM_EOL;
255
256   if (scm_is_string (running))
257     voice_name = running;
258
259   if (scm_is_string (voice_name)
260       && (!music_context_ || ly_scm2string (voice_name) != music_context_->id_string ()))
261     {
262       Context *t = get_outlet ();
263       while (t && t->get_parent_context ())
264         t = t->get_parent_context ();
265
266       string name = ly_scm2string (voice_name);
267       return find_context_below (t, ly_symbol2scm ("Voice"), name);
268     }
269
270   return 0;
271 }
272
273 void
274 Lyric_combine_music_iterator::process (Moment /* when */)
275 {
276   /* see if associatedVoice has been changed */
277   Context *new_voice = find_voice ();
278   if (new_voice)
279     set_music_context (new_voice);
280
281   if (!music_context_)
282     return;
283
284   if (!music_context_->get_parent_context ())
285     {
286       /*
287         The melody has died.
288         We die too.
289       */
290       if (lyrics_context_)
291         lyrics_context_->unset_property (ly_symbol2scm ("associatedVoiceContext"));
292       lyric_iter_ = 0;
293       set_music_context (0);
294     }
295
296
297   if (music_context_
298       && (start_new_syllable () ||
299           (busy_moment_ >= pending_grace_moment_))
300       && lyric_iter_->ok ())
301     {
302       Moment now = music_context_->now_mom ();
303       if (now.grace_part_)
304         {
305           pending_grace_moment_ = now;
306           pending_grace_moment_.grace_part_ = Rational (0);
307           return;
308         }
309       else
310         {
311           pending_grace_moment_.set_infinite (1);
312         }
313       
314       Moment m = lyric_iter_->pending_moment ();
315       lyrics_context_->set_property (ly_symbol2scm ("associatedVoiceContext"),
316                                      music_context_->self_scm ());
317       lyric_iter_->process (m);
318
319       music_found_ = true;
320     }
321
322   new_voice = find_voice ();
323   if (new_voice)
324     set_music_context (new_voice);
325 }
326
327 void
328 Lyric_combine_music_iterator::do_quit ()
329 {
330   if (!music_found_)
331     {
332       SCM voice_name = get_music ()->get_property ("associated-context");
333
334       string name;
335       if (scm_is_string (voice_name))
336         name = ly_scm2string (voice_name);
337
338       get_music ()->origin ()->warning (_f ("cannot find Voice `%s'",
339                                             name.c_str ()) + "\n");
340     }
341
342   if (lyric_iter_)
343     lyric_iter_->quit ();
344 }
345
346 IMPLEMENT_CTOR_CALLBACK (Lyric_combine_music_iterator);