]> git.donarmstrong.com Git - lilypond.git/blob - lily/lyric-combine-music-iterator.cc
Issue 4550 (2/2) Avoid "using namespace std;" in included files
[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--2015 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 using std::string;
30
31 /*
32   This iterator is hairy.  It tracks both lyric and melody contexts,
33   and has a complicated communication route, reading/writing
34   properties in both.
35
36   In the future, this should rather be done with
37
38      \interpretAsMelodyFor { MUSIC } { LYRICS LYRICS LYRICS }
39
40   This can run an interpret step on MUSIC, generating a stream.  Then
41   the stream can be perused at leisure to apply durations to all of
42   the LYRICS.
43 */
44
45 class Lyric_combine_music_iterator : public Music_iterator
46 {
47 public:
48   Lyric_combine_music_iterator ();
49   Lyric_combine_music_iterator (Lyric_combine_music_iterator const &src);
50   DECLARE_SCHEME_CALLBACK (constructor, ());
51 protected:
52   virtual void construct_children ();
53   virtual Moment pending_moment () const;
54   virtual void do_quit ();
55   virtual void process (Moment);
56   virtual bool run_always ()const;
57   virtual bool ok () const;
58   virtual void derived_mark () const;
59   virtual void derived_substitute (Context *, Context *);
60   void set_music_context (Context *to);
61 private:
62   bool start_new_syllable () const;
63   Context *find_voice ();
64   void set_busy (SCM);
65   void check_new_context (SCM);
66
67   bool music_found_;
68   bool lyrics_found_;
69   Context *lyrics_context_;
70   Context *music_context_;
71   SCM lyricsto_voice_name_;
72   SCM lyricsto_voice_type_;
73
74   Moment busy_moment_;
75   Moment pending_grace_moment_;
76
77   Music_iterator *lyric_iter_;
78 };
79
80 Lyric_combine_music_iterator::Lyric_combine_music_iterator ()
81 {
82   music_found_ = false;
83   lyrics_found_ = false;
84   pending_grace_moment_.set_infinite (1);
85   lyric_iter_ = 0;
86   music_context_ = 0;
87   lyrics_context_ = 0;
88   busy_moment_.set_infinite (-1);
89   lyricsto_voice_name_ = SCM_UNDEFINED;
90   lyricsto_voice_type_ = SCM_UNDEFINED;
91 }
92
93 /*
94   It's dubious whether we can ever make this fully work.  Due to
95   associatedVoice switching, this routine may be triggered for
96   the wrong music_context_
97  */
98 void
99 Lyric_combine_music_iterator::set_busy (SCM se)
100 {
101   Stream_event *e = unsmob<Stream_event> (se);
102
103   if ((e->in_event_class ("note-event") || e->in_event_class ("cluster-note-event"))
104       && music_context_)
105
106     busy_moment_ = std::max (music_context_->now_mom (),
107                         busy_moment_);
108
109 }
110
111 void
112 Lyric_combine_music_iterator::set_music_context (Context *to)
113 {
114   if (music_context_)
115     {
116       music_context_->events_below ()->
117       remove_listener (GET_LISTENER (Lyric_combine_music_iterator, set_busy), ly_symbol2scm ("rhythmic-event"));
118     }
119
120   music_context_ = to;
121   if (to)
122     {
123       to->events_below ()->add_listener (GET_LISTENER (Lyric_combine_music_iterator, set_busy),
124                                          ly_symbol2scm ("rhythmic-event"));
125     }
126 }
127
128 bool
129 Lyric_combine_music_iterator::start_new_syllable () const
130 {
131   if (busy_moment_ < music_context_->now_mom ())
132     return false;
133
134   if (!lyrics_context_)
135     return false;
136
137   if (!to_boolean (lyrics_context_->get_property ("ignoreMelismata")))
138     {
139       bool m = melisma_busy (music_context_);
140       if (m)
141         return false;
142     }
143
144   return true;
145 }
146
147 Moment
148 Lyric_combine_music_iterator::pending_moment () const
149 {
150   Moment m;
151
152   m.set_infinite (1);
153
154   return m;
155 }
156
157 bool
158 Lyric_combine_music_iterator::run_always () const
159 {
160   return true;
161 }
162
163 bool
164 Lyric_combine_music_iterator::ok () const
165 {
166   return lyric_iter_ && lyric_iter_->ok ()
167     && !(music_context_ && music_context_->is_removable ());
168 }
169
170 void
171 Lyric_combine_music_iterator::derived_mark ()const
172 {
173   if (lyric_iter_)
174     scm_gc_mark (lyric_iter_->self_scm ());
175   if (lyrics_context_)
176     scm_gc_mark (lyrics_context_->self_scm ());
177   if (music_context_)
178     scm_gc_mark (music_context_->self_scm ());
179   scm_gc_mark (lyricsto_voice_name_);
180   scm_gc_mark (lyricsto_voice_type_);
181 }
182
183 void
184 Lyric_combine_music_iterator::derived_substitute (Context *f, Context *t)
185 {
186   if (lyric_iter_)
187     lyric_iter_->substitute_outlet (f, t);
188   if (lyrics_context_ && lyrics_context_ == f)
189     lyrics_context_ = t;
190   if (music_context_ && music_context_ == f)
191     set_music_context (t);
192 }
193
194 void
195 Lyric_combine_music_iterator::construct_children ()
196 {
197   Music *m = unsmob<Music> (get_music ()->get_property ("element"));
198   lyric_iter_ = unsmob<Music_iterator> (get_iterator (m));
199   if (!lyric_iter_)
200     return;
201   lyrics_context_ = find_context_below (lyric_iter_->get_outlet (),
202                                         ly_symbol2scm ("Lyrics"), "");
203
204   if (!lyrics_context_)
205     {
206       m->origin ()->warning (_ ("argument of \\lyricsto should contain Lyrics context"));
207     }
208
209   lyricsto_voice_name_ = get_music ()->get_property ("associated-context");
210   lyricsto_voice_type_ = get_music ()->get_property ("associated-context-type");
211   if (!scm_is_symbol (lyricsto_voice_type_))
212     lyricsto_voice_type_ = ly_symbol2scm ("Voice");
213
214   Context *voice = find_voice ();
215   if (voice)
216     set_music_context (voice);
217
218   /*
219     Wait for a Create_context event. If this isn't done, lyrics can be
220     delayed when voices are created implicitly.
221   */
222   Global_context *g = get_outlet ()->get_global_context ();
223   g->events_below ()->add_listener (GET_LISTENER (Lyric_combine_music_iterator, check_new_context), ly_symbol2scm ("CreateContext"));
224
225   /*
226     We do not create a Lyrics context, because the user might
227     create one with a different name, and then we will not find that
228     one.
229   */
230 }
231
232 void
233 Lyric_combine_music_iterator::check_new_context (SCM /*sev*/)
234 {
235   if (!ok ())
236     return;
237
238   // Search for a possible candidate voice to attach the lyrics to. If none
239   // is found, we'll try next time again.
240   Context *voice = find_voice ();
241   if (voice)
242     {
243       set_music_context (voice);
244     }
245 }
246
247 /*
248   Look for a suitable voice to align lyrics to.
249
250   Returns 0 if nothing should change; i.e., if we already listen to the
251   right voice, or if we don't yet listen to a voice but no appropriate
252   voice could be found.
253 */
254 Context *
255 Lyric_combine_music_iterator::find_voice ()
256 {
257   SCM voice_name = lyricsto_voice_name_;
258   SCM running = lyrics_context_
259                 ? lyrics_context_->get_property ("associatedVoice")
260                 : SCM_EOL;
261   SCM voice_type = lyricsto_voice_type_;
262   if (scm_is_string (running)) {
263     voice_name = running;
264     voice_type = lyrics_context_->get_property ("associatedVoiceType");
265   }
266
267   if (scm_is_string (voice_name)
268       && (!music_context_ || ly_scm2string (voice_name) != music_context_->id_string ())
269       && scm_is_symbol (voice_type))
270     {
271       return find_context_below (find_top_context (get_outlet ()),
272                                  voice_type, ly_scm2string (voice_name));
273     }
274
275   return 0;
276 }
277
278 void
279 Lyric_combine_music_iterator::process (Moment /* when */)
280 {
281   /* see if associatedVoice has been changed */
282   Context *new_voice = find_voice ();
283   if (new_voice)
284     set_music_context (new_voice);
285
286   lyrics_found_ = true;
287   if (!music_context_)
288     return;
289
290   if (!music_context_->get_parent_context ())
291     {
292       /*
293         The melody has died.
294         We die too.
295       */
296       if (lyrics_context_)
297         lyrics_context_->unset_property (ly_symbol2scm ("associatedVoiceContext"));
298       lyric_iter_ = 0;
299       set_music_context (0);
300     }
301
302   if (music_context_
303       && (start_new_syllable ()
304           || (busy_moment_ >= pending_grace_moment_))
305       && lyric_iter_->ok ())
306     {
307       Moment now = music_context_->now_mom ();
308       if (now.grace_part_ && !to_boolean (lyrics_context_->get_property ("includeGraceNotes")))
309         {
310           pending_grace_moment_ = now;
311           pending_grace_moment_.grace_part_ = Rational (0);
312           return;
313         }
314       else
315         {
316           pending_grace_moment_.set_infinite (1);
317         }
318
319       Moment m = lyric_iter_->pending_moment ();
320       lyrics_context_->set_property (ly_symbol2scm ("associatedVoiceContext"),
321                                      music_context_->self_scm ());
322       lyric_iter_->process (m);
323
324       music_found_ = true;
325     }
326
327   new_voice = find_voice ();
328   if (new_voice)
329     set_music_context (new_voice);
330 }
331
332 void
333 Lyric_combine_music_iterator::do_quit ()
334 {
335   if (!music_found_)
336     {
337       SCM voice_name = get_music ()->get_property ("associated-context");
338       SCM voice_type = get_music ()->get_property ("associated-context-type");
339       string name, type;
340       if (scm_is_string (voice_name))
341         name = ly_scm2string (voice_name);
342       type = robust_symbol2string (voice_type, "Voice");
343       /* Don't print a warning for empty lyrics (in which case we don't try
344          to find the proper voice, so it will not be found) */
345       if (lyrics_found_)
346         get_music ()->origin ()->warning (_f ("cannot find %s `%s'",
347                                               type.c_str (), name.c_str ()) + "\n");
348     }
349
350   if (lyric_iter_)
351     lyric_iter_->quit ();
352 }
353
354 IMPLEMENT_CTOR_CALLBACK (Lyric_combine_music_iterator);