@c -*- coding: utf-8; mode: texinfo; documentlanguage: fr -*- @ignore Translation of GIT committish: 529eb23f3ddb597ae0738685d194cb88b4733054 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.19.24" @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:: * Retouches complexes:: @end menu @node Blocs de code LilyPond @section Blocs de code LilyPond @translationof LilyPond code blocks @cindex code, blocs LilyPond @cindex LilyPond, bloc de code @funindex #@{ ... #@} @funindex $ @funindex # L'utilisation de Scheme pour créer des expressions musicales peut s'avérer ardue, principalement à cause des imbrications et de la longueur du code Scheme qui en résulte. Dans le cas de tâches simples, on peut toutefois contourner une partie du problème en utilisant des blocs de code LilyPond, ce qui autorise la syntaxe habituelle de LilyPond au sein même de Scheme. Les blocs de code LilyPond ressemblent à @example #@{ @var{du code LilyPond} #@} @end example En voici un exemple basique : @lilypond[verbatim,quote] ritpp = #(define-event-function () () #{ ^"rit." \pp #} ) { c'4 e'4\ritpp g'2 } @end lilypond Les blocs de code LilyPond 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 @emph{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. Les variables définies dans d'autres modules Scheme, tels ceux contenant les blocs @code{\header} ou @code{\layout}, ne sont pas accessibles en tant que variables Scheme (préfixées par un @code{#}) mais en tant que variables LilyPond (préfixées par un @code{\}). Toute la musique générée au sein de ce bloc de code voit son @code{origine} établie à cet @emph{emplacement}. 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 (@var{arg1} @var{arg2}@dots{}) (@var{type1?} @var{type2?}@dots{}) @var{corps}) @end example @noindent où @multitable @columnfractions .33 .66 @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}. Il existe aussi 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 à 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} à l'emplacement courant du code. @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 « 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 La recevabilité des arguments est déterminée par un appel effectif au prédicat après que LilyPond les a déjà convertis 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 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 ? 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 par des interprétations successives du prédicat de l'argument, dans un ordre défini de sorte à minimiser les interprétations erronées et le besoin de lecture en avance. Un prédicat qui accepte par exemple aussi bien une expression musicale qu'une hauteur considèrera @code{c''} comme étant une hauteur plutôt qu'une expression musicale. Les durées ou @emph{postévénements} qui viennent juste après viendront modifier cette interprétation. C'est la raison pour laquelle il vaut mieux éviter des prédicats par trop permissifs tel que @code{Scheme?} lorsque l'application fait plutôt appel à des types d'argument plus spécifiques. Les différents types de prédicat propres à LilyPond sont recensés à l'annexe @ruser{Types de prédicats prédéfinis}. @morerefs 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 à partir de LilyPond 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 « 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. Pour des raisons de commodité, il est possible de faire appel à des fonctions Scheme directement en Scheme, courcircuitant ainsi l'analyseur de LilyPond. Leur nom s'utilise comme n'importe quel nom de fonction. Le contrôle de typologie des arguments et l'omission des arguments optionnels seront traîtés de la même manière que lorsque l'appel est fait à partir de LilyPond, la valeur Scheme @code{*unspecified*} ayant le rôle du mot réservé @code{\default} pour omettre explicitement les arguments optionnels. @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 () () (ly:set-option 'point-and-click #f)) @dots{} \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 (@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 @var{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}. @morerefs 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 » doit impérativement renvoyer une expression répondant au prédicat @code{ly:music?}. Ceci a pour conséquence d'autoriser l'appel à une fonction musicale en tant qu'argument de type @code{ly:music?} dans le cadre de l'appel à une autre fonction musicale. Certaines restrictions s'appliqueront selon le contexte où une fonction musicale est utilisée, de telle sorte que l'analyse syntaxique soit sans ambiguïté. @itemize @item Dans une expression musicale de haut niveau, aucun postévénement n'est toléré. @item Lorsqu'une fonction musicale -- contrairement à une fonction événementielle -- renvoie une expression de type postévénement, LilyPond requiert son introduction par un indicateur de positionnement -- à savoir @code{-}, @code{^} ou @code{_} -- de telle sorte que le postévénement produit par l'appel à cette fonction s'intègre correctement dans l'expression environnante. @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 Des fonctions « polymorphes » telles que @code{\tweak} peuvent s'appliquer aux postévénements, constituants d'accord et expressions de haut niveau. @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 « 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 (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 (beg end) (number? number?) #{ \once \override Beam.positions = #(cons beg end) #}) \relative c' { \manualBeam #3 #6 c8 d e f } @end lilypond @funindex \temporary @cindex temporaire, dérogation (override) @cindex dérogation temporaire (override) @cindex propriétés, retour à la valeur précédente L'entretien des propriétés peut se voir comme un empilement par propriété par objet par contexte. Les fonctions musicales peuvent nécessiter des dérogations pour une ou plusieurs propriétés pour la durée de la fonction, puis de revenir aux valeurs précédentes avant de quitter. Néanmoins, une dérogation normale va retirer de la pile -- ou dépiler -- et supprimer le sommet de la pile de la propriété avant d'y ajouter quoi que ce soit -- ou empiler -- ; la valeur précédente de la propriété est de fait perdue. Lorsque la valeur antérieure doit être préservée, l'instruction @code{\override} devra être préfixée d'un @code{\temporary}, comme ceci : @example \temporary \override @dots{} @end example L'utilisation d'un @code{\temporary} a pour effet d'effacer la propriété @code{pop-first} (@emph{commence par dépiler} normalement activée) de la dérogation ; la valeur antérieure ne sera alors pas supprimée de la pile de la propriété avant d'y empiler la nouvelle valeur. Lorsqu'un @code{\revert} viendra par la suite supprimer la valeur dérogatoire temporaire, réapparaitra la valeur antérieure. En d'autres termes, un @code{\revert} qui suit un @code{\temporary \override} pour la même propriété n'apporte rien. Ce principe est aussi valable pour un couple @code{\temporary} et @code{\undo} sur la même musique contenant des dérogations. Voici un exemple de fonction musicale utilisant cette fonctionnalité. La présence du @code{\temporary} permet de s'assurer qu'en sortant de la fonction, les propriétés @code{cross-staff} et @code{style} retrouveront les valeurs qu'elles avaient avant que ne soit appelée la fonction @code{crossStaff}. En l'absence de @code{\temporary}, ces propriétés auraient retrouvé leurs valeurs par défaut à la sortie de la fonction. @example crossStaff = #(define-music-function (notes) (ly:music?) (_i "Create cross-staff stems") #@{ \temporary \override Stem.cross-staff = #cross-staff-connect \temporary \override Flag.style = #'no-flag #notes \revert Stem.cross-staff \revert Flag.style #@}) @end example @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 (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'2 \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 (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'2 \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 () () (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 (arg) (markup?) (make-dynamic-script arg)) \relative { 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 @cindex markup, définition d'une commande @funindex \displayScheme Les expressions @emph{markup} sont représentées en Scheme de manière interne par la macro @code{markup} : @example (markup @var{expression}) @end example La commande @code{\displayScheme} permet d'obtenir la représentation en Scheme d'une expression @emph{markup} : @example \displayScheme \markup @{ \column @{ \line @{ \bold \italic "hello" \raise #0.4 "world" @} \larger \line @{ foo bar baz @} @} @} @end example @noindent Compiler ce code renverra en console les lignes suivantes : @example (markup #:line (#:column (#:line (#:bold (#:italic "hello") #:raise 0.4 "world") #:larger (#:line (#:simple "foo" #:simple "bar" #:simple "baz"))))) @end example L'impression du @emph{markup} sera suspendue dès lors qu'apparaîtra un @w{@samp{\void \displayScheme @var{markup}}}. Tout comme pour la commande @code{\displayMusic}, le résultat de @code{\displayScheme} peut être sauvegardé dans un fichier séparé. Voir à ce sujet @ref{Affichage d'expressions musicales}. @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@dots{} @}} @tab @code{(markup markup1 markup2@dots{} )} @item @code{\commande-markup} @tab @code{#:commande-markup} @item @code{\variable} @tab @code{variable} @item @code{\center-column @{ @dots{} @}} @tab @code{#:center-column ( @dots{} )} @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}@dots{}) (@var{arg1-type?} @var{arg2-type?}@dots{}) [ #:properties ((@var{propriété1} @var{valeur-par-défaut1}) @dots{}) ] @dots{}corps de la commande@dots{}) @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 « 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?} ; @item une liste de @emph{markups}, correspondant au type de prédicat @code{markup-list?} ; @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}. @cindex markup macro @cindex macro de markup @funindex \markup @funindex interpret-markup Les commandes de @emph{markup} ont un cycle de vie relativement complexe. Le corps de la définition d'une commande de @emph{markup} est chargé de convertir les arguments de la commande en expression stencil qui sera alors renvoyée. Bien souvent, ceci s'accomplit par un appel à la fonction @code{interpret-markup}, en lui passant les arguments @var{layout} et @var{props}. Ces arguments ne seront en principe connus que bien plus tardivement dans le processus typographique. Lors de l'expansion d'une expression LilyPond @code{\markup} ou d'une macro Scheme @code{markup}, les expressions @emph{markup} auront déjà vu leurs composants assemblés en expressions @emph{markup}. L'évaluation et le contrôle du type des arguments à une commande de @emph{markup} n'interviennent qu'au moment de l'interprétation de @code{\markup} ou @code{markup}. Seule l'application de @code{interpret-markup} sur une expression @emph{markup} réalisera effectivement la conversion des expressions @emph{markup} en stencil, au travers de l'exécution du corps des fonctions @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 @noindent 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 : 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} ; 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)) "@dots{}documentation@dots{}" (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)) "@dots{}documentation@dots{}" (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)) "@dots{}documentation@dots{}" @dots{} @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 @cindex liste de markup, définition de commande @funindex define-markup-list-command @funindex interpret-markup-list 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. La fonction @code{interpret-markup-list}, à l'instar de la fonction @code{interpret-markup}, permet de convertir une liste de @emph{markups} en 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ée @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} : @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 @funindex make-apply-context @funindex ly:context-property @funindex ly:context-set-property! @funindex ly:context-grob-definition @funindex ly:assoc-get @funindex ly:context-pushpop-property Un contexte peut être modifié, au moment même de son interprétation, par du code Scheme. La syntaxe consacrée au sein d'un bloc LilyPond est @example \applyContext @var{fonction} @end example @noindent et, dans le cadre d'un code Scheme : @example (make-apply-context @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. Cette fonction peut accéder aussi bien aux propriétés de @emph{grob} (y compris modifiées par @code{\override} ou @code{\set}) qu'aux propriétés de contexte. Toute action entreprise par la fonction et qui dépendrait de l'état du contexte sera limitée à l'état de ce contexte @b{au moment de l'appel à la fonction}. Par ailleurs, les adaptations résultant d'un appel à @code{\applyContext} seront effectives jusqu'à ce qu'elles soient à nouveau directement modifiées ou bien annulées, quand bien même les conditions initiales dont elles dépendent auraient changé. Voici quelques fonctions Scheme utiles avec @code{\applyContext} : @table @code @item ly:context-property recherche la valeur d'une propriété de contexte, @item ly:context-set-property! détermine une propriété de contexte, @item ly:context-grob-definition @itemx ly:assoc-get recherche la valeur d'une propriété de @emph{grob}, @item ly:context-pushpop-property réalise un @code{\temporary \override} ou un @code{\revert} sur une propriété de @emph{grob}. @end table L'exemple suivant recherche la valeur en cours de @code{fontSize} puis la double : @lilypond[quote,verbatim] doubleFontSize = \applyContext #(lambda (context) (let ((fontSize (ly:context-property context 'fontSize))) (ly:context-set-property! context 'fontSize (+ fontSize 6)))) { \set fontSize = -3 b'4 \doubleFontSize b' } @end lilypond L'exemple suivant recherche la couleur des objets @code{NoteHead}, @code{Stem} et @code{Beam}, puis diminue pour chacun d'eux le degré de saturation. @lilypond[quote,verbatim] desaturate = \applyContext #(lambda (context) (define (desaturate-grob grob) (let* ((grob-def (ly:context-grob-definition context grob)) (color (ly:assoc-get 'color grob-def black)) (new-color (map (lambda (x) (min 1 (/ (1+ x) 2))) color))) (ly:context-pushpop-property context grob 'color new-color))) (for-each desaturate-grob '(NoteHead Stem Beam))) \relative { \time 3/4 g'8[ g] \desaturate g[ g] \desaturate g[ g] \override NoteHead.color = #darkred \override Stem.color = #darkred \override Beam.color = #darkred g[ g] \desaturate g[ g] \desaturate g[ g] } @end lilypond Ceci pourrait tout à fait s'implémenter sous la forme d'une fonction musicale, afin d'en réduire les effets à un seul bloc de musique. Notez comment @code{ly:context-pushpop-property} est utilisé à la fois pour un @code{\temporary \override} et pour un @code{\revert} : @lilypond[quote,verbatim] desaturate = #(define-music-function (music) (ly:music?) #{ \applyContext #(lambda (context) (define (desaturate-grob grob) (let* ((grob-def (ly:context-grob-definition context grob)) (color (ly:assoc-get 'color grob-def black)) (new-color (map (lambda (x) (min 1 (/ (1+ x) 2))) color))) (ly:context-pushpop-property context grob 'color new-color))) (for-each desaturate-grob '(NoteHead Stem Beam))) #music \applyContext #(lambda (context) (define (revert-color grob) (ly:context-pushpop-property context grob 'color)) (for-each revert-color '(NoteHead Stem Beam))) #}) \relative { \override NoteHead.color = #darkblue \override Stem.color = #darkblue \override Beam.color = #darkblue g'8 a b c \desaturate { d c b a } g b d b g2 } @end lilypond @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 @cindex objets de mise en forme, appel de code @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 aussi bien à la syntaxe @example \applyOutput @var{Contexte} @var{procédure} @end example @noindent que @example \applyOutput @var{Contexte}.@var{Grob} @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 (nommé @var{Grob} si celui-ci est spécifié) 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 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 « 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 (< (abs (ly:grob-property grob 'staff-position)) 2) (set! (ly:grob-property grob 'transparent) #t))) \relative { a'4 e8 <<\applyOutput Voice.NoteHead #blanker a c d>> b2 } @end lilypond La @var{procédure} sera interprétée au niveau @code{Score} (partition) ou @code{Staff} (portée) dès lors que vous utiliserez l'une des syntaxes @example \applyOutput Score@dots{} \applyOutput Staff@dots{} @end example @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 : @example \override Stem.thickness = #2.0 @end example Une procédure Scheme peut aussi se charger de modifier des propriétés : @lilypond[fragment,verbatim,quote] \override Stem.thickness = #(lambda (grob) (if (= UP (ly:grob-property grob 'direction)) 2.0 7.0)) \relative { 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}). Cette procédure, grâce à un appel à la fonction de rappel dévolue à cette propriété -- mentionnée dans la référence des propriétés internes et dans le fichier @file{define-grobs.scm} --, pourra accéder à la valeur usuelle de la propriété : @example \relative @{ \override Flag.X-offset = #(lambda (flag) (let ((default (ly:flag::calc-x-offset flag))) (* default 4.0))) c''4. d8 a4. g8 @} @end example La valeur par défaut est aussi accessible à l'aide de la fonction @code{grob-transformer} : @lilypond[verbatim,quote] \relative { \override Flag.X-offset = #(grob-transformer 'X-offset (lambda (flag default) (* default 4.0))) c''4. d8 a4. g8 } @end lilypond 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 @ignore @n ode Code Scheme intégré @s ection Code Scheme intégré @t ranslationof Inline Scheme code À REVOIR : depuis la rédaction de cette section, LilyPond en est arrivé à un point tel que trouver un exemple @emph{simple} où l'on se retrouve obligé d'en venir à utiliser du code Scheme devient chose ardue. 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 de syntaxe (du moins, c'était le cas auparavant). @example F = \tweak font-size #-3 -\flageolet \relative @{ c''4^\F c4_\F @} @end example @noindent 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''4^\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. @end ignore @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 « 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 tronçons 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 tronçon 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 tronçons 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 { \override Tie.after-line-breaking = #my-callback c''1 ~ \break c2 ~ 2 } @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 % Nom de la sous-propriété (optionnel) . #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