Tag manipulating functions and additional filter by tag functions

(Redirected from LSR 871)

If you're using tags frequently, you may find the available functions quite limited. Here is a collection of additional functions to modify the tags, and to filter by the tags.
tag manipulating:

  • \removeAllTags music

returns a copy of the music, where all tags are erased. Thus they will not harm when tags are created in a higher level and the music is then filtered by \keepWithTag.

  • \copyTag from-tag-symbol to-tag-symbol music

returns a copy of the music, where all elements tagged with 'from-tag-symbol' also got a tag 'to-tag-symbol'.

  • \renameTag from-tag-symbol to-tag-symbol music

returns a copy of the music, where the tag names 'from-tag-symbol' are altered into 'to-tag-symbol'.

  • \removeTag tag-symbol music

returns a copy of the music, where only the tags 'tag-symbol' are erased, the music is kept.

  • \removeTags tag-symbol-list music

similar to '\removeTag', but takes a list to rub out multiple tags from the music.

filtering by tags:

  • \keepWithTags tag-symbol-list music

filters the music and keeps all tagged objects, who's tags are listed in the 'tag-symbol-list'. This 'keep with tag x or y or ...' is such a fundamental idea, it seems to be developed multiple times, and I heard it will be superseeded by the standard LILYPOND command \keepWithTag in near future.

  • \keepOrRemoveWithTag tag-symbol list-of-the-relevant-tag-symbols music

keeps all elements tagged by 'tag-symbol', but it only removes tagged elements out of the 'list-of-the-relevant-tag-symbols'. Thus, this list is an limitation which tags to check. This list my include the 'tag-symbol' which to keep.

  • \taggedRep start-tag-symbol end-tag-symbol rep-count-integer music

is allmost an "\repeat unfold" command. In the first repetition the elements tagged with 'start-tag-symbol' will be kept while the ones with 'end-tag-symbol' will be removed. In the last repetition it's inverted and only the 'end-tag-symbol' tagged elements will be kept while those with 'start-tag-symbol' will be removed. In all repetitions between both tagged elements will be removed.

combined funtions:

  • \cleansedKeepOrRemoveWithTag tag-symbol list-of-the-relevant-tag-symbols music

Additional to \keepOrRemoveWithTag all the tags listed in 'list-of-the-relevant-tag-symbols' will be removed from the result to reduce the chance of side effects when the music is filtered again.

  • \cleansedTaggedRep start-tag-symbol end-tag-symbol rep-count-integer music

Additional to \taggedRep the two tags 'start-tag-symbol' and 'end-tag-symbol' will be removed from the result to reduce the chance of side effects when the music is filtered again.

  • \keepPart music, \keepScore music and \keepMidi music

These are predefined \cleansedKeepOrRemoveWithTag commands. The list of the relevant tag symbols is stored in the Parameter PSM = #'(Part Score Midi); their name shows which tag they keep.

\version "2.24"

%% http://lsr.di.unimi.it/LSR/Item?id=871
%% see also http://lsr.di.unimi.it/LSR/Item?id=219

%by: ArnoldTheresius


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% START of my personal include file 'tagging.ly'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (remove-all-tags music)
  (if (ly:music? music)
   (let*
    ((es (ly:music-property music 'elements))
     (e (ly:music-property music 'element))
     (as (ly:music-property music 'articulations))
     (tags (ly:music-property music 'tags)))
    (if (not (eq? tags '()))
     (ly:music-set-property! music 'tags '()))
    (for-each remove-all-tags es)
    (remove-all-tags e)
    (for-each remove-all-tags as))
   music))

removeAllTags =
#(define-music-function
  (music) (ly:music?)
  (_i "Delete all the tags in all elements of @var{music}")
  (let
   ((new-music (ly:music-deep-copy music)))
   (remove-all-tags new-music)
   new-music))


#(define (copy-tags-in-music tag-from tag-to music)
  (if (ly:music? music)
   (let*
    ((es (ly:music-property music 'elements))
     (e (ly:music-property music 'element))
     (as (ly:music-property music 'articulations))
     (tags (ly:music-property music 'tags)))
    (if (memq tag-from tags)
     (if (not (memq tag-to tags))
      (ly:music-set-property! music 'tags (cons tag-to tags))))
    (for-each (lambda (x) (copy-tags-in-music tag-from tag-to x)) es)
    (copy-tags-in-music tag-from tag-to e)
    (for-each (lambda (x) (copy-tags-in-music tag-from tag-to x)) as))
   music))

copyTag =
#(define-music-function
  (tag-from tag-to music) (symbol? symbol? ly:music?)
  (_i "To all elements of @var{music} which are tagged with @var{tag-from} add a tag @var{tag-to}.")
  (let
   ((new-music (ly:music-deep-copy music)))
   (copy-tags-in-music tag-from tag-to new-music)
   new-music))


#(define (rename-tag-in-music tag-from tag-to music)
  (if (ly:music? music)
   (let*
    ((es (ly:music-property music 'elements))
     (e (ly:music-property music 'element))
     (as (ly:music-property music 'articulations))
     (tags (ly:music-property music 'tags)))
    (if (memq tag-from tags)
     (let
      ((nt (delq tag-from tags)))
      (if (memq tag-to tags)
       (ly:music-set-property! music 'tags nt)
       (ly:music-set-property! music 'tags (cons tag-to nt)))))
    (for-each (lambda (x) (rename-tag-in-music tag-from tag-to x)) es)
    (rename-tag-in-music tag-from tag-to e)
    (for-each (lambda (x) (rename-tag-in-music tag-from tag-to x)) as))
   music))

renameTag =
#(define-music-function
  (tag-from tag-to music) (symbol? symbol? ly:music?)
  (_i "Replace an existing tag @var{tag-from} with tag @var{tag-to} in all elements of @var{music}")
  (let
   ((new-music (ly:music-deep-copy music)))
   (rename-tag-in-music tag-from tag-to new-music)
   new-music))


#(define (remove-tag-in-music tag music)
  (if (ly:music? music)
   (let*
    ((es (ly:music-property music 'elements))
     (e (ly:music-property music 'element))
     (as (ly:music-property music 'articulations))
     (tags (ly:music-property music 'tags)))
    (if (memq tag tags)
     (ly:music-set-property! music 'tags
      (filter (lambda (x) (not (eq? x tag))) tags)))
    (for-each (lambda (x) (remove-tag-in-music tag x)) es)
    (remove-tag-in-music tag e)
    (for-each (lambda (x) (remove-tag-in-music tag x)) as))
   music))

removeTag =
#(define-music-function
  (tag music) (symbol? ly:music?)
  (_i "Remove the specified tag @var{tag} in all elements of @var{music}")
  (let
   ((new-music (ly:music-deep-copy music)))
   (remove-tag-in-music tag new-music)
   new-music))


#(define (remove-tags-in-music tag-list music)
  (if (ly:music? music)
   (let*
    ((es (ly:music-property music 'elements))
     (e (ly:music-property music 'element))
     (as (ly:music-property music 'articulations))
     (tags (ly:music-property music 'tags)))
    (ly:music-set-property! music 'tags
     (filter (lambda (x) (not (memq x tag-list))) tags))
    (for-each (lambda (x) (remove-tags-in-music tag-list x)) es)
    (remove-tags-in-music tag-list e)
    (for-each (lambda (x) (remove-tags-in-music tag-list x)) as))
   music))

removeTags =
#(define-music-function
  (tag-list music) (list? ly:music?)
  (_i "Remove all the tags specified in the list @var{tag-list} in all elements of @var{music}")
  (let
   ((new-music (ly:music-deep-copy music)))
   (remove-tags-in-music tag-list new-music)
   new-music))


keepWithTags =
#(define-music-function
  (taglist music) (list? ly:music?)
  (_i "Include only elements of @var{music} that are tagged with at least one tag out of the list @var{taglist}.")
  (music-filter
   (lambda (m)
    (let*
     ((tags (ly:music-property m 'tags))
      (res #f))
     (for-each
      (lambda (tag) (if (memq tag tags) (set! res #t)))
      taglist)
     (or
      (eq? tags '())
      res)))
   music))


#(define (remove-with-tags taglist music)
  (music-filter
   (lambda (m)
    (let*
     ((tags (ly:music-property m 'tags))
      (result #t))
     (for-each
      (lambda (tag) (if (memq tag tags) (set! result #f)))
      taglist)
     (or
      (eq? tags '())
      result)))
   music))


#(define (keep-or-remove-with-tag keep-tag remove-taglist music)
  (music-filter
   (lambda (m)
    (let*
     ((tags (ly:music-property m 'tags))
      (res #t))
     (if (not (memq keep-tag tags))
      (for-each
       (lambda (tag) (if (memq tag tags) (set! res #f)))
       remove-taglist))
     (or
      (eq? tags '())
      res)))
   music))

keepOrRemoveWithTag =
#(define-music-function
  (keep-tag remove-taglist music) (symbol? list? ly:music?)
  (_i "Keep the elements of @var{music} which are tagged with @var{keep-tag}, otherwise remove them only if they are tagged with at least one symbol out the list @var{remove-taglist}.")
  (music-filter
   (lambda (m)
    (let*
     ((tags (ly:music-property m 'tags))
      (res #t))
     (if (not (memq keep-tag tags))
      (for-each
       (lambda (tag) (if (memq tag tags) (set! res #f)))
       remove-taglist))
     (or
      (eq? tags '())
      res)))
   music))


cleansedKeepOrRemoveWithTag =
#(define-music-function
  (keep-tag remove-taglist music) (symbol? list? ly:music?)
  (_i "Keep the elements of @var{music} which are tagged with @var{keep-tag}, otherwise remove them only if they are tagged with at least one symbol out the list @var{remove-taglist}. Finally wipe out all tags listed in the @var{remove-taglist} so they will not harm any future keepWithTag.")
  (let
   ((new-music (ly:music-deep-copy 
      (music-filter
       (lambda (m)
        (let*
         ((tags (ly:music-property m 'tags))
          (res #t))
         (if (not (memq keep-tag tags))
          (for-each
           (lambda (tag) (if (memq tag tags) (set! res #f)))
           remove-taglist))
         (or
          (eq? tags '())
          res)))
       music))))
   (remove-tags-in-music remove-taglist new-music)
   new-music))


taggedRep =
#(define-music-function
  (start-tag end-tag rep-count music) (symbol? symbol? integer? ly:music?)
  (_i "Repeat the @var{music} unfolded @var{rep-count} times. In the first repeat output keep @var{start-tag} and remove @var{end-tag} tagged elements, in the middle remove both, and at the end keep @var{end-tag} and remove @var{start-tag} tagged elements.")
  (if (<= rep-count 1)
   music
   (make-music 'SequentialMusic 'elements
    (if (<= rep-count 2)
     (list
      (keep-or-remove-with-tag start-tag (list start-tag end-tag) (ly:music-deep-copy music))
      (keep-or-remove-with-tag end-tag (list start-tag end-tag) (ly:music-deep-copy music)))
     (list
      (keep-or-remove-with-tag start-tag (list start-tag end-tag) (ly:music-deep-copy music))
      (make-music 'UnfoldedRepeatedMusic
       'elements '() 'repeat-count (- rep-count 2) 'element
       (remove-with-tags (list start-tag end-tag) (ly:music-deep-copy music)))
      (keep-or-remove-with-tag end-tag (list start-tag end-tag) (ly:music-deep-copy music)))))))

cleansedTaggedRep =
#(define-music-function
  (start-tag end-tag rep-count music) (symbol? symbol? integer? ly:music?)
  (_i "Repeat the @var{music} unfolded @var{rep-count} times. In the first repeat output keep @var{start-tag} and remove @var{end-tag} tagged elements, in the middle remove both, and at the end keep @var{end-tag} and remove @var{start-tag} tagged elements. Finally 'rub out' the remaining tags @var{start-tag} and @var{end-tag} from the music so they will not harm any \\keepWithTag in the future.")
  (let
   ((new-music
     (if (<= rep-count 1)
      music
      (make-music 'SequentialMusic 'elements
       (if (<= rep-count 2)
        (list
         (keep-or-remove-with-tag start-tag (list start-tag end-tag) (ly:music-deep-copy music))
         (keep-or-remove-with-tag end-tag (list start-tag end-tag) (ly:music-deep-copy music)))
        (list
         (keep-or-remove-with-tag start-tag (list start-tag end-tag) (ly:music-deep-copy music))
         (make-music 'UnfoldedRepeatedMusic
          'elements '() 'repeat-count (- rep-count 2) 'element
          (remove-with-tags (list start-tag end-tag) (ly:music-deep-copy music)))
         (keep-or-remove-with-tag end-tag (list start-tag end-tag) (ly:music-deep-copy music))))))))
   (remove-tags-in-music (list start-tag end-tag) new-music)
   new-music))

PSM = #'(Part Score Midi)

keepPart =
#(define-music-function (music) (ly:music?)
  #{ \cleansedKeepOrRemoveWithTag #'Part \PSM $music #} )

keepScore =
#(define-music-function (music) (ly:music?)
  #{ \cleansedKeepOrRemoveWithTag #'Score \PSM $music #} )

keepMidi =
#(define-music-function (music) (ly:music?)
  #{ \cleansedKeepOrRemoveWithTag #'Midi \PSM $music #} )

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% END of my personal include file 'tagging.ly'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\markup { \null \translate #'( 1 . -3 ) \null } % 2.14.2 LSR problem workaroud

% Settings to keep all on one page:

\header  { tagline = ##f }

myLayout = \with {
  fontSize = #-4.5
  \override StaffSymbol.staff-space = #(magstep -4.5)
  \override StaffSymbol.thickness = #(magstep -4.5)
}

\paper { 
  score-markup-spacing =
    #'((basic-distance . 0)
       (minimum-distance . 0)
       (padding . 3.5)
       (stretchability . 0))
}

Music = {
  \tag #'A { a'4_"A"-\tag #'Strings ^"arco [for Strings]" -\tag #'Winds ^"non legato [for Winds]"}
  \tag #'B { b'4_"B" }
  \tag #'C { c''4_"C" }

  \tag #'Midi { d''32(_"Midi" \repeat unfold 7 { e''32 d'' } e'') }
  \tag #'Part \tag #'Score { e''2\trill -\tag #'Part _"Part" -\tag #'Score _"Score" }

  \tag #'InExposition { f''4_"Exp." }
  \tag #'InCoda { c''4_"Coda" }
}

\markup \fontsize #-3 \typewriter \override #'(baseline-skip . 1.7) \column {
  "Music = {"
  "  \\tag #'A { a'4_\"A\"-\\tag #'Strings ^\"arco [for Strings]\" -\\tag #'Winds ^\"non legato [for Winds]\"}"
  "  \\tag #'B { b'4_\"B\" }"
  "  \\tag #'C { c''4_\"C\" }"
  "  "
  "  \\tag #'Midi { d''32(_\"Midi\" \\repeat unfold 7 { e''32 d'' } e'') }"
  "  \\tag #'Part \\tag #'Score { e''2\\trill -\\tag #'Part _\"Part\" -\\tag #'Score _\"Score\" }"
  "  "
  "  \\tag #'InExposition { f''4_\"Exp.\" }"
  "  \\tag #'InCoda { c''4_\"Coda\" }"
  "}"
  " "
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'FOO \removeAllTags \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'FOO" \bold "\\removeAllTags" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTags #'(Strings A Part InCoda) \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \bold "\\keepWithTags #'(Strings A Part InCoda)" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'Part \copyTag #'A #'Part \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'Part" \bold "\\copyTag #'A #'Part" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'FOO \removeTag #'B \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'FOO" \bold "\\removeTag #'B" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'FOO \removeTags #'(A B C) \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'FOO" \bold "\\removeTags #'(A B C)" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'Part \renameTag #'B #'Part \renameTag #'InCoda #'Part \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'Part"
                      \bold "\\renameTag #'B #'Part \\renameTag #'InCoda #'Part" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepOrRemoveWithTag #'Part #'(Part Score Midi) \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \bold "\keepOrRemoveWithTag #'Part #'(Part Score Midi)" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'FOO \cleansedKeepOrRemoveWithTag #'Part #'(Part Score Midi) \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'FOO" \bold "\\cleansedKeepOrRemoveWithTag #'Part #'(Part Score Midi)" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \keepWithTag #'FOO \keepPart \Music
  }
  \header {
    piece = \markup \fontsize #-3 { \with-color #grey "\\keepWithTag #'FOO" \bold "\\keepPart" \italic "\\Music" }
  }
}

\score {
  \new Staff \with \myLayout {
    \taggedRep #'FirstOnly #'LastOnly #5 {
      c'16 -\tag #'FirstOnly \f \< d' e' f' g' a' b' c''
      d''\> c'' b' a' g' f' e' d' -\tag #'LastOnly \p
    }
  }
  \header {
    piece = \markup \fontsize #-3 {
      \bold "\\taggedRep #'FirstOnly #'LastOnly #5 "
      \scale #'(0.95 . 1.0) "{ c'16 -\\tag #'FirstOnly \\f \\< d' ... d''\\> ... d' -\\tag #'LastOnly \\p }"
    }
  }
}