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