]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/lyric-phrasing-engraver.cc
* THANKS: Create skeleton for 2.1.
[lilypond.git] / lily / lyric-phrasing-engraver.cc
index 045d86ead8bc5624c52f1f78b349c936c565d2c2..fab2a8e607c9f4a5ab010b4cd02512b3e95658e4 100644 (file)
@@ -3,7 +3,7 @@
 
   source file of the GNU LilyPond music typesetter
 
 
   source file of the GNU LilyPond music typesetter
 
-  (c)  2000 Glen Prideaux <glenprideaux@iname.com>
+  (c)  2000--2003 Glen Prideaux <glenprideaux@iname.com>
 */
 #include <string.h>
 
 */
 #include <string.h>
 
 #include "note-head.hh"
 #include "translator-group.hh"
 #include "spanner.hh"
 #include "note-head.hh"
 #include "translator-group.hh"
 #include "spanner.hh"
+#include "warn.hh"
 
 
 
 
-String get_context_id(Translator_group * ancestor, const char * type);
-String trim_suffix(String &id);
+String get_context_id (Translator_group * ancestor, SCM);
+String trim_suffix (String &id);
+
 
 
-ADD_THIS_TRANSLATOR (Lyric_phrasing_engraver);
 
 /*
   TODO: this code is too hairy, and does things that should be in the
   backend. Fixme.
 */
 
 
 /*
   TODO: this code is too hairy, and does things that should be in the
   backend. Fixme.
 */
 
+/*
+  TODO:
+
+  shared lyrics should be vertically centered:
+
+  
+
+  > About lyrics, it happens that there are common words for many bars, like
+  > for a refrain say.  When there is an even number of lyrics lines, I do not
+  > know how to force the positioning of the common lyric line in the plain
+  > middle of the others, because this is in between lines.  Not a big matter,
+  > but it would be a bit nicer if this was doable.
+
+*/
 
 /*
 
 /*
-  We find start and end of phrases, and align lyrics accordingly.
+  We find start and end of phrases, and align lyrics of multiple stanzas
+  accordingly.
+
   Also, lyrics at start of melismata should be left aligned.
   Also, lyrics at start of melismata should be left aligned.
+  (is that only lyrics that are followed by `__'?  Because
+  that seems to be the case now -- jcn)
+
+
+  |        |        |     |      |
+  x|       x|       x|    x|     x|
+
+  1:  Start  sentence  melisma      end.
+  2:  x         x         x_____       x
 
 
+  Only lyrics that are followed by '__' while there's a melisma,
+  are left-aligned, in this case the third x.
+
+  
   Alignment and melismata
 
   I've taken [a different] approach:
   Alignment and melismata
 
   I've taken [a different] approach:
-         |      |
-         |      |
-        O      O  <-- second note throws a melisma score element
-         \____/
+  |      |
+  |      |
+  O      O  <-- second note throws a melisma score element
+  \____/
 
 
-        ^      ^
-        |      |
-       Lyric (None)
+  ^      ^
+  |      |
+  Lyric (None)
 
   Lyric_phrasing_engraver keeps track of the current and previous notes and
   lyrics for each voice, and when it catches a melisma, it adjusts the
   alignment of the lyrics of the previous note. I hope this isn't
   unnecessarily convoluted.
 
   Lyric_phrasing_engraver keeps track of the current and previous notes and
   lyrics for each voice, and when it catches a melisma, it adjusts the
   alignment of the lyrics of the previous note. I hope this isn't
   unnecessarily convoluted.
- */
+*/
 
 
-Lyric_phrasing_engraver::Lyric_phrasing_engraver()
+Lyric_phrasing_engraver::Lyric_phrasing_engraver ()
 {
   voice_alist_ = SCM_EOL;
 {
   voice_alist_ = SCM_EOL;
-  any_notehead_l_ = 0;
+  any_notehead_ = 0;
 }
 
 }
 
-Lyric_phrasing_engraver::~Lyric_phrasing_engraver()
+Lyric_phrasing_engraver::~Lyric_phrasing_engraver ()
 {
   /*
     No need to delete alist_; that's what Garbage collection is for.
 {
   /*
     No need to delete alist_; that's what Garbage collection is for.
-   */
+  */
 }
 
 void
 }
 
 void
@@ -64,129 +94,142 @@ Lyric_phrasing_engraver::finalize ()
 {
   /*
     but do need to unprotect alist_, since Engravers are gc'd now.
 {
   /*
     but do need to unprotect alist_, since Engravers are gc'd now.
-   */
+  */
 
   voice_alist_ = SCM_EOL;
 }
 
 
 Syllable_group * 
 
   voice_alist_ = SCM_EOL;
 }
 
 
 Syllable_group * 
-Lyric_phrasing_engraver::lookup_context_id(const String &context_id)
+Lyric_phrasing_engraver::lookup_context_id (const String &context_id)
 {
 {
-  SCM key = ly_str02scm(context_id.ch_C());
-  if( ! gh_null_p(voice_alist_) ) {
-    SCM s = scm_assoc(key, voice_alist_);
-    if(! (gh_boolean_p(s) && !to_boolean(s))) {
-      /* match found */
-      // ( key . ( (alist_entry . old_entry) . previous_entry) )
-      if(to_boolean(gh_cdadr(s))) { // it's an old entry ... make it a new one
-       SCM val = gh_cons(gh_cons(gh_caadr(s), SCM_BOOL_F), gh_cddr(s)); 
-       voice_alist_ = scm_assoc_set_x(voice_alist_, gh_car(s), val);
-       return unsmob_voice_entry (gh_caar(val));
-      }
-      else { // the entry is current ... return it.
-       SCM entry_scm = gh_caadr(s);
-       return unsmob_voice_entry(entry_scm);
-      }
+  SCM key = scm_makfrom0str (context_id.to_str0 ());
+  if (! gh_null_p (voice_alist_))
+    {
+      SCM s = scm_assoc (key, voice_alist_);
+      if (! (gh_boolean_p (s) && !to_boolean (s)))
+       {
+         /* match found */
+         // (key . ((alist_entry . old_entry) . previous_entry))
+         if (to_boolean (ly_cdadr (s)))
+           {
+             // it's an old entry ... make it a new one
+             SCM val = gh_cons (gh_cons (ly_caadr (s), SCM_BOOL_F), ly_cddr (s)); 
+             voice_alist_ = scm_assoc_set_x (voice_alist_, ly_car (s), val);
+             return unsmob_voice_entry (ly_caar (val));
+           }
+         else
+           {
+             // the entry is current ... return it.
+           SCM entry_scm = ly_caadr (s);
+           return unsmob_voice_entry (entry_scm);
+         }
+       }
     }
     }
-  }
-  // ( ( alist_entry . old_entry ) . previous_entry )
-  SCM val = gh_cons(gh_cons(Syllable_group::make_entry (), SCM_BOOL_F), 
-                   Syllable_group::make_entry ()); 
+  // ((alist_entry . old_entry) . previous_entry)
+  SCM val = gh_cons (gh_cons (Syllable_group::make_entry (), SCM_BOOL_F), 
+                    Syllable_group::make_entry ()); 
 
 
-  voice_alist_ = scm_acons(key, val, voice_alist_);
-  return unsmob_voice_entry (gh_caar(val));
+  voice_alist_ = scm_acons (key, val, voice_alist_);
+  return unsmob_voice_entry (ly_caar (val));
 }
 
 
 void 
 }
 
 
 void 
-Lyric_phrasing_engraver::record_notehead(const String &context_id, 
-                                        Grob * notehead)
+Lyric_phrasing_engraver::record_notehead (const String &context_id, 
+                                         Grob * notehead)
 {
 {
-  Syllable_group * v = lookup_context_id(context_id);
-  v->set_notehead(notehead);
-  if(!any_notehead_l_)
-    any_notehead_l_ = notehead;
+  Syllable_group * v = lookup_context_id (context_id);
+  v->set_notehead (notehead);
+  if (!any_notehead_)
+    any_notehead_ = notehead;
 }
   
 void 
 }
   
 void 
-Lyric_phrasing_engraver::record_lyric(const String &context_id, Grob * lyric)
+Lyric_phrasing_engraver::record_lyric (const String &context_id, Grob * lyric)
 {
 {
-  Syllable_group * v = lookup_context_id(context_id);
-  v->add_lyric(lyric);
+  Syllable_group * v = lookup_context_id (context_id);
+  v->add_lyric (lyric);
 }
 
 void 
 }
 
 void 
-Lyric_phrasing_engraver::record_extender(const String &context_id, Grob * extender)
+Lyric_phrasing_engraver::record_extender (const String &context_id, Grob * extender)
 {
 {
-  SCM key = ly_str02scm(context_id.ch_C());
-  if( ! gh_null_p(voice_alist_) ) {
-    SCM s = scm_assoc(key, voice_alist_);
-    if(! (gh_boolean_p(s) && !to_boolean(s))) {
-      /* match found */
-      // ( key . ( (alist_entry . old_entry) . previous_entry) )
-      SCM previous_scm = gh_cddr(s);
-      if(previous_scm != SCM_EOL) {
-       Syllable_group * v = unsmob_voice_entry(previous_scm);
-       v->add_extender(extender);
-      }
+  SCM key = scm_makfrom0str (context_id.to_str0 ());
+  if (! gh_null_p (voice_alist_))
+    {
+      SCM s = scm_assoc (key, voice_alist_);
+      if (! (gh_boolean_p (s) && !to_boolean (s)))
+       {
+         /* match found */
+         // (key . ((alist_entry . old_entry) . previous_entry))
+         SCM previous_scm = ly_cddr (s);
+         if (previous_scm != SCM_EOL)
+           {
+             Syllable_group * v = unsmob_voice_entry (previous_scm);
+             v->add_extender (extender);
+           }
+       }
     }
     }
-  }
 }
 
 void 
 }
 
 void 
-Lyric_phrasing_engraver::record_melisma(const String &context_id)
+Lyric_phrasing_engraver::record_melisma (const String &context_id)
 {
 {
-  Syllable_group * v = lookup_context_id(context_id);
-  v->set_melisma();
+  Syllable_group * v = lookup_context_id (context_id);
+  v->set_melisma ();
 }
 }
-  
+
+/*
+  TODO: this engraver is always on, also for orchestral scores. That
+  is a waste of time and space. This should be switched on
+  automatically at the first Lyrics found.
+ */
 void
 void
-Lyric_phrasing_engraver::acknowledge_grob(Grob_info i)
+Lyric_phrasing_engraver::acknowledge_grob (Grob_info i)
 {
 {
-  SCM p = get_property("automaticPhrasing");
-  if(!to_boolean(p))
+  SCM p = get_property ("automaticPhrasing");
+  if (!to_boolean (p))
     return;
 
 
     return;
 
 
-  Grob *h = i.elem_l_;
+  Grob *h = i.grob_;
 
 
-  if (Note_head::has_interface(h)) {
-    /* caught a note head ... do something with it */
-    /* ... but not if it's a grace note ... */
-    bool grace= to_boolean (i.elem_l_->get_grob_property ("grace"));
-    SCM wg = get_property ("weAreGraceContext");
-    bool wgb = to_boolean (wg);
-    if (grace != wgb)
-      return;
+  if (Note_head::has_interface (h))
+    {
+      /* caught a note head ... do something with it */
 
 
-    /* what's its Voice context name? */
-    String voice_context_id = get_context_id(i.origin_trans_l_->daddy_trans_l_, "Voice");
-    record_notehead(voice_context_id, h);
+      /* what's its Voice context name? */
+      String voice_context_id = get_context_id (i.origin_trans_->daddy_trans_, ly_symbol2scm ("Voice"));
+      record_notehead (voice_context_id, h);
 
 
-    /* is it in a melisma ? */
-    if(to_boolean(i.origin_trans_l_->get_property("melismaEngraverBusy"))) {
-      record_melisma(voice_context_id);
+      /* is it in a melisma ? */
+      if (to_boolean (i.origin_trans_->get_property ("melismaEngraverBusy")))
+       {
+         record_melisma (voice_context_id);
+       }
+      return;
     }
     }
-    return;
-  }
 
   /* now try for a lyric */
 
   /* now try for a lyric */
-  if (h->has_interface (ly_symbol2scm ("lyric-syllable-interface"))) {
-
-    /* what's its LyricVoice context name? */
-    String voice_context_id;
-    SCM voice_context_scm = i.origin_trans_l_->get_property("associatedVoice");
-    if (gh_string_p (voice_context_scm)) {
-      voice_context_id = ly_scm2string(voice_context_scm);
-    }
-    else {
-      voice_context_id = get_context_id(i.origin_trans_l_->daddy_trans_l_, "LyricVoice");
-      voice_context_id = trim_suffix(voice_context_id);
+  if (h->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface")))
+    {
+
+      /* what's its LyricsVoice context name? */
+      String voice_context_id;
+      SCM voice_context_scm = i.origin_trans_->get_property ("associatedVoice");
+      if (gh_string_p (voice_context_scm))
+       {
+         voice_context_id = ly_scm2string (voice_context_scm);
+       }
+      else
+       {
+         voice_context_id = get_context_id (i.origin_trans_->daddy_trans_,ly_symbol2scm ( "LyricsVoice"));
+         voice_context_id = trim_suffix (voice_context_id);
+       }
+      record_lyric (voice_context_id, h);
+      return;
     }
     }
-    record_lyric(voice_context_id, h);
-    return;
-  }
 
   /* Catch any extender items and then if we have a melisma, 
      set the RIGHT item of the extender spanner to the melismatic note in 
 
   /* Catch any extender items and then if we have a melisma, 
      set the RIGHT item of the extender spanner to the melismatic note in 
@@ -194,102 +237,130 @@ Lyric_phrasing_engraver::acknowledge_grob(Grob_info i)
      This has the effect of finishing the extender under the last note
      of the melisma, instead of extending it to the next lyric.
      
      This has the effect of finishing the extender under the last note
      of the melisma, instead of extending it to the next lyric.
      
-     Problem: the extender request is thrown at the same moment as the next lyric,
+     Problem: the extender event is thrown at the same moment as the next lyric,
      by which time we have already passed the last note of the melisma.
      However, the Lyric_phrasing_engraver remembers the last note, so just 
      attach it to that, provided it was melismatic. If it was not melismatic, 
      then ignore it and let the Extender_engraver take care of it (i.e. finish at next
      lyric).
   */
      by which time we have already passed the last note of the melisma.
      However, the Lyric_phrasing_engraver remembers the last note, so just 
      attach it to that, provided it was melismatic. If it was not melismatic, 
      then ignore it and let the Extender_engraver take care of it (i.e. finish at next
      lyric).
   */
-  if(h->has_interface (ly_symbol2scm ("lyric-extender-interface"))) {
-    String voice_context_id = get_context_id(i.origin_trans_l_->daddy_trans_l_, "LyricVoice");
-    record_extender(trim_suffix(voice_context_id), h);
-    return;
-  }
+  if (h->internal_has_interface (ly_symbol2scm ("lyric-extender-interface")))
+    {
+      String voice_context_id = get_context_id (i.origin_trans_->daddy_trans_, ly_symbol2scm ("LyricsVoice"));
+      record_extender (trim_suffix (voice_context_id), h);
+      return;
+    }
 }
 
 String 
 }
 
 String 
-get_context_id(Translator_group * ancestor, const char *type)
+get_context_id (Translator_group * ancestor, SCM  type)
 {
 {
-  while(ancestor != 0 && ancestor->type_str_ != type) {
-    ancestor = ancestor->daddy_trans_l_;
-  }
+  while (ancestor != 0 && !ancestor->is_alias_b(type))
+    {
+      ancestor = ancestor->daddy_trans_;
+    }
 
 
-  if(ancestor != 0) {
-    return ancestor->id_str_;
-  }
+  if (ancestor != 0)
+    {
+      return ancestor->id_string_;
+    }
 
   return "";
 }
 
 String 
 
   return "";
 }
 
 String 
-trim_suffix(String &id)
+trim_suffix (String &id)
 {
 {
-  int index = id.index_i('-');
-  if(index >= 0) {
-    return id.left_str(index);
-  }
+  int index = id.index ('-');
+  if (index >= 0)
+    {
+      return id.left_string (index);
+    }
   return id;
 }
 
 
   return id;
 }
 
 
-void Lyric_phrasing_engraver::create_grobs () 
+void
+Lyric_phrasing_engraver::process_acknowledged_grobs () 
 {
 {
+  SCM p = get_property ("automaticPhrasing");
+  if (!to_boolean (p))
+    return;
+
+  
   /* iterate through entries in voice_alist_
   /* iterate through entries in voice_alist_
-     for each, call set_lyric_align(alignment). Issue a warning if this returns false.
+     for each, call set_lyric_align (alignment). Issue a warning if this returns false.
   */
   String punc;
   */
   String punc;
-  SCM sp = get_property("phrasingPunctuation");
-  punc = gh_string_p(sp) ? ly_scm2string(sp) : ".,;:?!\""; 
+  SCM sp = get_property ("phrasingPunctuation");
+  punc = gh_string_p (sp) ? ly_scm2string (sp) : ".,;:?!\""; 
   
   
-  for(SCM v=voice_alist_; gh_pair_p(v); v = gh_cdr(v)) {
-    SCM v_entry = gh_cdar(v);
-    // ((current . oldflag) . previous)
-    if(!to_boolean(gh_cdar(v_entry))) { // not an old entry left over from a prior note ...
-      Syllable_group *entry = unsmob_voice_entry(gh_caar(v_entry));
-
-      /*
-       TODO: give context for warning.
-       */
-      if(! entry->set_lyric_align(punc.ch_C(), any_notehead_l_))
-       warning (_ ("lyrics found without any matching notehead"));
-
-      // is this note melismatic? If so adjust alignment of previous one.
-      if(entry->get_melisma()) {
-       if(entry->lyric_count())
-         warning (_ ("Huh? Melismatic note found to have associated lyrics."));
-       SCM previous_scm = gh_cdr(v_entry);
-       if(previous_scm != SCM_EOL) {
-         Syllable_group *previous = unsmob_voice_entry(previous_scm);
-         if (previous->lyric_count())
-           previous->adjust_melisma_align();
+  for (SCM v=voice_alist_; gh_pair_p (v); v = ly_cdr (v))
+    {
+      SCM v_entry = ly_cdar (v);
+      // ((current . oldflag) . previous)
+      if (!to_boolean (ly_cdar (v_entry)))
+       { 
+         // not an old entry left over from a prior note ...
+         Syllable_group *entry = unsmob_voice_entry (ly_caar (v_entry));
+
+         /*
+           TODO: give context for warning.
+         */
+         if (! entry->set_lyric_align (punc.to_str0 (), any_notehead_))
+           warning (_ ("lyrics found without any matching notehead"));
+
+         // is this note melismatic? If so adjust alignment of previous one.
+         if (entry->get_melisma ())
+           {
+             if (entry->lyric_count ())
+               warning (_ ("Huh? Melismatic note found to have associated lyrics."));
+             SCM previous_scm = ly_cdr (v_entry);
+             if (previous_scm != SCM_EOL)
+               {
+                 Syllable_group *previous = unsmob_voice_entry (previous_scm);
+                 if (previous->lyric_count ())
+                   previous->adjust_melisma_align ();
+               }
+           }
        }
        }
-      }
     }
     }
-  }
 }
 
 
 void
 Lyric_phrasing_engraver::stop_translation_timestep ()
 {
 }
 
 
 void
 Lyric_phrasing_engraver::stop_translation_timestep ()
 {
-  for(SCM v=voice_alist_; gh_pair_p(v); v = gh_cdr(v)) {
-    SCM entry_scm = gh_cdar(v);
-    // ((alist_entry . entry_is_old) . previous_entry)
-    Syllable_group * entry = unsmob_voice_entry(gh_caar(entry_scm));
-
-    // set previous_entry, set entry_is_old, and resave it to alist_
-    // but only change if this current was not old.
-    if(! to_boolean(gh_cdar(entry_scm))) { 
-      Syllable_group * previous_entry = unsmob_voice_entry(gh_cdr(entry_scm));
-      previous_entry->copy(entry);
-      entry_scm = gh_cons(gh_cons(gh_caar(entry_scm), SCM_BOOL_T), gh_cdr(entry_scm));
-      voice_alist_ = scm_assoc_set_x(voice_alist_, gh_caar(v), entry_scm);
+  for (SCM v=voice_alist_; gh_pair_p (v); v = ly_cdr (v))
+    {
+      SCM entry_scm = ly_cdar (v);
+      // ((alist_entry . entry_is_old) . previous_entry)
+      Syllable_group * entry = unsmob_voice_entry (ly_caar (entry_scm));
+
+      // set previous_entry, set entry_is_old, and resave it to alist_
+      // but only change if this current was not old.
+      if (! to_boolean (ly_cdar (entry_scm)))
+       { 
+         Syllable_group * previous_entry = unsmob_voice_entry (ly_cdr (entry_scm));
+         previous_entry->copy (entry);
+         entry_scm = gh_cons (gh_cons (ly_caar (entry_scm), SCM_BOOL_T), ly_cdr (entry_scm));
+         voice_alist_ = scm_assoc_set_x (voice_alist_, ly_caar (v), entry_scm);
+       }
+      entry->next_lyric ();
     }
     }
-    entry->next_lyric();
-  }
-  any_notehead_l_ = 0;
+  any_notehead_ = 0;
 }
 
 
 
 }
 
 
 
+ENTER_DESCRIPTION(Lyric_phrasing_engraver,
+                 /* descr */       
+"This engraver combines note heads and lyrics for alignment. "
+"\n\n"
+"This engraver is switched on by default. Turn it off for faster " 
+"processing of orchestral scores. ",
+                 /* creats*/       "",
+                 /* accepts */     "",
+/* acks  */      "lyric-syllable-interface note-head-interface lyric-extender-interface",
+                 /* reads */       "automaticPhrasing melismaEngraverBusy associatedVoice phrasingPunctuation",
+                 /* write */       "");