From: David Kastrup Date: Wed, 21 Sep 2011 12:51:12 +0000 (+0200) Subject: Add \pushAtTag \appendAtTag music functions and map-music-copy function X-Git-Tag: release/2.15.13-1~40 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=264022bd6ebfed3220c0272d2c4a1c8ef9db4028;p=lilypond.git Add \pushAtTag \appendAtTag music functions and map-music-copy function --- diff --git a/Documentation/notation/input.itely b/Documentation/notation/input.itely index dc7045fe70..65d2507255 100644 --- a/Documentation/notation/input.itely +++ b/Documentation/notation/input.itely @@ -1329,7 +1329,10 @@ of different versions of a score from the same music source. Variables are perhaps most useful for combining lengthy sections of music and/or annotation in various ways, while tags are more useful for selecting one from several alternative shorter sections -of music. Whichever method is used, separating the notation from +of music. You can also employ tags for splicing pieced of music +together at several places. + +Whichever method is used, separating the notation from the structure of the score will make it easier to change the structure while leaving the notation untouched. @@ -1406,9 +1409,12 @@ LilyPond files}. @funindex \tag @funindex \keepWithTag @funindex \removeWithTag +@funindex \pushToTag +@funindex \appendToTag @cindex tag @cindex keep tagged music @cindex remove tagged music +@cindex splice into tagged music The @code{\tag #'@var{partA}} command marks a music expression with the name @var{partA}. @@ -1533,6 +1539,28 @@ expression will cause @emph{all} tagged sections to be removed, as the first filter will remove all tagged sections except the one named, and the second filter will remove even that tagged section. +Sometimes you want to splice some music at a particular place in an +existing music expression. You can use @code{\pushToTag} and +@code{\appendToTag} for adding material at the front or end of the +@code{elements} of an existing music construct. Not every music +construct has @code{elements}, but sequential and simultaneous music are +safe bets: + +@lilypond[verbatim,quote] +test = { \tag #'here { \tag #'here <> } } + +{ + \pushToTag #'here \pushToTag #'here + \pushToTag #'here \test g' e' c' + \appendToTag #'here \appendToTag #'here + \appendToTag #'here \test g' e' c' +} +@end lilypond + +Both commands get a tag, the tagged expression, and finally the material +you want to splice in at every given tag. The commands make sure to +copy everything that they change so that the original @code{\test} +retains its meaning. @seealso Learning Manual: diff --git a/input/regression/push-to-tag.ly b/input/regression/push-to-tag.ly new file mode 100644 index 0000000000..2cb4d43f3f --- /dev/null +++ b/input/regression/push-to-tag.ly @@ -0,0 +1,21 @@ +\version "2.15.13" + +\header{ + texidoc=" +Adding material to a tag in sequential and simultaneous expressions +using @code{\\pushToTag} and @code{\\appendToTag}. One should get the +equivalent of +@example +@{ c' e' g' <> <> g' e' c' @} +@end example +" +} + +\layout { ragged-right = ##t } + +test = { \tag #'here { \tag #'here <> }} + +{ + \pushToTag #'here \pushToTag #'here \pushToTag #'here \test g' e' c' + \appendToTag #'here \appendToTag #'here \appendToTag #'here \test g' e' c' +} diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index 5a95cc6e03..e0b36696ba 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -85,6 +85,21 @@ markups), or inside a score.") 'elements (list (make-music 'PageTurnEvent 'break-permission 'allow)))) +appendToTag = +#(define-music-function (parser location tag music more) + (symbol? ly:music? ly:music?) + (_i "Append @var{more} to the @code{elements} of all music +expressions in @var{music} that are tagged with @var{tag}.") + (music-map-copy (lambda (m) + (if (memq tag (ly:music-property m 'tags)) + (begin + (set! m (music-clone m)) + (set! (ly:music-property m 'elements) + (append (ly:music-property m 'elements) + (list more))))) + m) + music)) + applyContext = #(define-music-function (parser location proc) (procedure?) (_i "Modify context properties with Scheme procedure @var{proc}.") @@ -830,6 +845,20 @@ print @var{secondary-note} as a stemless note head in parentheses.") trill-events))))) main-note)) +pushToTag = +#(define-music-function (parser location tag music more) + (symbol? ly:music? ly:music?) + (_i "Add @var{more} to the front of @code{elements} of all music +expressions in @var{music} that are tagged with @var{tag}.") + (music-map-copy (lambda (m) + (if (memq tag (ly:music-property m 'tags)) + (begin + (set! m (music-clone m)) + (set! (ly:music-property m 'elements) + (cons more (ly:music-property m 'elements))))) + m) + music)) + quoteDuring = #(define-music-function (parser location what main-music) (string? ly:music?) (_i "Indicate a section of music to be quoted. @var{what} indicates the name diff --git a/scm/music-functions.scm b/scm/music-functions.scm index 4dc9df6568..e1ee530173 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -66,6 +66,36 @@ First it recurses over the children, then the function is applied to (music-map function e))) (function music))) +(define-public (music-map-copy function music) + "Apply @var{function} to @var{music} and all of the music it contains. + +First it recurses over the children, then the function is applied to +@var{music}. This can be used for non-destructive changes when the +original @var{music} should retain its meaning. When you change an +element, return a changed copy. Then all ancestors will recursively +become changed copies as well. The check for change is done via +@code{eq?}." + (let* ((es (ly:music-property music 'elements)) + (e (ly:music-property music 'element)) + (esnew (pair-fold-right + (lambda (y tail) + (let ((res (music-map-copy function (car y)))) + (if (and (eq? (cdr y) tail) + (eq? (car y) res)) + y + (cons res tail)))) + '() es)) + (enew (if (ly:music? e) + (music-map-copy function e) + e))) + (if (not (and (eq? es esnew) + (eq? e enew))) + (begin + (set! music (music-clone music)) + (set! (ly:music-property music 'elements) esnew) + (set! (ly:music-property music 'element) enew))) + (function music))) + (define-public (music-filter pred? music) "Filter out music expressions that do not satisfy @var{pred?}."