]> git.donarmstrong.com Git - lilypond.git/commitdiff
Chord repetition: \relative mode, cautionary accidentals
authorNicolas Sceaux <nicolas.sceaux@free.fr>
Thu, 3 Dec 2009 10:44:10 +0000 (11:44 +0100)
committerNicolas Sceaux <nicolas.sceaux@free.fr>
Mon, 21 Dec 2009 21:45:52 +0000 (22:45 +0100)
\relative mode: Introduce a new RepeatedChord music type, which element
property is the copied chord (as produced by the chord repetition function).
Define a relative callback for repeated chords, which avoids
octaviation of repeated chords, by modifying the first note octave
before applying the octave relativization.

Only memorize <..> chords.

When copying a chord, unset cautionary and forced accidental (fix #925).

Documentation/notation/simultaneous.itely
input/regression/chord-repetition-relative.ly [new file with mode: 0644]
lily/include/music-sequence.hh
lily/music-sequence.cc
lily/parser.yy
ly/chord-repetition-init.ly
scm/define-music-properties.scm
scm/define-music-types.scm
scm/ly-syntax-constructors.scm

index d4a89d00f0d7892fb920da74b0f8a7295783d513..afae1ded78eb0653222c886987466c065bef3240 100644 (file)
@@ -97,6 +97,15 @@ repeated.
 <c e g>8\p q q4-| q8.^"text" q16 q4-|
 @end lilypond
 
+Note chords (entered using angle brackets) only are memorized in order
+to be repeated by @code{q}: it is possible to repeat a chord even if for
+instance a simple note (without angle brackets) or a rest have been
+entered meanwhile.
+
+@lilypond[verbatim,quote,relative=1]
+<c e g>8 c' q c r4 q
+@end lilypond
+
 @seealso
 Installed Files:
 @file{ly/@/chord-repetition-init@/.ly}.
diff --git a/input/regression/chord-repetition-relative.ly b/input/regression/chord-repetition-relative.ly
new file mode 100644 (file)
index 0000000..560d8ed
--- /dev/null
@@ -0,0 +1,13 @@
+\version "2.13.9"
+
+\header {
+  texidoc = "
+Chord repetition handles \\relative mode: the repeated chords have
+the same octaves as the original one.
+"
+}
+
+{
+  <c''' d'' g''>4^"absolute" q q q
+  \relative c' { <c'' d, g>4^"relative" q q q }
+}
\ No newline at end of file
index 551266adad7fd7eab3b83587b643c90e7670de3b..7780d4dda473c616a88ffdc5e640cf337b414cb6 100644 (file)
@@ -33,6 +33,7 @@ public:
   DECLARE_SCHEME_CALLBACK (first_start_callback, (SCM));
   DECLARE_SCHEME_CALLBACK (simultaneous_relative_callback, (SCM, SCM));
   DECLARE_SCHEME_CALLBACK (event_chord_relative_callback, (SCM, SCM));
+  DECLARE_SCHEME_CALLBACK (repeated_chord_relative_callback, (SCM, SCM));
 
   Pitch do_relative_octave (Pitch p, bool b);
 
index fbb01ab50346353388822eda88c17df66b0d00b5..25d25bb604fe4582c57b2d0e60b04ac1389628c0 100644 (file)
@@ -193,3 +193,58 @@ Music_sequence::event_chord_relative_callback (SCM music, SCM pitch)
                                 p, true).smobbed_copy ();
 }
 
+MAKE_SCHEME_CALLBACK (Music_sequence, repeated_chord_relative_callback, 2);
+SCM
+Music_sequence::repeated_chord_relative_callback (SCM music, SCM pitch)
+{
+  Music *me = unsmob_music (music);
+  Music *repeated_chord = unsmob_music (me->get_property ("element"));
+  Music *original_chord = unsmob_music (me->get_property ("original-chord"));
+
+  /* A repeated chord octave is not computed from the previous pitch,
+   * (this function `pitch' argument), but from the original chord, so
+   * that repeated chords have the same octave have the original chord,
+   * even though other simple notes have been entered meanwhile.
+   */
+  assert (repeated_chord);
+  Pitch *p = 0;
+  /* Get the original chord first pitch */
+  if (original_chord)
+    {
+      for (SCM s = original_chord->get_property ("elements"); scm_is_pair (s); s = scm_cdr (s))
+       {
+         if (Music *m = unsmob_music (scm_car (s)))
+           {
+             p = unsmob_pitch (m->get_property ("pitch"));
+             if (p)
+               break;
+           }
+       }
+    }
+  /* Use the `pitch' argument if no pitch found in original chord. */
+  if (! p)
+    p = unsmob_pitch (pitch);
+
+  /* Change the first note pitch to -1, to avoid octaviation.  Indeed,
+   * the first pitch should be the same as the original chord first
+   * pitch. */
+  for (SCM s = repeated_chord->get_property ("elements"); scm_is_pair (s); s = scm_cdr (s))
+    {
+      if (Music *m = unsmob_music (scm_car (s)))
+       {
+         Pitch *first_pitch = unsmob_pitch (m->get_property ("pitch"));
+         if (first_pitch)
+           {
+             Pitch new_pitch = Pitch (-1,
+                                      first_pitch->get_notename (),
+                                      first_pitch->get_alteration ());
+             m->set_property ("pitch", new_pitch.smobbed_copy ());
+             break;
+           }
+       }
+    }
+  music_list_to_relative (repeated_chord->get_property ("elements"), *p, true).smobbed_copy ();
+  /* Return `pitch' instead of the repeated chord first pitch,
+   * because `pitch' is the last explicitly entered pitch */
+  return pitch;
+}
index 2ddc2c815f31b65c2c582e2e82de35de114d2803..d2f3a8c4dc9e9f0eae7f1512c673fbeabadcdf6d 100644 (file)
@@ -1417,11 +1417,6 @@ event_chord:
        /* TODO: Create a special case that avoids the creation of
           EventChords around simple_elements that have no post_events?
         */
-       /* event_chords like simple notes, note chords, etc, are
-          saved into PARSER->lexer_->chord_repetition_ so that
-          the chord repetition mechanism can copy them when a
-          chord repetition symbol is found
-       */
        simple_chord_elements post_events       {
                SCM elts = ly_append2 ($1, scm_reverse_x ($2, SCM_EOL));
 
@@ -1430,7 +1425,6 @@ event_chord:
                 * i = @$; */
                i.set_location (@1, @2);
                $$ = MAKE_SYNTAX ("event-chord", i, elts);
-               PARSER->lexer_->chord_repetition_.last_chord_ = $$;
        }
        | CHORD_REPETITION optional_notemode_duration post_events {
                Input i;
@@ -1446,6 +1440,10 @@ event_chord:
                $$ = MAKE_SYNTAX ("multi-measure-rest", i, $2, $3);
        }
        | command_element
+       /* note chord elements are memorized into
+          PARSER->lexer_->chord_repetition_ so that the chord repetition
+          mechanism copy them when a chord repetition symbol is found
+       */
        | note_chord_element    {
                PARSER->lexer_->chord_repetition_.last_chord_ = $$;
        }
index 3ff4578b60da24402d4d2173809a831b87f3c358..bea929f4c658a5a8ce6f2682c8abfb4261b4da26 100644 (file)
@@ -1,6 +1,8 @@
-\version "2.13.8"
+%%% -*- Mode: Scheme -*-
+\version "2.13.9"
 %{
-Two functions define the chord repetition behavior, and may
+
+The following functions define the chord repetition behavior, and may
 be invoked by the user to customize it.
 
 ly:parser-set-repetition-symbol
@@ -8,27 +10,48 @@ ly:parser-set-repetition-symbol
   `q' is the default value set in this file.
 
 ly:parser-set-repetition-function
+
   set the function that is invoked when a chord repetition symbol
-  is encountered by the parser: a three argument function
-  (previous-chord, duration, list of articulations) which is supposed
-  to return a new chord.
+  is encountered by the parser: a four argument function
+  (previous-chord, location, duration, list of articulations) which is
+  supposed to return a new chord.
   `default-repeat-chord' is the default function set in this file.
 %}
 
-#(define-public (default-repeat-chord previous-chord duration articulations)
-   "Copy the previous chord, filter out events which are not notes, set the
-chord duration, add articulations."
-   (let ((new-chord (ly:music-deep-copy previous-chord)))
+#(define-public (default-repeat-chord previous-chord location duration articulations)
+   "Copy the previous chord, filter out events which are not notes, set
+the chord duration, add articulations."
+   ;; If previous-chord has an length property, then it means that it
+   ;; has been processed by a music iterator.  In other words, the chord
+   ;; has been memorized in an other music block, which is certainly not
+   ;; what the user has intended.  In that case, raise a warning.
+   (if (not (and (ly:music? previous-chord)
+                 (null? (ly:music-property previous-chord 'length))))
+       (ly:input-message location
+                         (_ "No memorized chord in music block before chord repetition")))
+   (let* ((new-chord (ly:music-deep-copy previous-chord))
+          (notes (filter (lambda (event)
+                           (eqv? (ly:music-property event 'name) 'NoteEvent))
+                         (ly:music-property new-chord 'elements))))
+     ;; remove possible cautionary/forced accidentals from notes
+     (for-each (lambda (note)
+                 (if (eqv? (ly:music-property note 'cautionary) #t)
+                     (set! (ly:music-property note 'cautionary) #f))
+                 (if (eqv? (ly:music-property note 'force-accidental) #t)
+                     (set! (ly:music-property note 'force-accidental) #f)))
+               notes)
+     ;; Add articulations and notes to the new event chord
      (set! (ly:music-property new-chord 'elements)
-           (append! articulations
-                    (filter (lambda (event)
-                              (eqv? (ly:music-property event 'name) 'NoteEvent))
-                            (ly:music-property new-chord 'elements))))
+           (append! notes articulations))
+     ;; Set the duration of each event
      (for-each (lambda (event)
                  (if (ly:duration? (ly:music-property event 'duration))
                      (set! (ly:music-property event 'duration) duration)))
                (ly:music-property new-chord 'elements))
-    new-chord))
+     ;; Set the new chord origin
+     (set! (ly:music-property new-chord 'origin) location)
+     ;; return the new chord
+     new-chord))
 
 #(ly:parser-set-repetition-symbol parser 'q)
 #(ly:parser-set-repetition-function parser default-repeat-chord)
index d86517b29d79da163124f81801168b0b1bbb0574..61c69248fefd0e66b4dc2f20b46908fe4b311bd0 100644 (file)
@@ -113,6 +113,8 @@ whether to allow, forbid or force a line break.")
 For chord inversions, this is negative.")
      (once ,boolean? "Apply this operation only during one time step?")
      (origin ,ly:input-location? "Where was this piece of music defined?")
+     (original-chord ,ly:music? "Original chord of a repeated chord.
+Used by repeated chords in \\relative mode, to determine the first note octave")
 
      (page-break-permission ,symbol? "When the music is at top-level,
 whether to allow, forbid or force a page break.")
index 35ff74eccbb7c04830fb34eeab2e9ab5358cf313..0ed92520443c7d10f5fafa5fae77856644c6529c 100644 (file)
@@ -416,6 +416,15 @@ Syntax: @code{\\unset @var{context}.@var{prop}}")
        (types . (music-wrapper-music general-music relative-octave-music))
        ))
 
+    (RepeatedChord
+     . ((description . "A chord repetition")
+       (to-relative-callback . ,ly:music-sequence::repeated-chord-relative-callback)
+       (iterator-ctor . ,ly:music-wrapper-iterator::constructor)
+       (start-callback . ,ly:music-wrapper::start-callback)
+       (length-callback . ,ly:music-wrapper::length-callback)
+       (types . (general-music music-wrapper-music))
+       ))
+
     (RepeatedMusic
      . ((description . "Repeat music in different ways.")
        (types . (general-music repeated-music))
index 01b054133f47d9dd14f4a0eb7fc177d65ffc9c04..765fa6ef16f24a4b3629339a2a77f3d7ca86a61d 100644 (file)
              'origin location))
 
 (define-ly-syntax (repetition-chord parser location previous-chord repetition-function duration articulations)
-  (let ((new-chord (repetition-function previous-chord duration articulations)))
-    (set! (ly:music-property new-chord 'origin) location)
-    new-chord))
+  (make-music 'RepeatedChord
+             'original-chord previous-chord
+             'element (repetition-function previous-chord location duration articulations)
+             'origin location))
 
 (define-ly-syntax-simple (context-specification type id mus ops create-new)
   (let* ((type-sym (if (symbol? type) type (string->symbol type)))