S4) Les backticks

Quand BPscript ouvre une fenêtre

Du code natif dans le flux temporel

BPscript fait une chose : ordonner des symboles dans le temps. Quand il faut du vrai code — définir un son, calculer une valeur — il ouvre une fenêtre vers un autre langage, puis la referme. Cette fenêtre, c’est le backtick.

Où se situe cet article ?

On a vu en S3 que chaque symbole appartient à un acteur, lequel désigne un runtime (environnement d’exécution en aval). Ici, on comprend comment BPscript transmet du code à ce runtime : les backticks.


Le principe : confier du code sans l’interpréter

Les backticks (` « ) délimitent du code que BPscript ne parse pas, ne comprend pas, ne valide pas. Il le transmet au runtime concerné, qui l’évalue.

 

Sa(vel:`rrand(40,127)`)

 

BPscript voit : « le symbole Sa a un paramètre vel dont la valeur est un fragment entre backticks ». Il confie rrand(40,127) au runtime de Sa, qui l’évalue et retourne un nombre. BPscript ne sait pas ce que rrand signifie — et n’a pas besoin de le savoir. Cette frontière nette est ce qui permet au langage de rester petit : il décrit le quand, le runtime décide du comment.

État actuel : aujourd’hui, l’évaluateur opérationnel est JavaScript, avec une sortie Web Audio. Les tags sc: (SuperCollider), py: (Python) ou tidal: (TidalCycles) qu’on verra dans les exemples illustrent l’architecture cible — un même mécanisme de backticks, plusieurs runtimes possibles en aval — mais ces évaluateurs ne sont pas encore branchés.


Trois types de backticks

BPscript distingue trois types selon la position du backtick et la façon dont le runtime cible est désigné.

Inline — le runtime est implicite

À l’intérieur d’un appel de symbole, le runtime est celui du symbole : pas besoin de le préciser.

 

Sa(vel:`rrand(40,127)`)          // le runtime de Sa évalue le fragment

 

Le compilateur sait quel runtime appeler — celui de l’acteur du symbole porteur.

Orphelin — en tête de scène, le tag est obligatoire

Quand un backtick n’est attaché à aucun symbole et se trouve en tête de scène (hors des règles), il faut indiquer explicitement à quel runtime il est destiné :

 

`sc: SynthDef(\sitar, { |freq, vel=80| ... }).add`    // → SuperCollider
`py: import dmx; d = dmx.open()`                       // → Python

 

Standalone — dans le flux, le tag est obligatoire

Quand un backtick non attaché apparaît à l’intérieur d’une règle, il s’exécute au temps de la dérivation où il se trouve. Le tag reste obligatoire :

 

melodie -> Sa(vel:`sc: i*20`) `sc: i=i+1` Re(vel:`sc: i*20`)

 

Le tag (sc:, py:, tidal:, js:…) précède le code, séparé par un :. Sans tag, le compilateur ne sait pas où envoyer le code — c’est une erreur. Seul le type inline en dispense, parce que le symbole porteur fournit déjà le runtime.


Trois usages

Les trois types correspondent à trois moments dans la vie d’une scène.

1. Init — préparer le runtime, avant la dérivation

Les backticks orphelins, en tête de scène, sont exécutés une fois au chargement, avant toute règle :

 

`js: synth = makeGrainSynth()`

 

C’est là qu’on prépare le terrain : définir un synthétiseur, importer une bibliothèque, initialiser une variable.

2. Flux — au moment de la dérivation

Un backtick standalone, au milieu d’une séquence, s’exécute au temps où la dérivation l’atteint :

 

melodie -> Sa(vel:`js: i*20`) `js: i=i+1` Re(vel:`js: i*20`)

 

Ici, ` js: i=i+1 ` est un événement de durée nulle dans le flux : il s’exécute entre deux notes, au moment précis calculé par BP3, et modifie l’état du runtime.

3. Résolution — évaluer et retourner une valeur

Un backtick inline, dans un appel, calcule la valeur d’un paramètre :

 

Sa(vel:`rrand(40,127)`)          // le runtime évalue et retourne un entier
Sa(vel:120)                      // pas de backtick → littéral, aucune évaluation

 

La résolution est un aller-retour : BPscript envoie l’expression, le runtime l’évalue et retourne la valeur, BPscript l’injecte dans le paramètre. C’est le seul cas où une réponse est attendue ; l’init et le flux sont des envois sans retour (fire-and-forget).


Variables, portées et isolation

Le modèle prévoit que chaque runtime maintienne sa propre session persistante — comme un REPL (Read-Eval-Print Loop, une console interactive) qui reste ouverte pendant toute la scène. Une variable y vit dans la portée de son runtime :

 

`js: var i = 0`                  // portée du runtime : i = 0
Sa(vel:`js: i`) `js: i = i + 1` Re(vel:`js: i`)
// i persiste d'un backtick à l'autre, dans la même session

 

Dans l’architecture cible, plusieurs runtimes coexistent sans état partagé : une variable définie dans l’un n’est pas visible dans l’autre. La coordination entre eux ne passe pas par des variables, mais par les mécanismes propres à BPscript :

  • les flags (gérés par BP3 — voir S6) ;
  • les triggers ! et <! (voir S7).

C’est une isolation voulue : chaque runtime est un bac à sable, sans couplage caché ni effet de bord d’un langage sur l’autre.


Un fichier sans backticks reste portable

Un fichier .bps sans aucun backtick ne dépend d’aucun langage externe : sa structure est pure.

 

@tempo:120

S -> { melodie, rythme }
melodie -> Sa Re Ga Pa
rythme  -> -!dha - -!ti -!dha

 

Un tel fichier fonctionne avec n’importe quel adaptateur (programme qui traduit les événements BPscript en commandes pour un runtime) capable de recevoir des gates et des triggers — Web Audio aujourd’hui, d’autres demain. Dès qu’on ajoute un backtick, on lie une partie du fichier à un runtime précis : c’est un compromis assumé entre puissance et portabilité.


Ce qu’il faut retenir

  1. Les backticks délimitent du code que BPscript n’interprète pas — il le transmet au runtime
  2. État actuel — l’évaluateur opérationnel est JS / Web Audio ; les tags sc:/py:/tidal: illustrent l’architecture cible, pas encore branchée
  3. Trois types — inline (dans un appel, runtime implicite), orphelin (en tête de scène, tag obligatoire), standalone (dans le flux, tag obligatoire)
  4. Trois usages — initialisation, exécution au moment de la dérivation, calcul d’un paramètre
  5. Sessions persistantes — chaque runtime garde son état ; les variables y vivent dans leur propre portée
  6. Pas d’état partagé entre runtimes — la coordination passe par les flags et les triggers
  7. Sans backtick = portable — la structure ne dépend d’aucun langage externe

Glossaire

  • Backtick : caractère ` «  délimitant du code natif destiné à un runtime — BPscript le transmet sans l’interpréter
  • Backtick inline : backtick dans un appel de symbole — le runtime est déduit du symbole, pas de tag
  • Backtick orphelin : backtick en tête de scène, non attaché à un symbole, exécuté à l’init — nécessite un tag de runtime
  • Backtick standalone : backtick dans le flux d’une règle, exécuté au moment de la dérivation — nécessite un tag de runtime
  • Runtime : environnement d’exécution, en aval, qui évalue le code et produit le son
  • REPL : Read-Eval-Print Loop — console interactive qui conserve un état entre les commandes
  • Portée : zone de visibilité d’une variable — chaque runtime a la sienne, isolée des autres
  • Adaptateur : programme qui traduit les événements horodatés de BPscript en commandes pour un runtime

Liens dans la série

  • S3 — Types, actors et bindings — comment les types et runtimes sont déclarés
  • S6 — Flags et parcours — la coordination par les flags
  • S7 — L’instant partagé — la coordination par les triggers !/<!
  • S9 — Le système de hauteurs en six couches — le routage vers les adaptateurs
  • S10 — Sous le capot — comment le runtime aval reçoit les backticks

Prérequis : S3
Temps de lecture : 10 min
Tags : #BPscript #backticks #runtime #live-coding


Prochain article : S5 (à venir) — Structurer le temps : polymétrie, silences, vitesse


Retour à l’index