@c -*- coding: utf-8; mode: texinfo; documentlanguage: fr -*- @ignore Translation of GIT committish: ea78e54687b8beb80958e55bcd3ddfbe90ab5967 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.17.6" @c Translators: Valentin Villenave, Jean-Charles Malahieude @c Translation checkers: Gilles Thibault @node Interfaces pour programmeurs @chapter Interfaces pour programmeurs @translationof Interfaces for programmers Scheme permet de réaliser des affinages très pointus. Si vous ne connaissez rien de Scheme, vous en aurez un aperçu au travers de notre @ref{Tutoriel Scheme}. @menu * Blocs de code LilyPond:: * Fonctions Scheme:: * Fonctions musicales:: * Fonctions événementielles:: * Fonctions pour markups:: * Contextes pour programmeurs:: * Fonctions de rappel:: * Code Scheme intégré:: * Retouches complexes:: @end menu @node Blocs de code LilyPond @section Blocs de code LilyPond @translationof Lilypond code blocks Les blocs de code LilyPond ressemblent à @example #@{ @var{du code LilyPond} #@} @end example Ils peuvent s'utiliser partout où vous pouvez écrire du code Scheme. Le lecteur Scheme est en fait quelque peu adapté pour accepter des blocs de code LilyPond ; il est capable de traiter des expressions Scheme intégrées débutant par @code{$} ou @code{#}. Le lecteur Scheme extrait le bloc de code LilyPond et déclenche un appel à l'analyseur grammatical de LilyPond (le @code{parser}) qui réalise en temps réel l'interprétation de ce bloc de code LilyPond. Toute expression Scheme imbriquée est exécutée dans l'environnement lexical du bloc de code LilyPond, de telle sorte que vous avez accès aux variables locales et aux paramètres de la fonction au moment même où le bloc de code LilyPond est écrit. Un bloc de code LilyPond peut contenir tout ce que vous pourriez mettre à droite de l'assignation. Par ailleurs, un bloc LilyPond vide correspond à une expression fantôme, et un bloc LilyPond de multiples événements musicaux sera transformé en une expression de musique séquentielle. @node Fonctions Scheme @section Fonctions Scheme @translationof Scheme functions @cindex Scheme, fonctions (syntaxe LilyPond) Les @emph{fonctions Scheme} sont des procédures Scheme chargées de créer des expressions Scheme à partir de code rédigé selon la syntaxe de LilyPond. Elles peuvent être appelées en de nombreux endroits, à l'aide d'un @code{#}, où spécifier une valeur en syntaxe Scheme est autorisé. Bien que Scheme dispose de fonctions en propre, nous nous intéresserons, au fil des paragraphes qui suivent, aux fonctions @emph{syntaxiques}, autrement dit des fonctions qui reçoivent des arguments libellés dans la syntaxe de LilyPond. @menu * Définition de fonctions Scheme:: * Utilisation de fonctions Scheme:: * Fonctions Scheme fantômes:: @end menu @node Définition de fonctions Scheme @subsection Définition de fonctions Scheme @translationof Scheme function definitions @funindex define-scheme-function D'une manière générale, une fonction Scheme se définit ainsi : @example fonction = #(define-scheme-function (parser location @var{arg1} @var{arg2} @dots{}) (@var{type1?} @var{type2?} @dots{}) @var{corps}) @end example @noindent où @multitable @columnfractions .33 .66 @item @code{parser} @tab doit être littéralement @code{parser}, de telle sorte que l'analyseur grammatical puisse accéder aux blocs de code LilyPond (@code{#@{}@dots{}@code{#@}}). @item @code{@var{argN}} @tab @var{n}ième argument @item @code{@var{typeN?}} @tab un @emph{type de prédicat} Scheme pour lequel @code{@var{argN}} devra retourner @code{#t}. Certains de ces prédicats, comme nous le verrons plus loin, bénéficient d'un traitement particulier de la part du @emph{parser}. De même existe une forme spécifique -- @code{(@emph{prédicat?} @emph{default})} -- qui permet de fournir des argument optionnels. En l'absence d'argument réel au moment de l'appel de la fonction, c'est la valeur par défaut qui lui sera substituée. Les valeurs par défaut sont évaluées dès l'apparition de la définition, y compris dans le cas de blocs de code LilyPond ; vous devrez donc, si ces valeurs par défaut ne peuvent être déterminées que plus tard, mentionner une valeur spéciale que vous reconnaîtrez facilement. Lorsque vous mentionnez un prédicat entre parenthèses sans toutefois fournir sa valeur par défaut, celle-ci sera considérée comme étant @code{#f}. Les valeurs par défaut d'un @code{prédicat?} ne sont vérifiées ni au moment de la définition, ni à l'exécution ; il est de votre ressort de gérer les valeurs que vous spécifiez. Une valeur par défaut constituée d'une expression musicale est recopiée dès la définition de @code{origin} vers le paramètre @code{location}. @item @code{@var{corps}} @tab une séquence de formules Scheme évaluées dans l'ordre, la dernière servant de valeur de retour de la fonction. Il peut contenir des blocs de code LilyPond, enchâssés dans des accolades et @emph{hashes} -- @w{@code{#@{@dots{}#@}}}  -- comme indiqué à la rubrique @ref{Blocs de code LilyPond}. Au sein d'un bloc de code LilyPond, un @code{#} permet de référencer des arguments de la fonction -- tel @samp{#arg1} -- ou d'ouvrir une expression Scheme contenant les arguments de la fonction -- par exemple @w{@samp{#(cons arg1 arg2)}}. Dans le cas où une expression Scheme introduite par @code{#} ne vous permet pas de parvenir à vos fins, vous pourriez devoir revenir à une expression Scheme @qq{immédiate} à l'aide d'un @code{$}, comme @samp{$music}. Lorsque votre fonction retourne une expression musicale, lui est attribuée la valeur @code{origin}. @end multitable @noindent Certains types de prédicat font l'objet d'un traitement spécial de la part de l'analyseur grammatical, dans la mesure où il n'a aucun autre moyen de reconnaître efficacement les arguments. Il s'agit, à l'heure actuelle, de @code{ly:pitch?} et @code{ly:duration?}. Pour tous les autres prédicats, la recevabilité des arguments est déterminée par un appel effectif au prédicat après que LilyPond les a déjà converti en expression Scheme. Par voie de conséquence, l'argument peut tout à fait se libeller en syntaxe Scheme -- introduite par un @code{#} ou en tant que résultat d'un appel à une fonction Scheme. Par ailleurs, LilyPond convertira en Scheme un certain nombre de constructions purement LilyPond avant même d'en avoir vérifié le prédicat. C'est notamment le cas de la musique, des @emph{postévénements}, des chaînes simples (avec ou sans guillemets), des nombres, des @emph{markups} et listes de @emph{markups}, ainsi que des blocs @emph{score}, @emph{book}, @emph{bookpart}, ou qui définissent un contexte ou un format de sortie. Il existe certaines formes d'expression, comme la plupart du temps où la musique n'est pas bornée par des accolades, où LilyPond doit lire au-delà de cette même expression afin d'en déterminer la fin. Si une telle expression devait, après évaluation du prédicat, faire l'objet d'un argument optionnel, LilyPond n'aurait aucun moyen, à partir du moment où il aura décidé que l'expression ne correspond pas au paramètre, de @qq{revenir en arrière}. C'est la raison pour laquelle certaines formes musicales devraient être bornées par des accolades pour que LilyPond puisse les reconnaître efficacement. Il existe d'autres situations pour lesquelles LilyPond lèvera toute ambiguïté grâce aux fonctions de prédicat : un @samp{-3} est-il un @emph{postévénement} de type doigté ou un nombre négatif@tie{}? Un @code{"a" 4} en mode paroles est-il une chaîne suivie d'un nombre ou bien un événement syllabe de durée @code{4} ? LilyPond répondra à ces questions après consultation du prédicat. Pour toutes ces raisons, nous vous enjoignons à éviter d'utiliser des prédicats permissifs tel que @code{scheme?}, dès que vous voulez les utiliser dans un but particulier plutôt que dans une fonction de portée générale. Les différents types des prédicat propres à LilyPond sont recensés à l'annexe @ruser{Types de prédicats prédéfinis}. @seealso Manuel de notation : @ruser{Types de prédicats prédéfinis}. Fichiers d'initialisation : @file{lily/music-scheme.cc}, @file{scm/c++.scm}, @file{scm/lily.scm}. @node Utilisation de fonctions Scheme @subsection Utilisation de fonctions Scheme @translationof Scheme function usage Vous pouvez appeler une fonction Scheme pratiquement partout où une expression Scheme derrière un @code{#} peut prendre place. Vous appelez une fonction Scheme en faisant précéder son nom d'un @code{\}, et en le faisant suivre de ses arguments. Lorsqu'un prédicat d'argument optionnel ne correspond pas à un argument, LilyPond l'ignore ainsi que tous les arguments optionnels qui suivent, les remplaçant par leur valeur par défaut, et @qq{sauvegarde} en tant que prochain argument obligatoire l'argument qui ne correspondait pas. Dans la mesure où l'argument sauvegardé doit servir, les argument optionnels ne sont en fait pas considérés comme optionnels, sauf à être suivis d'un argument obligatoire. Une exception cependant à cette règle : le fait de donner un @code{\default} en tant qu'argument optionnel aura pour résultat que cet argument et tous les autres arguments optionnels qui suivent seront ignorés et remplacés par leur valeur par défaut. Il en va de même lorsqu'aucun argument obligatoire ne suit, du fait que @code{\default} ne requiert pas de sauvegarde. C'est d'ailleurs ainsi que fonctionnent les commandes @code{mark} et @code{key}, qui retrouvent leur comportement par défaut lorsque vous les faites suivre d'un @code{\default}. En plus de là où une expression Scheme est requise, il y a quelques endroits où des expressions @code{#} sont acceptées et évaluées uniquement pour leurs effets annexes. Il s'agit, dans la plupart des cas, d'endroits où une affectation serait tout à fait envisageable. Dans la mesure où il n'est pas bon de renvoyer une valeur qui pourrait être mal interprétée dans certains contextes, nous vous enjoignons à utiliser des fonctions Scheme normales uniquement dans les cas où vous renvoyez toujours une valeur utile, et une fonction fantôme -- voir @ref{Fonctions Scheme fantômes} -- dans le cas contraire. @node Fonctions Scheme fantômes @subsection Fonctions Scheme fantômes @translationof Void scheme functions @cindex @emph{void}, fonction @cindex fantôme, fonction @funindex define-void-function @funindex \void Il arrive qu'une procédure soit exécutée pour réaliser une action, non pour renvoyer une valeur. Certains langages de programmation, tels le C et Scheme, utilisent des fonctions dans les deux cas et se débarrassent tout bonnement de la valeur renvoyée ; en règle générale, il suffit que l'expression fasse office de déclaration, et d'ignorer le résultat. C'est futé, mais pas sans risque d'erreur : la plupart des compilateurs C actuels déclenchent un avertissement si l'on se débarrasse de certaines expressions non @emph{void}. Pour de nombreuses fonctions réalisant une action, les standards Scheme déclarent que la valeur de retour est indéfinie. L'interpréteur Guile qu'utilise le Scheme de LilyPond dispose d'une valeur unique @code{*unspecified*} qu'il retourne alors, en règle générale -- notamment lorsqu'on utilise @code{set!} directement sur une variable -- mais malheureusement pas toujours. Une fonction LilyPond définie à l'aide de la clause @code{define-void-function} vous apporte l'assurance que c'est cette valeur spéciale -- la seule valeur qui satisfasse au prédicat @code{void?} -- qui sera retournée. @example noPointAndClick = #(define-void-function (parser location) () (ly:set-option 'point-and-click #f)) ... \noPointAndClick % desactive le "pointer-cliquer" @end example L'utilisation d'un préfixe @code{\void} permet ainsi d'évaluer une expression pour ses effets annexes sans interprétation d'une quelconque valeur de retour : @example \void #(hashq-set! une-table une-clé une-valeur) @end example Vous serez alors assuré que LilyPond ne tentera pas d'affecter un sens à la valeur de retour, à quelque endroit qu'elle ressorte. Ceci est aussi opérationnel dans le cadre de fonctions musicales telles que @code{\displayMusic}. @node Fonctions musicales @section Fonctions musicales @translationof Music functions @cindex musicale, fonction Les @emph{fonctions musicales} sont des procédures Scheme capables de créer automatiquement des expressions musicales ; elles permettent de grandement simplifier un fichier source. @menu * Définition de fonctions musicales:: * Utilisation de fonctions musicales:: * Fonctions de substitution simple:: * Fonctions de substitution intermédiaires:: * De l'usage des mathématiques dans les fonctions:: * Fonctions dépourvues d'argument:: * Fonctions musicales fantômes:: @end menu @node Définition de fonctions musicales @subsection Définition de fonctions musicales @translationof Music function definitions @cindex fonction musicale, définition @funindex define-music-function Une fonction musicale se définit ainsi : @example fonction = #(define-music-function (parser location @var{arg1} @var{arg2} @dots{}) (@var{type1?} @var{type2?} @dots{}) @var{corps}) @end example @noindent de manière similaire aux @ref{Définition de fonctions Scheme, fonctions Scheme}. La plupart du temps, le @code{corps} sera constitué d'un @ref{Blocs de code LilyPond, bloc de code Lilypond}. Les différents types des prédicat sont recensés à l'annexe @ruser{Types de prédicats prédéfinis}. @seealso Manuel de notation : @ruser{Types de prédicats prédéfinis}. Fichiers d'initialisation : @file{lily/music-scheme.cc}, @file{scm/c++.scm}, @file{scm/lily.scm}. @node Utilisation de fonctions musicales @subsection Utilisation de fonctions musicales @translationof Music function usage Une fonction musicale peut prendre place en différents endroits. Certaines restrictions s'appliqueront selon l'endroit où elle est utilisée, de telle sorte que l'analyse syntaxique soit sans ambiguïté. Le résultat renvoyé par une fonction musicale doit être compatible avec le contexte dans lequel elle est appelée. @itemize @item Dans une expression musicale de haut niveau@tie{}: aucune restriction. @item En tant que post-événement, explicitement introduit par un indicateur de positionnement -- à savoir @code{-}, @code{^}, ou@tie{}@code{_}. Notez bien que le renvoi d'un post-événement est valide lorsque la fonction musicale est appelée comme de la musique normale ; ceci amène à un résultat ressemblant à @example s 1*0-\fonction @end example Dans ce cas particulier, vous ne pouvez utiliser une expression musicale @emph{ouverte} en tant que dernier argument -- argument qui se composerait d'une expression musicale susceptible d'accepter des post-événements additionnels. @item En tant que partie d'un accord. L'expression musicale renvoyée doit être du type @code{rhythmic-event}, et plus particulièrement un @code{NoteEvent}. @end itemize @noindent Les règles spécifiques en matière d'arguments de fin rendent possible l'écriture de fonctions polymorphes telles que @code{\tweak}, qui peuvent s'appliquer à différentes constructions. @node Fonctions de substitution simple @subsection Fonctions de substitution simple @translationof Simple substitution functions Une fonction de substitution simple renvoie une expression musicale écrite au format LilyPond et contient des arguments au format de l'expression résultante. Vous en trouverez une description détaillée à la rubrique @ruser{Exemples de fonction de substitution}. @node Fonctions de substitution intermédiaires @subsection Fonctions de substitution intermédiaires @translationof Intermediate substitution functions Une fonction de substitution intermédiaire est une fonction dont l'expression musicale résultante mélangera du code Scheme au code LilyPond. Certaines commandes @code{\override} nécessitent un argument supplémentaire constitué d'une paire de nombres, appelée @emph{cons cell} en Scheme -- que l'on pourrait traduire par @qq{construction de cellule}. Cette paire peut se mentionner directement dans la fonction musicale à l'aide d'une variable @code{pair?} : @example manualBeam = #(define-music-function (parser location beg-end) (pair?) #@{ \once \override Beam #'positions = #beg-end #@}) \relative c' @{ \manualBeam #'(3 . 6) c8 d e f @} @end example Autre manière de procéder, les nombres formant la paire sont transmis comme arguments séparés ; le code Scheme chargé de créer la paire pourra alors être inclus dans l'expression musicale : @lilypond[quote,verbatim,ragged-right] manualBeam = #(define-music-function (parser location beg end) (number? number?) #{ \once \override Beam #'positions = #(cons beg end) #}) \relative c' { \manualBeam #3 #6 c8 d e f } @end lilypond @node De l'usage des mathématiques dans les fonctions @subsection De l'usage des mathématiques dans les fonctions @translationof Mathematics in functions Une fonction musicale peut requérir, en plus d'une simple substitution, une part de programmation en Scheme. @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 } \relative c' { c2 \AltOn #0.5 c4 c \AltOn #1.5 c c \AltOff c2 } @end lilypond @noindent Cette fonction pourrait tout à fait être réécrite de telle sorte qu'elle s'applique à une expression musicale : @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 #}) \relative c' { c2 \withAlt #0.5 { c4 c } \withAlt #1.5 { c c } c2 } @end lilypond @node Fonctions dépourvues d'argument @subsection Fonctions dépourvues d'argument @translationof Functions without arguments Dans la plupart des cas, une fonction dépourvue d'argument devrait être créée à l'aide d'une variable : @example dolce = \markup@{ \italic \bold dolce @} @end example Il peut, dans certains cas particuliers, s'avérer utile de créer une fonction sans argument comme ici, @example displayBarNum = #(define-music-function (parser location) () (if (eq? #t (ly:get-option 'display-bar-numbers)) #@{ \once \override Score.BarNumber #'break-visibility = ##f #@} #@{#@})) @end example @noindent de manière à pouvoir afficher les numéros de mesure grâce à un appel à cette fonction. En pareil cas, vous devrez invoquer @command{lilypond} en respectant la syntaxe @example lilypond -d display-bar-numbers MONFICHIER.ly @end example @node Fonctions musicales fantômes @subsection Fonctions musicales fantômes @translationof Void music functions Une fonction musicale doit renvoyer une expression musicale. Toutefois, une fonction musicale peut n'être exécutée que dans le but d'en retenir les effets annexes ; vous devrez alors utiliser une procédure @code{define-void-function}. Il peut cependant arriver que vous ayez besoin d'une fonction qui, selon le cas, produise ou non (comme dans l'exemple de la rubrique précédente) une expression musicale. L'utilisation d'un @code{#@{ #@}} vous permettra de renvoyer une expression musicale @code{void}. @node Fonctions événementielles @section Fonctions événementielles @translationof Event functions @funindex define-event-function @cindex événementielle, fonction L'utilisation d'une fonction musicale pour placer un événement requiert l'insertion d'un indicateur de position, ce qui peut ne pas correspondre à la syntaxe de la construction à remplacer. C'est par exemple le cas lorsque vous voulez écrire une commande de nuance, instruction qui ne comporte habituellement pas d'indicateur de positionnement, comme dans @code{c'\pp}. Voici de quoi vous permettre de mentionner n'importe quelle nuance : @lilypond[quote,verbatim,ragged-right] dyn=#(define-event-function (parser location arg) (markup?) (make-dynamic-script arg)) \relative c' { c\dyn pfsss } @end lilypond Vous pourriez obtenir le même résultat avec une fonction musicale, à ceci près que chaque appel à la fonction devra être précédé d'un indicateur de positionnement, comme @code{c-\dyn pfsss}. @node Fonctions pour markups @section Fonctions pour @emph{markups} @translationof Markup functions Les @emph{markups} sont implémentés au travers de fonctions Scheme spécifiques qui produisent des objets @code{Stencil} comprenant un certain nombre d'arguments. @menu * Construction d'un markup en Scheme:: * Fonctionnement interne des markups:: * Définition d'une nouvelle commande de markup:: * Définition d'une nouvelle commande de liste de markups:: @end menu @node Construction d'un markup en Scheme @subsection Construction d'un @emph{markup} en Scheme @translationof Markup construction in Scheme @cindex définition d'une commande markup La macro @code{markup} construit en Scheme des expressions @emph{markup} tout en disposant d'une syntaxe proche de celle de LilyPond. Par exemple, @example (markup #:column (#:line (#:bold #:italic "hello" #:raise 0.4 "world") #:larger #:line ("foo" "bar" "baz"))) @end example @noindent est équivalent à @example #@{ \markup \column @{ \line @{ \bold \italic "hello" \raise #0.4 "world" @} \larger \line @{ foo bar baz @} @} #@} @end example @noindent Vous pouvez constater les principales règles de traduction entre les syntaxes respectives de LilyPond et de Scheme en matière de @emph{markup}. Bien que le passage en syntaxe LilyPond grâce à @code{#@{ @dots{} #@}} apporte de la souplesse, nous allons voir comment utiliser la macro @code{markup} en Scheme exclusivement. @quotation @multitable @columnfractions .3 .3 @item @b{LilyPond} @tab @b{Scheme} @item @code{\markup markup1} @tab @code{(markup markup1)} @item @code{\markup @{ markup1 markup2 ... @}} @tab @code{(markup markup1 markup2 ... )} @item @code{\commande-markup} @tab @code{#:commande-markup} @item @code{\variable} @tab @code{variable} @item @code{\center-column @{ ... @}} @tab @code{#:center-column ( ... )} @item @code{chaîne} @tab @code{"chaîne"} @item @code{#argument-scheme} @tab @code{argument-scheme} @end multitable @end quotation L'intégralité du langage Scheme est accessible à l'intérieur même de la macro @code{markup}. Vous pouvez ainsi appeler des fonctions à partir de @code{markup} pour manipuler des chaînes de caractères, ce qui est particulièrement pratique lorsque vous créez votre propre commande de @emph{markup} -- voir @ref{Définition d'une nouvelle commande de markup}. @knownissues L'argument @var{markup-list} des commandes @code{#:line}, @code{#:center} ou @code{#:column} ne saurait être une variable ni le résultat de l'appel à une fonction. @lisp (markup #:line (fonction-qui-retourne-des-markups)) @end lisp @noindent n'est pas valide. Il vaut mieux, en pareil cas, utiliser les fonctions @code{make-line-markup}, @code{make-center-markup} ou @code{make-column-markup} : @lisp (markup (make-line-markup (fonction-qui-retourne-des-markups))) @end lisp @node Fonctionnement interne des markups @subsection Fonctionnement interne des @emph{markups} @translationof How markups work internally Dans un @emph{markup} tel que @example \raise #0.5 "text example" @end example @noindent @code{\raise} représente en fait la fonction @code{raise-markup}. L'expression @emph{markup} est enregistrée sous la forme @example (list raise-markup 0.5 (list simple-markup "text example")) @end example Lorsque ce @emph{markup} est converti en objets imprimables (stencils), la fonction @code{raise-markup} est appelée ainsi : @example (apply raise-markup @var{\layout objet} @var{liste des alists de propriété} 0.5 @var{le @emph{markup} "text example"}) @end example La fonction @code{raise-markup} commence par créer le stencil pour la chaîne @code{text example}, puis remonte ce stencil d'un demi espace de portée. Il s'agit là d'un exemple relativement simple, et nous en aborderons de plus complexes au fil des paragraphes suivants ; d'autres exemples se trouvent directement dans le fichier @file{scm/define-markup-commands.scm}. @node Définition d'une nouvelle commande de markup @subsection Définition d'une nouvelle commande de @emph{markup} @translationof New markup command definition Nous allons étudier dans ce qui suit la manière de définir une nouvelle commande de @emph{markup}. @menu * Syntaxe d'une commande markup:: * Attribution de propriétés:: * Exemple commenté:: * Adaptation d'une commande incorporée:: @end menu @node Syntaxe d'une commande markup @unnumberedsubsubsec Syntaxe d'une commande @emph{markup} @translationof Markup command definition syntax Une commande de @emph{markup} personnalisée se définit à l'aide de la macro Scheme @code{define-markup-command}, placée en tête de fichier. @lisp (define-markup-command (@var{nom-commande} @var{layout} @var{props} @var{arg1} @var{arg2} ...) (@var{arg1-type?} @var{arg2-type?} ...) [ #:properties ((@var{propriété1} @var{valeur-par-défaut1}) ...) ] ..corps de la commande..) @end lisp Quelques commentaires sur les arguments : @table @code @item @var{nom-commande} le nom que vous attribuez à votre commande de @emph{markup}. @item layout la définition du @qq{layout} -- son formatage. @item props une liste de listes associatives, comprenant toutes les propriétés actives. @item @var{argi} le @var{ième} argument de la commande. @item @var{argi-type?} un type de prédicat pour le @var{ième} argument. @end table Si la commande utilise des propriétés à partir des arguments @code{props}, le mot-clé @code{#:properties} permet de spécifier ces différentes propriétés ainsi que leur valeur par défaut. Les arguments se distinguent selon leur type : @itemize @item un @emph{markup}, correspondant au type de prédicat @code{markup?}@tie{}; @item une liste de @emph{markups}, correspondant au type de prédicat @code{markup-list?}@tie{}; @item tout autre objet Scheme, correspondant au types de prédicat tels que @code{list?}, @code{number?}, @code{boolean?}, etc. @end itemize Il n'existe aucune restriction quant à l'ordre des arguments fournis à la suite des arguments @code{layout} et @code{props}. Néanmoins, les fonctions @emph{markup} qui ont en dernier argument un @emph{markup} ont ceci de particulier qu'elles peuvent s'appliquer à des listes de @emph{markups} ; ceci résultera en une liste de @emph{markups} où tous les éléments de la liste originelle se verront appliquer cette fonction @emph{markup} avec ses arguments de tête. La réplication des arguments de tête dans le but d'appliquer une fonction @emph{markup} à une liste de markups est économique, principalement lorsqu'il s'agit d'arguments Scheme. Vous éviterez ainsi d'éventuelles pertes de performance en utilisant des arguments Scheme en tant qu'arguments principaux d'une fonction @emph{markup} dont le dernier argument est un @emph{markup}. @node Attribution de propriétés @unnumberedsubsubsec Attribution de propriétés @translationof On properties Les arguments @code{layout} et @code{props} d'une commande de @emph{markup} fournissent un contexte à l'interprétation du @emph{markup} : taille de fonte, longueur de ligne etc. L'argument @code{layout} permet d'accéder aux propriétés définies dans les blocs @code{\paper}, grâce à la fonction @code{ly:output-def-lookup}. Par exemple, la longueur de ligne, identique à celle de la partition, est lue au travers de @example (ly:output-def-lookup layout 'line-width) @end example L'argument @code{props} rend certaines propriétés accessibles aux commandes de @emph{markup}. Il en va ainsi lors de l'interprétation d'un @emph{markup} de titre d'ouvrage : toutes les variables définies dans le bloc @code{\header} sont automatiquement ajoutées aux @code{props}, de telle sorte que le @emph{markup} de titrage de l'ouvrage pourra accéder aux différents champs titre, compositeur etc. Ceci permet aussi de configurer le comportement d'une commande de @emph{markup} : la taille des fontes, par exemple, est lue à partir de @code{props} plutôt que grâce à un argument @code{font-size}. La fonction appelant une commande de @emph{markup} peut altérer la valeur de la propriété taille des fontes et donc en modifier le comportement. L'utilisation du mot-clé @code{#:properties}, attaché à @code{define-markup-command}, permet de spécifier les propriétés devant être lues parmi les arguments @code{props}. L'exemple proposé à la rubrique suivante illustre comment, au sein d'une commande de @emph{markup}, accéder aux différentes propriétés et les modifier. @node Exemple commenté @unnumberedsubsubsec Exemple commenté @translationof A complete example Nous allons, dans cet exemple, nous attacher à encadrer du texte avec un double liseré. Commençons par construire quelque chose d'approximatif à l'aide d'un simple @emph{markup}. La lecture de @ruser{Commandes pour markup} nous indique la commande @code{\box}, qui semble ici appropriée. @lilypond[quote,verbatim,ragged-right] \markup \box \box HELLO @end lilypond Dans un souci d'esthétique, nous aimerions que le texte et les encadrements ne soient pas autant accolés. Selon la documentation de @code{\box}, cette commande utilise la propriété @code{box-padding}, fixée par défaut à 0,2. Cette même documentation nous indique aussi comment la modifier : @lilypond[quote,verbatim,ragged-right] \markup \box \override #'(box-padding . 0.6) \box A @end lilypond L'espacement des deux liserés est cependant toujours trop réduit ; modifions le à son tour : @lilypond[quote,verbatim,ragged-right] \markup \override #'(box-padding . 0.4) \box \override #'(box-padding . 0.6) \box A @end lilypond Vous conviendrez que recopier une telle définition de @emph{markup} deviendra vite fastidieux. C'est pourquoi nous écrivons la commande de @emph{markup} @code{double-box} qui prendra un seul argument -- le texte. Cette commande se chargera de dessiner les encadrements, en tenant compte des espacements. @lisp #(define-markup-command (double-box layout props text) (markup?) "Dessine un double encadrement autour du texte." (interpret-markup layout props #@{\markup \override #'(box-padding . 0.4) \box \override #'(box-padding . 0.6) \box @{ #text @}#@})) @end lisp ou bien son équivalent @lisp #(define-markup-command (double-box layout props text) (markup?) "Dessine un double encadrement autour du texte." (interpret-markup layout props (markup #:override '(box-padding . 0.4) #:box #:override '(box-padding . 0.6) #:box text))) @end lisp @code{text} est le nom de l'argument de notre commande, et @code{markup?} son type -- l'argument sera identifié comme étant un @emph{markup}. La fonction @code{interpret-markup}, utilisée dans la plupart des commandes de @emph{markup}, construira un stencil à partir de @code{layout}, @code{props} et un @emph{markup}. Dans la seconde variante, ce @emph{markup} sera construit à l'aide de la macro Scheme @code{markup} -- voir @ref{Construction d'un markup en Scheme}. La transformation d'une expression @code{\markup} en expression Scheme est des plus triviales. Notre commande personnalisée s'utilise ainsi : @example \markup \double-box A @end example Il serait intéressant de rendre cette commande @code{double-box} plus souple@tie{}: les valeurs de @code{box-padding} sont figées et ne peuvent être modifiées à l'envie. Pareillement, il serait bien de distinguer l'espacement entre les encadrements de l'espacement entre le texte et ses encadrements. Nous allons donc introduire une propriété supplémentaire, que nous appellerons @code{inter-box-padding}, chargée de gérer l'espacement des encadrements ; @code{box-padding} ne servira alors que pour l'espacement intérieur. Voici le code adapté à ces évolutions : @lisp #(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Dessine un double encadrement autour du texte." (interpret-markup layout props #@{\markup \override #`(box-padding . ,inter-box-padding) \box \override #`(box-padding . ,box-padding) \box @{ #text @} #@})) @end lisp Ainsi que son équivalent à partir de la macro @emph{markup} : @lisp #(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Dessine un double encadrement autour du texte." (interpret-markup layout props (markup #:override `(box-padding . ,inter-box-padding) #:box #:override `(box-padding . ,box-padding) #:box text))) @end lisp C'est ici le mot-clé @code{#:properties} qui permet de lire les propriétés @code{inter-box-padding} et @code{box-padding} à partir de l'argumenet @code{props}@tie{}; on leur a d'ailleurs fourni des valeurs par défaut au cas où elles ne seraient pas définies. Ces valeurs permettront alors d'adapter les propriétés de @code{box-padding} utilisées par les deux commandes @code{\box}. Vous aurez remarqué, dans l'argument @code{\override}, la présence de l'apostrophe inversée (@code{`}) et de la virgule ; elles vous permettent d'insérer une valeur variable au sein d'une expression littérale. Notre commande est maintenant prête à servir dans un @emph{markup}, et les encadrements sont repositionnables. @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 Adaptation d'une commande incorporée @unnumberedsubsubsec Adaptation d'une commande incorporée @translationof Adapting builtin commands Le meilleur moyen de construire ses propres commandes de @emph{markup} consiste à prendre exemple sur les commandes déjà incorporées. La plupart des commandes de @emph{markup} fournies avec LilyPond sont répertoriées dans le fichier @file{scm/define-markup-commands.scm}. Nous pourrions, par exemple, envisager d'adapter la commande @code{\draw-line} pour dessiner plutôt une ligne double. Voici comment est définie la commande @code{\draw-line}, expurgée de sa documentation : @lisp (define-markup-command (draw-line layout props dest) (number-pair?) #:category graphic #:properties ((thickness 1)) "..documentation.." (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 Avant de définir notre propre commande basée sur l'une de celles fournies par LilyPond, commençons par en recopier la définition, puis attribuons lui un autre nom. Le mot-clé @code{#:category} peut être supprimé sans risque ; il ne sert que lors de la génération de la documentation et n'est d'aucune utilité pour une commande personnalisée. @lisp (define-markup-command (draw-double-line layout props dest) (number-pair?) #:properties ((thickness 1)) "..documentation.." (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 Nous ajoutons ensuite une propriété pour gérer l'écart entre les deux lignes, que nous appelons @code{line-gap}, et lui attribuons une valeur par défaut de 6 dixièmes : @lisp (define-markup-command (draw-double-line layout props dest) (number-pair?) #:properties ((thickness 1) (line-gap 0.6)) "..documentation.." ... @end lisp Nous ajoutons enfin le code qui dessinera nos deux lignes. Deux appels à @code{make-line-stencil} permettrons de dessiner les lignes dont nous regrouperons les stencils à l'aide de @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 Définition d'une nouvelle commande de liste de markups @subsection Définition d'une nouvelle commande de liste de @emph{markups} @translationof New markup list command definition Une commande traitant une liste de @emph{markups} se définit à l'aide de la macro Scheme @code{define-markup-list-command}, de manière analogue à la macro @code{define-markup-command} abordée à la rubrique @ref{Définition d'une nouvelle commande de markup} à ceci près que cette dernière renvoie un seul stencil, non une liste de stencils. Dans l'exemple suivant, nous définissons @code{\paragraph}, une commande de liste de @emph{markups}, qui renverra une liste de lignes justifiées dont la première sera indentée. La largeur de l'alinéa sera récupérée par l'argument @code{props}. @example #(define-markup-list-command (paragraph layout props args) (markup-list?) #:properties ((par-indent 2)) (interpret-markup-list layout props #@{\markuplist \justified-lines @{ \hspace #par-indent #args @} #@})) @end example La version purement Scheme est un peu plus complexe : @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 En dehors des habituels arguments @code{layout} et @code{props}, la commande de liste de @emph{markups} @code{paragraph} prend en argument une liste de @emph{markups} appelé @code{args}. Le prédicat des listes de @emph{markups} est @code{markup-list?}. Pour commencer, la fonction récupère la taille de l'alinéa, propriété ici dénommée @code{par-indent}, à partir de la liste de propriétés @code{props}. En cas d'absence, la valeur par défaut sera de @code{2}. Ensuite est créée une liste de lignes justifiées grâce à la commande prédéfinie @code{\justified-lines}, liée à la fonction @code{make-justified-lines-markup-list}. Un espace horizontal est ajouté en tête, grâce à @code{\hspace} ou à la fonction @code{make-hspace-markup}. Enfin, la liste de @emph{markups} est interprétée par la fonction @code{interpret-markup-list}. Voici comment utiliser cette nouvelle commande de liste de @emph{markups}@tie{}: @example \markuplist @{ \paragraph @{ The art of music typography is called \italic @{(plate) engraving.@} The term derives from the traditional process of music printing. Just a few decades ago, sheet music was made by cutting and stamping the music into a zinc or pewter plate in mirror image. @} \override-lines #'(par-indent . 4) \paragraph @{ The plate would be inked, the depressions caused by the cutting and stamping would hold ink. An image was formed by pressing paper to the plate. The stamping and cutting was completely done by hand. @} @} @end example @node Contextes pour programmeurs @section Contextes pour programmeurs @translationof Contexts for programmers @menu * Évaluation d'un contexte:: * Application d'une fonction à tous les objets de mise en forme:: @end menu @node Évaluation d'un contexte @subsection Évaluation d'un contexte @translationof Context evaluation @cindex appel de code durant l'interprétation @funindex \applyContext Un contexte peut être modifié, au moment même de son interprétation, par du code Scheme. La syntaxe consacrée en pareil cas est @example \applyContext @var{fonction} @end example @code{@var{fonction}} est constitué d'une fonction Scheme comportant un unique argument : le contexte au sein duquel la commande @code{\applyContext} est appelée. Les lignes de code qui suivent auront pour effet d'afficher à l'écran, en cours de compilation, le numéro de mesure en cours. @example \applyContext #(lambda (x) (format #t "\nNous en sommes à la mesure ~a.\n" (ly:context-property x 'currentBarNumber))) @end example @node Application d'une fonction à tous les objets de mise en forme @subsection Application d'une fonction à tous les objets de mise en forme @translationof Running a function on all layout objects @cindex appel de code sur des objets de mise en forme @funindex \applyOutput La manière la plus souple d'affiner un objet consiste à utiliser la commande @code{\applyOutput}. Celle-ci va insérer un événement (@rinternals{ApplyOutputEvent}) dans le contexte spécifié. Elle répond à la syntaxe @example \applyOutput @var{contexte} @var{procédure} @end example @noindent où @code{@var{procédure}} est une fonction Scheme à trois arguments. Lors de l'interprétation de cette commande, la fonction @code{@var{procédure}} est appelée pout tout objet de rendu appartenant au contexte @code{@var{contexte}} à cet instant précis, avec les arguments suivants : @itemize @item l'objet de rendu en lui-même, @item le contexte au sein duquel cet objet est créé, @item et le contexte dans lequel @code{\applyOutput} est effectué. @end itemize De plus, ce qui est à l'origine de l'objet de rendu -- l'expression musicale ou l'objet qui l'a générée -- se retrouve en tant que propriété d'objet @code{cause}. Il s'agit, pour une tête de note, d'un événement @rinternals{NoteHead}, et d'un objet @rinternals{Stem} pour une hampe. Voici une fonction utilisable avec la commande @code{\applyOutput} : elle @qq{blanchit} la tête des notes se trouvant sur la ligne médiane ou bien directement à son contact. @lilypond[quote,verbatim,ragged-right] #(define (blanker grob grob-origin context) (if (and (memq 'note-head-interface (ly:grob-interfaces grob)) (< (abs (ly:grob-property grob 'staff-position)) 2)) (set! (ly:grob-property grob 'transparent) #t))) \relative c' { a'4 e8 <<\applyOutput #'Voice #blanker a c d>> b2 } @end lilypond @node Fonctions de rappel @section Fonctions de rappel @translationof Callback functions Certaines propriétés, entre autres @code{thickness} ou @code{direction}, peuvent voir leur valeur figée à l'aide d'un @code{\override} comme ici@tie{}: @example \override Stem #'thickness = #2.0 @end example Une procédure Scheme peut aussi se charger de modifier des propriétés@tie{}: @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 Dans ce cas, la procédure est exécutée dès que la valeur de la propriété est nécessaire au processus de mise en forme. La majeure partie du procédé typographique consiste en la réalisation de tels rappels (@emph{callbacks} en anglais). Entre autres propriétés utilisant particulièrement des rappels, nous mentionnerons @table @code @item stencil Routine d'impression, construisant le dessin du symbole @item X-offset Routine effectuant le positionnement horizontal @item X-extent Routine calculant la largeur d'un objet @end table La procédure prend un unique argument, en l'occurrence l'objet graphique (le @emph{grob}). Dans le cas où la routine doit faire appel à plusieurs arguments, le @emph{grob} en cours peut s'insérer à l'aide d'un @emph{grob} enchâssé. Voici, à titre d'illustration, un réglage pour @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 Ici, aussi bien @code{ly:self-alignment-interface::x-aligned-on-self} que @code{ly:self-alignment-interface::centered-on-x-parent} font appel à l'argument @emph{grob}. La fonction @code{+} a pour effet d'additionner les résultats. L'ensemble est inclus dans un @code{ly:make-simple-closure} de telle sorte que l'addition s'exécute de façon correcte Dans les faits, l'utilisation d'une unique procédure en tant que valeur d'une propriété revient à écrire @example (ly:make-simple-closure (ly:make-simple-closure (list @var{proc}))) @end example @noindent Le @code{ly:make-simple-closure} intérieur fournit le @emph{grob} en argument à la procédure @var{proc}, l'extérieur s'assure que le résultat de la fonction sera bien renvoyé, non pas l'objet @code{simple-closure}. Au sein d'un @emph{callback}, le meilleur moyen d'évaluer un @emph{markup} consiste à utiliser la fonction @code{grob-interpret-markup}, comme ici : @example my-callback = #(lambda (grob) (grob-interpret-markup grob (markup "foo"))) @end example @node Code Scheme intégré @section Code Scheme intégré @translationof Inline Scheme code À REVOIR : l'exemple de cette rubrique n'est pas des plus judicieux puisque @example F = -\tweak #'font-size #-3 -\flageolet @end example (notez le @samp{-} qui qualifie d'événement postérieur) fonctionne correctement dans ce cas d'espèce. En attendant un remaniement de cette section, faisons comme si nous l'ignorions. L'inconvénient principal de la commande @code{\tweak} est la rigidité de sa syntaxe. Par exemple, le code suivant produit une erreur. @example F = \tweak #'font-size #-3 -\flageolet \relative c'' @{ c4^\F c4_\F @} @end example @noindent En d'autres termes, @code{\tweak} ne se comporte pas comme une articulation : il ne peut notamment pas être accolé avec les symboles @samp{^} ou @samp{_}. C'est en se servant du langage Scheme que l'on peut résoudre ce problème. Dans cet exemple, on a recours aux méthodes décrites dans @ref{Ajout d'articulation à des notes (exemple)}, en particulier quant à l'emploi de @code{\displayMusic}. @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 Ici les propriétés @code{tweak} de l'objet flageolet nommé @samp{m} (créé au moyen de @code{make-music}) sont extraites par @code{ly:music-property}, une nouvelle valeur de la taille de fonte est ajoutée à la liste de ses propriétés (grâce à la fonction Scheme @code{acons}), et le résultat de cette opération est renvoyé par @code{set!}. Le dernier élément, dans ce bloc @code{let}, est la valeur finale de @samp{m} lui-même. @node Retouches complexes @section Retouches complexes @translationof Difficult tweaks Certains réglages sont plus délicats que d'autres. @itemize @item L'un d'entre eux est l'apparence des objets dits @qq{extenseurs} (@emph{spanner}), qui s'étendent horizontalement, tels que les liaisons. Si, en principe, un seul de ces objets est créé à la fois et peut donc être modifié de façon habituelle, lorsque ces objets doivent enjamber un changement de ligne, ils sont dupliqués au début du ou des systèmes suivants. Comme ces objets sont des clones de l'objet d'origine, ils en héritent toutes les propriétés, y compris les éventuelles commandes @code{\override}. En d'autres termes, une commande @code{\override} affecte toujours les deux extrémités d'un objet @emph{spanner}. Pour ne modifier que la partie précédant ou suivant le changement de ligne, il faut intervenir directement dans le processus de mise en page. La fonction de rappel @code{after-line-breaking} contient toute l'opération Scheme effectuée lorsque les sauts de lignes ont été déterminés, et que des objets graphiques ont été divisés sur des systèmes différents. Dans l'exemple suivant, on définit une nouvelle opération nommée @code{my-callback}. Cette opération @itemize @item détermine si l'objet a été divisé à l'occasion d'un changement de ligne @item dans l'affirmative, recherche les différents morceaux de l'objet @item vérifie si l'objet considéré est bien la deuxième moitié d'un objet divisé @item dans l'affirmative, applique un espacement supplémentaire (@code{extra-offset}). @end itemize On ajoute cette procédure à l'objet @rinternals{Tie} (liaison de tenue), de façon à ce que le deuxième morceau d'une liaison divisée soit rehaussé. @c KEEP LY @lilypond[quote,verbatim,ragged-right] #(define (my-callback grob) (let* ( ;; l'objet a-t-il été divisé ? (orig (ly:grob-original grob)) ;; si oui, rechercher les morceaux frères (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 Lorsque cette astuce va être appliquée, notre nouvelle fonction de rappel @code{after-line-breaking} devra également appeler celle d'origine (@code{after-line-breaking}), si elle existe. Ainsi, pour l'utiliser dans le cas d'un crescendo (objet @code{Hairpin}), il faudra également appeler @code{ly:spanner::kill-zero-spanned-time}. @item Pour des raisons d'ordre technique, certains objets ne peuvent être modifiés par @code{\override}. Parmi ceux-là, les objets @code{NonMusicalPaperColumn} et @code{PaperColumn}. La commande @code{\overrideProperty} sert à les modifier, de façon similaire à @code{\once \override} mais avec une syntaxe différente : @example \overrideProperty Score.NonMusicalPaperColumn % Nom de l'objet #'line-break-system-details % Nom de la propriété #'((next-padding . 20)) % Valeur @end example Notez toutefois que la commande @code{\override} peut tout de même être appliquée à @code{NonMusicalPaperColumn} et @code{PaperColumn} dans un bloc @code{\context}. @end itemize @node Interfaces LilyPond Scheme @chapter Interfaces LilyPond Scheme @translationof LilyPond Scheme interfaces Ce chapitre aborde les différents outils fournis par LilyPond à l'intention des programmeurs en Scheme désireux d'obtenir des informations à partir et autour des fluxs de musique. TODO -- figure out what goes in here and how to organize it