From: Neil Puttock <n.puttock@gmail.com>
Date: Tue, 21 Apr 2009 21:41:16 +0000 (+0100)
Subject: Fix #733.
X-Git-Tag: release/2.12.3-1~131
X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=f5fc21366d4cc4a49e5b4e8dbc063f4ae27fbeed;p=lilypond.git

Fix #733.

- when testing whether a pitch matches the key signature, try using
keySignature if no match is found in localKeySignature

- move check_pitch_against_signature () and related code to SCM,
passing context instead of localKeySignature

- remove ly:find-accidentals-simple
(cherry picked from commit f40d49de1cc111dcbd0795245cb9b88256db0c57)
---

diff --git a/input/regression/key-signature-scordatura-persist.ly b/input/regression/key-signature-scordatura-persist.ly
new file mode 100644
index 0000000000..6e71b36d9b
--- /dev/null
+++ b/input/regression/key-signature-scordatura-persist.ly
@@ -0,0 +1,19 @@
+\version "2.13.1"
+
+\header {
+  texidoc = "When a custom key signature has entries which are
+limited to a particular octave, such alterations should persist
+indefinitely or until a new key signature is set.
+
+Here, only the fis' shows an accidental, since it is outside the
+octave defined in @code{keySignature}.
+"
+}
+
+\relative c' {
+  \set Staff.keySignature = #`(((0 . 3) . ,SHARP)
+                               ((0 . 5) . ,FLAT)
+                               ((0 . 6) . ,FLAT))
+  fis fis as bes
+  fis' as, as bes
+}
diff --git a/lily/accidental-engraver.cc b/lily/accidental-engraver.cc
index 24340ed70f..f84e281d48 100644
--- a/lily/accidental-engraver.cc
+++ b/lily/accidental-engraver.cc
@@ -114,59 +114,19 @@ Accidental_engraver::update_local_key_signature (SCM new_sig)
 
   Context *trans = context ()->get_parent_context ();
 
-  /* Reset parent contexts so that e.g. piano-accidentals won't remember old
-     cross-staff accidentals after key-sig-changes */
+  /*
+    Reset parent contexts so that e.g. piano-accidentals won't remember old
+    cross-staff accidentals after key-sig-changes.
+  */
 
   SCM val;
-  while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature"), &val)==trans)
+  while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature"), &val) == trans)
     {
       trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
       trans = trans->get_parent_context ();
     }
 }
 
-
-/** Calculate the number of accidentals on basis of the current local key
-    sig (passed as argument)
-
-    * First check step+octave (taking into account barnumbers if necessary).
-
-    * Then check the global signature (only step).
-
-    Return number of accidentals (0, 1 or 2).  */
-
-static bool
-recent_enough (int bar_number, SCM alteration_def, SCM laziness)
-{
-  if (scm_is_number (alteration_def)
-      || laziness == SCM_BOOL_T)
-    return true;
-
-  return (bar_number <= scm_to_int (scm_cadr (alteration_def)) + scm_to_int (laziness));
-}
-
-static Rational
-extract_alteration (SCM alteration_def)
-{
-  if (scm_is_number (alteration_def))
-    return ly_scm2rational (alteration_def);
-  else if (scm_is_pair (alteration_def))
-    return ly_scm2rational (scm_car (alteration_def));
-  else if (alteration_def == SCM_BOOL_F)
-    return Rational (0);
-  else
-    assert (0);
-  return Rational (0);
-}
-
-bool
-is_tied (SCM alteration_def)
-{
-  SCM tied = ly_symbol2scm ("tied");
-  return (alteration_def == tied
-	  || (scm_is_pair (alteration_def) && scm_car (alteration_def) == tied));
-}
-
 struct Accidental_result
 {
   bool need_acc;
@@ -196,95 +156,6 @@ struct Accidental_result
   }
 };
 
-Accidental_result
-check_pitch_against_signature (SCM key_signature, Pitch const &pitch,
-			       int bar_number, SCM laziness, bool ignore_octave)
-{
-  Accidental_result result;
-  int n = pitch.get_notename ();
-  int o = pitch.get_octave ();
-
-  SCM previous_alteration = SCM_BOOL_F;
-
-  SCM from_same_octave = ly_assoc_get (scm_cons (scm_from_int (o),
-						 scm_from_int (n)), key_signature, SCM_BOOL_F);
-  SCM from_key_signature = ly_assoc_get (scm_from_int (n), key_signature, SCM_BOOL_F);
-  SCM from_other_octaves = SCM_BOOL_F;
-  for (SCM s = key_signature; scm_is_pair (s); s = scm_cdr (s))
-    {
-      SCM entry = scm_car (s);
-      if (scm_is_pair (scm_car (entry))
-	  && scm_cdar (entry) == scm_from_int (n))
-	{
-	  from_other_octaves = scm_cdr (entry);
-	  break;
-	}
-    }
-
-  if (!ignore_octave
-      && from_same_octave != SCM_BOOL_F
-      && recent_enough (bar_number, from_same_octave, laziness))
-    previous_alteration = from_same_octave;
-  else if (ignore_octave
-	   && from_other_octaves != SCM_BOOL_F
-	   && recent_enough (bar_number, from_other_octaves, laziness))
-    previous_alteration = from_other_octaves;
-  else if (from_key_signature != SCM_BOOL_F)
-    previous_alteration = from_key_signature;
-
-  if (is_tied (previous_alteration))
-    {
-      result.need_acc = true;
-    }
-  else
-    {
-      Rational prev = extract_alteration (previous_alteration);
-      Rational alter = pitch.get_alteration ();
-
-      if (alter != prev)
-        {
-	  result.need_acc = true;
-	  if (alter.sign ()
-	      && (alter.abs () < prev.abs ()
-		  || (prev * alter).sign () < 0))
-	    result.need_restore = true;
-	}
-    }
-
-  return result;
-}
-
-// TODO: consider moving check_pitch_against_signature to SCM (in which case
-// we can delete this function).
-LY_DEFINE (ly_find_accidentals_simple, "ly:find-accidentals-simple", 5, 0, 0,
-	   (SCM keysig, SCM pitch_scm, SCM barnum, SCM laziness, SCM octaveness ),
-	   "Checks the need for an accidental and a @q{restore} accidental against a"
-	   " key signature.  The @var{laziness} is the number of bars for which reminder"
-	   " accidentals are used (ie. if @var{laziness} is zero, we only cancel accidentals"
-	   " in the same bar; if @var{laziness} is three, we cancel accidentals up to three"
-	   " bars after they first appear.  @var{octaveness} is either"
-	   " @code{'same-octave} or @code{'any-octave} and it specifies whether"
-	   " accidentals should be canceled in different octaves.")
-{
-  LY_ASSERT_TYPE (unsmob_pitch, pitch_scm, 2);
-  LY_ASSERT_TYPE (scm_is_integer, barnum, 3);
-  LY_ASSERT_TYPE (ly_is_symbol, octaveness, 5);
-
-  bool symbol_ok = octaveness == ly_symbol2scm ("any-octave") ||
-    octaveness == ly_symbol2scm ("same-octave");
-
-  SCM_ASSERT_TYPE (symbol_ok, octaveness, SCM_ARG5, __FUNCTION__, "'any-octave or 'same-octave");
-
-  Pitch *pitch = unsmob_pitch (pitch_scm);
-
-  int bar_number = scm_to_int (barnum);
-  bool ignore_octave = ly_symbol2scm ("any-octave") == octaveness; 
-  Accidental_result result = check_pitch_against_signature (keysig, *pitch, bar_number,
-							    laziness, ignore_octave);
-
-  return scm_cons (scm_from_bool (result.need_restore), scm_from_bool (result.need_acc));
-}
-
 static
 Accidental_result
 check_pitch_against_rules (Pitch const &pitch, Context *origin,
@@ -298,23 +169,23 @@ check_pitch_against_rules (Pitch const &pitch, Context *origin,
     warning (_f ("accidental typesetting list must begin with context-name: %s",
 		 ly_scm2string (scm_car (rules)).c_str ()));
 
-  for (; scm_is_pair (rules) && origin;
-       rules = scm_cdr (rules))
+  for (; scm_is_pair (rules) && origin; rules = scm_cdr (rules))
     {
       SCM rule = scm_car (rules);
       if (ly_is_procedure (rule))
 	{
 	  SCM rule_result_scm = scm_call_4 (rule, origin->self_scm (),
 					    pitch_scm, barnum_scm, measurepos);
-
 	  Accidental_result rule_result (rule_result_scm);
 
 	  result.need_acc |= rule_result.need_acc;
 	  result.need_restore |= rule_result.need_restore;
 	}
 
-      /* if symbol then it is a context name.  Scan parent contexts to
-	 find it. */
+      /*
+	If symbol then it is a context name.  Scan parent contexts to
+	find it.
+      */
       else if (scm_is_symbol (rule))
 	{
 	  Context *dad = origin;
@@ -373,8 +244,10 @@ Accidental_engraver::process_acknowledged ()
 	  if (!acc.need_acc && forced)
 	    acc.need_acc = true;
 
-	  /* Cannot look for ties: it's not guaranteed that they reach
-	     us before the notes. */
+	  /*
+	    Cannot look for ties: it's not guaranteed that they reach
+	    us before the notes.
+	  */
 	  if (!note->in_event_class ("trill-span-event"))
 	    {
 	      if (acc.need_acc)	      
@@ -526,14 +399,13 @@ Accidental_engraver::stop_translation_timestep ()
 	      */
 	      localsig = ly_assoc_prepend_x (localsig, key,scm_cons (ly_symbol2scm ("tied"),
 								     position));
-
 	      change = true;
 	    }
 	  else
 	    {
 	      /*
-		not really really correct if there are more than one
-		noteheads with the same notename.
+		not really really correct if there is more than one
+		note head with the same notename.
 	      */
 	      localsig = ly_assoc_prepend_x (localsig, key,
 					     scm_cons (ly_rational2scm (a),
diff --git a/scm/music-functions.scm b/scm/music-functions.scm
index 233ce575c4..7a8f940b60 100644
--- a/scm/music-functions.scm
+++ b/scm/music-functions.scm
@@ -967,7 +967,103 @@ Syntax:
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; accidentals
 
-(define-public ((make-accidental-rule octaveness lazyness) context pitch barnum measurepos)
+(define (recent-enough? bar-number alteration-def laziness)
+  (if (or (number? alteration-def)
+	  (equal? laziness #t))
+      #t
+      (<= bar-number (+ (cadr alteration-def) laziness))))
+
+(define (is-tied? alteration-def)
+  (let* ((def (if (pair? alteration-def)
+		 (car alteration-def)
+		 alteration-def)))
+
+    (if (equal? def 'tied) #t #f)))
+
+(define (extract-alteration alteration-def)
+  (cond ((number? alteration-def)
+	 alteration-def)
+	((pair? alteration-def)
+	 (car alteration-def))
+	(else 0)))
+
+(define (check-pitch-against-signature context pitch barnum laziness octaveness)
+  "Checks the need for an accidental and a @q{restore} accidental against
+@code{localKeySignature}. The @var{laziness} is the number of measures
+for which reminder accidentals are used (i.e., if @var{laziness} is zero,
+only cancel accidentals in the same measure; if @var{laziness} is three,
+we cancel accidentals up to three measures after they first appear.
+@var{octaveness} is either @code{'same-octave} or @code{'any-octave} and
+specifies whether accidentals should be canceled in different octaves."
+  (let* ((ignore-octave (cond ((equal? octaveness 'any-octave) #t)
+			      ((equal? octaveness 'same-octave) #f)
+			      (else
+			       (ly:warning (_ "Unknown octaveness type: ~S ") octaveness)
+			       (ly:warning (_ "Defaulting to 'any-octave."))
+			       #t)))
+	 (key-sig (ly:context-property context 'keySignature))
+	 (local-key-sig (ly:context-property context 'localKeySignature))
+	 (notename (ly:pitch-notename pitch))
+	 (octave (ly:pitch-octave pitch))
+	 (pitch-handle (cons octave notename))
+	 (need-restore #f)
+	 (need-accidental #f)
+	 (previous-alteration #f)
+	 (from-other-octaves #f)
+	 (from-same-octave (ly:assoc-get pitch-handle local-key-sig))
+	 (from-key-sig (ly:assoc-get notename local-key-sig)))
+
+    ;; If no key signature match is found from localKeySignature, we may have a custom
+    ;; type with octave-specific entries of the form ((octave . pitch) alteration)
+    ;; instead of (pitch . alteration).  Since this type cannot coexist with entries in
+    ;; localKeySignature, try extracting from keySignature instead.
+    (if (equal? from-key-sig #f)
+	(set! from-key-sig (ly:assoc-get pitch-handle key-sig)))
+
+    ;; loop through localKeySignature to search for a notename match from other octaves
+    (let loop ((l local-key-sig))
+      (if (pair? l)
+	  (let ((entry (car l)))
+	    (if (and (pair? (car entry))
+		     (= (cdar entry) notename))
+		(set! from-other-octaves (cdr entry))
+		(loop (cdr l))))))
+
+    ;; find previous alteration-def for comparison with pitch
+    (cond
+     ;; from same octave?
+     ((and (eq? ignore-octave #f)
+	   (not (equal? from-same-octave #f))
+	   (recent-enough? barnum from-same-octave laziness))
+      (set! previous-alteration from-same-octave))
+
+     ;; from any octave?
+     ((and (eq? ignore-octave #t)
+	   (not (equal? from-other-octaves #f))
+	   (recent-enough? barnum from-other-octaves laziness))
+      (set! previous-alteration from-other-octaves))
+
+     ;; not recent enough, extract from key signature/local key signature
+     ((not (equal? from-key-sig #f))
+      (set! previous-alteration from-key-sig)))
+
+    (if (is-tied? previous-alteration)
+	(set! need-accidental #t)
+
+	(let* ((prev-alt (extract-alteration previous-alteration))
+	       (this-alt (ly:pitch-alteration pitch)))
+
+	  (if (not (= this-alt prev-alt))
+	      (begin
+		(set! need-accidental #t)
+		(if (and (not (= this-alt 0))
+			 (or (< (abs this-alt) (abs prev-alt))
+			     (< (* prev-alt this-alt) 0)))
+		    (set! need-restore #t))))))
+
+    (cons need-restore need-accidental)))
+
+(define-public ((make-accidental-rule octaveness laziness) context pitch barnum measurepos)
   "Creates an accidental rule that makes its decision based on the octave of the note
   and a laziness value.
   octaveness is either 'same-octave or 'any-octave and defines whether the rule should
@@ -975,13 +1071,12 @@ Syntax:
   normal way to typeset accidentals - an accidental is made if the alteration is different
   from the last active pitch in the same octave. 'any-octave looks at the last active pitch
   in any octave.
-  lazyness states over how many bars an accidental should be remembered.
+  laziness states over how many bars an accidental should be remembered.
   0 is default - accidental lasts over 0 bar lines, that is, to the end of current measure.
   A positive integer means that the accidental lasts over that many bar lines.
   -1 is 'forget immediately', that is, only look at key signature.
   #t is forever."
-  (let ((keysig (ly:context-property context 'localKeySignature)))
-    (ly:find-accidentals-simple keysig pitch barnum lazyness octaveness)))
+  (check-pitch-against-signature context pitch barnum laziness octaveness))
 
 (define (key-entry-notename entry)
   "Return the pitch of an entry in localKeySignature. The entry is either of the form