X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Flyric-combine-music-iterator.cc;h=336b167f2e98cdf2d4179b687330d1913b6d21b9;hb=3af0951f9a11677240efa6228683dd4fcea13eaf;hp=b459aac1cc06a84407503582528e0912ea52ad0c;hpb=94a1966c72301b8bd1d8bb3b8628c3f089d007cf;p=lilypond.git diff --git a/lily/lyric-combine-music-iterator.cc b/lily/lyric-combine-music-iterator.cc index b459aac1cc..336b167f2e 100644 --- a/lily/lyric-combine-music-iterator.cc +++ b/lily/lyric-combine-music-iterator.cc @@ -1,196 +1,352 @@ -/* - lyric-combine-music-iterator.cc -- implement Lyric_combine_music_iterator - - source file of the GNU LilyPond music typesetter - - (c) 1999--2004 Han-Wen Nienhuys - - */ +/* + This file is part of LilyPond, the GNU music typesetter. + + Copyright (C) 2004--2015 Han-Wen Nienhuys + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . +*/ -#include "context.hh" -#include "lyric-combine-music.hh" -#include "event.hh" -#include "note-head.hh" +#include "dispatcher.hh" +#include "global-context.hh" #include "grob.hh" +#include "input.hh" +#include "international.hh" +#include "listener.hh" #include "music-iterator.hh" +#include "music.hh" + +/* + This iterator is hairy. It tracks both lyric and melody contexts, + and has a complicated communication route, reading/writing + properties in both. + + In the future, this should rather be done with + + \interpretAsMelodyFor { MUSIC } { LYRICS LYRICS LYRICS } + + This can run an interpret step on MUSIC, generating a stream. Then + the stream can be perused at leisure to apply durations to all of + the LYRICS. +*/ class Lyric_combine_music_iterator : public Music_iterator { public: Lyric_combine_music_iterator (); - Lyric_combine_music_iterator (Lyric_combine_music_iterator const&src); + Lyric_combine_music_iterator (Lyric_combine_music_iterator const &src); DECLARE_SCHEME_CALLBACK (constructor, ()); protected: virtual void construct_children (); virtual Moment pending_moment () const; - virtual void do_quit (); + virtual void do_quit (); virtual void process (Moment); - virtual Music_iterator *try_music_in_children (Music *) const; - + virtual bool run_always ()const; virtual bool ok () const; virtual void derived_mark () const; - virtual void derived_substitute (Context *,Context *) ; + virtual void derived_substitute (Context *, Context *); + void set_music_context (Context *to); private: - bool get_busy_status ()const ; - bool melisma_busy (); + bool start_new_syllable () const; + Context *find_voice (); + void set_busy (SCM); + void check_new_context (SCM); + + bool music_found_; + bool lyrics_found_; + Context *lyrics_context_; + Context *music_context_; + SCM lyricsto_voice_name_; + SCM lyricsto_voice_type_; + + Moment busy_moment_; + Moment pending_grace_moment_; - Music_iterator * music_iter_; - Music_iterator * lyric_iter_; + Music_iterator *lyric_iter_; }; +Lyric_combine_music_iterator::Lyric_combine_music_iterator () +{ + music_found_ = false; + lyrics_found_ = false; + pending_grace_moment_.set_infinite (1); + lyric_iter_ = 0; + music_context_ = 0; + lyrics_context_ = 0; + busy_moment_.set_infinite (-1); + lyricsto_voice_name_ = SCM_UNDEFINED; + lyricsto_voice_type_ = SCM_UNDEFINED; +} -bool -melisma_busy (Context* tr) +/* + It's dubious whether we can ever make this fully work. Due to + associatedVoice switching, this routine may be triggered for + the wrong music_context_ + */ +void +Lyric_combine_music_iterator::set_busy (SCM se) { - SCM melisma_properties = tr->get_property ("melismaBusyProperties"); - bool busy = false; + Stream_event *e = unsmob (se); - for (; ly_pair_p (melisma_properties); - melisma_properties = ly_cdr (melisma_properties)) - busy = busy || to_boolean (tr->internal_get_property (ly_car (melisma_properties))); + if ((e->in_event_class ("note-event") || e->in_event_class ("cluster-note-event")) + && music_context_) - return busy; -} + busy_moment_ = max (music_context_->now_mom (), + busy_moment_); +} +void +Lyric_combine_music_iterator::set_music_context (Context *to) +{ + if (music_context_) + { + music_context_->events_below ()-> + remove_listener (GET_LISTENER (Lyric_combine_music_iterator, set_busy), ly_symbol2scm ("rhythmic-event")); + } -/* - Ugh, why static? - */ -Music *busy_req; -Music *melisma_playing_req; + music_context_ = to; + if (to) + { + to->events_below ()->add_listener (GET_LISTENER (Lyric_combine_music_iterator, set_busy), + ly_symbol2scm ("rhythmic-event")); + } +} -Lyric_combine_music_iterator::Lyric_combine_music_iterator () +bool +Lyric_combine_music_iterator::start_new_syllable () const { - music_iter_ =0; - lyric_iter_ =0; + if (busy_moment_ < music_context_->now_mom ()) + return false; - if (!busy_req) + if (!lyrics_context_) + return false; + + if (!to_boolean (lyrics_context_->get_property ("ignoreMelismata"))) { - busy_req - = make_music_by_name (ly_symbol2scm ("BusyPlayingEvent")); - melisma_playing_req - = make_music_by_name (ly_symbol2scm ("MelismaPlayingEvent")); + bool m = melisma_busy (music_context_); + if (m) + return false; } + + return true; } Moment Lyric_combine_music_iterator::pending_moment () const { - Moment musnext = music_iter_->pending_moment (); - return musnext; + Moment m; + + m.set_infinite (1); + + return m; +} + +bool +Lyric_combine_music_iterator::run_always () const +{ + return true; } bool Lyric_combine_music_iterator::ok () const { - return music_iter_->ok (); + return lyric_iter_ && lyric_iter_->ok () + && !(music_context_ && music_context_->is_removable ()); } void Lyric_combine_music_iterator::derived_mark ()const { - if (music_iter_) - scm_gc_mark (music_iter_->self_scm ()); if (lyric_iter_) scm_gc_mark (lyric_iter_->self_scm ()); + if (lyrics_context_) + scm_gc_mark (lyrics_context_->self_scm ()); + if (music_context_) + scm_gc_mark (music_context_->self_scm ()); + scm_gc_mark (lyricsto_voice_name_); + scm_gc_mark (lyricsto_voice_type_); } void -Lyric_combine_music_iterator::derived_substitute (Context *f,Context * t) +Lyric_combine_music_iterator::derived_substitute (Context *f, Context *t) { - if (music_iter_) - music_iter_->substitute_outlet (f,t); if (lyric_iter_) - lyric_iter_->substitute_outlet (f,t); + lyric_iter_->substitute_outlet (f, t); + if (lyrics_context_ && lyrics_context_ == f) + lyrics_context_ = t; + if (music_context_ && music_context_ == f) + set_music_context (t); } void Lyric_combine_music_iterator::construct_children () { - Lyric_combine_music const * m = dynamic_cast (get_music ()); - - music_iter_ = unsmob_iterator (get_iterator (m->get_music ())); - lyric_iter_ = unsmob_iterator (get_iterator (m->get_lyrics ())); + Music *m = unsmob (get_music ()->get_property ("element")); + lyric_iter_ = unsmob (get_iterator (m)); + if (!lyric_iter_) + return; + lyrics_context_ = find_context_below (lyric_iter_->get_outlet (), + ly_symbol2scm ("Lyrics"), ""); + + if (!lyrics_context_) + { + m->origin ()->warning (_ ("argument of \\lyricsto should contain Lyrics context")); + } + + lyricsto_voice_name_ = get_music ()->get_property ("associated-context"); + lyricsto_voice_type_ = get_music ()->get_property ("associated-context-type"); + if (!scm_is_symbol (lyricsto_voice_type_)) + lyricsto_voice_type_ = ly_symbol2scm ("Voice"); + + Context *voice = find_voice (); + if (voice) + set_music_context (voice); + + /* + Wait for a Create_context event. If this isn't done, lyrics can be + delayed when voices are created implicitly. + */ + Global_context *g = get_outlet ()->get_global_context (); + g->events_below ()->add_listener (GET_LISTENER (Lyric_combine_music_iterator, check_new_context), ly_symbol2scm ("CreateContext")); + + /* + We do not create a Lyrics context, because the user might + create one with a different name, and then we will not find that + one. + */ } -bool -Lyric_combine_music_iterator::get_busy_status () const +void +Lyric_combine_music_iterator::check_new_context (SCM /*sev*/) { - /* - We have to use both the event and the busyGrobs queue. The - busyGrobs queue doesn't contain any notes that have started this - instant. */ - if (try_music (busy_req)) - return true; - - Context * tr = music_iter_->get_outlet (); - - SCM grobs = tr->get_property ("busyGrobs"); - Moment now = tr->now_mom (); - for (; ly_pair_p (grobs); grobs = ly_cdr (grobs)) - { - SCM grob = ly_cdar (grobs); - Moment end =*unsmob_moment (ly_caar (grobs)); + if (!ok ()) + return; - - /* - This is slightly ugh: we are now confunding the frontend - (iterators) and the backend (note heads) */ - if (end > now - && Note_head::has_interface (unsmob_grob (grob))) - return true; + // Search for a possible candidate voice to attach the lyrics to. If none + // is found, we'll try next time again. + Context *voice = find_voice (); + if (voice) + { + set_music_context (voice); } - - return false; } +/* + Look for a suitable voice to align lyrics to. -bool -Lyric_combine_music_iterator::melisma_busy () + Returns 0 if nothing should change; i.e., if we already listen to the + right voice, or if we don't yet listen to a voice but no appropriate + voice could be found. +*/ +Context * +Lyric_combine_music_iterator::find_voice () { - /* - We can not read the property, since music_iter_->get_outlet () might - not be the context that sets the melisma properties, but rather a - parent context. - */ - return music_iter_->try_music (melisma_playing_req); + SCM voice_name = lyricsto_voice_name_; + SCM running = lyrics_context_ + ? lyrics_context_->get_property ("associatedVoice") + : SCM_EOL; + SCM voice_type = lyricsto_voice_type_; + if (scm_is_string (running)) { + voice_name = running; + voice_type = lyrics_context_->get_property ("associatedVoiceType"); + } + + if (scm_is_string (voice_name) + && (!music_context_ || ly_scm2string (voice_name) != music_context_->id_string ()) + && scm_is_symbol (voice_type)) + { + return find_context_below (find_top_context (get_outlet ()), + voice_type, ly_scm2string (voice_name)); + } + + return 0; } void -Lyric_combine_music_iterator::process (Moment m) +Lyric_combine_music_iterator::process (Moment /* when */) { - Moment my_next = music_iter_->pending_moment (); - if (my_next > m) + /* see if associatedVoice has been changed */ + Context *new_voice = find_voice (); + if (new_voice) + set_music_context (new_voice); + + lyrics_found_ = true; + if (!music_context_) return; - - music_iter_->process (m); - if (get_busy_status () && !melisma_busy () && lyric_iter_->ok ()) + if (!music_context_->get_parent_context ()) + { + /* + The melody has died. + We die too. + */ + if (lyrics_context_) + lyrics_context_->unset_property (ly_symbol2scm ("associatedVoiceContext")); + lyric_iter_ = 0; + set_music_context (0); + } + + if (music_context_ + && (start_new_syllable () + || (busy_moment_ >= pending_grace_moment_)) + && lyric_iter_->ok ()) { - Moment m= lyric_iter_->pending_moment (); + Moment now = music_context_->now_mom (); + if (now.grace_part_ && !to_boolean (lyrics_context_->get_property ("includeGraceNotes"))) + { + pending_grace_moment_ = now; + pending_grace_moment_.grace_part_ = Rational (0); + return; + } + else + { + pending_grace_moment_.set_infinite (1); + } + + Moment m = lyric_iter_->pending_moment (); + lyrics_context_->set_property (ly_symbol2scm ("associatedVoiceContext"), + music_context_->self_scm ()); lyric_iter_->process (m); + + music_found_ = true; } + + new_voice = find_voice (); + if (new_voice) + set_music_context (new_voice); } void Lyric_combine_music_iterator::do_quit () { - if (music_iter_) - music_iter_->quit (); + if (!music_found_) + { + SCM voice_name = get_music ()->get_property ("associated-context"); + SCM voice_type = get_music ()->get_property ("associated-context-type"); + string name, type; + if (scm_is_string (voice_name)) + name = ly_scm2string (voice_name); + type = robust_symbol2string (voice_type, "Voice"); + /* Don't print a warning for empty lyrics (in which case we don't try + to find the proper voice, so it will not be found) */ + if (lyrics_found_) + get_music ()->origin ()->warning (_f ("cannot find %s `%s'", + type.c_str (), name.c_str ()) + "\n"); + } + if (lyric_iter_) lyric_iter_->quit (); } -Music_iterator* -Lyric_combine_music_iterator::try_music_in_children (Music *m) const -{ - Music_iterator * i = music_iter_->try_music (m); - if (i) - return i; - else - return lyric_iter_->try_music (m); -} - - IMPLEMENT_CTOR_CALLBACK (Lyric_combine_music_iterator);