lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Using the input-tag music property


From: Michael Ellis
Subject: Using the input-tag music property
Date: Wed, 19 Jan 2011 18:00:35 -0500

A couple of days ago I came across the "input-tag" property in the
Program Reference.

input-tag (any type)
Arbitrary marker to relate input and output

Couldn't find any snippets that use it, but I took a guess that it's
meant to be a hook for user-supplied music information.  Seems to work
pretty well for that purpose.  Attached is some scheme code that's
probably too long for a snippet, but perhaps someone will find it
useful.

The code provides a way to mark up music in the manner similar to the
"outlining" rehearsal technique recommended by James Boyk and Abigail
Whiteside.  It defines a music function "\oLevel" that can be nested
around sequences of notes to indicate an outline level. The basic idea
is practice passages at tempo from the very start by omitting most of
the notes and gradually add more and more of the notes.  It's a good
rehearsal technique, but it takes some time to get used to.  Hence the
thought that a convenient way to visually and aurally demonstrate the
idea might be useful.

Once the music has been marked up with the \oLevel function,  it can
be rendered at any level of outlining by calling "\outlineMusic" in
the score block with a numerical value to indicate the desired level
of detail. It's somewhat similar to the way programmers often provide
a "debug-level" command-line option.  The higher the level, the more
detail in the output.

As implemented here, the notes that are beyond the desired level of
detail are replaced by repeats of prior notes and are shown with grey
noteheads.  See the attached image and example .ly file for more
detail.  Other implementations are certainly possible, e.g. replace
the notes with rests instead of showing them as repeated notes in
grey.

>From a programming standpoint, the 'input-tag' property seems like a
nice tool anytime you need to encode some information in the notation
for later use when the output is rendered in a way that can't be
handled by the \keepWithTag and \removeWithTag functions.   There's
one pitfall that took me a while to figure out -- ly:music-deep-copy
does NOT copy the contents of input-tag even though it's a documented
property.  You need to provide some logic to handle that if you are
generating multiple versions of the music and need to alter tags in
the process of doing so.

Cheers,
Mike

\version "2.12.3"
#(define empty? null?)

%% Top level variable that holds the outline level as music expressions
%% are parsed.
#(define outline-level 0)
%% Procedures to increment and decrement the outline level
#(define (inc-outline-level!) (set! outline-level (+ 1 outline-level)))
#(define (dec-outline-level!) (set! outline-level (- 1 outline-level)))

%% Factory for input-tag closures that attach to Note Events.
%% Storage outline level and output color.  These are set at
%% different phases of processing.  Outline level is set as the
%% music is defined, but outline level is set just before it is
%% ready to render into the score.
#(define (make-input-tag outline-level)
    (let ((ol outline-level)
          (outc (x11-color 'black)))
        (lambda args
             (case (car args)
               ((get-outline-level) ol)
               ((get-output-color) outc)
               ((set-outline-level!) (set! ol (car (cdr args))))
               ((set-output-color!) (set! outc (car (cdr args))))
               (else (error "make-input-tag: Invalid method!"))))))

%% Function that replaces an input-tag with a copy and
%% returns the copy. We need this because ly:music-deep-copy doesn't
%% copy the 'input-tag property. (grrr!)
#(define (replace-input-tag music tag)
    (define newtag '())
    (set! newtag (make-input-tag 0))
    (newtag 'set-outline-level! (tag 'get-outline-level))
    (newtag 'set-output-color! (tag 'get-output-color))
    (ly:music-set-property! music 'input-tag newtag)
    newtag)

%% Function that installs and updates input-tag closures on
%% pitch elements.
#(define (setOutlineLevel! music level)
   (let ((es (ly:music-property music 'elements))
         (e (ly:music-property music 'element))
         (p (ly:music-property music 'pitch)))
     (if (pair? es)
         (ly:music-set-property!
          music 'elements
          (map (lambda (x) (setOutlineLevel! x level)) es)))
     (if (ly:music? e)
         (ly:music-set-property!
          music 'element
          (setOutlineLevel! e level)))
     (if (ly:pitch? p)
         (let ((tag (ly:music-property music 'input-tag)))
             (if (empty? tag)
                (ly:music-set-property! music 'input-tag
                    (make-input-tag level))
                (tag 'set-outline-level! (+ 1 level)))))
     music))

%% Music function wrapper for setOutlineLevel.
%% Usage:  \oLevel {a b c \oLevel { d e f } } ...
%% Nested calls result in deeper outlining.
oLevel =
#(define-music-function (p l m) (ly:music?)
    (inc-outline-level!)
    (setOutlineLevel! m outline-level)
    (dec-outline-level!)
    m)

%% top level variable to hold last non-suppressed pitch value.
#(define lastp (ly:make-pitch 1 1 0))
#(define (setlastp! p) (set! lastp p))
#(define (getlastp) lastp)

%% Function to enforce outlining on music. Any note whose outline
%% level is greater than the level specified has its pitch set to the
%% previous note and its color set to grey.
#(define (outline music level)
   (let ((es (ly:music-property music 'elements))
         (e (ly:music-property music 'element))
         (p (ly:music-property music 'pitch))
         (tag (ly:music-property music 'input-tag))
         (newtag '())
         (lastp lastp))
     (if (pair? es)
         (ly:music-set-property!
          music 'elements
          (map (lambda (x) (outline x level)) es)))
     (if (ly:music? e)
         (ly:music-set-property!
          music 'element
          (outline e level)))
     (if (ly:pitch? p)
         (cond
               ((empty? tag)
                    (ly:music-set-property! music 'input-tag
                        (make-input-tag level))
                    (setlastp! p))

               ((> (tag 'get-outline-level) level)
                    (set! newtag (replace-input-tag music tag))
                    (ly:music-set-property! music 'pitch (getlastp))
                    (newtag 'set-output-color! (x11-color 'grey)))

               (else
                    (set! newtag (replace-input-tag music tag))
                    (newtag 'set-output-color! (x11-color 'black))
                    (setlastp! p))))

     music))

%% Music function wrapper for outline procedure.
outlineMusic =
#(define-music-function (parser location level m)
   (number? ly:music?)
   (begin
        (display "outlineMusic") (newline)
        (outline (ly:music-deep-copy m) level)))


%% Function used by engraver to fetch notehead color from tag
#(define (color-notehead grob)
    (define (tag-to-color tag) (tag 'get-output-color))
    (tag-to-color (ly:event-property
                    (ly:grob-property grob 'cause) 'input-tag)))


%% --------------------------------------------
%% Music
%% --------------------------------------------
#(set-global-staff-size 18)

melody = \relative g'' {
            \clef treble
            \key d \minor
            \time 4/4
\tempo "" 4 = 20
\set Staff.instrumentName = #"Violin"
\partial 4 \drums {
    \set Staff.instrumentName = #"Click" wbh16 wbl wbl wbl
    } |
<<
{
%{ Main Voice %}
\override NoteHead #'color = #color-notehead
%{ 1 %}
<bes, g'>4 ~ g'32 [f(
    \oLevel { ees d c
        \oLevel {bes a bes64 g)] }}
g8 [ fis ] ~ fis32 [ e(
    \oLevel { d e
        \oLevel { fis g a c64 bes )]}}
%{ 2 %}
c8 ~ [ c32
    \oLevel { d64 ( c bes32 c ]
        \oLevel {c16. \trill [ bes64 c d16 }}
a )] g'8 r32 g,32([
    \oLevel {
        \oLevel { a64 bes c d ] }}
ees8 ) [ bes ]
}

\\  {
%{ Voice 2 %}
%{ 1 %}  <g, d'>8  s8 s4 a  s4
%{ 2 %}  d8 s8 s4 g,8 s8 r8 g'8
}
 \\
{ %{ Voice 3 %}
%{ 1 %}  s4 s4 c4 s4
%{ 2 %}  fis4 s4 <d, bes'>8 s4.
}
 >>
\bar "|."
}

%% -----------------------------------------
%% Output
%% -----------------------------------------
\book {
    %% Maximum outlining
    \markup {  \column { "BWV 1001 Excerpt"
                         "Outline Level 0" } }
    \score {
        \new Staff {
            \outlineMusic #0 \melody
         }
         \layout {}
    }
    % Medium outlining
    \markup {  \column { "BWV 1001 Excerpt"
                         "Outline Level 2" } }
    \score {
        \new Staff {
            \outlineMusic #1 \melody
         }
         \layout {}
    }
    %% No outlining
    \markup {  \column { "BWV 1001 Excerpt"
                         "No Outlining" } }
    \score {
        \new Staff {
            \outlineMusic #2 \melody
         }
         \layout {}
    }
    %% Consecutive midi for all 3 scores
    \score {
        \new Staff {
            \outlineMusic #0 \melody
            \outlineMusic #1 \melody
            \outlineMusic #2 \melody
        }
        \midi {}
    }

}
%% End of example

Attachment: outline_example.png
Description: PNG image


reply via email to

[Prev in Thread] Current Thread [Next in Thread]