X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Ffr%2Fextending%2Fprogramming-interface.itely;h=541b744b495f11aee56f7f18f2f4a0ff436d3bca;hb=958e95822083954cad00e0a598eb9f12ceba67b9;hp=a66da00d6c6e37d2a1462ca06e76d0b4894b0b4f;hpb=2940cdd12c135ef94e4c95ac815e76014eef9c79;p=lilypond.git diff --git a/Documentation/fr/extending/programming-interface.itely b/Documentation/fr/extending/programming-interface.itely index a66da00d6c..541b744b49 100644 --- a/Documentation/fr/extending/programming-interface.itely +++ b/Documentation/fr/extending/programming-interface.itely @@ -1,275 +1,1550 @@ @c -*- coding: utf-8; mode: texinfo; documentlanguage: fr -*- @ignore - Translation of GIT committish: 3f4496001441e0b1b27d7bc5395c4520f4f2088c + Translation of GIT committish: e8c4826ed3fca952984600bcce60b53e76aff5d2 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.14.0" +@c \version "2.19.22" -@c Translators: Valentin Villenave -@c Translation checkers: Gilles Thibault, Jean-Charles Malahieude +@c Translators: Valentin Villenave, Jean-Charles Malahieude +@c Translation checkers: Gilles Thibault - -@node Interfaces pour les programmeurs -@chapter Interfaces pour les programmeurs +@node Interfaces pour programmeurs +@chapter Interfaces pour programmeurs @translationof Interfaces for programmers -@untranslated - +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:: -* Interfaces de programmation:: -* Construction de fonctions complexes:: -* Interface de programmation des marqueurs de texte:: +* Fonctions événementielles:: +* Fonctions pour markups:: * Contextes pour programmeurs:: -* Utilisation de procédures Scheme en tant que propriétés:: -* Utilisation de code Scheme au lieu de \"tweak\":: +* 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{#}. + +@cindex parser (fonction argument) +@cindex location + +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. 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{\}). + +Lorsque l'emplacement (@code{location} -- voir @ref{Fonctions Scheme}) +fait référence à un endroit valide dans la source -- ce qui est en +général le cas au sein de fonctions musicales ou Scheme --, 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{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{location} +@tab doit être littéralement @code{location}, de telle sorte que soit +accessible l'emplacement de l'objet dans la source, aux fins de +transmettre aux messages d'erreur les fichier et numéro de ligne. + +@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} 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 « 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 type 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}. + +@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 « 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 + () + () + (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 -@untranslated +@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 -* Aperçu des fonctions musicales:: +* Définition de fonctions musicales:: +* Utilisation de fonctions musicales:: * Fonctions de substitution simple:: -* Fonctions de substitution par paire:: +* Fonctions de substitution intermédiaires:: * De l'usage des mathématiques dans les fonctions:: -* Fonctions fantômes:: * Fonctions dépourvues d'argument:: -* Liste des fonctions musicales prédéfinies:: +* Fonctions musicales fantômes:: @end menu -@node Aperçu des fonctions musicales -@subsection Aperçu des fonctions musicales -@translationof Overview of music functions -@untranslated +@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 @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 » 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 -@untranslated +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 par paire -@subsection Fonctions de substitution par paire -@translationof Paired substitution functions +@node Fonctions de substitution intermédiaires +@subsection Fonctions de substitution intermédiaires +@translationof Intermediate substitution functions -@untranslated +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 -@untranslated +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 +} -@node Fonctions fantômes -@subsection Fonctions fantômes -@translationof Void functions +\relative { + c'2 \AltOn #0.5 c4 c + \AltOn #1.5 c c \AltOff c2 +} +@end lilypond -@untranslated +@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 -@untranslated +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 -@node Liste des fonctions musicales prédéfinies -@subsection Liste des fonctions musicales prédéfinies -@translationof Overview of available music functions +Il peut, dans certains cas particuliers, s'avérer utile de créer une +fonction sans argument comme ici, -@untranslated +@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 -@include identifiers.tely -@node Interfaces de programmation -@section Interfaces de programmation -@translationof Programmer interfaces +@example +lilypond -d display-bar-numbers MONFICHIER.ly +@end example -@untranslated +@node Fonctions musicales fantômes +@subsection Fonctions musicales fantômes +@translationof Void music functions -@menu -* Variables d'entrée et Scheme:: -* Représentation interne de la musique:: -@end menu +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 Variables d'entrée et Scheme -@subsection Variables d'entrée et Scheme -@translationof Input variables and Scheme -@untranslated +@node Fonctions événementielles +@section Fonctions événementielles +@translationof Event functions +@funindex define-event-function +@cindex événementielle, fonction -@node Représentation interne de la musique -@subsection Représentation interne de la musique -@translationof Internal music representation +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 : -@untranslated +@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 Construction de fonctions complexes -@section Construction de fonctions complexes -@translationof Building complicated functions -@untranslated +@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 -* Affichage d'expressions musicales:: -* Propriétés de la musique:: -* Exemple : redoubler une note avec liaison:: -* Exemple : ajouter une articulation à plusieurs notes:: +* 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 Affichage d'expressions musicales -@subsection Affichage d'expressions musicales -@translationof Displaying music expressions -@untranslated +@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 + +@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 -@node Propriétés de la musique -@subsection Propriétés de la musique -@translationof Music properties +@noindent +Compiler ce code renverra en console les lignes suivantes : -@untranslated +@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}. -@node Exemple : redoubler une note avec liaison -@subsection Exemple : redoubler une note avec liaison -@translationof Doubling a note with slurs (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@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 -@untranslated +@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 Exemple : ajouter une articulation à plusieurs notes -@subsection Exemple : ajouter une articulation à plusieurs notes -@translationof Adding articulation to notes (example) -@untranslated +@node Fonctionnement interne des markups +@subsection Fonctionnement interne des @emph{markups} +@translationof How markups work internally +Dans un @emph{markup} tel que -@node Interface de programmation des marqueurs de texte -@section Interface de programmation des marqueurs de texte -@translationof Markup programmer interface +@example +\raise #0.5 "text example" +@end example -@untranslated +@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 -* Construction Scheme d'un marqueur:: -* Fonctionnement interne des marqueurs:: -* Définition d'une nouvelle commande de marqueur:: -* Définition d'une nouvelle commande de liste de marqueurs:: +* Syntaxe d'une commande markup:: +* Attribution de propriétés:: +* Exemple commenté:: +* Adaptation d'une commande incorporée:: @end menu -@node Construction Scheme d'un marqueur -@subsection Construction Scheme d'un marqueur -@translationof Markup construction in Scheme -@untranslated +@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 -@node Fonctionnement interne des marqueurs -@subsection Fonctionnement interne des marqueurs -@translationof How markups work internally +@example +(ly:output-def-lookup layout 'line-width) +@end example -@untranslated +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 -@node Définition d'une nouvelle commande de marqueur -@subsection Définition d'une nouvelle commande de marqueur -@translationof New markup command definition +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 -@untranslated +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 : 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 Définition d'une nouvelle commande de liste de marqueurs -@subsection Définition d'une nouvelle commande de liste de marqueurs +@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 -@untranslated +@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 -@untranslated - - @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 -@untranslated +@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 -@untranslated +@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 +à la syntaxe +@example +\applyOutput @var{Contexte} @var{procédure} +@end example -@node Utilisation de procédures Scheme en tant que propriétés -@section Utilisation de procédures Scheme en tant que propriétés -@translationof Scheme procedures as properties +@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 le contexte dans lequel @code{\applyOutput} est effectué. +@end itemize -@untranslated +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. -@menu -* Utilisation de code Scheme au lieu de \"tweak\":: -* Retouches complexes:: -@end menu +@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))) -@node Utilisation de code Scheme au lieu de \"tweak\" -@section Utilisation de code Scheme au lieu de @code{\tweak} -@translationof Using Scheme code instead of tweak +\relative { + a'4 e8 <<\applyOutput #'Voice #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 #@var{procédure} +\applyOutput #'Staff #@var{procédure} +@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,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}). + +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 + +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. +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 +F = \tweak font-size #-3 -\flageolet -\relative c'' @{ - c4^\F c4_\F +\relative @{ + c''4^\F c4_\F @} @end example @noindent -En d'autres termes, @code{\tweak} ne se comporte pas comme une -articulation@tie{}: 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{Exemple : ajouter une articulation à plusieurs notes}, en +@ref{Ajout d'articulation à des notes (exemple)}, en particulier quant à l'emploi de @code{\displayMusic}. @example @@ -280,20 +1555,22 @@ F = #(let ((m (make-music 'ArticulationEvent (ly:music-property m 'tweaks))) m) -\relative c'' @{ - c4^\F c4_\F +\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 +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 @@ -301,22 +1578,20 @@ finale de @samp{m} lui-même. Certains réglages sont plus délicats que d'autres. -@itemize @bullet - - +@itemize @item -L'un d'entre eux est l'apparence des objets dits @qq{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}. - +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 @q{spanner}. Pour ne modifier que la partie -précédant ou suivant le changement de ligne, il faut intervenir +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é @@ -326,68 +1601,83 @@ systèmes différents. Dans l'exemple suivant, on définit une nouvelle opération nommée @code{my-callback}. Cette opération -@itemize @bullet +@itemize @item détermine si l'objet a été divisé à l'occasion d'un changement de ligne @item -si oui, recherche les différents morceaux de l'objet +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 -si oui, applique un espacement supplémentaire (@code{extra-offset}). +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 +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)) + (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) '() ))) + ;; 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))))) + (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 = +\relative { + \override Tie.after-line-breaking = #my-callback - c1 ~ \break c2 ~ c + 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 appeler également -@code{ly:spanner::kill-zero-spanned-time}. +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 +@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@tie{}: +@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 +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 cependant que la commande @code{\override} peut tout de même être +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