From: Nicolas Sceaux Date: Thu, 3 Dec 2009 10:44:10 +0000 (+0100) Subject: Chord repetition: \relative mode, cautionary accidentals X-Git-Tag: release/2.13.10-1~113 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=811cdc2809ee224dfbe79576c4ac6e1fe9057db5;p=lilypond.git Chord repetition: \relative mode, cautionary accidentals \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). --- diff --git a/Documentation/notation/simultaneous.itely b/Documentation/notation/simultaneous.itely index d4a89d00f0..afae1ded78 100644 --- a/Documentation/notation/simultaneous.itely +++ b/Documentation/notation/simultaneous.itely @@ -97,6 +97,15 @@ repeated. 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] +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 index 0000000000..560d8ed5b6 --- /dev/null +++ b/input/regression/chord-repetition-relative.ly @@ -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. +" +} + +{ + 4^"absolute" q q q + \relative c' { 4^"relative" q q q } +} \ No newline at end of file diff --git a/lily/include/music-sequence.hh b/lily/include/music-sequence.hh index 551266adad..7780d4dda4 100644 --- a/lily/include/music-sequence.hh +++ b/lily/include/music-sequence.hh @@ -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); diff --git a/lily/music-sequence.cc b/lily/music-sequence.cc index fbb01ab503..25d25bb604 100644 --- a/lily/music-sequence.cc +++ b/lily/music-sequence.cc @@ -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; +} diff --git a/lily/parser.yy b/lily/parser.yy index 2ddc2c815f..d2f3a8c4dc 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -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_ = $$; } diff --git a/ly/chord-repetition-init.ly b/ly/chord-repetition-init.ly index 3ff4578b60..bea929f4c6 100644 --- a/ly/chord-repetition-init.ly +++ b/ly/chord-repetition-init.ly @@ -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) diff --git a/scm/define-music-properties.scm b/scm/define-music-properties.scm index d86517b29d..61c69248fe 100644 --- a/scm/define-music-properties.scm +++ b/scm/define-music-properties.scm @@ -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.") diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm index 35ff74eccb..0ed9252044 100644 --- a/scm/define-music-types.scm +++ b/scm/define-music-types.scm @@ -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)) diff --git a/scm/ly-syntax-constructors.scm b/scm/ly-syntax-constructors.scm index 01b054133f..765fa6ef16 100644 --- a/scm/ly-syntax-constructors.scm +++ b/scm/ly-syntax-constructors.scm @@ -143,9 +143,10 @@ '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)))