A.3.1 Una primera aproximación (ejemplo) (Extender LilyPond)

#### A.3.1 Una primera aproximación (ejemplo)

1. El problema

   Usando la entrada siguiente, el signo de ‘espressivo’ (es decir, los dos reguladores pequeños) deberían hacerse más anchos, pero no más altos.

   >     { f'\espressivo }

2. Soluciones posibles
   1. crear un sello nuevo
   2. escalar el sello por omisión en la dirección del eje X
3. Comentarios sobre esta solución

   La solución A es factible. Sin embargo, dará como resultado un esfuerzo de codificación mayor. Por otro lado, la solución B es más económica, aunque tiene la desventaja de que no imprime resultados aceptables en todos los casos (si bien usted podría no saberlo hasta después de haberlo intentado).

   ⇒ elija la solución B.

4. Codificación
   - Si quiere manipular el sello por omisión tendrá que encontrar información sobre él.

     El signo de espressivo es un grob del tipo `Script`. Si buscamos esto en el IR (Script), encontramos

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

     Vamos a comprobar si es correcto. PAra asegurarnos de que afectamos al grob correcto, vamos a emplear colores. Existe una función predefinida para ello, véase 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”).

     Usando un color con nombre (‘`red`’, rojo), ello da como resultado el siguiente código.

     >     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 }

     Estupendo, funciona.

   - Esta clase de operación ( tomar un sello o propiedad por omisión y devolver una versión modificada del mismo) es muy común, y LilyPond aporta una función especial para ello: `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.

     Vamos a probarla (el concepto de ‘pure’ frente a ‘unpure’ se explica más tarde, see Contenedores Unpure-pure; podemos ignorarlo por el momento).

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

     >     { \colorDefaultStencil f'\espressivo }

     ¡Éxito total!

   - Para obtener un sello a diferente escala tenemos que usar

         -- 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.

     tomado del mismo capítulo en el manual de Referencia del Funcionamiento Interno.

     Resultado:

     >     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 }

     Aparte del color, parece ser la salida deseada.

   - Sin embargo existen algunos problemas, como se puede ver en el ejemplo siguiente:

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

     - − El calderón está a escala también.
     - − Es fastidioso tener que teclear la instrucción antes de la nota o notas y el `\espressivo` después de la nota.

     La solución para ambos: ¡use `\tweak`!

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

     Esto hace lo que queríamos aunque no es agradable teclear tanto; vamos a definir una variable y usarla para el trucaje. Con algo de copiar y pegar obtenemos lo siguiente.

     >     #(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
     >     }

     FUnciona correctamente, aunque los valores de escalado están codificados de forma rígida, y escribir repetidamente `-\tweak stencil #longer-script` aún resulta incómodo.

     Para corregirlo, vamos a introducir primero una serie de variables dentro de la función `longer-script` para los valores de escalado en x e y.

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

     En segundo lugar, para reducir la cantidad de tecleo, definimos una función de evento, see Funciones de eventos. Los elementos dentro del primer grupo de paréntesis (‘`(x-val y-val)`’) son las variables de función, los elementos dentro del segundo paréntesis agrupan los respectivos tipos de variables (comprobamos para número dos veces; consulte Predefined type predicates para ver todas las comprobaciones posibles).

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

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

     Correcto, ¡funciona como se esperaba! Observe el uso de `#{ … #}`: dentro de esta construcción de Scheme, podemos usar sintaxis de LilyPond, y los argumentos de la función (como x-val) se expanden adecuadamente. See Bloques de código de LilyPond.

5. El código terminado

   Para terminar, vamos a poner algo de orden.

   - − Solo queremos realizar un escalado en la dirección del eje X, por lo que codificamos de forma rígida el escalado en el eje Y dentro de la función de evento.
   - − Eliminar el color.

   Al final, todo esto conduce al siguiente código.

   >     #(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à!

   La principal ventaja de usar `grob-transformer` es que ya no nos tenemos que preocupar por el nombre concreto de la función de sello. En el siguiente ejemplo rotamos 90 grados el sello de una ligadura de expresión y una barra; el hecho de que las funciones de sello se llamen respectivamente `ly:slur::print` y `ly:beam::print`, no nos preocupa.

   >     #(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'] }

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

- Appendix A Tutorial de Scheme
  - A.1 Introducción a Scheme
    - A.1.1 Cajón de arena de Scheme
    - A.1.2 Variables de Scheme
    - A.1.3 Tipos de datos simples de Scheme
    - A.1.4 Tipos de datos compuestos de Scheme
      - Parejas
      - Listas
      - Listas asociativas (listas-A)
      - Tablas de hash
    - A.1.5 Cálculos en Scheme
    - A.1.6 Procedimientos de Scheme
      - Definir procedimientos
      - Predicados
      - Valores de retorno
    - A.1.7 Condicionales de Scheme
      - if
      - cond
  - A.2 Scheme dentro de LilyPond
    - A.2.1 Sintaxis del Scheme de LilyPond
    - A.2.2 Variables de LilyPond
    - A.2.3 Depuración del código de Scheme
    - A.2.4 Variables de entrada y Scheme
    - A.2.5 Importación de Scheme dentro de LilyPond
    - A.2.6 Propiedades de los objetos
    - A.2.7 Variables de LilyPond compuestas
      - Desplazamientos
      - Fracciones
      - Dimensiones
      - Listas-A de propiedades
      - Cadenas de listas-A
    - A.2.8 El modelo de caja de LilyPond
    - A.2.9 Representación interna de la música
  - A.3 Construir funciones complicadas
    - A.3.1 Una primera aproximación (ejemplo)
    - A.3.2 Presentación de las expresiones musicales
    - A.3.3 Propiedades musicales
    - A.3.4 Duplicar una nota con ligaduras (ejemplo)
    - A.3.5 Añadir articulaciones a las notas (ejemplo)
- B Interfaces para programadores
  - B.1 Bloques de código de LilyPond
  - B.2 Funciones de Scheme
    - B.2.1 Definición de funciones de Scheme
    - B.2.2 Uso de las funciones de Scheme
    - B.2.3 Funciones de Scheme vacías
  - B.3 Funciones musicales
    - B.3.1 Definiciones de funciones musicales
    - B.3.2 Uso de las funciones musicales
    - B.3.3 Funciones de sustitución sencillas
    - B.3.4 Funciones de sustitución intermedias
    - B.3.5 Matemáticas dentro de las funciones
    - B.3.6 Funciones sin argumentos
    - B.3.7 Funciones musicales vacías
  - B.4 Funciones de eventos
  - B.5 Funciones de marcado
    - B.5.1 Construcción de elementos de marcado en Scheme
    - B.5.2 Cómo funcionan internamente los elementos de marcado
    - B.5.3 Definición de una instrucción de marcado nueva
      - Sintaxis de la definición de instrucciones de marcado
      - Acerca de las propiedades
      - Un ejemplo completo
      - Adaptación de instrucciones incorporadas
      - Convertir elementos de marcado en cadenas
    - B.5.4 Definición de nuevas instrucciones de lista de marcado
  - B.6 Contextos para programadores
    - B.6.1 Evaluación de contextos
    - B.6.2 Ejecutar una función sobre todos los objetos de la presentación
  - B.7 Funciones de callback
  - B.8 Contenedores Unpure-pure
  - B.9 Trucos difíciles
- C Interfaces de Scheme de LilyPond
- Appendix D GNU Free Documentation License
- Appendix E Índice