]> git.donarmstrong.com Git - lilypond.git/commitdiff
Reimplement ChordRepetition facility.
authorDavid Kastrup <dak@gnu.org>
Fri, 27 Jan 2012 16:44:18 +0000 (17:44 +0100)
committerDavid Kastrup <dak@gnu.org>
Fri, 3 Feb 2012 08:19:20 +0000 (09:19 +0100)
Remove basically all configurability.  Reduce parser and lexer support
to hardwired minimum.  Don't track the last chord in the lexer/parser.
Instead establish it at toplevel-music-functions level if the user has
not already called \chordRepeats to do so himself.

Addresses issue 1110 and my sense of aesthetics.

20 files changed:
Documentation/changes.tely
Documentation/notation/fretted-strings.itely
Documentation/notation/simultaneous.itely
input/regression/chord-repetition-times.ly [new file with mode: 0644]
input/regression/display-lily-tests.ly
input/regression/tablature-chord-repetition-finger.ly [new file with mode: 0644]
input/regression/tablature-chord-repetition.ly
lily/include/lily-lexer.hh
lily/include/music-sequence.hh
lily/lexer.ll
lily/lily-lexer.cc
lily/lily-parser-scheme.cc
lily/music-sequence.cc
lily/parser.yy
ly/chord-repetition-init.ly
ly/music-functions-init.ly
scm/define-music-display-methods.scm
scm/define-music-types.scm
scm/ly-syntax-constructors.scm
scm/music-functions.scm

index 8281811608c63ac3b6cf840898e44f43e5c0b298..c07c270ba166035e7c83aa46adaced811face466 100644 (file)
@@ -104,7 +104,7 @@ more closely are numerous: music functions previously worked differently
 when used inside or outside of chords.  Now they are the same, including
 all the possibilities of argument parsing.  You can now use music
 variables inside of chords: a construct like
-@lilypond[quote,ragged-right]
+@lilypond[verbatim,quote,ragged-right]
 tonic=fis'
 { <\tonic \transpose c g \tonic> }
 @end lilypond
@@ -113,10 +113,19 @@ would have been unthinkable previously.  You can use
 @code{#@{@dots{}#@}} for constructing chord constituents.  Music
 functions inside of chords are no longer specially treated and thus
 accept the same arguments as outside of chords.  @code{\tweak} now works
-on single notes without needing to wrap them in a chord.  String number
-indications like @code{\1} can be used on single notes.  If you need to
-suppress them in some context, override their @code{stencil} with
-@code{##f}.
+on single notes without needing to wrap them in a chord.  In theory, it
+can also work on command events and lyrics now.  Since that was not
+possible before, it depends on luck on a case-by-case basis whether the
+tweak internals are already receiving the necessary information.  Users
+are asked to report those cases where they find @code{\tweak} not
+working according to reasonable expectations.
+
+@item
+As one consequence, it was possible to reimplement the repetitive chord
+entry aid @code{q}.  Repeated chords are now replaced right before
+interpreting a music expression.  In case the user wants to retain
+some events of the original chord, he can run the repeat chord
+replacement function @code{\chordRepeats} manually.
 
 @item
 Scheme expressions inside of embedded Lilypond (@code{#@{@dots{}#@}})
@@ -184,7 +193,7 @@ For defining commands executed only for their side-effects,
 @item
 There is a new @code{define-event-function} command in analogy to
 @code{define-music-function} that can be used for defining music
-functions acting as events without requiring a direction specifier
+functions acting as post events without requiring a direction specifier
 (@code{-}, @code{^}, or @code{_}) placed before them.
 @lilypond[quote,verbatim,ragged-right]
 dyn=#(define-event-function (parser location arg) (markup?)
index c4bf631efefd6e190bcd8190eba15bfc9c6c47be..f01e968b13c40a97e87acc3c6bfffaef58836f7f 100644 (file)
@@ -253,31 +253,34 @@ minimumFret is 0.
 >>
 @end lilypond
 
-@funindex \tabChordRepetition
+@funindex \tabChordRepeats
+@funindex \chordRepeats
 @cindex Chord, repetition
 @cindex repetition, using @code{q}
 
 Chord constructs can be repeated by the chord repetition symbol
-@code{q}. To use this feature in combination with tablature,
-@code{\tabChordRepetition} is provided.  It preserves the string
-information explicitly given within chord constructs so repeated chords
-get identical tablature representations.
+@code{q}.  In combination with tabulatures, its behavior of removing
+string and finger numbers alongside with other events is cumbersome, so
+you'll want to run
+@example
+\chordRepeats #'(string-number-event fingering-event)
+@end example
+explicitly on music expressions in tabulature using @ref{Chord
+repetition}.  This particular command is so common that it is available
+as @code{\tabChordRepeats}.
 
 @lilypond[quote,verbatim]
-\tabChordRepetition
-
 guitar = \relative c' {
-  r8 <gis\4 cis\3 b\2>~ q4 q8~ q q4
+  r8 <gis-6 cis-6 b-0>~ q4 q8~ q q4
 }
 
 \new StaffGroup <<
   \new Staff {
     \clef "treble_8"
-    \override Voice.StringNumber #'transparent = ##t
     \guitar
   }
   \new TabStaff {
-    \guitar
+    \tabChordRepeats \guitar
   }
 >>
 @end lilypond
@@ -374,9 +377,9 @@ pitches:
 
 @lilypond[verbatim,quote]
 firstHarmonic = {
-  <d'\4\harmonic>4
-  <g'\3\harmonic>4
-  <b'\2\harmonic>2
+  d'4\4\harmonic
+  g'4\3\harmonic
+  b'2\2\harmonic
 }
 \score {
   <<
@@ -386,8 +389,9 @@ firstHarmonic = {
 }
 @end lilypond
 
-Note that the command @code{\harmonic} must always be defined
-inside a chord construct. It only makes sense for open-string harmonics
+Note that the command @code{\harmonic} must always be attached to single
+notes (possibly inside of a chord) instead of whole chords.
+It only makes sense for open-string harmonics
 in the 12th fret. All other harmonics should be calculated by LilyPond.
 This can be achieved by indicating the fret where a finger of the
 fretting hand should touch a string.
index bbb4f387c049d3565318d6747837c6c660ccaa34..00740cdfcef9e5a83f42c0ec261c384877f8f139 100644 (file)
@@ -156,16 +156,60 @@ a chord so it is possible to repeat the most recent chord even if
 other non-chorded notes or rests have been added since.
 
 @lilypond[verbatim,quote,relative=2]
-<a c e>1 c4 q2 r8 q8
+<a c e>1 c'4 q2 r8 q8 |
+q2 c, |
 @end lilypond
 
 However, the chord repetition symbol does not retain any dynamics,
-articulation or ornamentation within, or attached to the previous chord.
+articulation or ornamentation within, or attached to, the previous
+chord.
 
 @lilypond[verbatim,quote,relative=2]
-<a-. c\prall e>1\sfz c4 q2 r8 q8
+<a-. c\prall e>1\sfz c'4 q2 r8 q8 |
+q2 c, |
 @end lilypond
 
+To have some of them retained, the @code{\chordRepeats} function can be
+be called explicitly with an extra argument specifying a list of
+@var{event types} to keep unless events of that type are already
+present on the @code{q} chord itself.
+
+@lilypond[verbatim,quote]
+\relative c'' {
+  \chordRepeats #'(articulation-event)
+  { <a-. c\prall e>1\sfz c'4 q2 r8 q8-. } |
+  q2 c, |
+}
+@end lilypond
+
+Here using @code{\chordRepeats} inside of a @code{\relative} construction
+produces unexpected results: once chord events have been expanded, they
+are indistinguishable from having been entered as regular chords, making
+@code{\relative} assign an octave based on their current context.
+
+Since nested instances of @code{\relative} don't affect one another,
+another @code{\relative} inside of @code{\chordRepeats} can be used for
+establishing the octave relations before expanding the repeat chords.
+In that case, the whole content of the inner @code{\relative} does not
+affect the outer one; hence the different octave entry of the final note
+in this example.
+
+@c Without \new Voice, implicit voice creation does the dumbest thing.
+@lilypond[verbatim,quote]
+\new Voice
+\relative c'' {
+  \chordRepeats #'(articulation-event)
+  \relative c''
+  { <a-. c\prall e>1\sfz c'4 q2 r8 q8-. } |
+  q2 c |
+}
+@end lilypond
+
+Interactions with @code{\relative} occur only with explicit calls of
+@code{\chordRepeats}: the implicit expansion at the start of typesetting
+is done at a time where all instances of @code{\relative} have already
+been processed.
+
 @seealso
 Notation Reference:
 @ref{Chord notation},
diff --git a/input/regression/chord-repetition-times.ly b/input/regression/chord-repetition-times.ly
new file mode 100644 (file)
index 0000000..339d7cf
--- /dev/null
@@ -0,0 +1,15 @@
+\version "2.15.28"
+
+
+\header {
+  texidoc = "
+Chord repetitions are expanded late in the processing order and get
+their note events only then.  Check that @code{\\times} still works
+correctly on them.
+"
+}
+
+\relative c' {
+  <c e g>4 r <c e g>2 ~ |
+  \times 2/3 { <c e g>4 q q } \times 2/3 { q q q } |
+}
index 76bfc420c629a0eef4cca54e48818fedcb8934da..22eaf893d3c2b8dc0c364aad924b061b04985961 100644 (file)
@@ -70,7 +70,7 @@ stderr of this run."
 \test ##[ \skip 2.*3/4 #]              % SkipMusic
 \test ##[ < c\1 e\3 >4.*3/4-. #]       % EventChord, NoteEvent, StringNumberEvent, ArticulationEvent
 \test ##[ < c-1\4 >8 #]
-\test "NOT A BUG" ##[ { < c e g c' > q8-. } #] % RepeatedChord
+\test ##[ { < c e g c' >4 q8-. } #] % RepeatedChord
 
 %% tags
 \test ##[ { \tag #'foo { c4 d } } #]
diff --git a/input/regression/tablature-chord-repetition-finger.ly b/input/regression/tablature-chord-repetition-finger.ly
new file mode 100644 (file)
index 0000000..035128f
--- /dev/null
@@ -0,0 +1,28 @@
+\version "2.15.28"
+
+\header {
+  texidoc = "In a TabStaff, the chord repetition function needs
+to retain string and fingering information.  Using
+@code{\\tabChordRepeats} achieves that, in contrast to the music
+on the main staff."
+}
+
+Guitar = \relative c' {
+  r8 < gis-6 cis-6 b-0 > ~ q4 q8 ~ q q4
+}
+
+\score {
+  \new StaffGroup <<
+    \new Staff {
+      \new Voice {
+        \clef "treble_8"
+        \Guitar
+      }
+    }
+    \new TabStaff {
+      \new TabVoice {
+        \tabChordRepeats \Guitar
+      }
+    }
+  >>
+}
index e89b1c52f1401095606058f376eac8df685add69..5b10f19a94d6fb2d08b83c8a5e6c14301a50a563 100644 (file)
@@ -2,8 +2,10 @@
 
 \header {
   texidoc = "In a TabStaff, the chord repetition function needs
-to save the string information. This is handled by
-@code{\\tabChordRepetition}."
+to save the string information.  The obsolete function
+@code{\\tabChordRepetition} establishes this setting score-wide.
+Nowadays, you would rather use just @code{\\tabChordRepeat} on the
+music in the tabstaff, not affecting other contexts."
 }
 
 \tabChordRepetition
index 3fe836277f1ef10a08dcaf94764fafb139ea7ec0..a10d89cee31465a6f7bffd3acfecd627f96e4abc 100644 (file)
@@ -31,20 +31,6 @@ bool busy_parsing ();
 void kill_lexer ();
 void set_lexer ();
 
-struct Chord_repetition
-{
-  Chord_repetition ()
-  {
-    last_chord_ = SCM_EOL;
-    repetition_function_ = SCM_EOL;
-    repetition_symbol_ = SCM_EOL;
-  }
-
-  SCM repetition_symbol_;
-  SCM repetition_function_;
-  SCM last_chord_;
-};
-
 class Lily_lexer : public Includable_lexer
 {
   DECLARE_SMOBS (Lily_lexer);
@@ -77,8 +63,6 @@ public:
   SCM chordmodifier_tab_;
   SCM pitchname_tab_stack_;
 
-  Chord_repetition chord_repetition_;
-
   int error_level_;
   Input last_input_;
 
index 0f82ba0f76084db29d090b95f22da4b0a86254cf..590b48b22d94c46bd8c2ad241fe61d172caf7af6 100644 (file)
@@ -29,11 +29,11 @@ struct Music_sequence
 public:
   DECLARE_SCHEME_CALLBACK (cumulative_length_callback, (SCM));
   DECLARE_SCHEME_CALLBACK (maximum_length_callback, (SCM));
+  DECLARE_SCHEME_CALLBACK (event_chord_length_callback, (SCM));
   DECLARE_SCHEME_CALLBACK (minimum_start_callback, (SCM));
   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 3b88982e8825b8f96b39cad6182a4a229a07f6a7..8c016f6405da0c1227162d3ccbe5d53484d6645f 100644 (file)
@@ -399,6 +399,10 @@ BOM_UTF8   \357\273\277
        yylval.scm = scm_from_locale_string (s);
        return RESTNAME;
 }
+<chords,notes,figures>q        {
+       return CHORD_REPETITION;
+}
+
 <chords,notes,figures>R                {
        return MULTI_MEASURE_REST;
 }
@@ -948,11 +952,7 @@ Lily_lexer::scan_bare_word (string str)
                    yylval.scm = scm_cdr (handle);
                    return CHORD_MODIFIER;
                }
-               if ((chord_repetition_.repetition_symbol_ != SCM_EOL)
-                   && to_boolean (scm_equal_p (chord_repetition_.repetition_symbol_, sym)))
-                       return CHORD_REPETITION;
        }
-
        yylval.scm = ly_string2scm (str);
        return STRING;
 }
index 19ed8e41af73bcad259a62a9977a30d0e1610b65..7f017a437bb8874b15b77b73e3741e07e090db2c 100644 (file)
@@ -97,7 +97,6 @@ Lily_lexer::Lily_lexer (Sources *sources, Lily_parser *parser)
   error_level_ = 0;
   is_main_input_ = false;
   start_module_ = SCM_EOL;
-  chord_repetition_ = Chord_repetition ();
   extra_tokens_ = SCM_EOL;
   smobify_self ();
 
@@ -115,7 +114,6 @@ Lily_lexer::Lily_lexer (Lily_lexer const &src, Lily_parser *parser)
   pitchname_tab_stack_ = src.pitchname_tab_stack_;
   sources_ = src.sources_;
   start_module_ = SCM_EOL;
-  chord_repetition_ = src.chord_repetition_;
 
   error_level_ = 0;
   is_main_input_ = src.is_main_input_;
index d8b9265e4d21297911e499c5073d52d1725610f9..5281f7e2a741c3b8867816ea0ec995afcbb0606a 100644 (file)
@@ -278,32 +278,6 @@ LY_DEFINE (ly_parser_set_note_names, "ly:parser-set-note-names",
   return SCM_UNSPECIFIED;
 }
 
-LY_DEFINE (ly_parser_set_repetition_symbol, "ly:parser-set-repetition-symbol",
-           2, 0, 0, (SCM parser, SCM sym),
-           "Replace the current repetition symbol in @var{parser}."
-           "  @var{sym} is the new repetition symbol.")
-{
-  LY_ASSERT_SMOB (Lily_parser, parser, 1);
-  Lily_parser *p = unsmob_lily_parser (parser);
-
-  p->lexer_->chord_repetition_.repetition_symbol_ = sym;
-
-  return SCM_UNSPECIFIED;
-}
-
-LY_DEFINE (ly_parser_set_repetition_function, "ly:parser-set-repetition-function",
-           2, 0, 0, (SCM parser, SCM fun),
-           "Replace the current repetition function in @var{parser}."
-           "  @var{fun} is the new repetition function.")
-{
-  LY_ASSERT_SMOB (Lily_parser, parser, 1);
-  Lily_parser *p = unsmob_lily_parser (parser);
-
-  p->lexer_->chord_repetition_.repetition_function_ = fun;
-
-  return SCM_UNSPECIFIED;
-}
-
 LY_DEFINE (ly_parser_output_name, "ly:parser-output-name",
            1, 0, 0, (SCM parser),
            "Return the base name of the output file.")
index db5bdb95f6ca83cac5fffc18b7d2532e0bd6d06a..dde9b653c9a3b6d84542b2de321f61e41b9c66ab 100644 (file)
@@ -21,6 +21,8 @@
 
 #include "warn.hh"
 #include "program-option.hh"
+#include "duration.hh"
+#include "moment.hh"
 #include "music.hh"
 #include "input.hh"
 
@@ -79,6 +81,20 @@ Music_sequence::maximum_length_callback (SCM m)
   return maximum_length (me->get_property ("elements")).smobbed_copy ();
 }
 
+MAKE_SCHEME_CALLBACK (Music_sequence, event_chord_length_callback, 1);
+SCM
+Music_sequence::event_chord_length_callback (SCM m)
+{
+  Music *me = unsmob_music (m);
+  Duration *d = unsmob_duration (me->get_property ("duration"));
+  // Preset duration is used in chord repetitions.
+  if (d) {
+    Moment mom = d->get_length ();
+    return mom.smobbed_copy ();
+  }
+  return maximum_length (me->get_property ("elements")).smobbed_copy ();
+}
+
 MAKE_SCHEME_CALLBACK (Music_sequence, cumulative_length_callback, 1);
 SCM
 Music_sequence::cumulative_length_callback (SCM m)
@@ -190,59 +206,3 @@ Music_sequence::event_chord_relative_callback (SCM music, SCM pitch)
   return music_list_to_relative (me->get_property ("elements"),
                                  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 22a45b279786e62e8fc0d5156d4b5647634fcc2e..71b476cca0c6348e11d5643546cb64e04bd0811a 100644 (file)
@@ -2054,8 +2054,6 @@ event_chord:
                Input i;
                i.set_location (@1, @3);
                $$ = MAKE_SYNTAX ("repetition-chord", i,
-                                 parser->lexer_->chord_repetition_.last_chord_,
-                                 parser->lexer_->chord_repetition_.repetition_function_,
                                  $2, scm_reverse_x ($3, SCM_EOL));
        }
        | MULTI_MEASURE_REST optional_notemode_duration post_events {
@@ -2065,13 +2063,7 @@ event_chord:
                                  scm_reverse_x ($3, SCM_EOL));
        }
        | 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_ = $$;
-       }
+       | note_chord_element
        ;
 
 
index 209e2676e0ba970131c48ed28b03e0fbb9dfd26e..0ff0498f58af04f868031a08802f897ef26ce97d 100644 (file)
@@ -1,79 +1,41 @@
 %%% -*- Mode: Scheme -*-
-\version "2.14.0"
+\version "2.15.28"
 %{
-
-The following functions define the chord repetition behavior, and may
-be invoked by the user to customize it.
-
-ly:parser-set-repetition-symbol
-  set the chord repetition shortcut.
-  `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 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.
-  `tab-repeat-chord' may be used in tablatures to preserve the string information.
+  Chord repetition behavior is not customizable in the parser.  That
+  is due to it usually being done by the toplevel music handler
+  affecting every bit of music at the same time, not closely related
+  to music input.  Customized behavior is instead accomplished by
+  calling \chordRepeats explicitly on some music list with a list of
+  event types you wish to keep by default (if any events of that kind
+  are found already on the repeat chord, however, they still get
+  removed from the original).
+
+  The default behavior is straightforward: don't keep anything but the
+  rhythmic events themselves.
 %}
 
-#(define-public ((make-repeat-chord-function chord-element-types note-articulation-types)
-                 previous-chord location duration articulations)
-   "Make a chord repetition function.
-The returned functions copies the notes from @var{previous-chord} into a new chord.
-Chord elements, which type is found in @var{chord-element-types}, are copied into
-the new chord. Note articulations, which type is found in @var{note-articulation-types},
-are also copied. All other events are not copied into the new chord."
-   (define (filter-events events event-types)
-     (filter (lambda (event)
-              (and (memq (ly:music-property event 'name) event-types) event))
-            events))
-   ;; 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 from an other music block, which is certainly not
-   ;; what the user has intended, as anywy the result will be buggy.
-   ;; 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")))
-   ;; Instead of copying the previous chord, then removing the
-   ;; undesired elements (like articulations), a new empty chord is built.
-   ;; Then, the pitch found in the previous chord are added to the new
-   ;; chord, without any "decoration" (e.g. cautionary accidentals,
-   ;; fingerings, text scripts, articulations).  Only the events of types
-   ;; given in `chord-elements-types' and `note-articulation-types' are
-   ;; copied from the original chord elements and note articulations,
-   ;; respectively.
-   (let ((elements (ly:music-property (ly:music-deep-copy previous-chord) 'elements)))
-     (make-music
-      'EventChord
-      'origin location
-      'elements (append!
-                 (map (lambda (note)
-                        (let ((new-note (make-music 'NoteEvent
-                                                    'origin location
-                                                    'pitch (ly:music-property note 'pitch)
-                                                    'duration duration))
-                              (articulations
-                               (filter-events (ly:music-property note 'articulations)
-                                              note-articulation-types)))
-                          (if (not (null? articulations))
-                              (set! (ly:music-property new-note 'articulations)
-                                    articulations))
-                          new-note))
-                      (filter-events elements '(NoteEvent)))
-                 (filter-events elements chord-element-types)
-                 articulations))))
-
-#(define-public default-repeat-chord
-   (make-repeat-chord-function '() '()))
-
-#(define-public tab-repeat-chord
-   (make-repeat-chord-function '(StringNumberEvent) '(StringNumberEvent)))
-
-% default settings
-#(ly:parser-set-repetition-symbol parser 'q)
-#(ly:parser-set-repetition-function parser default-repeat-chord)
+chordRepeats =
+#(define-music-function (parser location event-types music)
+   ((list? '()) ly:music?)
+   "Walk through @var{music} putting the notes of the previous chord
+into repeat chords, as well as an optional list of @var{event-types}
+such as @code{#'(string-number-event)}."
+   (expand-repeat-chords! (cons 'rhythmic-event event-types) music))
+
+tabChordRepeats =
+#(define-music-function (parser location event-types music)
+   ((list? '()) ly:music?)
+   "Walk through @var{music} putting the notes, fingerings and string
+numbers of the previous chord into repeat chords, as well as an
+optional list of @var{event-types} such as @code{#'(articulation-event)}."
+   #{ \chordRepeats
+      #(append '(string-number-event fingering-event) event-types)
+      #music
+   #})
+
+tabChordRepetition =
+#(define-void-function (parser location) ()
+   (_i "Include the string and fingering information in a chord repetition.
+This function is deprecated; try using @code{\tabChordRepeats} instead.")
+   (ly:parser-define! parser '$chord-repeat-events
+                     '(string-number-event fingering-event)))
index 59c452065c3a03cc33fda0e489908c5f15faff69..a22292657fcb968b480df290ee347acea9253d18 100644 (file)
@@ -1019,14 +1019,6 @@ styledNoteHeads =
    (_i "Set @var{heads} in @var{music} to @var{style}.")
    (style-note-heads heads style music))
 
-
-
-tabChordRepetition =
-#(define-music-function (parser location) ()
-   (_i "Include the string information in a chord repetition.")
-   (ly:parser-set-repetition-function parser tab-repeat-chord)
-   (make-music 'SequentialMusic 'void #t))
-
 tag =
 #(define-music-function (parser location tag arg) (symbol? ly:music?)
 
index db21afda90aacfb9aa62b0f45e169582c9762213..3e87b4a8a8bab722ac34ac4846fccbaacd3c27ed 100644 (file)
@@ -419,8 +419,7 @@ Otherwise, return #f."
 ;;;
 
 (define-display-method EventChord (chord parser)
-  ;; event_chord : simple_element post_events
-  ;;              | command_element
+  ;; event_chord : command_element
   ;;              | note_chord_element
 
   ;; TODO : tagged post_events
@@ -428,41 +427,45 @@ Otherwise, return #f."
   ;; tagged_post_event: '-' \tag embedded_scm post_event
 
   (let* ((elements (ly:music-property chord 'elements))
-        (simple-elements (filter (make-music-type-predicate
-                                  'NoteEvent 'ClusterNoteEvent 'RestEvent
-                                  'SkipEvent 'LyricEvent)
-                                 elements)))
-           (let ((chord-elements (filter (make-music-type-predicate
-                                          'NoteEvent 'ClusterNoteEvent 'BassFigureEvent)
-                                         elements))
-                 (post-events (filter post-event? elements)))
-             (if (not (null? chord-elements))
-                 ;; note_chord_element : '<' (notepitch | drumpitch)* '>" duration post_events
-                 (let* ((duration (duration->lily-string
-                                   (ly:music-property (car chord-elements) 'duration)
-                                   #:remember #t)))
-                   (format #f "< ~{~a ~}>~a~{~a~^ ~}"
-                           (map-in-order (lambda (music)
-                                           (music->lily-string music parser))
-                                         chord-elements)
-                           duration
-                           (map-in-order (lambda (music)
-                                           (music->lily-string music parser))
-                                         post-events)))
-                 ;; command_element
-                 (format #f "~{~a~^ ~}" (map-in-order (lambda (music)
-                                                      (music->lily-string music parser))
-                                                    elements))))))
-
-(define-display-method MultiMeasureRestMusic (mmrest parser)
-  (let* ((dur (ly:music-property mmrest 'duration))
-        (ly (format #f "R~a~{~a~^ ~}"
-                    (duration->lily-string dur)
+        (chord-elements (filter (lambda (m)
+                                   (music-is-of-type? m 'rhythmic-event))
+                                elements))
+        (post-events (filter post-event? elements))
+        (chord-repeat (ly:music-property chord 'duration)))
+    (cond ((ly:duration? chord-repeat)
+          (let ((duration (duration->lily-string chord-repeat #:remember #t)))
+            (format #f "q~a~{~a~^ ~}"
+                    duration
+                    (map-in-order (lambda (music)
+                                    (music->lily-string music parser))
+                                  post-events))))
+         ((pair? chord-elements)
+          ;; note_chord_element : '<' (notepitch | drumpitch)* '>" duration post_events
+          (let ((duration (duration->lily-string (ly:music-property
+                                                  (car chord-elements)
+                                                  'duration) #:remember #t)))
+            ;; Format duration first so that it does not appear on chord elements
+            (format #f "< ~{~a ~}>~a~{~a~^ ~}"
+                    (map-in-order (lambda (music)
+                                    (music->lily-string music parser))
+                                  chord-elements)
+                    duration
                     (map-in-order (lambda (music)
                                     (music->lily-string music parser))
-                                  (ly:music-property mmrest 'articulations)))))
-    (*previous-duration* dur)
-    ly))
+                                  post-events))))
+         (else
+          ;; command_element
+          (format #f "~{~a~^ ~}" (map-in-order (lambda (music)
+                                                 (music->lily-string music parser))
+                                               elements))))))
+
+(define-display-method MultiMeasureRestMusic (mmrest parser)
+  (format #f "R~a~{~a~^ ~}"
+         (duration->lily-string (ly:music-property mmrest 'duration)
+                                #:remember #t)
+         (map-in-order (lambda (music)
+                         (music->lily-string music parser))
+                       (ly:music-property mmrest 'articulations))))
 
 (define-display-method SkipMusic (skip parser)
   (format #f "\\skip ~a" (duration->lily-string (ly:music-property skip 'duration) #:force-duration #t)))
index 7b60887bd698a8de035b0d73751fb1218c093d41..0f1b1e619e412f75e1ef6ce9cb27b2349db2d1f8 100644 (file)
@@ -202,7 +202,7 @@ An alternative syntax is @var{note}@code{\\decr} @dots{}
     (EventChord
      . ((description . "Internally used to group a set of events.")
        (iterator-ctor . ,ly:event-chord-iterator::constructor)
-       (length-callback . ,ly:music-sequence::maximum-length-callback)
+       (length-callback . ,ly:music-sequence::event-chord-length-callback)
        (to-relative-callback .
         ,ly:music-sequence::event-chord-relative-callback)
        (types . (general-music event-chord simultaneous-music))
@@ -458,15 +458,6 @@ 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 40a1c05907aded52786ec5ceff61344653a7baf8..45d308c4a92b9097b995dad731124a7a72a8e22c 100644 (file)
@@ -158,10 +158,10 @@ into a @code{MultiMeasureTextEvent}."
              'duration duration
              'origin location))
 
-(define-ly-syntax (repetition-chord parser location previous-chord repetition-function duration articulations)
-  (make-music 'RepeatedChord
-             'original-chord previous-chord
-             'element (repetition-function previous-chord location duration articulations)
+(define-ly-syntax (repetition-chord parser location duration articulations)
+  (make-music 'EventChord
+             'duration duration
+             'elements articulations
              'origin location))
 
 (define-ly-syntax-simple (context-specification type id ops create-new mus)
index 6114e144d6bc8e6b5d8400fd7b5b2d3bd04ed132..c6ac33a54734f2a4c93e39f7e27f65bc481cb821 100644 (file)
@@ -255,7 +255,9 @@ properly."
   (define (first-note-duration music)
     "Finds the duration of the first NoteEvent by searching depth-first
 through MUSIC."
-    (if (memq 'note-event (ly:music-property music 'types))
+    ;; NoteEvent or a non-expanded chord-repetition
+    ;; We just take anything that actually sports an announced duration.
+    (if (ly:duration? (ly:music-property music 'duration))
        (ly:music-property music 'duration)
        (let loop ((elts (if (ly:music? (ly:music-property music 'element))
                             (list (ly:music-property music 'element))
@@ -276,7 +278,7 @@ through MUSIC."
     (set! (ly:music-property r 'repeat-count) (max times 1))
     (set! (ly:music-property r 'elements) talts)
     (if (and (equal? name "tremolo")
-            (pair? (extract-named-music main 'NoteEvent)))
+            (pair? (extract-named-music main '(EventChord NoteEvent))))
        ;; This works for single-note and multi-note tremolos!
        (let* ((children (if (music-is-of-type? main 'sequential-music)
                             ;; \repeat tremolo n { ... }
@@ -577,6 +579,65 @@ inside of and outside of chord construct."
   (let ((ts (ly:music-property m 'types)))
     (memq 'separator ts)))
 
+;;; expanding repeat chords
+(define-public (copy-repeat-chord original-chord repeat-chord duration
+                                 event-types)
+  "Copies all events in @var{event-types} (be sure to include
+@code{rhythmic-events}) from @var{original-chord} over to
+@var{repeat-chord} with their articulations filtered as well.  Any
+duration is replaced with the specified @var{duration}."
+  ;; First remove everything from event-types that can already be
+  ;; found in the repeated chord.  We don't need to look for
+  ;; articulations on individual events since they can't actually get
+  ;; into a repeat chord given its input syntax.
+  (for-each (lambda (e)
+             (for-each (lambda (x)
+                         (set! event-types (delq x event-types)))
+                       (ly:music-property e 'types)))
+           (ly:music-property repeat-chord 'elements))
+  ;; now treat the elements
+  (set! (ly:music-property repeat-chord 'elements)
+       (append!
+        (filter-map
+         (lambda (m)
+           (and (any (lambda (t) (music-is-of-type? m t)) event-types)
+                (begin
+                  (set! m (ly:music-deep-copy m))
+                  (if (pair? (ly:music-property m 'articulations))
+                      (set! (ly:music-property m 'articulations)
+                            (filter
+                             (lambda (a)
+                               (any (lambda (t) (music-is-of-type? a t))
+                                    event-types))
+                             (ly:music-property m 'articulations))))
+                  (if (ly:duration? (ly:music-property m 'duration))
+                      (set! (ly:music-property m 'duration) duration))
+                  m)))
+         (ly:music-property original-chord 'elements))
+        (ly:music-property repeat-chord 'elements))))
+
+(define-public (expand-repeat-chords! event-types music)
+  "Walks through @var{music} and fills repeated chords (notable by
+having a duration in @code{duration}) with the notes from their
+respective predecessor chord."
+  (let loop ((music music) (last-chord #f))
+    (if (music-is-of-type? music 'event-chord)
+       (let ((chord-repeat (ly:music-property music 'duration)))
+         (cond
+          ((not (ly:duration? chord-repeat))
+           music)
+          (last-chord
+           (set! (ly:music-property music 'duration) '())
+           (copy-repeat-chord last-chord music chord-repeat event-types)
+           music)
+          (else
+           (ly:music-warning music (_ "Bad chord repetition"))
+           #f)))
+       (let ((elt (ly:music-property music 'element)))
+         (fold loop (if (ly:music? elt) (loop elt last-chord) last-chord)
+               (ly:music-property music 'elements)))))
+  music)
+
 ;;; splitting chords into voices.
 (define (voicify-list lst number)
   "Make a list of Musics.
@@ -1037,6 +1098,10 @@ then revert skipTypesetting."
 
 (define-public toplevel-music-functions
   (list
+   (lambda (music parser) (expand-repeat-chords!
+                          (cons 'rhythmic-event
+                                (ly:parser-lookup parser '$chord-repeat-events))
+                          music))
    (lambda (music parser) (voicify-music music))
    (lambda (x parser) (music-map music-check-error x))
    (lambda (x parser) (music-map precompute-music-length x))