@c -*- coding: utf-8; mode: texinfo; documentlanguage: es -*- @c This file is part of extending.tely @ignore Translation of GIT committish: e90fa9a8a1d71251a2c047e69798be05938a1241 When revising a translation, copy the HEAD committish of the version that you are working on. See TRANSLATION for details. @end ignore @c \version "2.12.0" @node Interfaces para programadores @chapter Interfaces para programadores @translationof Interfaces for programmers Se pueden realizar trucos avanzados mediante el uso de Scheme. Si no está familiarizado con Scheme, le conviene leer nuestro tutorial de Scheme, @ref{Tutorial de Scheme}. @menu * Funciones musicales:: * Funciones de marcado:: * Contextos para programadores:: * Funciones de callback:: * Código de Scheme en línea:: * Trucos difíciles:: @end menu @node Funciones musicales @section Funciones musicales @translationof Music functions Las funciones musicales son funciones de Scheme que se utilizan para crear automáticamente expresiones musicales. Se pueden usar para simplificar enormemente el archivo de entrada. @menu * Sintaxis de las funciones musicales:: * Funciones de sustitución sencillas:: * Funciones de sustitución intermedias:: * Matemáticas dentro de las funciones:: * Funciones vacías:: * Funciones sin argumentos:: @end menu @node Sintaxis de las funciones musicales @subsection Sintaxis de las funciones musicales @translationof Music function syntax La sintaxis general de una función musical es: @example miFuncion = #(define-music-function (parser location @var{var_1} @var{var_2}...@var{var_n}) (@var{var_1-type?} @var{var_2-type?}...@var{var_n-type?}) @var{...expresión musical válida...}) @end example @noindent donde @multitable @columnfractions .33 .66 @item @var{var_i} @tab @var{i}-ésima variable @item @var{var_i-type?} @tab tipo de la @var{i}-ésima variable @item @var{...expresión musical válida...} @tab expresión que devuelve música válida, generalmente en la forma de una expresión de Scheme. También hay una sintaxis especial que permite la existencia de código de entrada de LilyPond dentro de esta expresión musical. @end multitable Los comprobadores de tipo de variable son procedimientos de Scheme que devuelven @code{#t} si una variable es de un tipo dado. Se muestran algunos tipos comunes en la tabla de abajo. Hay más tipos en los archivos @file{lily/music-scheme.cc} y @file{scm/c++.scm}. La lista completa de los comprobadores de tipo con nombre para LilyPond está en la lista @var{type-p-name-alist} de @file{scm/lily.scm}. @c TODO -- automatically document type-p-name-alist @multitable @columnfractions .33 .66 @headitem Tipo de entrada @tab notación de @var{vari-type?} @item Entero @tab @code{integer?} @item Flotante (número decimal) @tab @code{number?} @item Cadena de texto @tab @code{string?} @item Marcado @tab @code{markup?} @item Expresión musical @tab @code{ly:music?} @item Pareja de variables @tab @code{pair?} @end multitable Los argumentos @code{parser} y @code{location} son obligatorios. El argumento @code{parser} se usa en el cuerpo de la función para tener acceso al valor de otra variable de LilyPond. El argumento @code{location} se usa para establecer el @q{origen} de la expresión musical que se construye por parte de la función musical, de forma que en caso de un error de sintaxis LilyPond pueda comunicar al usuario el lugar adecuado del archivo de entrada en que buscar. @node Funciones de sustitución sencillas @subsection Funciones de sustitución sencillas @translationof Simple substitution functions Una función de sustitución sencilla es una función musical cuya expresión musical de salida está escrita en código de LilyPond, pero con una variable de entrada sustituida en el código de LilyPond. La forma general de estas funciones es: @example miFuncion = #(define-music-function (parser location @var{var1}) (@var{var1-type?}) #@{ @emph{... código de entrada de LilyPond con} @code{#$var1} @emph{para sustituir ...} #@}) @end example Observe que los caracteres especiales @code{#@{} y @code{#@}} encierran la música de LilyPond. @multitable @columnfractions .33 .66 @item @var{vari} @tab @var{i}-ésima variable @item @var{vari-type?} @tab tipo de la @var{i}-ésima variable @item @var{...música...} @tab código de entrada normal de LilyPond, que utiliza variables como @code{#$var1}, etc. @end multitable Por ejemplo, se puede definir una función que simplifique el establecimiento de un relleno para un guión de texto TextScript: @lilypond[quote,verbatim,ragged-right] padText = #(define-music-function (parser location padding) (number?) #{ \once \override TextScript #'padding = #$padding #}) \relative c''' { c4^"piu mosso" b a b \padText #1.8 c4^"piu mosso" d e f \padText #2.6 c4^"piu mosso" fis a g } @end lilypond Además de números, podemos usar expresiones musicales, como por ejemplo notas, como argumentos de las funciones musicales: @lilypond[quote,verbatim,ragged-right] custosNote = #(define-music-function (parser location note) (ly:music?) #{ \once \override Voice.NoteHead #'stencil = #ly:text-interface::print \once \override Voice.NoteHead #'text = \markup \musicglyph #"custodes.mensural.u0" \once \override Voice.Stem #'stencil = ##f $note #}) @end lilypond @node Funciones de sustitución intermedias @subsection Funciones de sustitución intermedias @translationof Intermediate substitution functions Algo más complicadas que las funciones de sustitución sencillas, las funciones de sustitución intermedias contienen una mezcla de código de Scheme y de LilyPond dentro de la expresión musical que se devuelve. Algunas instrucciones @code{\override} requieren un argumento que consiste en una pareja de números (llamada una @code{célula cons} en Scheme). La pareja se puede pasar directamente dentro de la función musical, usando una variable @code{pair?}: @quotation @example barraManual = #(define-music-function (parser location principio-final) (pair?) #@{ \once \override Beam #'positions = #$principio-final #@}) \relative @{ \barraManual #'(3 . 6) c8 d e f @} @end example @end quotation De forma alternativa, los números que componen la pareja se pueden pasar como argumentos separados, y el código de Scheme que se ha usado para crear la pareja se puede incluir dentro de la expresión musical: @lilypond[quote,verbatim,ragged-right] manualBeam = #(define-music-function (parser location beg end) (number? number?) #{ \once \override Beam #'positions = #(cons $beg $end) #}) \relative { \manualBeam #3 #6 c8 d e f } @end lilypond @node Matemáticas dentro de las funciones @subsection Matemáticas dentro de las funciones @translationof Mathematics in functions Las funciones musicales pueden contar con programación de Scheme además de la simple sustitución: @lilypond[quote,verbatim,ragged-right] AltOn = #(define-music-function (parser location mag) (number?) #{ \override Stem #'length = #$(* 7.0 mag) \override NoteHead #'font-size = #$(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) #}) AltOff = { \revert Stem #'length \revert NoteHead #'font-size } { c'2 \AltOn #0.5 c'4 c' \AltOn #1.5 c' c' \AltOff c'2 } @end lilypond @noindent Este ejemplo se puede reescribir de forma que pase expresiones musicales: @lilypond[quote,verbatim,ragged-right] withAlt = #(define-music-function (parser location mag music) (number? ly:music?) #{ \override Stem #'length = #$(* 7.0 mag) \override NoteHead #'font-size = #$(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) $music \revert Stem #'length \revert NoteHead #'font-size #}) { c'2 \withAlt #0.5 {c'4 c'} \withAlt #1.5 {c' c'} c'2 } @end lilypond @node Funciones vacías @subsection Funciones vacías @translationof Void functions Una función musical debe devolver una expresión musical, pero a veces podemos necesitar una función en la que no hay música en juego (como la desactivación de la funcionalidad Apuntar y Pulsar). Para hacerlo, devolvemos una expresión musical @code{void} (vacía). Este es el motivo por el que la forma que se devuelve es @code{(make-music ...)}. Con el valor de la propiedad @code{'void} establecido a @code{#t}, le decimos al analizador que descarte la expresión musical devuelta. así, la parte importante de la función musical vacía es el proceso realizado por la función, no la expresión musical que se devuelve. @example noApuntarYPulsar = #(define-music-function (parser location) () (ly:set-option 'point-and-click #f) (make-music 'SequentialMusic 'void #t)) ... \noApuntarYPulsar % desactivar la funcionalidad Apuntar y Pulsar. @end example @node Funciones sin argumentos @subsection Funciones sin argumentos @translationof Functions without arguments En casi todos los casos, una función sin argumentos se debe escribir con una variable: @example dolce = \markup@{ \italic \bold dolce @} @end example Sin embargo, en raras ocasiones puede ser de utilidad crear una función musical sin argumentos: @example mostrarNumeroDeCompas = #(define-music-function (parser location) () (if (eq? #t (ly:get-option 'display-bar-numbers)) #@{ \once \override Score.BarNumber #'break-visibility = ##f #@} #@{#@})) @end example Para la impresión real de los números de compás donde se llama a esta función, invoque a @command{lilypond} con @example lilypond -d display-bar-numbers ARCHIVO.ly @end example @node Funciones de marcado @section Funciones de marcado @translationof Markup functions Los elementos de marcado están implementados como funciones de Scheme especiales que producen un objeto @code{Stencil} dada una serie de argumentos. @menu * Construcción de elementos de marcado en Scheme:: * Cómo funcionan internamente los elementos de marcado:: * Definición de una instrucción de marcado nueva:: * Definición de nuevas instrucciones de lista de marcado:: @end menu @node Construcción de elementos de marcado en Scheme @subsection Construcción de elementos de marcado en Scheme @translationof Markup construction in Scheme @cindex marcado, definir instrucciones de El macro @code{markup} construye expresiones de marcado en Scheme, proporcionando una sintaxis similar a la de LilyPond. Por ejemplo: @example (markup #:column (#:line (#:bold #:italic "hola" #:raise 0.4 "mundo") #:larger #:line ("fulano" "fulanito" "menganito"))) @end example @noindent equivale a: @example \markup \column @{ \line @{ \bold \italic "hola" \raise #0.4 "mundo" @} \larger \line @{ fulano fulanito menganito @} @} @end example @noindent Este ejemplo muestra las principales reglas de traducción entre la sintaxis del marcado normal de LilyPond y la sintaxis del marcado de Scheme. @quotation @multitable @columnfractions .3 .3 @item @b{LilyPond} @tab @b{Scheme} @item @code{\markup marcado1} @tab @code{(markup marcado1)} @item @code{\markup @{ marcado1 marcado2 ... @}} @tab @code{(markup marcado1 marcado2 ... )} @item @code{\instruccion} @tab @code{#:instruccion} @item @code{\variable} @tab @code{variable} @item @code{\center-column @{ ... @}} @tab @code{#:center-column ( ... )} @item @code{cadena} @tab @code{"cadena"} @item @code{#argumento-de-scheme} @tab @code{argumento-de-scheme} @end multitable @end quotation Todo el lenguaje Scheme está accesible dentro del macro @code{markup}. Por ejemplo, podemos usar llamadas a funciones dentro de @code{markup} para así manipular cadenas de caracteres. Esto es útil si se están definiendo instrucciones de marcado nuevas (véase @ref{Definición de una instrucción de marcado nueva}). @knownissues El argumento markup-list de instrucciones como @code{#:line}, @code{#:center} y @code{#:column} no puede ser una variable ni el resultado de la llamada a una función. @lisp (markup #:line (funcion-que-devuelve-marcados)) @end lisp @noindent no es válido. Hay que usar las funciones @code{make-line-markup}, @code{make-center-markup} o @code{make-column-markup} en su lugar: @lisp (markup (make-line-markup (funcion-que-devuelve-marcados))) @end lisp @node Cómo funcionan internamente los elementos de marcado @subsection Cómo funcionan internamente los elementos de marcado @translationof How markups work internally En un elemento de marcado como @example \raise #0.5 "ejemplo de texto" @end example @noindent @code{\raise} se representa en realidad por medio de la función @code{raise-markup}. La expresión de marcado se almacena como @example (list raise-markup 0.5 (list simple-markup "ejemplo de texto")) @end example Cuando el marcado se convierte en objetos imprimibles (Stencils o sellos), se llama la función @code{raise-markup} como @example (apply raise-markup @var{\objeto de marcado} @var{lista de listas asociativas de propiedades} 0.5 @var{el marcado "ejemplo de texto"}) @end example Primero la función @code{raise-markup} crea el sello para la cadena @code{ejemplo de texto}, y después eleva el sello Stencil en 0.5 espacios de pentagrama. Este es un ejemplo bastante simple; en el resto de la sección podrán verse ejemplos más complejos, así como en @file{scm/@/define@/-markup@/-commands@/.scm}. @node Definición de una instrucción de marcado nueva @subsection Definición de una instrucción de marcado nueva @translationof New markup command definition Esta sección trata sobre la definición de nuevas instrucciones de marcado. @menu * Sintaxis de la definición de instrucciones de marcado:: * Acerca de las propiedades:: * Un ejemplo completo:: * Adaptación de instrucciones incorporadas:: @end menu @node Sintaxis de la definición de instrucciones de marcado @unnumberedsubsubsec Sintaxis de la definición de instrucciones de marcado @translationof Markup command definition syntax Se pueden definir instrucciones de marcado nuevas usando el macro de Scheme @code{define-markup-command}, en el nivel sintáctico superior. @lisp (define-markup-command (@var{nombre-de-la-instruccion} @var{layout} @var{props} @var{arg1} @var{arg2} ...) (@var{tipo-de-arg1?} @var{tipo-de-arg2?} ...) [ #:properties ((@var{propiedad1} @var{valor-predeterminado1}) ...) ] ..command body..) @end lisp Los argumentos son @table @var @item nombre-de-la-instruccion nombre de la instrucción de marcado @item layout la definición de @q{layout} (disposición). @item props una lista de listas asociativas, que contienen todas las propiedades activas. @item argi argumento @var{i}-ésimo de la instrucción @item tipo-de-argi? predicado de tipo para el argumento @var{i}-ésimo @end table Si la instrucción utiliza propiedades de los argumentos @var{props}, se puede usar la palabra clave @code{#:properties} para especificar qué propiedades se usan, y sus valores predeterminados. @knownissues Existen algunas restricciones sobre los argumentos posibles de una instrucción de marcado. Los argumentos se distinguen según su tipo: @itemize @item un marcado, que corresponde al predicado de tipo @code{markup?}; @item una lista de marcados, que corresponde al predicado de tipo @code{markup-list?}; @item cualquier otro objeto de Scheme, que corresponde a predicados de tipo como @code{list?}, @code{number?}, @code{boolean?}, etc. @end itemize Actualmente las combinaciones de argumentos que hay disponibles (después de los argumentos estándar @var{layout} y @var{props}) para una instrucción de marcado definida con @code{define-markup-command} se limitan a la siguiente lista: @table @asis @item (sin argumentos) @itemx @var{markup-list} @itemx @var{markup} @itemx @var{markup markup} @itemx @var{scheme} @itemx @var{scheme markup} @itemx @var{scheme scheme} @itemx @var{scheme scheme markup} @itemx @var{scheme scheme markup markup} @itemx @var{scheme markup markup} @itemx @var{scheme scheme scheme} @end table @noindent Esto significa que no es posible definir con p.ej. tres argumentos de Scheme y un argumento de marcado, como: @example #(define-markup-command (fulanito layout props num1 num2 lista marcado) (number? number? list? markup?) ...) @end example @noindent Si la aplicamos como, digamos, @example \markup \fulanito #1 #2 #'(mengano zutano) Loquesea @end example @cindex firma de Scheme @cindex Scheme, firma de @noindent @command{lilypond} protesta diciendo que no puede analizar @code{fulanito} debido a su firma de Scheme desconocida. @node Acerca de las propiedades @unnumberedsubsubsec Acerca de las propiedades @translationof On properties Los argumentos @code{layout} y @code{props} de las instrucciones de marcado traen a escena un contexto para la interpretación del marcado: tamaño de la tipografía, grueso de línea, etc. El argumento @code{layout} permite el acceso a las propiedades definidas en los bloques @code{paper}, usando la función @code{ly:output-def-lookup}. Por ejemplo, el grueso de línea (el mismo que el que se usa en las partituras) se lee usando: @example (ly:output-def-lookup layout 'line-width) @end example El argumento @code{props} hace accesibles algunas propiedades a las instrucciones de marcado. Por ejemplo, cuando se interpreta el marcado del título de un libro, todas las variables definidas dentro del bloque @code{\header} se añaden automáticamente a @code{props}, de manera que el marcado del título del libro puede acceder al título del libro, el autor, etc. También es una forma de configurar el comportamiento de una instrucción de marcado: por ejemplo, cuando una instrucción utiliza tamaños de tipografía durante el procesado, el tamaño se lee de @code{props} en vez de tener un argumento @code{font-size}. El que llama a una instrucción de marcado puede cambiar el valor de la propiedad del tamaño de la tipografía con el objeto de modificar el comportamiento. Utilice la palabra clave @code{#:properties} de @code{define-markup-command} para especificar qué propiedades se deben leer a partir de los argumentos de @code{props}. El ejemplo de la sección siguiente ilustra cómo acceder y sobreescribir las propiedades de una instrucción de marcado. @node Un ejemplo completo @unnumberedsubsubsec Un ejemplo completo @translationof A complete example El ejemplo siguiente define una instrucción de marcado para trazar un rectángulo doble alrededor de un fragmento de texto. En primer lugar, necesitamos construir un resultado aproximado utilizando marcados. Una consulta a @ruser{Text markup commands} nos muestra que es útil la instrucción @code{\box}: @lilypond[quote,verbatim,ragged-right] \markup \box \box HELLO @end lilypond Ahora, consideramos que es preferible tener más separación entre el texto y los rectángulos. Según la documentación de @code{\box}, esta instrucción usa una propiedad @code{box-padding}, cuyo valor predeterminado es 0.2. La documentación también menciona cómo sobreescribir este valor: @lilypond[quote,verbatim,ragged-right] \markup \box \override #'(box-padding . 0.6) \box A @end lilypond Después, el relleno o separación entre los dos rectángulos nos parece muy pequeño, así que lo vamos a sobreescribir también: @lilypond[quote,verbatim,ragged-right] \markup \override #'(box-padding . 0.4) \box \override #'(box-padding . 0.6) \box A @end lilypond Repetir esta extensa instrucción de marcado una y otra vez sería un quebradero de cabeza. Aquí es donde se necesita una instrucción de marcado. Así pues, escribimos una instrucción de marcado @code{double-box}, que toma un argumento (el texto). Dibuja los dos rectángulos y añade una separación. @lisp #(define-markup-command (double-box layout props text) (markup?) "Trazar un rectángulo doble rodeando el texto." (interpret-markup layout props (markup #:override '(box-padding . 0.4) #:box #:override '(box-padding . 0.6) #:box text))) @end lisp @code{text} es el nombre del argumento de la instrucción, y @code{markup?} es el tipo: lo identifica como un elemento de marcado. La función @code{interpret-markup} se usa en casi todas las instrucciones de marcado: construye un sello, usando @code{layout}, @code{props}, y un elemento de marcado. Aquí, la marca se construye usando el macro de Scheme @code{markup}, véase @ref{Construcción de elementos de marcado en Scheme}. La transformación de una expresión @code{\markup} en una expresión de marcado de Scheme es directa. La instrucción nueva se puede usar como sigue: @example \markup \double-box A @end example Sería buen hacer que la instrucción @code{double-box} fuera personalizable: aquí, los valores de relleno @code{box-padding} son fijos, y no se pueden cambiar por parte del usuario. Además, sería mejor distinguir la separación entre los dos rectángulos, del relleno entre el rectángulo interno y el texto. Así pues, introducimos una nueva propiedad, @code{inter-box-padding}, para el relleno entre los rectángulos. El @code{box-padding} se usará para el relleno interno. Ahora el código nuevo es como se ve a continuación: @lisp #(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Trazar un rectángulo doble rodeando el texto." (interpret-markup layout props (markup #:override `(box-padding . ,inter-box-padding) #:box #:override `(box-padding . ,box-padding) #:box text))) @end lisp Aquí, la palabra clave @code{#:properties} se usa de manera que las propiedades @code{inter-box-padding} y @code{box-padding} se leen a partir del argumento @code{props}, y se les proporcionan unos valores predeterminados si las propiedades no están definidas. Después estos valores se usan para sobreescribir las propiedades @code{box-padding} usadas por las dos instrucciones @code{\box}. Observe el apóstrofo invertido y la coma en el argumento de @code{\override}: nos permiten introducir un valor de variable dentro de una expresión literal. Ahora, la instrucción se puede usar dentro de un elemento de marcado, y el relleno de los rectángulos se puede personalizar: @lilypond[quote,verbatim,ragged-right] #(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Draw a double box around text." (interpret-markup layout props (markup #:override `(box-padding . ,inter-box-padding) #:box #:override `(box-padding . ,box-padding) #:box text))) \markup \double-box A \markup \override #'(inter-box-padding . 0.8) \double-box A \markup \override #'(box-padding . 1.0) \double-box A @end lilypond @node Adaptación de instrucciones incorporadas @unnumberedsubsubsec Adaptación de instrucciones incorporadas @translationof Adapting builtin commands Una buena manera de comenzar a escribir una instrucción de marcado nueva, es seguir el ejemplo de otra instrucción ya incorporada. Casi todas las instrucciones de marcado que están incorporadas en LilyPond se pueden encontrar en el archivo @file{scm/@/define@/-markup@/-commands@/.scm}. Por ejemplo, querríamos adaptar la instrucción @code{\draw-line}, para que trace una línea doble. La instrucción @code{\draw-line} está definida como sigue (se han suprimido los comentarios de documentación): @lisp (define-markup-command (draw-line layout props dest) (number-pair?) #:category graphic #:properties ((thickness 1)) "...documentación..." (let ((th (* (ly:output-def-lookup layout 'line-thickness) thickness)) (x (car dest)) (y (cdr dest))) (make-line-stencil th 0 0 x y))) @end lisp Para definir una instrucción nueva basada en otra existente, copie la definición y cámbiele el nombre. La palabra clave @code{#:category} se puede eliminar sin miedo, pues sólo se utiliza para generar documentación de LilyPond, y no tiene ninguna utilidad para las instrucciones de marcado definidas por el usuario. @lisp (define-markup-command (draw-double-line layout props dest) (number-pair?) #:properties ((thickness 1)) "...documentación..." (let ((th (* (ly:output-def-lookup layout 'line-thickness) thickness)) (x (car dest)) (y (cdr dest))) (make-line-stencil th 0 0 x y))) @end lisp A continuación se añade una propiedad para establecer la separación entre las dos líneas, llamada @code{line-gap}, con un valor predeterminado de p.ej. 0.6: @lisp (define-markup-command (draw-double-line layout props dest) (number-pair?) #:properties ((thickness 1) (line-gap 0.6)) "...documentación..." ... @end lisp Finalmente, se añade el código para trazar las dos líneas. Se usan dos llamadas a @code{make-line-stencil} para trazar las líneas, y los sellos resultantes se combinan usando @code{ly:stencil-add}: @lilypond[quote,verbatim,ragged-right] #(define-markup-command (my-draw-line layout props dest) (number-pair?) #:properties ((thickness 1) (line-gap 0.6)) "..documentation.." (let* ((th (* (ly:output-def-lookup layout 'line-thickness) thickness)) (dx (car dest)) (dy (cdr dest)) (w (/ line-gap 2.0)) (x (cond ((= dx 0) w) ((= dy 0) 0) (else (/ w (sqrt (+ 1 (* (/ dx dy) (/ dx dy)))))))) (y (* (if (< (* dx dy) 0) 1 -1) (cond ((= dy 0) w) ((= dx 0) 0) (else (/ w (sqrt (+ 1 (* (/ dy dx) (/ dy dx)))))))))) (ly:stencil-add (make-line-stencil th x y (+ dx x) (+ dy y)) (make-line-stencil th (- x) (- y) (- dx x) (- dy y))))) \markup \my-draw-line #'(4 . 3) \markup \override #'(line-gap . 1.2) \my-draw-line #'(4 . 3) @end lilypond @node Definición de nuevas instrucciones de lista de marcado @subsection Definición de nuevas instrucciones de lista de marcado @translationof New markup list command definition Las instrucciones de listas de marcado se definen con el macro de Scheme @code{define-markup-list-command}, que es similar al macro @code{define-markup-command} descrito en @ref{Definición de una instrucción de marcado nueva}, excepto que donde éste devuelve un sello único, aquél devuelve una lista de sellos. En el siguiente ejemplo se define una instrucción de lista de marcado @code{\paragraph}, que devuelve una lista de líneas justificadas, estando la primera de ellas sangrada. La anchura del sangrado se toma del argumento @code{props}. @example #(define-markup-list-command (paragraph layout props args) (markup-list?) #:properties ((par-indent 2)) (interpret-markup-list layout props (make-justified-lines-markup-list (cons (make-hspace-markup par-indent) args)))) @end example Aparte de los argumentos usuales @code{layout} y @code{props}, la instrucción de lista de marcados @code{paragraph} toma un argumento de lista de marcados, llamado @code{args}. El predicado para listas de marcados es @code{markup-list?}. En primer lugar, la función toma el ancho del sangrado, una propiedad llamada aquí @code{par-indent}, de la lista de propiedades @code{props}. Si no se encuentra la propiedad, el valor predeterminado es @code{2}. Después, se hace una lista de líneas justificadas usando la función @code{make-justified-lines-markup-list}, que está relacionada con la instrucción incorporada de lista de marcados @code{\justified-lines}. Se añade un espacio horizontal al principio usando la función @code{make-hspace-markup}. Finalmente, la lista de marcados se interpreta usando la función @code{interpret-markup-list}. Esta nueva instrucción de lista de marcados se puede usar como sigue: @example \markuplines @{ \paragraph @{ El arte de la tipografía musical se llama \italic @{grabado (en plancha).@} El término deriva del proceso tradicional de impresión de música. hace sólo algunas décadas, las partituras se hacían cortando y estampando la música en una plancha de zinc o lata en una imagen invertida. @} \override-lines #'(par-indent . 4) \paragraph @{ La plancha se tenía que entintar, y las depresiones causadas por los cortes y estampados retienen la tinta. Se formaba una imagen presionando el papel contra la plancha. El estampado y cortado se hacía completamente a mano. @} @} @end example @node Contextos para programadores @section Contextos para programadores @translationof Contexts for programmers @menu * Evaluación de contextos:: * Ejecutar una función sobre todos los objetos de la presentación:: @end menu @node Evaluación de contextos @subsection Evaluación de contextos @translationof Context evaluation @cindex código, llamadas durante la interpretación @funindex \applyContext Se pueden modificar los contextos durante la interpretación con código de Scheme. La sintaxis para esto es @example \applyContext @var{función} @end example @var{función} debe ser una función de Scheme que toma un único argumento, que es el contexto al que aplicarla. El código siguiente imprime el número del compás actual sobre la salida estándar durante la compilación: @example \applyContext #(lambda (x) (format #t "\nSe nos ha llamado en el compás número ~a.\n" (ly:context-property x 'currentBarNumber))) @end example @node Ejecutar una función sobre todos los objetos de la presentación @subsection Ejecutar una función sobre todos los objetos de la presentación @translationof Running a function on all layout objects @cindex código, llamar sobre objetos de presentación @funindex \applyOutput La manera más versátil de realizar el ajuste fino de un objeto es @code{\applyOutput}. Su sintaxis es @example \applyOutput @var{contexto} @var{proc} @end example @noindent donde @var{proc} es una función de Scheme, que toma tres argumentos. Al interpretarse, la función @var{proc} se llama para cada objeto de presentación que se encuentra en el contexto @var{contexto}, con los siguientes argumentos: @itemize @item el propio objeto de presentación, @item el contexto en que se creó el objeto de presentación, y @item el contexto en que se procesa @code{\applyOutput}. @end itemize Además, la causa del objeto de presentación, es decir el objeto o expresión musical que es responsable de haberlo creado, está en la propiedad @code{cause} del objeto. Por ejemplo, para la cabeza de una nota, éste es un evento @rinternals{NoteHead}, y para un objeto @rinternals{Stem} (plica), éste es un objeto @rinternals{Stem}. @c Impossible - changed to Stem --FV He aquí una función que usar para @code{\applyOutput}; borra las cabezas de las notas que están sobre la línea central: @lilypond[quote,verbatim,ragged-right] #(define (blanker grob grob-origin context) (if (and (memq 'note-head-interface (ly:grob-interfaces grob)) (eq? (ly:grob-property grob 'staff-position) 0)) (set! (ly:grob-property grob 'transparent) #t))) \relative { e4 g8 \applyOutput #'Voice #blanker b d2 } @end lilypond @node Funciones de callback @section Funciones de callback @translationof Callback functions Las propiedades (como @code{thickness} (grosor), @code{direction} (dirección), etc.) se pueden establecer a valores fijos con \override, p. ej.: @example \override Stem #'thickness = #2.0 @end example Las propiedades pueden fijarse también a un procedimiento de scheme, @lilypond[fragment,verbatim,quote,relative=2] \override Stem #'thickness = #(lambda (grob) (if (= UP (ly:grob-property grob 'direction)) 2.0 7.0)) c b a g b a g b @end lilypond @noindent En este caso, el procedimiento se ejecuta tan pronto como el valor de la propiedad se reclama durante el proceso de formateo. Casi todo el motor de tipografiado está manejado por estos @emph{callbacks}. Entre las propiedades que usan normalmente @emph{callbacks} están @table @code @item stencil La rutina de impresión, que construye un dibujo para el símbolo @item X-offset La rutina que establece la posición horizontal @item X-extent La rutina que calcula la anchura de un objeto @end table El procedimiento siempre toma un argumento único, que es el grob (el objeto gráfico). Si se deben llamar rutinas con varios argumentos, el grob actual se puede insertar con una cerradura de grob. He aquí un ajuste procedente de @code{AccidentalSuggestion}, @example `(X-offset . ,(ly:make-simple-closure `(,+ ,(ly:make-simple-closure (list ly:self-alignment-interface::centered-on-x-parent)) ,(ly:make-simple-closure (list ly:self-alignment-interface::x-aligned-on-self))))) @end example @noindent En este ejemplo, tanto @code{ly:self-alignment-interface::x-aligned-on-self} como @code{ly:self-alignment-interface::centered-on-x-parent} se llaman con el grob como argumento. El resultado se añade con la función @code{+}. Para asegurar que esta adición se ejecuta adecuadamente, todo ello se encierra dentro de @code{ly:make-simple-closure}. De hecho, usar un solo procedimiento como valor de una propiedad equivale a @example (ly:make-simple-closure (ly:make-simple-closure (list @var{proc}))) @end example @noindent El @code{ly:make-simple-closure} interior aporta el grob como argumento de @var{proc}, el exterior asegura que el resultado de la función es lo que se devuelve, en lugar del objeto @code{simple-closure}. Desde dentro de un callback, el método más fácil para evaluar un elemento de marcado es usar grob-interpret-markup. Por ejemplo: @example mi-callback = #(lambda (grob) (grob-interpret-markup grob (markup "fulanito"))) @end example @node Código de Scheme en línea @section Código de Scheme en línea @translationof Inline Scheme code La principal desventaja de @code{\tweak} es su inflexibilidad sintáctica. Por ejemplo, lo siguiente produce un error de sintaxis. @example F = \tweak #'font-size #-3 -\flageolet \relative c'' @{ c4^\F c4_\F @} @end example @noindent En otras palabras, @code{\tweak} no se comporta como una articulación en cuando a la sintaxis; concretamente, no se puede adjuntar con @code{^} y @code{_}. Usando Scheme, se puede dar un rodeo a este problema. La ruta hacia el resultado se da en @ref{Añadir articulaciones a las notas (ejemplo)}, especialmente cómo usar @code{\displayMusic} como guía de ayuda. @example F = #(let ((m (make-music 'ArticulationEvent 'articulation-type "flageolet"))) (set! (ly:music-property m 'tweaks) (acons 'font-size -3 (ly:music-property m 'tweaks))) m) \relative c'' @{ c4^\F c4_\F @} @end example @noindent Aquí, las propiedades @code{tweaks} del objeto flageolet @code{m} (creado con @code{make-music}) se extraen con @code{ly:music-property}, se antepone un nuevo par clave-valor para cambiar el tamaño de la tipografía a la lista de propiedades con la función de Scheme @code{acons}, y finalmente el resultado se escribe de nuevo con @code{set!}. El último elemento del bloque @code{let} es el valor de retorno, el propio @code{m}. @node Trucos difíciles @section Trucos difíciles @translationof Difficult tweaks Hay un cierto número de tipos de ajustes difíciles. @itemize @item Un tipo de ajuste difícil es la apariencia de los objetos de extensión, como las ligaduras de expresión y de unión. Inicialmente, sólo se crea uno de estos objetos, y pueden ajustarse con el mecanismo normal. Sin embargo, en ciertos casos los objetos extensores cruzan los saltos de línea. Si esto ocurre, estos objetos se clonan. Se crea un objeto distinto por cada sistema en que se encuentra. Éstos son clones del objeto original y heredan todas sus propiedades, incluidos los @code{\override}s. En otras palabras, un @code{\override} siempre afecta a todas las piezas de un objeto de extensión fragmentado. Para cambiar sólo una parte de un extensor en el salto de línea, es necesario inmiscuirse en el proceso de formateado. El @emph{callback} @code{after-line-breaking} contiene el procedimiento Scheme que se llama después de que se han determinado los saltos de línea, y los objetos de presentación han sido divididos sobre los distintos sistemas. En el ejemplo siguiente, definimos un procedimiento @code{my-callback}. Este procedimiento @itemize @item determina si hemos sido divididos por los saltos de línea @item en caso afirmativo, reúne todos los objetos divididos @item comprueba si somos el último de los objetos divididos @item en caso afirmativo, establece @code{extra-offset}. @end itemize Este procedimiento se instala en @rinternals{Tie} (ligadura de unión), de forma que la última parte de la ligadura dividida se traslada hacia arriba. @lilypond[quote,verbatim,ragged-right] #(define (my-callback grob) (let* ( ; have we been split? (orig (ly:grob-original grob)) ; if yes, get the split pieces (our siblings) (siblings (if (ly:grob? orig) (ly:spanner-broken-into orig) '() ))) (if (and (>= (length siblings) 2) (eq? (car (last-pair siblings)) grob)) (ly:grob-set-property! grob 'extra-offset '(-2 . 5))))) \relative c'' { \override Tie #'after-line-breaking = #my-callback c1 ~ \break c2 ~ c } @end lilypond @noindent Al aplicar este truco, la nueva función de callback @code{after-line-breaking} también debe llamar a la antigua @code{after-line-breaking}, si existe. Por ejemplo, si se usa con @code{Hairpin}, se debe llamar también a @code{ly:hairpin::after-line-breaking}. @item Algunos objetos no se pueden cambiar con @code{\override} por razones técnicas. Son ejemplos @code{NonMusicalPaperColumn} y @code{PaperColumn}. Se pueden cambiar con la función @code{\overrideProperty} que funciona de forma similar a @code{\once \override}, pero usa una sintaxis distinta. @example \overrideProperty #"Score.NonMusicalPaperColumn" % Nombre del grob #'line-break-system-details % Nombre de la propiedad #'((next-padding . 20)) % Valor @end example Observe, sin embargo, que @code{\override}, aplicado a @code{NonMusicalPaperColumn} y a @code{PaperColumn}, aún funciona como se espera dentro de los bloques @code{\context}. @end itemize @node Interfaces de Scheme de LilyPond @chapter Interfaces de Scheme de LilyPond @translationof LilyPond Scheme interfaces Este capítulo cubre las diversas herramientas proporcionadas por LilyPond como ayuda a los programadores de Scheme a extraer e introducir información de los flujos musicales. HACER @c TODO -- figure out what goes in here and how to organize it