1.4 De la théorie à la pratique

Nous allons nous intéresser, dans les paragraphes qui suivent, aux arbitrages en terme de programmation qui ont participé à l’élaboration de LilyPond.


Représentation de la musique

Le format de saisie idéal pour un système évolué de formatage consiste en la description abstraite du contenu. Dans le cas qui nous occupe, il s’agit tout bonnement de la musique. Cela n’est pas sans poser un énorme problème : comment définir ce qu’est la musique ? Pour résoudre ce problème, nous l’avons inversé. Au lieu de définir ce qu’est la musique, notre programme sert de définition : nous avons écrit un programme capable de produire des partitions, et avons ajusté son format de saisie pour être aussi léger que possible. Lorsque le format ne peut plus être simplifié, nous avons par définition atteint le contenu même. Autrement dit, notre programme utilise la définition formelle d’un document musical.

La syntaxe est également celle de l’interface utilisateur de LilyPond, puisqu’elle peut être facilement tapée au clavier :

{
  c'4 d'8
}

sont un do médium noire et un ré croche juste au-dessus.

[image of music]

À petite échelle, une telle syntaxe est pratique. À plus grande échelle, une structuration est nécessaire. Sans cela, comment saisir des pièces aussi complexes qu’un opéra ou une symphonie ? La structure est construite selon le même concept que les expressions musicales : c’est en combinant de petits fragments qu’une musique plus complexe peut être représentée. Par exemple :

f'4

[image of music]

Quand des notes sont combinées simultanément, il suffit de les insérer entre << et >> :

<<c4 d4 e4>>

[image of music]

Insérées dans une paire d’accolades { … }, ces expressions sont intégrées dans une séquence :

{ f4 <<c4 d4 e4>> }

[image of music]

Ceci constitue une nouvelle expression, qui peut être à son tour combinée avec une autre expression simultanée (en l’occurrence une blanche) à l’aide de <<, \\ et >> :

<< g2 \\ { f4 <<c4 d4 e4>> } >>

[image of music]

De telles structures récursives peuvent être définies formellement et de manière ordonnée avec une grammaire libre de tout contexte. Le code d’analyse de la saisie est également généré depuis cette grammaire. Autrement dit, la définition de la syntaxe de LilyPond est claire et sans ambiguïté.

Ce sont la syntaxe et l’interface qui concernent les utilisateurs. Elles sont sujettes à discussion, puisque relatives aux goûts de chacun. Bien que ces discussions soient intéressantes, elles ne sont pas productives. À l’échelle de LilyPond, l’importance du format de saisie est minime : inventer une syntaxe claire est bien plus aisé qu’écrire un code de mise en page décent. Ceci est d’ailleurs illustré par le nombre de lignes de code de ces composants respectifs : l’analyse du texte de saisie et sa représentation pèsent moins de 10 % du code.

Dans la conception des structures utilisées par LilyPond, nous avons pris certaines options divergentes des autres logiciels. Considérons la nature par essence hiérarchique de la notation musicale :

[image of music]

Nous y voyons des hauteurs regroupées en accords appartenant à des mesures elles-mêmes rattachées à des portées. On pourrait représenter cela par un système de boîtes imbriquées :

nestedboxes

Cette structure est cependant réductrice car reposant sur des postulats extrêmement restrictifs. Cela devient plus évident dès lors que la musique se complique :

[image of music]

Ici, les portées débutent et s’arrêtent n’importe où, les voix passent d’une portée à l’autre, et parfois même occupent deux portées. Nombre de logiciels auront d’énormes difficultés à reproduire cet exemple, pour la bonne et simple raison qu’ils se basent sur le modèle hiérarchique de boîtes imbriquées. LilyPond, quant à lui, tend à préserver la dichotomie entre le format de saisie et une structure aussi flexible que possible.


Les constituants de la gravure

Si le processus de formatage se charge de décider où positionner les symboles, encore faudrait-il savoir quels symboles doivent apparaître – ou bien, quelle notation utiliser.

La notation musicale constitue une forme d’enregistrement de la musique qui a d’ailleurs évolué sur un millier d’années. La forme que nous utilisons de nos jours remonte au début de la Renaissance. Bien que ses fondements n’aient pas changé – des têtes de note réparties sur une portée à cinq lignes –, certains détails sont apparus qui permettent de rendre compte des apports de la notation contemporaine. La notation musicale couvre cinq siècles de musique, allant de la simple monodie à de gigantesques contrepoints orchestraux.

Comment intégrer un tel monstre aux si nombreux visages ? Notre solution est de clairement distinguer la notation – quels symboles utiliser – de la gravure – les placer. Pour appréhender la notation, nous l’avons disséquée en éléments simples (et programmables) : chaque type de symbole est géré par un module – ou greffon – différent. Tous ces modules sont entièrement configurables et indépendants, chacun pouvant être développé et amélioré séparément. Nous appelons ces modules des graveurs (engravers en anglais), par analogie avec ces professionnels qui mettent en forme graphiquement les idées musicales.

Le module le plus basique, qu’on appelle Note_heads_engraver, crée les têtes de notes :

[image of music]

Puis le module Staff_symbol_engraver génère les objets représentant les lignes de portée :

[image of music]

le module Clef_engraver fournit au Note_heads_engraver la hauteur à laquelle doit être placée chaque tête de note :

[image of music]

et, enfin, les hampes et leurs crochets sont gérés par le Stem_engraver :

[image of music]

Le module Stem_engraver est appelé dès qu’une tête de note apparaît. Pour chaque note rencontrée – ou groupe de notes s’il s’agit d’un accord –, un objet hampe est créé et attaché à la tête de note. Si l’on ajoute les modules en charge des ligatures, des liaisons, des accents, des barres de mesure, de la métrique et de l’armure, nous avons tout ce qui est nécessaire à une partition.

[image of music]

Le principe décrit plus haut fonctionne très bien pour de la musique monophonique, mais qu’en est-il de la polyphonie ? Dans ce cas, plusieurs voix peuvent partager la même portée :

[image of music]

Ici, la portée et les altérations sont partagées mais les hampes, liaisons, liens, etc. dépendent de chaque voix. De plus, les graveurs doivent être regroupés. Les graveurs de têtes de note, hampes, liaisons, etc. sont donc inclus dans un groupe appelé Voice context (contexte de voix), alors que l’armure, les altérations, les barres de mesures, etc. sont placées dans un groupe Staff context (contexte de portée). Dans le cas d’une polyphonie, un contexte de portée contient plusieurs contextes de voix. De même, plusieurs contextes de portée peuvent être assemblés dans un contexte de partition (Score context) :

[image of music]

Voir aussi

Référence des propriétés internes : Contexts.


Flexibilité architecturale

Pour ses premiers pas, LilyPond était totalement écrit en langage C++ ; les programmeurs gravaient les fonctionnalités du programme dans le marbre. Cela s’est avéré peu satisfaisant pour plusieurs raisons :

Nous avons résolu ces problème par l’adjonction d’un interpréteur pour le Scheme et la réécriture de parties entières de LilyPond dans ce langage. L’architecture du programme repose sur la notion d’objets graphiques que l’on peut décrire par des variables ou des fonctions Scheme. Elle doit prendre en compte les règles de formatage, le style typographique et les décisions de formatage individuelles. L’utilisateur de LilyPond peut accéder directement à la majorité de ces contrôles.

La mise en page dépend des variables Scheme. Par exemple, la variable direction définie pour de nombreux objets, encode le choix entre haut et bas (ou gauche et droite). L’image qui suit présente deux accords, accentués et arpégés. Pour le premier accord, la direction de chaque objet est vers le bas (ou la gauche). Les objets sont dirigés vers le haut (ou la droite) pour le second.

[image of music]

Le processus de mise en forme d’une partition consiste à lire et écrire des variables d’objets. Certaines variables ont une valeur prédéfinie. Par exemple, l’épaisseur de nombreuses lignes – caractéristiques d’un style typographique – est préétablie. La changer entraîne un changement du style typographique.

[image of music]

Les valeurs prédéfinies de certaines variables font aussi partie des règles de formatage. Chaque objet possède des variables contenant des procédures. Ces procédures réalisent le formatage prédéfini, dont l’apparence peut être changée en remplaçant ces procédures par d’autres. Dans l’exemple suivant, la règle permettant à l’objet « tête de note » de dessiner son symbole est modifiée au cours de l’extrait musical :

[image of music]


LilyPond — Essai sur la gravure musicale automatisée v2.24.4 (branche stable).