]> git.donarmstrong.com Git - lilypond.git/commitdiff
Issue 461: LilyPond should accept a tie between notes which are enharmonically identical
authorDavid Kastrup <dak@gnu.org>
Sat, 18 Oct 2014 11:53:50 +0000 (13:53 +0200)
committerDavid Kastrup <dak@gnu.org>
Mon, 3 Nov 2014 08:42:08 +0000 (09:42 +0100)
This patch connects the respective notes in Tie_engraver and
Tie_performer as a fallback after exhausting regular tie associations.
It also keeps the accidental engraver from applying its special rules
for tied notes that don't have exactly matching pitches.

No attempt is made to adjust the visuals of the ties: they will remain
horizontal, focused on the left note head.

input/regression/tie-enharmonic.ly [new file with mode: 0644]
lily/accidental-engraver.cc
lily/tie-engraver.cc
lily/tie-performer.cc

diff --git a/input/regression/tie-enharmonic.ly b/input/regression/tie-enharmonic.ly
new file mode 100644 (file)
index 0000000..3d4d972
--- /dev/null
@@ -0,0 +1,18 @@
+\version "2.19.16"
+
+\header { texidoc = "
+LilyPond should accept a tie between notes which are
+enharmonically identical.
+" }
+
+\score
+{
+  {
+    \time 3/4
+    \repeat unfold 3 {
+      cis'4~ des'
+    }
+  }
+  \layout { ragged-right = ##t }
+  %\midi {}
+}
index af6ce5ee8ee823f98846f6170436b649f2665986..dde27348f8a501c2c4289aaaef00eee28f3954ad 100644 (file)
@@ -367,6 +367,18 @@ Accidental_engraver::stop_translation_timestep ()
   for (vsize j = ties_.size (); j--;)
     {
       Grob *r = Tie::head (ties_[j], RIGHT);
+      Grob *l = Tie::head (ties_[j], LEFT);
+      if (l && r)
+        {
+          // Don't mark accidentals as "tied" when the pitch is not
+          // actually the same.  This is relevant for enharmonic ties.
+          Stream_event *le = Stream_event::unsmob (l->get_property ("cause"));
+          Stream_event *re = Stream_event::unsmob (r->get_property ("cause"));
+          if (le && re
+              && !ly_is_equal (le->get_property ("pitch"), re->get_property ("pitch")))
+            continue;
+        }
+
       for (vsize i = accidentals_.size (); i--;)
         if (accidentals_[i].head_ == r)
           {
index 73fd6bfc91d69d53ddf499f9b355c311cfc50f3a..4f72920efff97a683ffc1318c7e40e4236febc17 100644 (file)
@@ -23,6 +23,7 @@
 #include "international.hh"
 #include "item.hh"
 #include "note-head.hh"
+#include "pitch.hh"
 #include "protected-scm.hh"
 #include "spanner.hh"
 #include "staff-symbol-referencer.hh"
@@ -77,6 +78,7 @@ class Tie_engraver : public Engraver
   vector<Grob *> ties_;
 
   Spanner *tie_column_;
+  bool tie_notehead (Grob *h, bool enharmonic);
 
 protected:
   void process_acknowledged ();
@@ -145,12 +147,11 @@ Tie_engraver::process_music ()
     context ()->set_property ("tieMelismaBusy", SCM_BOOL_T);
 }
 
-void
-Tie_engraver::acknowledge_note_head (Grob_info i)
+bool
+Tie_engraver::tie_notehead (Grob *h, bool enharmonic)
 {
-  Grob *h = i.grob ();
+  bool found = false;
 
-  now_heads_.push_back (h);
   for (vsize i = 0; i < heads_to_tie_.size (); i++)
     {
       Grob *th = heads_to_tie_[i].head_;
@@ -167,7 +168,12 @@ Tie_engraver::acknowledge_note_head (Grob_info i)
         Make a tie only if pitches are equal or if event end was not generated by
         Completion_heads_engraver.
       */
-      if (ly_is_equal (right_ev->get_property ("pitch"), left_ev->get_property ("pitch"))
+      SCM p1 = left_ev->get_property ("pitch");
+      SCM p2 = right_ev->get_property ("pitch");
+      if ((enharmonic
+           ? (Pitch::is_smob (p1) && Pitch::is_smob (p2) &&
+              Pitch::unsmob (p1)->tone_pitch () == Pitch::unsmob (p2)->tone_pitch ())
+           : ly_is_equal (p1, p2))
           && (!Tie_engraver::has_autosplit_end (left_ev)))
         {
           Grob *p = heads_to_tie_[i].tie_;
@@ -191,6 +197,7 @@ Tie_engraver::acknowledge_note_head (Grob_info i)
           ties_.push_back (p);
           heads_to_tie_.erase (heads_to_tie_.begin () + i);
 
+          found = true;
           /*
             Prevent all other tied notes ending at the same moment (assume
             implicitly the notes have also started at the same moment!)
@@ -205,6 +212,18 @@ Tie_engraver::acknowledge_note_head (Grob_info i)
           break;
         }
     }
+  return found;
+}
+
+void
+Tie_engraver::acknowledge_note_head (Grob_info i)
+{
+  Grob *h = i.grob ();
+
+  now_heads_.push_back (h);
+
+  if (!tie_notehead (h, false))
+    tie_notehead (h, true);
 
   if (ties_.size () && ! tie_column_)
     tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
index 77fb9e336df5c90c70f2a30e67379d30ed642b92..97af3eac38814c28c96af238023ad220cba42480 100644 (file)
@@ -113,6 +113,31 @@ Tie_performer::acknowledge_audio_element (Audio_element_info inf)
               it = heads_to_tie_.erase (it);
             }
         }
+      if (found)
+        return;
+      for (it = heads_to_tie_.begin ();
+           !found && (it != heads_to_tie_.end ());
+           it++)
+        {
+          Audio_element_info et = (*it).head_;
+          Audio_note *th = dynamic_cast<Audio_note *> (et.elem_);
+          Stream_event *left_mus = et.event_;
+
+          if (!(th && right_mus && left_mus))
+            continue;
+
+          SCM p1 = left_mus->get_property ("pitch");
+          SCM p2 = right_mus->get_property ("pitch");
+          if (Pitch::is_smob (p1) && Pitch::is_smob (p2)
+              && Pitch::unsmob (p1)->tone_pitch () == Pitch::unsmob (p2)->tone_pitch ())
+            {
+              found = true;
+              // (*it).moment_ already stores the end of the tied note!
+              Moment skip = now_mom () - (*it).end_moment_;
+              an->tie_to (th, skip);
+              it = heads_to_tie_.erase (it);
+            }
+        }
     }
 }