Subject: [LilyPond] [Général] Programmation Scheme : accéder aux variables définies dans le header

From: Pierre via LilyPond <[email-masked]>
Date: Fri, 17 Nov 2023 20:42:26 +0000

Bonjour,
Je suis en train d'essayer de comprendre comment fonctionne Scheme et la façon dont les fonctions scheme interagissent avec Lilypond pendant la compilation, mais comme je n'ai pas trouvé de manuel détaillé à ce sujet, malgré de la doc ( https://extending-lilypond.gitlab.io/fr/scheme ,  https://scheme-book.readthedocs.io/en/latest/scheme/index.html et https://lilypond.org/doc/v2.23/Documentation/extending/introduction-to-scheme ), j'ai voulu commencer à me faire la main de mon coté.

Bref, en résumé, j'essaye d'afficher dans le prompt lors de la compilation le titre (défini dans le header, avec une partition toute bête, qui affiche bien le titre dans le PDF lors de la compilation), mais ne trouve pas la solution...
Mes tests et recherches m'ont menées à ces 3 essais, qui compilent, mais n'affichent que lors de la compilation :
000------000 
111------111
222------222

```
\layout{
    #( define headertitle (markup->string ( ly:get-option 'header:title ) ) )
    #( ly:message ( string-append  "000--" headertitle "--000" ) )			
    #( ly:message ( string-append  "111--" ( markup->string #:fromproperty 'header:title ) "--111" ) )
    #( ly:message ( string-append  "222--" ( markup->string #:fromproperty 'title ) "--222" ) )
}
```
Auriez-vous éventuellement des manuels plus détaillés sur ce sujet et/ou une idée de  comment faire ? 

(ps : j'ai demandé à chatGPT, mais qui est bien perdu dans l'utilisation de Scheme avec Lilypond : on ne remplacera pas les humains aussi facilement que ça :joy: )

---
Pour poster dans ce sujet, visitez la [page du sujet](https://lilypond.community/t/programmation-scheme-acceder-aux-variables-definies-dans-le-header/5529/1).

Pour créer un nouveau sujet, écrivez à [[email-masked]](mailto:[email-masked]) ou visitez la [page d'accueil de lilypond.community](https://lilypond.community).

---

From: Jean Abou Samra via LilyPond <[email-masked]>
Date: Fri, 17 Nov 2023 21:22:31 +0000

Tout dépend de ce que vous voulez faire exactement de cette variable de `\header`.

Déjà, il faut savoir de quel `\header` on parle, parce qu'il peut y en avoir plusieurs dans un fichier `.ly`, comme dans

```
\version "2.24.2"

\header {
  title = "Foo"
}

\score {
  \header {
    piece = "Bar"
  }
  { c' }
}
```

Ensuite, il faut bien comprendre que le code exécuté avec un `#` l'est pendant la phase où LilyPond déchiffre la syntaxe du fichier et construit une représentation de la musique qu'il contient, mais cette phase n'est que le début, il y a d'autres phases de compilation ensuite (il suffit de regarder les messages du log pour s'en convaincre), et lire les variables `\header` est généralement plus utile dans une autre phase. Les `\header` participent à la structure du fichier (organisé en `\book`, `\bookpart`, `\score`).  Or cette structure n'est pas encore en place dans la phase où le code derrière un `#` est exécuté — puisque c'est justement la phase où elle est en train d'être construite.

Ce qui se rapproche le plus de votre demande est

```
#(ly:message "~s" (module-ref $defaultheader 'title))
```

mais c'est vraiment inhabituel d'utiliser ainsi cette variable spéciale « $defaultheader », qui est à la limite du détail interne. Ça ne marchera que si vous mettez ce code après le `\header`, et si c'est un `\header` au plus au niveau (pas dans un `\book`, `\bookpart` ou `\score`).

Votre `ly:get-option` ne marche pas parce que les « options » dont il s'agit sont celles qui se règlent avec `-dfoo=bar` en ligne de commande, ou `#(ly:set-option 'foo bar)` dans le fichier (cela n'a rien à voir avec les `\header`).

Une première raison pour laquelle votre `markup->string` ne marche pas non plus, c'est qu'il faudrait plutôt écrire `(markup->string (markup #:fromproperty 'header:title))`, avec `(markup ...)` pour créer la valeur de type markup, ce qui peut aussi se faire (plus simplement) en passant temporairement en syntaxe LilyPond à l'intérieur du Scheme avec `#{ \markup \fromproperty #'header:title #}`. Mais la raison plus profonde pour laquelle ça ne marche pas est que `markup->string` n'utilise pas un certain état ambiant qui lui donnerait les propriétés. Tout vient de ses arguments. Et `\markup \fromproperty #'header:title` ne contient pas en soi la valeur du `title`, c'est juste un `\markup` qui dit « au moment où vous me dessinerez, merci de lire la propriété `title` du `header` ». Donc `markup->string` n'a aucune information de laquelle extraire la valeur de `title`.

Par contre, si vous faites ceci, vous verrez la valeur :

```
\version "2.24.2"

\header {
  title = "Foo title"
}

#(define-markup-command (foo layout props) ()
   (ly:message "~s" (markup->string #{ \markup \fromproperty #'header:title #} #:props props))
   empty-stencil)

\paper {
  bookTitleMarkup = \markup \foo
}

{ c' }
```

Plusieurs changements ici. D'abord, j'ai défini une commande markup, et je l'utilise dans `bookTitleMarkup`. Notez bien que le code autour de `ly:message` ne s'exécute pas directement au moment de l'analyse du fichier. Tout ce qui est exécuté par le `#`, c'est la *définition* d'une fonction. Ensuite, cette fonction est appelée parce qu'elle a été mise dans `bookTitleMarkup`, et *ça*, c'est exécuté à un moment où les `\header` sont bien en place. Et puis j'ai aussi mis un `#:props props` pour transmettre les propriétés que reçoit la commande markup à `markup->string`, parce qu'en fait, c'est comme ça que les variables de `\header` sont transmises à `\fromproperty`. Et d'ailleurs, vous pouvez extraire directement le `title` de `props` au lieu de passer par `markup->string`, avec `(chain-assoc-get 'header:title props)`.

Ce n'est qu'un exemple. La manière de trouver le bloc `\header` dépend vraiment du contexte. Désolé, ma réponse est compliquée. En même temps, votre question est un exemple parfait de question XY :slight_smile: N'hésitez pas à donner plus de précisions sur ce que vous voulez faire exactement pour qu'on puisse vous aider mieux.

---
Pour poster dans ce sujet, visitez la [page du sujet](https://lilypond.community/t/programmation-scheme-acceder-aux-variables-definies-dans-le-header/5529/2).

---

From: Pierre via LilyPond <[email-masked]>
Date: Fri, 17 Nov 2023 21:49:23 +0000

Merci beaucoup Jeanas !
Je lirai en détail et testerai votre réponse à tête reposée demain, mais j'ai déjà commencé à comprendre 2 3 trucs  :wink: 

Pour le moment, il s'agit surtout de comprendre comment fonctionne Lilypond (vraiment découvert en début de semaine, et dans lequel je me suis plongé à fond tous les soirs afin de lire un maximum de documentation et multiplier les tests de personnalisation, l'écriture de partitions en elles-même ayant pris moins d'une heure dans mon cas - il s'agit de chants à une voix avec couplets et refrains).
Mon but au final est de me faire une entête aux petits oignons, ayant pris l'habitude avec LaTeX d'absolument tout personnaliser (quitte à coder en plain-tex par endroit) même si je sais que ce n'est pas la même syntaxe, et donc là, ce que j'aimerais, ce serait comprendre la logique sous-jacente ce Scheme pour automatiquement ajouter "T : " quand header:poet est renseigné (et laisser vide sinon) et "M : " quand header:composer l'est (et ensuite, ajouter la personne ayant fait l'harmonisation avec un "Harm :", l'éditeur, et 2 systèmes de cotations différents), le tout en ayant juste un fichier include à ajouter, pour n'avoir qu'à éditer le \header et laisser la magie opérer ^^
Je sais que cela est bien ambitieux quand on commence à peine  :joy:  (mais d'où l'importance de comprendre le fonctionnement subtile de Lilypond et Scheme)

---
Pour poster dans ce sujet, visitez la [page du sujet](https://lilypond.community/t/programmation-scheme-acceder-aux-variables-definies-dans-le-header/5529/3).

---

From: Jean Abou Samra via LilyPond <[email-masked]>
Date: Fri, 17 Nov 2023 22:25:51 +0000

[quote="1sixunhuit, post:3, topic:5529"]
Merci beaucoup Jeanas !
[/quote]

Appelez-moi Jean :)

[quote="1sixunhuit, post:3, topic:5529"]
et donc là, ce que j'aimerais, ce serait comprendre la logique sous-jacente ce Scheme pour automatiquement ajouter "T : " quand header:poet est renseigné (et laisser vide sinon)
[/quote]

Un truc comme ça ?

```
\version "2.24.2"

is-defined =
#(define-scheme-function (sym) (symbol?)
   (lambda (layout props)
     (any (lambda (alist) (assq sym alist)) props)))

\paper {
  % Valeur par défaut :
  % https://gitlab.com/lilypond/lilypond/-/blob/676d48aa61a415387cca352878f66fd08a9fb40f/ly/titling-init.ly#L79
  bookTitleMarkup = \markup {
    \override #'(baseline-skip . 3.5)
    \column {
      \fill-line { \fromproperty #'header:dedication }
      \override #'(baseline-skip . 3.5)
      \column {
        \fill-line {
          \huge \larger \larger \bold
          \fromproperty #'header:title
        }
        \fill-line {
          \large \bold
          \fromproperty #'header:subtitle
        }
        \fill-line {
          \smaller \bold
          \fromproperty #'header:subsubtitle
        }
        \fill-line {
          %%% Modifié :
          \concat {
            \if \is-defined #'header:poet "T : "
            \fromproperty #'header:poet
          }
          { \large \bold \fromproperty #'header:instrument }
          \fromproperty #'header:composer
        }
        \fill-line {
          \fromproperty #'header:meter
          \fromproperty #'header:arranger
        }
      }
    }
  }
}

\header {
  poet = "Foo"
}

{ c' }
```

---
Pour poster dans ce sujet, visitez la [page du sujet](https://lilypond.community/t/programmation-scheme-acceder-aux-variables-definies-dans-le-header/5529/4).

---

From: Pierre via LilyPond <[email-masked]>
Date: Sun, 26 Nov 2023 12:05:57 +0000

Bonjour Jean,
Un grand merci pour votre aide et la proposition de fonction (c'est effectivement quelque-chose du genre que je cherchais à faire, à ceci près que quand on déclare la variable comme fausse, il ne faille pas afficher de résultat), qui m'ont permis de voir que j'étais bien prétentieux d'essayer de comprendre directement la programmation Scheme via des tâtonnements  et des prints, le langage étant bien différent des autres langages informatiques que je connais (le paradigme étant non pas déclaratif mais fonctionnel). Je suis donc reparti "sur les bancs de l'école" en regardant le mooc de Queinnec (https://www.youtube.com/watch?v=42GxCJB7IVw&list=PLBk_pGscJ8eOEksRRhoEroZjw88UOkLKE qui est assez long - je n'en suis qu'aux 2/3) afin d'assimiler tout ça et être capable de comprendre et réécrire votre fonction en modifiant son comportement comme je l'attends ;) (donc ne me donnez pas la solution à la modification à apporter s'il vous plait, afin que je ne sois pas tenté par la méthode de la facilité qui consiste à copier-coller ; je posterai la fonction ici une fois celle-ci terminée au cas où cela pourrait servir à quelqu'un).

---
Pour poster dans ce sujet, visitez la [page du sujet](https://lilypond.community/t/programmation-scheme-acceder-aux-variables-definies-dans-le-header/5529/5).