]> git.donarmstrong.com Git - lilypond.git/commitdiff
Add \pushAtTag \appendAtTag music functions and map-music-copy function
authorDavid Kastrup <dak@gnu.org>
Wed, 21 Sep 2011 12:51:12 +0000 (14:51 +0200)
committerDavid Kastrup <dak@gnu.org>
Wed, 21 Sep 2011 15:27:38 +0000 (17:27 +0200)
Documentation/notation/input.itely
input/regression/push-to-tag.ly [new file with mode: 0644]
ly/music-functions-init.ly
scm/music-functions.scm

index dc7045fe70c7482af2d30a6cf043772a9c041826..65d25072558752e8ef3629313859e48ec560ef11 100644 (file)
@@ -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 <<c''>> } }
+
+{
+  \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 (file)
index 0000000..2cb4d43
--- /dev/null
@@ -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' <<c' e' g' c''>> <<c'' g' e' c'>> g' e' c' @}
+@end example
+"
+}
+
+\layout { ragged-right = ##t }
+
+test = { \tag #'here { \tag #'here <<c''>> }}
+
+{
+  \pushToTag #'here \pushToTag #'here \pushToTag #'here \test g' e' c'
+  \appendToTag #'here \appendToTag #'here \appendToTag #'here \test g' e' c'
+}
index 5a95cc6e035787eabcccb0266a4a5f30c6d304b1..e0b36696bab23c20eefaede5e1c1e634baa87faa 100644 (file)
@@ -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
index 4dc9df6568b38b06133d4a38b24233617bab2507..e1ee53017392219563bd04d0700903421ac673db 100644 (file)
@@ -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?}."