X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Fes%2Fextending%2Fscheme-tutorial.itely;h=1081c7560df9182b1672ef28354261bc824d494b;hb=13da8b27aabc5d5a752d00ed1e2b99ad20f0f264;hp=47993d45fba757a4312f968b05385ca14c6ab4ba;hpb=579274a935b22567ac15b3651a9d651474f88ec4;p=lilypond.git diff --git a/Documentation/es/extending/scheme-tutorial.itely b/Documentation/es/extending/scheme-tutorial.itely index 47993d45fb..1081c7560d 100644 --- a/Documentation/es/extending/scheme-tutorial.itely +++ b/Documentation/es/extending/scheme-tutorial.itely @@ -1,20 +1,19 @@ @c -*- coding: utf-8; mode: texinfo; documentlanguage: es -*- @ignore - Translation of GIT committish: e18567dccce0f16b680e6b7e562329e5f8bcae59 + Translation of GIT committish: 26a079ca2393d053315ef8dbef626c897dc9645a When revising a translation, copy the HEAD committish of the version that you are working on. For details, see the Contributors' Guide, node Updating translation committishes.. @end ignore -@c \version "2.12.0" +@c \version "2.17.6" @node Tutorial de Scheme @appendix Tutorial de Scheme @translationof Scheme tutorial -@funindex # @cindex Scheme @cindex GUILE @cindex Scheme, código en línea @@ -74,13 +73,21 @@ Guile de Scheme. Sobre casi todos los sistemas puede experimentar en una @qq{caja de arena} de Scheme abriendo una ventana del terminal y tecleando @q{guile}. En algunos sistemas, sobre todo en Windows, podría necesitar ajustar la variable de entorno @code{GUILE_LOAD_PATH} -a la carpeta @code{../usr/shr/guile/1.8} dentro de la instalación de +a la carpeta @code{../usr/share/guile/1.8} dentro de la instalación de LilyPond (para conocer la ruta completa a esta carpeta, consulte @rlearning{Otras fuentes de información}). Como alternativa, los usuarios de Windows pueden seleccionar simplemente @q{Ejecutar} del menú Inicio e introducir @q{guile}. -Una vez está funcionando el cajón de arena de Guile, verá un indicador +Sin embargo, está disponible un cajón de arena de Scheme listo para +funcionar con todo LilyPond cargado, con esta instrucción de la línea +de órdenes: +@example +lilypond scheme-sandbox +@end example + +@noindent +Una vez está funcionando el cajón de arena, verá un indicador del sistema de Guile: @lisp @@ -88,7 +95,12 @@ guile> @end lisp Podemos introducir expresiones de Scheme en este indicador para -experimentar con Scheme. +experimentar con Scheme. Si quiere usar la biblioteca readline de GNU +para una más cómoda edición de la línea de órdenes de Scheme, consulte +el archivo @file{ly/scheme-sandbox.ly} para más información. Si ya ha +activado la biblioteca readline para las sesiones de Guile +interactivas fuera de LilyPond, debería funcionar también en el cajón +de arena. @node Variables de Scheme @subsection Variables de Scheme @@ -161,7 +173,7 @@ Los valores Booleanos son Verdadero y Falso. Verdadero en Scheme es @item Números Los números se escriben de la forma normal, @code{1} es el número -(entero) uno, mientras que @code{-1.5} es un número en coma flotante +(entero) uno, mientras que @w{@code{-1.5}} es un número en coma flotante (un número no entero). @item Cadenas @@ -547,6 +559,11 @@ guile> (menor-que-diez? 15) @subheading Valores de retorno +Los procedimientos de Scheme siempre devuelven un valor de retorno, +que es el valor de la última expresión ejecutada en el procedimiento. +El valor de retorno puede ser cualquier valor de Scheme válido, +incluso una estructura de datos compleja o un procedimiento. + A veces, el usuario quiere tener varias expresiones de Scheme dentro de un procedimiento. Existen dos formas en que se pueden combinar distintas expresiones. La primera es el procedimiento @code{begin}, @@ -626,6 +643,7 @@ guile> (cond ((< a b) "a es menor que b") * Sintaxis del Scheme de LilyPond:: * Variables de LilyPond:: * Variables de entrada y Scheme:: +* Importación de Scheme dentro de LilyPond:: * Propiedades de los objetos:: * Variables de LilyPond compuestas:: * Representación interna de la música:: @@ -634,23 +652,90 @@ guile> (cond ((< a b) "a es menor que b") @node Sintaxis del Scheme de LilyPond @subsection Sintaxis del Scheme de LilyPond @translationof LilyPond Scheme syntax +@funindex $ +@funindex # -En un archivo de música, los fragmentos de código de Scheme se -escriben con el signo de almohadilla @code{#}. Así, los ejemplos -anteriores traducidos a LilyPond son: +El intérprete Guile forma parte de LilyPond, lo que significa que se +puede incluir Scheme dentro de los archivos de entrada de LilyPond. +Existen varios métodos para incluir Scheme dentro de LilyPond. + +La manera más sencilla es utilizar el símbolo de +almohadilla@tie{}@code{#} antes de una expresión de Scheme. + +Ahora bien, el código de entrada de LilyPond se estructura en +elementos y expresiones, de forma parecida a cómo el lenguaje humano +se estructura en palabras y frases. LilyPond tiene un analizador +léxico que reconoce elementos indivisibles (números literales, cadenas +de texto, elementos de Scheme, nombres de nota, etc.), y un analizador +sintáctico que entiende la sintaxis, la @ruser{Gramática de LilyPond}. +Una vez que sabe que se aplica una regla sintáctica concreta, ejecuta +las acciones asociadas con ella. + +El método del símbolo de almohadilla@tie{}@code{#} para incrustar +Scheme se adapta de forma natural a este sistema. Una vez que el +analizador léxico ve un símbolo de almohadilla, llama al lector de +Scheme para que lea una expresión de Scheme completa (que puede ser un +identificador, una expresión encerrada entre paréntesis, o algunas +otras cosas). Después de que se ha leído la expresión de Scheme, se +almacena como el valor de un elemento @code{SCM_TOKEN} de la +gramática. Después de que el analizador sintáctico ya sabe cómo hacer +uso de este elemento, llama a Guila para que evalúe la expresión de +Scheme. Dado que el analizador sintáctico suele requerir un poco de +lectura por delante por parte del analizador léxico para tomar sus +decisiones de análisis sintáctico, esta separación de lectura y +evaluación entre los analizadores léxico y sintáctico es justamente lo +que se necesita para mantener sincronizadas las ejecuciones de +expresiones de LilyPond y de Scheme. Por este motivo se debe usar el +símbolo de almohadilla@tie{}@code{#} para llamar a Scheme siempre que +sea posible. + +Otra forma de llamar al intérprete de Scheme desde lilyPond es el uso +del símbolo de dólar@tie{}@code{$} en lugar de la almohadilla para +introducir las expresiondes de Scheme. En este caso, LilyPond evalúa +el código justo después de que el analizador léxico lo ha leído. +Comprueba el tipo resultante de la expresión de Scheme y después +selecciona un tipo de elemento (uno de los varios elementos +@code{xxx_IDENTIFIER} dentro de la sintaxis) para él. Crea una +@emph{copia} del valor y la usa como valor del elemento. Si el valor +de la expresión es vacío (El valor de Guile de @code{*unspecified*}), +no se pasa nada en absoluto al analizador sintáctico. + +Éste es, de hecho, el mismo mecanismo exactamente que LilyPond emplea +cuando llamamos a cualquier variable o función musical por su nombre, +como @code{\nombre}, con la única diferencia de que el nombre viene +determinado por el analizador léxico de LilyPond sin consultar al +lector de Scheme, y así solamente se aceptan los nombres de variable +consistentes con el modo actual de LilyPond. + +La acción inmediata de @code{$} puede llevar a alguna que otra +sorpresa, véase @ref{Variables de entrada y Scheme}. La utilización +de @code{#} donde el analizador sintáctico lo contempla es normalmente +preferible. Dentro de las expresiones musicales, aquellas que se +crean utilizando @code{#} @emph{se interprentan} como música. Sin +embargo, @emph{no se copian} antes de ser utilizadas. Si forman parte +de alguna estructura que aún podría tener algún uso, quizá tenga que +utilizar explícitamente @code{ly:music-deep-copy}. + +@funindex $@@ +@funindex #@@ +También existen los operadores de @q{división de listas} @code{$@@} y +@code{#@@} que insertan todos los elementos de una lista dentro del +contexto circundante. + +Ahora echemos un vistazo a algo de código de Scheme real. Los +procedimientos de Scheme se pueden definir dentro de los archivos de +entrada de LilyPond: @example -##t ##f -#1 #-1.5 -#"esto es una cadena" -#"esto -es -una cadena" +#(define (media a b c) (/ (+ a b c) 3)) @end example Observe que los comentarios de LilyPond (@code{%} y @code{%@{ %@}}) no -se pueden utilizar dentro del código de Scheme. Los comentarios en -el Scheme de Guile se introducen como sigue: +se pueden utilizar dentro del código de Scheme, ni siquiera dentro de +un archivo de entrada de LilyPond, porque es el intérprete Guile, y no +el analizador léxico de LilyPond, el que está leyendo la expresión de +Scheme. Los comentarios en el Scheme de Guile se introducen como +sigue: @example ; esto es un comentario de una línea @@ -662,9 +747,13 @@ el Scheme de Guile se introducen como sigue: !# @end example -Se pueden combinar en un mismo archivo de música varias expresiones de -Scheme consecutivas mediante la utilización del operador @code{begin}. -Ello permite que el número de marcas de cuadradillo se reduzca a una. +Durante el resto de esta sección, supondremos que los datos se +introducen en un archivo de música, por lo que añadiremos +almohadillas@tie{}@code{#} al principio de todas las expresiones de Scheme. + +Todas las expresiones de Scheme del nivel jerárquico superior dentro +de un archivo de entrada de LilyPond se pueden combinar en una sola +expresión de Scheme mediante la utilización del operador @code{begin}: @example #(begin @@ -672,38 +761,47 @@ Ello permite que el número de marcas de cuadradillo se reduzca a una. (define menganito 1)) @end example -Si el @code{#} va seguido de un paréntesis de apertura, @code{(}, como -en el ejemplo anterior, el analizador sintáctico permanece dentro del -modo de Scheme hasta que encuentra el paréntesis de cierre -correspondiente, @code{)}, por lo que no son necesarios más símbolos -de @code{#} para introducir una sección de Scheme. - -Durante el resto de esta sección, supondremos que los datos se -introducen en un archivo de música, por lo que añadiremos almohadillas -@code{#} en todas partes. @node Variables de LilyPond @subsection Variables de LilyPond @translationof LilyPond variables -@c TODO -- make this read right - -Algo similar ocurre con las variables. Después de definir una -variable, +Las variables de LilyPond se almacenan internamente en la forma de +variables de Scheme. Así, @example doce = 12 @end example @noindent -las variables se pueden usar también dentro de expresiones, aquí +equivale a + +@example +#(define doce 12) +@end example + +Esto significa que las variables de LilyPond están disponibles para su +uso dentro de expresiones de Scheme. Por ejemplo, podríamos usar @example veintiCuatro = (* 2 doce) @end example @noindent -el número 24 se almacena dentro de la variable @code{veintiCuatro}. +lo que daría lugar a que el número 24 se almacenase dentro de la +variable @code{veintiCuatro} de LilyPond (y de Scheme). + +La forma usual de referirse a las variables de LilyPond, +@ref{Sintaxis del Scheme de LilyPond}, + +es llamarlas usando una barra invertida, es decir +@code{\veintiCuatro}. Dado que esto crea una copia para la mayor +parte de los tipos internos de LilyPond, concretamente las expresiones +musicales, las funciones musicales no sueln crear copias del material +que ellas mismas modifican. Por este motivo, las expresiones +musicales dadas con @code{#} no deberían, por lo general, contener +material que no se haya creado partiendo de cero o copiado +explícitamente en lugar de estar referenciado directamente. @node Variables de entrada y Scheme @subsection Variables de entrada y Scheme @@ -727,6 +825,7 @@ independiente de la @code{\traLaLa} externa. traLaLa = @{ c'4 d'4 @} \layout @{ traLaLa = 1.0 @} @end example + @c En efecto, cada archivo de entrada constituye un ámbito, y cada bloque @code{\header}, @code{\midi} y @code{\layout} son ámbitos anidados @@ -734,20 +833,20 @@ dentro del ámbito de nivel superior. Tanto las variables como los ámbitos están implementados en el sistema de módulos de GUILE. A cada ámbito se adjunta un módulo anónimo de -Scheme. Una asignación de la forma +Scheme. Una asignación de la forma: @example traLaLa = @{ c'4 d'4 @} @end example @noindent -se convierte internamente en una definición de Scheme +se convierte internamente en una definición de Scheme: @example (define traLaLa @var{Valor Scheme de `@code{... }'}) @end example -Esto significa que las variables de entrada y las variables de Scheme +Esto significa que las variables de LilyPond y las variables de Scheme se pueden mezclar con libertad. En el ejemplo siguiente, se almacena un fragmento de música en la variable @code{traLaLa}, y se duplica usando Scheme. El resultado se importa dentro de un bloque @@ -756,40 +855,81 @@ usando Scheme. El resultado se importa dentro de un bloque @lilypond[verbatim] traLaLa = { c'4 d'4 } -%% dummy action to deal with parser lookahead -#(display "this needs to be here, sorry!") - #(define newLa (map ly:music-deep-copy (list traLaLa traLaLa))) #(define twice (make-sequential-music newLa)) -{ \twice } +\twice @end lilypond @c Due to parser lookahead -En este ejemplo, la asignación se produce después de que el analizador -sintáctico ha verificado que no ocurre nada interesante después de -@code{traLaLa = @{ ... @}}. Sin la sentencia muda del ejemplo -anterior, la definición de @code{newLa} se ejecuta antes de que se -defina @code{traLaLa}, produciendo un error de sintaxis. +En realidad, éste es un ejemplo bastante interesante. La asignación +solo tiene lugar después de que el analizador sintáctico se ha +asegurado de que no sigue nada parecido a @code{\addlyrics}, de manera +que necesita comprobar lo que viene a continuación. Lee el símbolo +@code{#} y la expresión de Scheme siguiente @emph{sin} evaluarla, de +forma que puede proceder a la asignación, y @emph{posteriormente} +ejecutar el código de Scheme sin problema. + +@node Importación de Scheme dentro de LilyPond +@subsection Importación de Scheme dentro de LilyPond +@translationof Importing Scheme in LilyPond +@funindex $ +@funindex # El ejemplo anterior muestra cómo @q{exportar} expresiones musicales desde la entrada al intérprete de Scheme. Lo contrario también es -posible. Envolviendo un valor de Scheme en la función -@code{ly:export}, se interpreta un valor de Scheme como si hubiera -sido introducido en la sintaxis de LilyPond. En lugar de definir -@code{\twice}, el ejemplo anterior podría también haberse escrito como +posible. Colocándolo después de @code{$}, un valor de Scheme se +interpreta como si hubiera sido introducido en la sintaxis de +LilyPond. En lugar de definir @code{\twice}, el ejemplo anterior +podría también haberse escrito como @example ... -@{ #(ly:export (make-sequential-music (list newLa))) @} +$(make-sequential-music newLa) +@end example + +Podemos utilizar @code{$} con una expresión de Scheme en cualquier +lugar en el que usaríamos @code{\@var{nombre}} después de haber +asignado la expresión de Scheme a una variable @var{nombre}. Esta +sustitución se produce dentro del @q{analizador léxico}, de manera que +LilyPond no llega a darse cuenta de la diferencia. + +Sin embargo, existe un inconveniente, el de la medida del tiempo. Si +hubiésemos estado usando @code{$} en vez de @code{#} para definir +@code{newLa} en el ejemplo anterior, la siguiente definición de Scheme +habría fracasado porque @code{traLaLa} no habría sido definida aún. +Para ver una explicación de este problema de momento temporal, véase +@ref{Sintaxis del Scheme de LilyPond}. + +@funindex $@@ +@funindex #@@ +Un conveniente aspecto posterior pueden ser los operadores de +@q{división de listas} @code{$@@} y @code{#@@} para la inserción de +los elementos de una lista dentro del contexto circundante. +Utilizándolos, la última parte del ejemplo se podría haber escrito +como + +@example +... +@{ #@@newLa @} +@end example + +Aquí, cada elemento de la lista que está almacenado en @code{newLa} se +toma en secuencia y se inserta en la lista, como si hubiésemos escrito + +@example +@{ #(first newLa) #(second newLa) @} @end example -El código de Scheme se evalúa tan pronto como el analizador sintáctico -lo encuentra. Para definir código de Scheme dentro de un macro (para -llamarse más tarde), utilice @ref{Funciones vacías}, o bien +Ahora bien, en todas esas formas, el código de Scheme se evalúa en el +momento en que el código de entrada aún se está procesando, ya sea en +el analizador léxico o en el analizador sintáctico. Si necesitamos +que se ejecute en un momento posterior, debemos consultar +@ref{Funciones de Scheme vacías}, o almacenarlo dentro de un +procedimiento: @example #(define (nopc) @@ -803,35 +943,38 @@ llamarse más tarde), utilice @ref{Funciones vacías}, o bien @knownissues No es posible mezclar variables de Scheme y de LilyPond con la opción -@code{--safe}. - - +@option{--safe}. @node Propiedades de los objetos @subsection Propiedades de los objetos @translationof Object properties -Esta sintaxis se usará con mucha frecuencia, pues muchos de los trucos -de presentación consisten en asignar valores (de Scheme) a variables -internas, por ejemplo +Las propiedades de los objetos se almacenan en LilyPond en forma de +cadenas de listas-A, que son listas de listas-A. Las propiedades se +establecen añadiendo valores al principio de la lista de propiedades. +Las propiedades se leen extrayendo valores de las listas-A. + +El establecimiento de un valor nuevo para una propiedad requiere la +asignación de un valor a la lista-A con una clave y un valor. La +sintaxis de LilyPond para hacer esto es la siguiente: @example -\override Stem #'thickness = #2.6 +\override Stem.thickness = #2.6 @end example -Esta instrucción ajusta el aspecto de las plicas. El valor @code{2.6} -se pone dentro de la variable @code{thickness} de un objeto -@code{Stem}. @code{thickness} se mide a partir del grosor de las -líneas del pentagrama, y así estas plicas serán @code{2.6} veces el -grosor de las líneas del pentagrama. Esto hace que las plicas sean -casi el doble de gruesas de lo normal. Para distinguir entre las -variables que se definen en los archivos de entrada (como -@code{veintiCuatro} en el ejemplo anterior) y las variables de los -objetos internos, llamaremos a las últimas @q{propiedades} y a las -primeras @q{variables.} Así, el objeto plica tiene una propiedad -@code{thickness} (grosor), mientras que @code{veintiCuatro} es una -variable. +Esta instrucción ajusta el aspecto de las plicas. Se añade una +entrada de lista-A @code{'(thickness . 2.6)} a la lista de propiedades +de un objeto @code{Stem}. @code{thickness} se mide a partir del +grosor de las líneas del pentagrama, y así estas plicas serán +@code{2.6} veces el grosor de las líneas del pentagrama. Esto hace +que las plicas sean casi el doble de gruesas de lo normal. Para +distinguir entre las variables que se definen en los archivos de +entrada (como @code{veintiCuatro} en el ejemplo anterior) y las +variables de los objetos internos, llamaremos a las últimas +@q{propiedades} y a las primeras @q{variables.} Así, el objeto plica +tiene una propiedad @code{thickness} (grosor), mientras que +@code{veintiCuatro} es una variable. @cindex propiedades frente a variables @cindex variables frente a propiedades @@ -846,43 +989,87 @@ variable. @subheading Desplazamientos -Los desplazamientos bidimensionales (coordenadas X e Y) así como los -tamaños de los objetos (intervalos con un punto izquierdo y otro -derecho) se introducen como @code{parejas}. Una pareja@footnote{En la -terminología de Scheme, la pareja se llama @code{cons}, y sus dos -elementos se llaman @code{car} y @code{cdr} respectivamente.} se -introduce como @code{(primero . segundo)} y, como los símbolos, se deben -preceder de un apóstrofo: +Los desplazamientos bidimensionales (coordenadas X e Y) se almacenan +como @emph{parejas}. El @code{car} del desplazamiento es la +coordenada X, y el @code{cdr} es la coordenada Y. @example -\override TextScript #'extra-offset = #'(1 . 2) +\override TextScript.extra-offset = #'(1 . 2) @end example -Esto asigna la pareja (1, 2) a la propiedad @code{extra-offset} del -objeto TextScript. Estos números se miden en espacios de pentagrama, -y así esta instrucción mueve el objeto un espacio de pentagrama a la -derecha, y dos espacios hacia arriba. +Esto asigna la pareja @code{(1 . 2)} a la propiedad +@code{extra-offset} del objeto TextScript. Estos números se miden en +espacios de pentagrama, y así esta instrucción mueve el objeto un +espacio de pentagrama a la derecha, y dos espacios hacia arriba. + +Los procedimientos para trabajar con desplazamientos están en +@file{scm/lily-library.scm}. + +@subheading Fractions + +Fractions as used by LilyPond are again stored as @emph{pairs}, this +time of unsigned integers. While Scheme can represent rational numbers +as a native type, musically @samp{2/4} and @samp{1/2} are not the same, +and we need to be able to distinguish between them. Similarly there are +no negative @q{fractions} in LilyPond's mind. So @code{2/4} in LilyPond +means @code{(2 . 4)} in Scheme, and @code{#2/4} in LilyPond means +@code{1/2} in Scheme. @subheading Dimensiones -HACER @c todo -- write something about extents +Las parejas se usan también para almacenar intervalos, que representan +un rango de números desde el mínimo (el @code{car}) hasta el máximo +(el @code{cdr}). Los intervalos se usan para almacenar las +dimensiones en X y en Y de los objetos imprimibles. Para dimensiones +en X, el @code{car} es la coordenada X de la parte izquierda, y el +@code{cdr} es la coordenada X de la parte derecha. Para las +dimensiones en Y, el @code{car} es la coordenada inferior, y el +@code{cdr} es la coordenada superior. + +Los procedimientos para trabajar con intervalos están en +@file{scm/lily-library.scm}. Se deben usar estos procedimientos +siempre que sea posible, para asegurar la consistencia del código. @subheading Listas-A de propiedades -HACER @c todo -- write something about property alists +Una lista-A de propiedades es una estructura de datos de LilyPond que +es una lista-A cuyas claves son propiedades y cuyos valores son +expresiones de Scheme que dan el valor deseado de la propiedad. + +Las propiedades de LilyPond son símbolos de Scheme, como por ejemplo +@code{'thickness}. @subheading Cadenas de listas-A -HACER @c todo -- write something about alist chains +Una cadena de listas-A es una lista que contiene listas-A de +propiedades. + +El conjunto de todas las propiedades que se aplican a un grob se +almacena por lo general como una cadena de listas-A. Para poder +encontrar el valor de una propiedad determinada que debería tener un +grob, se busca por todas las listas-A de la cadena, una a una, +tratando de encontrar una entrada que contenga la clave de la +propiedad. Se devuelve la primera entrada de lista-A que se +encuentre, y el valor es el valor de la propiedad. + +El procedimiento de Scheme @code{chain-assoc-get} se usa normalmente +para obtener los valores de propiedades. @node Representación interna de la música @subsection Representación interna de la música @translationof Internal music representation +Internamente, la música se representa como una lista de Scheme. La +lista contiene varios elementos que afectan a la salida impresa. El +análisis sintáctico es el proceso de convertir la música de la +representación de entrada de LilyPond a la representación interna de +Scheme. + Cuando se analiza una expresión musical, se convierte en un conjunto de objetos musicales de Scheme. La propiedad definitoria de un objeto -musical es que ocupa un tiempo. El tiempo es un número racional que -mide la longitud de una pieza de música en redondas. +musical es que ocupa un tiempo. El tiempo que ocupa se llama +@emph{duración}. Las duraciones se expresan como un número racional +que mide la longitud del objeto musical en redondas. Un objeto musical tiene tres clases de tipos: @itemize @@ -968,18 +1155,16 @@ imprime lo siguiente: 'SequentialMusic 'elements (list (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 0 0)) - (make-music 'AbsoluteDynamicEvent 'text - "f"))))) + "f")) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 0 0)))) @end example De forma predeterminada, LilyPond imprime estos mensajes sobre la @@ -991,39 +1176,75 @@ salida a un archivo. lilypond archivo.ly >salida.txt @end example -Con la aplicación de un poco de formateo, la información anterior es -fácil de leer, +Con un poco de magia combinada de LilyPond y Scheme, podemos realmente +hacer que LilyPond dirija solamente esta salida a su propio archivo: + +@example +@{ + #(with-output-to-file "display.txt" + (lambda () #@{ \displayMusic @{ c'4\f @} #@})) +@} +@end example + + +Un poco de reformateo hace a la información anterior más fácil de +leer: @example (make-music 'SequentialMusic - 'elements (list (make-music 'EventChord - 'elements (list (make-music 'NoteEvent - 'duration (ly:make-duration 2 0 1 1) - 'pitch (ly:make-pitch 0 0 0)) - (make-music 'AbsoluteDynamicEvent - 'text "f"))))) + 'elements (list + (make-music 'NoteEvent + 'articulations (list + (make-music 'AbsoluteDynamicEvent + 'text + "f")) + 'duration (ly:make-duration 2 0 1 1) + 'pitch (ly:make-pitch 0 0 0)))) @end example Una secuencia musical @code{@{ ... @}} tiene el nombre @code{SequentialMusic}, y sus expresiones internas se almacenan coma una lista dentro de su propiedad @code{'elements}. Una nota se -representa como una expresión @code{EventChord} que contiene un objeto -@code{NoteEvent} (que almacena las propiedades de duración y altura) y -cualquier información adicional (en este caso, un evento -@code{AbsoluteDynamicEvent} con una propiedad @code{"f"} de texto. +representa como un objeto @code{NoteEvent} (que almacena las +propiedades de duración y altura) con información adjunta (en este +caso, un evento @code{AbsoluteDynamicEvent} con una propiedad +@code{"f"} de texto) almacenada en su propiedad @code{articulations}. +@funindex{\void} +@code{\displayMusic} devuelve la música que imprime en la consola, y +por ello se interpretará al tiempo que se imprime en la consola. Para +evitar la interpretación, escriba @code{\void} antes de +@code{\displayMusic}. @node Propiedades musicales @subsection Propiedades musicales @translationof Music properties -El objeto @code{NoteEvent} es el primer objeto de la propiedad -@code{'elements} de @code{someNote}. +@c TODO -- make sure we delineate between @emph{music} properties, +@c @emph{context} properties, and @emph{layout} properties. These +@c are potentially confusing. + +Veamos un ejemplo: @example someNote = c' \displayMusic \someNote ===> +(make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 0 0)) +@end example + +The @code{NoteEvent} object is the representation of @code{someNote}. +Straightforward. How about putting c' in a chord? + +@example +someNote = +\displayMusic \someNote +===> (make-music 'EventChord 'elements @@ -1035,6 +1256,9 @@ someNote = c' (ly:make-pitch 0 0 0)))) @end example +Ahora el objeto @code{NoteEvent} es el primer objeto +de la propiedad @code{'elements} de @code{someNote}. + La función @code{display-scheme-music} es la función que se usa por parte de @code{\displayMusic} para imprimir la representación de Scheme de una expresión musical. @@ -1081,7 +1305,7 @@ d' @translationof Doubling a note with slurs (example) Supongamos que queremos crear una función que convierte una entrada -como @code{a} en @code{a( a)}. Comenzamos examinando la +como @code{a} en @code{@{ a( a) @}}. Comenzamos examinando la representación interna de la música con la que queremos terminar. @example @@ -1091,71 +1315,68 @@ representación interna de la música con la que queremos terminar. 'SequentialMusic 'elements (list (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 5 0)) - (make-music 'SlurEvent 'span-direction - -1))) + -1)) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)) (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 5 0)) - (make-music 'SlurEvent 'span-direction - 1))))) + 1)) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)))) @end example -Las malas noticias son que las expresiones @code{SlurEvent} se deben -añadir @q{dentro} de la nota (o para ser más exactos, dentro de la -expresión @code{EventChord}). +La mala noticia es que las expresiones @code{SlurEvent} se deben +añadir @q{dentro} de la nota (dentro de la +propiedad @code{articulations}). Ahora examinamos la entrada, @example +\displayMusic a' +===> (make-music - 'SequentialMusic - 'elements - (list (make-music - 'EventChord - 'elements - (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 5 0)))))) + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)))) @end example Así pues, en nuestra función, tenemos que clonar esta expresión (de forma que tengamos dos notas para construir la secuencia), añadir -@code{SlurEvents} a la propiedad @code{'elements} de cada una de +@code{SlurEvent} a la propiedad @code{'articulations} de cada una de ellas, y por último hacer una secuencia @code{SequentialMusic} con los -dos @code{EventChords}. +dos @code{EventChords}. Para añadir a una propiedad, es útil saber +que una propiedad no establecida se lee como @code{'()}, la lista +vacía, así que no se requiere ninguna comprobación especial antes de +que pongamos otro elemento delante de la propiedad +@code{articulations}. + @example doubleSlur = #(define-music-function (parser location note) (ly:music?) "Return: @{ note ( note ) @}. - `note' is supposed to be an EventChord." + `note' is supposed to be a single note." (let ((note2 (ly:music-deep-copy note))) - (set! (ly:music-property note 'elements) + (set! (ly:music-property note 'articulations) (cons (make-music 'SlurEvent 'span-direction -1) - (ly:music-property note 'elements))) - (set! (ly:music-property note2 'elements) + (ly:music-property note 'articulations))) + (set! (ly:music-property note2 'articulations) (cons (make-music 'SlurEvent 'span-direction 1) - (ly:music-property note2 'elements))) + (ly:music-property note2 'articulations))) (make-music 'SequentialMusic 'elements (list note note2)))) @end example @@ -1167,7 +1388,11 @@ doubleSlur = #(define-music-function (parser location note) (ly:music?) La manera fácil de añadir articulación a las notas es mezclar dos expresiones musicales en un solo contexto, como se explica en @ruser{Crear contextos}. Sin embargo, supongamos que queremos -escribir una función musical que lo haga. +escribir una función musical que lo haga. Esto tiene la ventaja +adicional de que podemos usar esa función musical para añadir una +articulación (como una instrucción de digitación) a una nota única +dentro de un acorde, lo cual no es posible si nos limitamos a mezclar +fragmentos de música independientes. Una @code{$variable} dentro de la notación @code{#@{...#@}} es como una @code{\variable} normal en la notación clásica de LilyPond. @@ -1179,10 +1404,10 @@ Sabemos que @noindent no funciona en LilyPond. Podríamos evitar este problema adjuntando la -articulación a una nota falsa, +articulación a un acorde vacío, @example -@{ << \music s1*0-.-> @} +@{ << \music <> -. -> >> @} @end example @noindent @@ -1194,61 +1419,54 @@ Scheme. Empezamos examinando nuestra entrada y la salida deseada, \displayMusic c4 ===> (make-music - 'EventChord - 'elements - (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch -1 0 0)))) + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch -1 0 0)))) ===== % desired output \displayMusic c4-> ===> (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch -1 0 0)) - (make-music 'ArticulationEvent 'articulation-type - "marcato"))) + "accent")) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch -1 0 0)) @end example Vemos que una nota (@code{c4}) se representa como una expresión -@code{EventChord}, con una expresión @code{NoteEvent} en su lista de -elementos. Para añadir una articulación de marcato, se debe añadir -una expresión @code{ArticulationEvent} a la propiedad elements de la -expresión @code{EventChord}. +@code{NoteEvent}. Para añadir una articulación de acento, se debe +añadir una expresión @code{ArticulationEvent} a la propiedad +@code{articulations} de la expresión @code{NoteEvent}. Para construir esta función, empezamos con @example -(define (add-marcato event-chord) - "Add a marcato ArticulationEvent to the elements of `event-chord', - which is supposed to be an EventChord expression." - (let ((result-event-chord (ly:music-deep-copy event-chord))) - (set! (ly:music-property result-event-chord 'elements) - (cons (make-music 'ArticulationEvent - 'articulation-type "marcato") - (ly:music-property result-event-chord 'elements))) - result-event-chord)) +(define (add-accent note-event) + "Add an accent ArticulationEvent to the articulations of `note-event', + which is supposed to be a NoteEvent expression." + (set! (ly:music-property note-event 'articulations) + (cons (make-music 'ArticulationEvent + 'articulation-type "accent") + (ly:music-property note-event 'articulations))) + note-event) @end example La primera línea es la forma de definir una función en Scheme: el -nombre de la función es @code{add-marcato}, y tiene una variable -llamada @code{event-chord}. En Scheme, el tipo de variable suele +nombre de la función es @code{add-accent}, y tiene una variable +llamada @code{note-event}. En Scheme, el tipo de variable suele quedar claro a partir de su nombre (¡esto también es una buena práctica en otros lenguajes de programación!) @example -"Add a marcato..." +"Add an accent..." @end example @noindent @@ -1256,85 +1474,102 @@ es una descripción de lo que hace la función. No es estrictamente necesaria, pero de igual forma que los nombres claros de variable, es una buena práctica. -@example -(let ((result-event-chord (ly:music-deep-copy event-chord))) -@end example - -Se usa @code{let} para declarar las variables locales. Aquí usamos -una variable local, llamada @code{result-event-chord}, a la que le -damos el valor @code{(ly:music-deep-copy event-chord)}. -@code{ly:music-deep-copy} es una función específica de LilyPond, como -todas las funciones que comienzan por @code{ly:}. Se usa para hacer -una copia de una expresión musical. Aquí, copiamos @code{event-chord} -(el parámetro de la función). Recuerde que nuestro propósito es -añadir un marcato a una expresión @code{EventChord}. Es mejor no -modificar el @code{EventChord} que se ha dado como argumento, porque -podría utilizarse en algún otro lugar. - -Ahora tenemos un @code{result-event-chord}, que es una expresión -@code{NoteEventChord} y es una copia de @code{event-chord}. Añadimos -el marcato a su propiedad de la lista de @code{'elements}. +Se preguntará porqué modificamos el evento de nota directamente en +lugar de trabajar sobre una copia (se puede usar +@code{ly:music-deep-copy} para ello). La razón es un contrato +silencioso: se permite que las funciones musicales modifiquen sus +argumentos; o bien se generan partiendo de cero (como la entrada del +usuario) o están ya copiadas (referenciar una variable de música con +@samp{\name} o la música procedente de expresiones de Scheme +inmediatas @samp{$(@dots{})} proporcionan una copia). Dado que sería +ineficiente crear copias innecesarias, el valor devuelto de una +función musical @emph{no} se copia. Así pues, para cumplir dicho +contrato, no debemos usar ningún argumento más de una vez, y +devolverlo cuenta como una vez. + +En un ejemplo anterior, hemos construido música mediante la repetición +de un argumento musical dado. En tal caso, al menos una repetidión +tuvo que ser una copia de sí misma. Si no lo fuese, podrían ocurrir +cosas muy extrañas. Por ejemplo, si usamos @code{\relative} o +@code{\transpose} sobre la música resultante que contiene los mismos +elementos varias veces, estarían sujetos varias veces a la +relativización o al transporte. Si los asignamos a una variable de +música, se rompe el curso porque hacer referencia a @samp{\name} +creará de nuevo una copia que no retiene la identidad de los elementos +repetidos. + +Ahora bien, aun cuando la función anterior no es una función musical, +se usará normalmente dentro de funciones musicales. Así pues, tiene +sentido obedecer el mismo convenio que usamos para las funciones +musicales: la entrada puede modificarse para producir la salida, y el +código que llama es responsable de crear las copias si aún necesita el +propio argumento sin modificar. Si observamos las propias funciones +de LilyPond como @code{music-map}, veremos que se atienen a los mismos +principios. + +¿En qué punto nos encontramos? Ahora tenemos un @code{note-event} que +podemos modificar, no a causa de la utilización de +@code{ly:music-deep-copy} sino por una explicación muy desarrollada. +Añadimos el acento a su propiedad de lista @code{'articulations}. @example -(set! lugar valor-nuevo) +(set! place new-value) @end example -Aquí, lo que queremos establecer (el @q{lugar}) es la propiedad -@code{'elements} de la expresión @code{result-event-chord}. +Aquí, lo que queremos establecer (el @q{place}) es la propiedad +@code{'articulations} de la expresión @code{note-event}. @example -(ly:music-property result-event-chord 'elements) +(ly:music-property note-event 'articulations) @end example -@code{ly:music-property} es la función que se usa para acceder a las -propiedades musicales (los @code{'elements}, @code{'duration}, -@code{'pitch}, etc, que vimos en la salida de @code{\displayMusic} -anterior). El nuevo valor es la antigua propiedad @code{'elements}, -con un elemento adicional: la expresión @code{ArticulationEvent}, que -copiamos a partir de la salida de @code{\displayMusic}, +@code{ly:music-property} es la función ustilizada para acceder a las +propiedades musicales (las @code{'articulations}, @code{'duration}, +@code{'pitch}, etc, que vemos arriba en la salida de +@code{\displayMusic}). El nuevo valor es la antigua propiedad +@code{'articulations}, con un elemento adicional: la expresión +@code{ArticulationEvent}, que copiamos a partir de la salida de +@code{\displayMusic}, @example (cons (make-music 'ArticulationEvent - 'articulation-type "marcato") - (ly:music-property result-event-chord 'elements)) + 'articulation-type "accent") + (ly:music-property result-event-chord 'articulations)) @end example -@code{cons} se usa para añadir un elemento a una lista sin modificar -la lista original. Esto es lo que queremos: la misma lista que antes, -más la expresión @code{ArticulationEvent} nueva. El orden dentro de -la propiedad @code{'elements} no es importante aquí. +Se usa @code{cons} para añadir un elemento a la parte delantera de una +lista sin modificar la lista original. Esto es lo que queremos: la +misma lista de antes, más la nueva expresión @code{ArticulationEvent}. +El orden dentro de la propiedad @code{'articulations} no tiene +importancia aquí. -Finalmente, una vez hemos añadido la articulación marcato a su -propiedad @code{elements}, podemos devolver @code{result-event-chord}, -de ahí la última línea de la función. +Finalmente, una vez hemos añadido la articulación de acento a su +propiedad @code{articulations}, podemos devolver @code{note-event}, de +aquí la última línea de la función. -Ahora transformamos la función @code{add-marcato} en una función -musical: +Ahora transformamos la función @code{add-accent} en una función +musical (es cuestión de un poco de aderezo sintáctico y una +declaración del tipo de su único argumento @q{real}). @example -addMarcato = #(define-music-function (parser location event-chord) +addAccent = #(define-music-function (parser location note-event) (ly:music?) - "Add a marcato ArticulationEvent to the elements of `event-chord', - which is supposed to be an EventChord expression." - (let ((result-event-chord (ly:music-deep-copy event-chord))) - (set! (ly:music-property result-event-chord 'elements) - (cons (make-music 'ArticulationEvent - 'articulation-type "marcato") - (ly:music-property result-event-chord 'elements))) - result-event-chord)) + "Add an accent ArticulationEvent to the articulations of `note-event', + which is supposed to be a NoteEvent expression." + (set! (ly:music-property note-event 'articulations) + (cons (make-music 'ArticulationEvent + 'articulation-type "accent") + (ly:music-property note-event 'articulations))) + note-event) @end example Podemos verificar que esta función musical funciona correctamente: @example -\displayMusic \addMarcato c4 +\displayMusic \addAccent c4 @end example - - - - @ignore @menu * Trucos con Scheme:: @@ -1342,11 +1577,10 @@ Podemos verificar que esta función musical funciona correctamente: @c @nod e Trucos con Scheme @c @sectio n Trucos con Scheme -@translationof Tweaking with Scheme +@c @transl ationof Tweaking with Scheme Hemos visto cómo la salida de LilyPond se puede modificar -profundamente usando instrucciones como @code{\override TextScript -#'extra-offset = ( 1 . -1)}. Pero tenemos incluso mucho más poder si +profundamente usando instrucciones como @code{\override TextScript.extra-offset = ( 1 . -1)}. Pero tenemos incluso mucho más poder si utilizamos Scheme. Para ver una explicación completa de esto, consulte el @ref{Tutorial de Scheme}, y @ruser{Interfaces para programadores}. @@ -1362,7 +1596,7 @@ TODO Find a simple example @lilypond[quote,verbatim,ragged-right] padText = #(define-music-function (parser location padding) (number?) #{ - \once \override TextScript #'padding = #$padding + \once \override TextScript.padding = #padding #}) \relative c''' { @@ -1384,16 +1618,16 @@ Lo podemos usar para crear instrucciones nuevas: @lilypond[quote,verbatim,ragged-right] tempoPadded = #(define-music-function (parser location padding tempotext) - (number? string?) + (number? markup?) #{ - \once \override Score.MetronomeMark #'padding = $padding - \tempo \markup { \bold $tempotext } + \once \override Score.MetronomeMark.padding = #padding + \tempo \markup { \bold #tempotext } #}) \relative c'' { \tempo \markup { "Low tempo" } c4 d e f g1 - \tempoPadded #4.0 #"High tempo" + \tempoPadded #4.0 "High tempo" g4 f e d c1 } @end lilypond @@ -1404,7 +1638,7 @@ Incluso se le pueden pasar expresiones musicales: @lilypond[quote,verbatim,ragged-right] pattern = #(define-music-function (parser location x y) (ly:music? ly:music?) #{ - $x e8 a b $y b a e + #x e8 a b #y b a e #}) \relative c''{