1.3.1 A first approach (example) (Extending LilyPond)

#### 1.3.1 A first approach (example)

1. The problem

   Using the following input, the ‘espressivo’ sign (i.e., the two small hairpins) should become wider, but not taller.

   >     { f'\espressivo }

2. Possible solutions
   1. create a new stencil
   2. scale the default stencil in direction of the X-axis
3. Thoughts about previous item

   Solution A is doable. However, it will result in a higher coding effort. On the other hand, solution B is cheaper, but it has the disadvantage that it doesn’t print acceptable results in all cases (although you may not know this before you’ve actually tried).

   ⇒ choose solution B.

4. Coding
   - If you want to manipulate the default stencil you have to find information about it.

     The espressivo sign is a `Script` grob. Looking this up in the IR (Script), we see

         ‘stencil’ (stencil):
              ‘ly:script-interface::print’

     Let us test whether this is correct. To be sure that we affect the correct grob we are going to use colors. There is a predefined function for that, see Scheme functions.

         -- Function: stencil-with-color stencil color
             Return a modified version of the given stencil that is
             colored with the given color.  See ‘normalize-color’ for
             possible color formats.

         -- Function: normalize-color color
             Convert a color given in any of the supported formats into
             a list of 4 numbers: R, G, B, A.  Possible formats are:
             such a list of 4 numbers; a list of 3 numbers (transparency
             defaults to 1.0); a CSS string (named color, or “#RRGGBB”,
             or “#RRGGBBAA”, or “#RGB”, or “#RGBA”).

     Using a named color (‘`red`’), this results in the following code.

     >     colorDefaultStencil =
     >     \once \override Script.stencil =
     >       % `lambda` starts a procedure; its argument is `grob`.
     >       #(lambda (grob)
     >          ;; With `let`, local variables are defined.
     >          (let ((stil (ly:script-interface::print grob)))
     >            ;; The procedure returns the colored default stencil.
     >            (stencil-with-color stil red)))

     >     { \colorDefaultStencil f'\espressivo }

     Fine, works.

   - This kind of operation – taking a default stencil or property and returning a modified version of it – is very common, and LilyPond provides a special function for it: `grob-transformer`.

         -- Function: grob-transformer property func
             Create an override value good for applying FUNC to either
             pure or unpure values.  FUNC is called with the
             respective grob as first argument and the default value
             (after resolving all callbacks) as the second.

     Let’s try that. (The concept of pure vs. unpure gets explained later, see Unpure-pure containers; we can ignore it for now).

     >     colorDefaultStencil =
     >     \once \override Script.stencil =
     >       #(grob-transformer 'stencil
     >          (lambda (grob orig)
     >            (stencil-with-color orig red)))

     >     { \colorDefaultStencil f'\espressivo }

     Success!

   - To get a scaled stencil we have to use

         -- Function: ly:stencil-scale stil x y
             Scale stencil STIL using the horizontal and vertical
             scaling factors X and optional Y (defaulting to X).
             Negative values flip or mirror STIL without changing its
             origin; this may result in collisions unless it is
             repositioned.

     from the same chapter in the Internals Reference.

     Result:

     >     scaleColorDefaultStencil =
     >     \once \override Script.stencil =
     >       #(grob-transformer 'stencil
     >          (lambda (grob orig)
     >            (stencil-with-color
     >             (ly:stencil-scale
     >              orig
     >              ;; 1 is the neutral element with ly:stencil-scale,
     >              ;; i.e., scaling with '1 1' (for x- and y-axis)
     >              ;; returns a visibly unchanged stencil.
     >              2 1)
     >             red)))

     >     { \scaleColorDefaultStencil f'\espressivo }

     Apart from the color that seems to be the desired output.

   - There are some problems, though, as can be seen in the following example

         { \scaleColorDefaultStencil f'_\espressivo ^\fermata }

     - − The fermata is scaled, too.
     - − Having to type the command before the note(s) and `\espressivo` after the note(s) is annoying.

     Solution for both: use `\tweak`!

     >     {
     >       f'-\tweak stencil
     >           #(grob-transformer 'stencil
     >              (lambda (grob orig)
     >                (stencil-with-color
     >                 (ly:stencil-scale orig 2 1)
     >                 red)))
     >           _\espressivo
     >         ^\fermata
     >     }

     This does what we want, however, typing so much isn’t nice – let’s define a variable and use it with the tweak. With some copy and paste we get

     >     #(define longer-script
     >        (grob-transformer 'stencil
     >          (lambda (grob orig)
     >            (stencil-with-color
     >             (ly:stencil-scale orig 2 1)
     >             red))))

     >     {
     >       f'-\tweak stencil #longer-script _\espressivo
     >         ^\fermata
     >     }

     Works fine, though the scaling values are hard-coded, and typing `-\tweak stencil #longer-script` repeatedly is still awkward.

     To fix that, let’s first introduce some variables in `longer-script` for x and y scaling values.

         #(define (longer-script x y)
            (grob-transformer 'stencil
              (lambda (grob orig)
                (stencil-with-color
                 (ly:stencil-scale orig x y)
                 red))))

     Second, to reduce typing, we define an event function, see Event functions. The elements in the first parenthesis group (‘`(x-val y-val)`’) are the function variables, the elements in the second parenthesis group the respective variable types (two times we check for a number; see Predefined type predicates for possible tests).

         scaleEspr =
         #(define-event-function (x-val y-val) (number? number?)
           #{
             \tweak stencil #(longer-script x-val y-val)
             \espressivo
           #})

         { f'_\scaleEspr 2 1
              ^\fermata }

     Works as expected, fine! Note the use of `#{ … #}`: Within this Scheme construction, we can use LilyPond syntax, and the function’s arguments (like x-val) are properly expanded. See LilyPond code blocks.

5. The finished code

   Finally, we want to do some clean-up.

   - − We only want scaling in the x-direction, so let’s hard-code y-scaling in the event function.
   - − Delete the color.

   This eventually leads to the following code.

   >     #(define (longer-script x y)
   >        (grob-transformer 'stencil
   >          (lambda (grob orig)
   >            (ly:stencil-scale orig x y))))

   >     longEspressivo =
   >     #(define-event-function (x-val) (number?)
   >       #{
   >         \tweak stencil #(longer-script x-val 1)
   >         \espressivo
   >       #})

   >     { f'^\longEspressivo #2 _\fermata }

   Voilà!

   The main benefit of using `grob-transformer` is that we no longer have to take care of the actual name of the stencil function. In the next example we rotate the stencil of a slur and a beam by 90 degrees – that the actual stencil functions are called `ly:slur::print` and `ly:beam::print`, respectively, is of no concern here.

   >     #(define rotate-90
   >        (grob-transformer 'stencil
   >          (lambda (grob orig)
   >            (ly:stencil-rotate orig 90 0 0))))

   >     { f'8( g')
   >       f'8-\tweak stencil #rotate-90 ( g')
   >       f'8[ g']
   >       f'8-\tweak stencil #rotate-90 [ g'] }

#### Extending LilyPond v2.26.0 (stable-branch).

- 1 Scheme tutorial
  - 1.1 Introduction to Scheme
    - 1.1.1 Scheme sandbox
    - 1.1.2 Scheme variables
    - 1.1.3 Scheme simple data types
    - 1.1.4 Scheme compound data types
      - Pairs
      - Lists
      - Association lists (alists)
      - Hash tables
    - 1.1.5 Calculations in Scheme
    - 1.1.6 Scheme procedures
      - Defining procedures
      - Predicates
      - Return values
    - 1.1.7 Scheme conditionals
      - if
      - cond
  - 1.2 Scheme in LilyPond
    - 1.2.1 LilyPond Scheme syntax
    - 1.2.2 LilyPond variables
    - 1.2.3 Debugging Scheme code
    - 1.2.4 Input variables and Scheme
    - 1.2.5 Importing Scheme in LilyPond
    - 1.2.6 Object properties
    - 1.2.7 LilyPond compound variables
      - Offsets
      - Fractions
      - Extents
      - Property alists
      - Alist chains
    - 1.2.8 LilyPond’s box model
    - 1.2.9 Internal music representation
  - 1.3 Building complicated functions
    - 1.3.1 A first approach (example)
    - 1.3.2 Displaying music expressions
    - 1.3.3 Music properties
    - 1.3.4 Doubling a note with slurs (example)
    - 1.3.5 Adding articulation to notes (example)
- 2 Interfaces for programmers
  - 2.1 LilyPond code blocks
  - 2.2 Scheme functions
    - 2.2.1 Scheme function definitions
    - 2.2.2 Scheme function usage
    - 2.2.3 Void Scheme functions
  - 2.3 Music functions
    - 2.3.1 Music function definitions
    - 2.3.2 Music function usage
    - 2.3.3 Simple substitution functions
    - 2.3.4 Intermediate substitution functions
    - 2.3.5 Mathematics in functions
    - 2.3.6 Functions without arguments
    - 2.3.7 Void music functions
  - 2.4 Event functions
  - 2.5 Markup functions
    - 2.5.1 Markup construction in Scheme
    - 2.5.2 How markups work internally
    - 2.5.3 New markup command definition
      - Markup command definition syntax
      - On properties
      - A complete example
      - Adapting builtin commands
      - Converting markups to strings
    - 2.5.4 New markup list command definition
  - 2.6 Contexts for programmers
    - 2.6.1 Context evaluation
    - 2.6.2 Running a function on all layout objects
  - 2.7 Callback functions
  - 2.8 Unpure-pure containers
  - 2.9 Difficult tweaks
- 3 LilyPond Scheme interfaces
- Appendix A GNU Free Documentation License
- Appendix B Index