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
@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{}#@}})
@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?)
>>
@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
@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 {
<<
}
@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.
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},
--- /dev/null
+\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 } |
+}
\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 } } #]
--- /dev/null
+\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
+ }
+ }
+ >>
+}
\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
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);
SCM chordmodifier_tab_;
SCM pitchname_tab_stack_;
- Chord_repetition chord_repetition_;
-
int error_level_;
Input last_input_;
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);
yylval.scm = scm_from_locale_string (s);
return RESTNAME;
}
+<chords,notes,figures>q {
+ return CHORD_REPETITION;
+}
+
<chords,notes,figures>R {
return MULTI_MEASURE_REST;
}
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;
}
error_level_ = 0;
is_main_input_ = false;
start_module_ = SCM_EOL;
- chord_repetition_ = Chord_repetition ();
extra_tokens_ = SCM_EOL;
smobify_self ();
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_;
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.")
#include "warn.hh"
#include "program-option.hh"
+#include "duration.hh"
+#include "moment.hh"
#include "music.hh"
#include "input.hh"
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)
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;
-}
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 {
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
;
%%% -*- 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)))
(_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?)
;;;
(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
;; 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)))
(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))
(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))
'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)
(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))
(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 { ... }
(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.
(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))