From 8e68e07fb060daf5e87ffbb51f081e720e42583c Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sat, 18 Oct 2014 13:53:50 +0200 Subject: [PATCH] Issue 461: LilyPond should accept a tie between notes which are enharmonically identical 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 | 18 ++++++++++++++++++ lily/accidental-engraver.cc | 12 ++++++++++++ lily/tie-engraver.cc | 29 ++++++++++++++++++++++++----- lily/tie-performer.cc | 25 +++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 input/regression/tie-enharmonic.ly diff --git a/input/regression/tie-enharmonic.ly b/input/regression/tie-enharmonic.ly new file mode 100644 index 0000000000..3d4d972463 --- /dev/null +++ b/input/regression/tie-enharmonic.ly @@ -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 {} +} diff --git a/lily/accidental-engraver.cc b/lily/accidental-engraver.cc index af6ce5ee8e..dde27348f8 100644 --- a/lily/accidental-engraver.cc +++ b/lily/accidental-engraver.cc @@ -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) { diff --git a/lily/tie-engraver.cc b/lily/tie-engraver.cc index 73fd6bfc91..4f72920eff 100644 --- a/lily/tie-engraver.cc +++ b/lily/tie-engraver.cc @@ -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 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 ()); diff --git a/lily/tie-performer.cc b/lily/tie-performer.cc index 77fb9e336d..97af3eac38 100644 --- a/lily/tie-performer.cc +++ b/lily/tie-performer.cc @@ -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 (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); + } + } } } -- 2.39.2