Aller au contenu
roomi fields

BPscript · S10 · Conception

Sous le capot

Le pipeline complet — du fichier .bps au son

Publié le 19 mars 2026 · mis à jour 21 mai 2026 · Read in English

Compilateur, moteur et dispatcher

Le pipeline complet — du fichier .bps au son

Un fichier .bps entre. Du son en sort. Entre les deux : un compilateur JavaScript qui produit un arbre de scène, le moteur BPx qui dérive cet arbre, et — en aval — un dispatcher qui résout les notes par acteur et route vers les transports.

Périmètre : la chaîne va de la source .bps à l’arbre de scène, puis à la dérivation par BPx, dont la sortie est une suite de tokens datés (une projection de l’arbre). Le runtime (dispatcher, resolver, transports, sessions de code) est le consommateur aval de ces tokens. On le décrit ici comme spécification du système complet.

Où se situe cet article ?

Après les acteurs (S3) et les six couches pitch (S9), on ouvre le capot. Cet article est technique — il décrit comment tout s’assemble.


Le pipeline en un schéma

flowchart TD
    subgraph Depot["Langage + transpileur + moteur"]
        SRC["Source .bps"]
        TOK["Tokenizer"]
        PAR["Parser"]
        TREE["Arbre de scène"]
        BPX["BPx — dérivation"]
        TOKENS["Tokens datés (projection)"]
    end

    subgraph Data["lib/"]
        ALP[("alphabets")]
        OCT[("octaves")]
        TUN[("tunings")]
        TMP[("temperaments")]
        CTR[("controls")]
        ROU[("routing")]
    end

    subgraph Parite["Parité (validation)"]
        BP3TXT["Texte BP3"]
        NAT["Moteur natif (oracle)"]
    end

    subgraph Aval["Runtime (aval)"]
        DISP["Dispatcher"]
        RES["Resolver par acteur"]
        WAUDIO["Web Audio"]
        MIDIT["MIDI"]
        JSEVAL["JS inline"]
    end

    subgraph Prevu["Prévu (aval)"]
        OSCT["OSC"]
        SCL["sclang"]
        PYT["Python"]
    end

    SRC --> TOK
    ALP -.-> TOK
    OCT -.-> TOK
    TOK --> PAR
    PAR --> TREE
    CTR -.-> TREE
    TREE --> BPX
    BPX --> TOKENS
    TREE -.-> BP3TXT
    BP3TXT -.-> NAT
    TOKENS --> DISP
    TUN -.-> RES
    TMP -.-> RES
    ROU -.-> DISP
    DISP --> RES
    RES --> WAUDIO
    RES --> MIDIT
    DISP --> JSEVAL
    JSEVAL --> DISP

    DISP -.-> OSCT
    DISP -.-> SCL
    DISP -.-> PYT

    style SRC fill:#4a7ab5,stroke:#3a6aa5,color:#fff
    style TOK fill:#4a9a8a,stroke:#3a8a7a,color:#fff
    style PAR fill:#4a9a8a,stroke:#3a8a7a,color:#fff
    style TREE fill:#4a9a8a,stroke:#3a8a7a,color:#fff
    style BPX fill:#c8842a,stroke:#b8742a,color:#fff
    style DISP fill:#8a5ab5,stroke:#7a4aa5,color:#fff
    style RES fill:#7a4a9a,stroke:#6a3a8a,color:#fff
    style WAUDIO fill:#2a6a4a,stroke:#1a5a3a,color:#fff
    style MIDIT fill:#2a6a4a,stroke:#1a5a3a,color:#fff
    style JSEVAL fill:#4a5ab5,stroke:#3a4aa5,color:#fff
    style TOKENS fill:#444,stroke:#666,color:#ddd
    style BP3TXT fill:#555,stroke:#777,color:#999
    style NAT fill:#555,stroke:#777,color:#999
    style ALP fill:#333,stroke:#666,color:#ccc
    style OCT fill:#333,stroke:#666,color:#ccc
    style TUN fill:#333,stroke:#666,color:#ccc
    style TMP fill:#333,stroke:#666,color:#ccc
    style CTR fill:#333,stroke:#666,color:#ccc
    style ROU fill:#333,stroke:#666,color:#ccc
    style OSCT fill:#555,stroke:#777,color:#999
    style SCL fill:#555,stroke:#777,color:#999
    style PYT fill:#555,stroke:#777,color:#999

Figure 1 — Pipeline BPscript. La chaîne de production va de la source à l’arbre de scène, dérivé par BPx en tokens datés (une projection de l’arbre). Le runtime aval consomme ces tokens. Trait plein = implémenté (Web Audio, MIDI, JS inline). Trait pointillé = prévu (OSC, sclang, Python) ou voie de parité (encodage en texte BP3 comparé au moteur natif). Les fichiers JSON (lib/) alimentent le compilateur et le resolver.


Le compilateur BPscript (3 étapes)

Le compilateur est écrit en JavaScript. Il transforme le source .bps en arbre de scène en trois étapes.

1. Tokenizer

Découpe le source en tokens. Lit alphabets.json et octaves.json pour reconnaître les noms de notes et les suffixes/préfixes de registre.

@actor sitar alphabet.sargam ...  →  [@, actor, sitar, alphabet, ., sargam, ...]
Sa_^(vel:120)                     →  [Sa_^, (, vel, :, 120, )]
[phase==1] S -> A                 →  [[, phase, ==, 1, ], S, ->, A]

Les backticks sont tokenizés comme des blocs opaques — le contenu n’est pas analysé.

2. Parser

Construit l’AST (Abstract Syntax Tree — voir S13). Vérifie la syntaxe, enregistre les @actor et les déclarations. Pas de vérificateur de types séparé (prévu, pas implémenté). L’expansion textuelle des macros (accent(x) = x(vel:120)) se fait dans cette phase.

3. Arbre de scène

Le parser produit l’arbre de scène : la forme normalisée qui est l’entrée directe du moteur. C’est lui que BPx consomme — pas un texte intermédiaire. Chaque feuille porte sa charge opaque (vélocité, timbre…) et son acteur, qui voyagent avec elle à travers la dérivation. Les [] (moteur) deviennent des attributs de structure ; les () (runtime) deviennent la charge non-temporelle de la feuille ; gardes, captures, templates et liaisons deviennent les nœuds correspondants.

La voie de parité. Un second chemin encode le même arbre en texte BP3 pour le rejouer sur le moteur natif de Bernard Bel et prouver l’équivalence (l’ancien portage WebAssembly de ce moteur sert d’outil de validation, désormais figé). Ce texte n’est pas la voie de production — c’est un échafaudage de vérité, pas le chemin du son.

Structure d’un côté, contenu de l’autre

BPx s’occupe de la structure (quand, combien de temps, dans quel ordre). Le contenu (quelle fréquence, quel canal, quel son) reste une charge opaque que le moteur transporte sans l’interpréter, et que le resolver en aval résout. Cette frontière est ce qui rend possible le rendu multi-runtime.


Le moteur BPx

BPx est le portage TypeScript du moteur de Bernard Bel — sa dimension d’ordonnancement temporel — conçu pour tourner dans le navigateur et consommer l’arbre directement. Il reçoit l’arbre de scène et produit l’arbre de dérivation, dont la suite plate de tokens datés est une projection :

[
  { name: "Sa", actor: "sitar", start: 0,    duration: 1000, controls: { vel: 120 } },
  { name: "Re", actor: "sitar", start: 1000, duration: 1000, controls: {} },
  ...
]

L’arbre est l’artefact de vérité unique : la suite plate n’en est qu’une vue, choisie par calques (temps, identité, contrôles résolus, acteur, structure…). Le moteur gère : dérivation, polymétrie (PolyMake — B13), repos indéterminés, flags, captures, templates, homomorphismes, poids, et le rangement des contrôles non-temporels sur chaque terminal en fin de dérivation.


Le dispatcher (runtime aval)

Le composant le plus riche du runtime — en aval du moteur. Pour chaque token daté à l’instant T :

1. Identifier l’acteur

L’acteur voyage dans le token (il fait partie de sa charge). Le dispatcher route donc par acteur, sans table à plat terminal → acteur : la voie historique en avait besoin parce que le détour par le texte BP3 perdait l’acteur ; l’arbre, lui, le conserve.

2. Résoudre le pitch

Si l’acteur a un tuning, son resolver traduit le token en fréquence via les six couches (S9) :

"Sa_^" → parse → note="sa", register=2
       → alphabet: sa = degré 0
       → octave: register 2 - défaut 1 = +1
       → tuning: degré 0 → step 0
       → tempérament: step 0 → ratio 1/1
       → freq = baseHz × ratio × period_ratio = 480 Hz

3. Lire les contrôles

Les contrôles non-temporels (vélocité, timbre, panoramique) sont déjà résolus par le moteur, rangés sur le token (controls). Le dispatcher les lit — il ne les recalcule pas.

4. Le CV engine

Les objets CV (Control Voltage — enveloppes ADSR, LFO, ramps) sont une couche à part entière. Déclarés en tête de scène et placés dans la grammaire comme des symboles ordinaires, ils sont résolus par le dispatcher en bus audio (Web Audio), en séquences de messages CC (MIDI), ou en paramètres interpolés (OSC) selon le transport de destination.

5. Router

Deux flux :

  • Terminaux → transport de l’acteur (acteur.transport.send({frequency, duration, controls}))
  • Backticks → évaluation de code (acteur.eval ou tag pour les orphelins)

La lecture symbolique (afficher les bols, mots ou nombres joués) n’est pas un troisième transport : c’est une vue de l’arbre de dérivation, au même titre que la visualisation.

6. Gestion avancée

  • Simultanéité (!) : même timestamp, plusieurs sorties
  • Mode boucle : re-dérivation optionnelle au bouclage (la grammaire produit des variations à chaque cycle)
  • Remplacement à chaud : recompilation à chaud, quantisée par défaut (S11)

Transports

Implémentés : Web Audio et MIDI. Prévu : OSC, DMX.

TransportProtocoleUsageÉtat
Web AudioAPI navigateurSon dans le navigateurImplémenté
MIDIWeb MIDI APIDAW, hardware, synthésImplémenté
OSCUDPscsynth, Processing, TouchDesignerPrévu
DMXOSC/serialLumières, moteursPrévu

Les transports sont universels — pas d’état, pas de session. Le dispatcher convertit l’événement en appel natif et l’envoie, sans attendre de réponse.


Évaluation de code

Aujourd’hui : JS inline (new Function()) dans le navigateur. Prévu : sessions de code externes (sclang, Python, GHCi+Tidal) via stdin/TCP/WebSocket.

L’évaluation de code n’est pas un simple adaptateur de sortie — c’est un composant bidirectionnel :

DirectionCe qui se passe
Dispatcher → évaluateurCode envoyé au temps T (playback)
Évaluateur → dispatcherValeur retournée (résolution de paramètres)

Trois temps (S4) :

  1. Init : backticks orphelins → avant la dérivation
  2. Playback : backticks dans le flux → au temps T
  3. Résolution : backticks-paramètres → évalués, valeur injectée
ÉvaluateurLangageÉtat
JS inlineJavaScript (new Function())Implémenté
sclangSuperColliderPrévu
PythonPython (Pyodide ou exec)Prévu
GHCi + TidalHaskellPrévu

Les trois interfaces

InterfaceEntreFormatÉtat
Interface 1Frontal → moteurArbre de scène (entrée directe)Existe
Interface 2BPx → DispatcherTokens datés (projection de l’arbre, acteur + contrôles inclus)Existe
Interface 3Dispatcher → Transports + évaluateursMessages horodatés + codeExiste (Web Audio + MIDI + JS)

L’interface 1 est le contrat central : les deux frontaux (BPScript et BP3) produisent le même arbre, et le moteur le consomme tel quel.


Ce qu’il faut retenir

  1. 3 étapes de compilation : tokenizer → parser → arbre de scène
  2. L’arbre est la source unique : la suite plate de tokens en est une projection
  3. L’acteur voyage dans le token : le dispatcher route par acteur, sans table à plat
  4. Un resolver par acteur : même note, fréquences différentes selon le tuning
  5. Contrôles résolus en amont : le dispatcher les lit, il ne les recalcule pas
  6. Deux flux : terminaux → transports, backticks → évaluateurs ; le texte = une vue
  7. Le texte BP3 ne sert qu’à prouver la parité avec le moteur natif

Glossaire

  • Tokenizer : Étape 1 — découpe le source en tokens, lit alphabets et octaves
  • Parser : Étape 2 — construit l’AST (et expanse les macros)
  • Arbre de scène : Forme normalisée produite par le frontal, entrée directe du moteur — chaque feuille porte son acteur et sa charge opaque
  • BPx : Portage TypeScript du moteur de Bernard Bel ; dérive l’arbre dans le navigateur
  • Projection : La suite plate de tokens datés, vue de l’arbre par calques (temps, identité, contrôles, acteur…)
  • Resolver : Composant aval (1 par acteur) qui traduit token → fréquence via les six couches pitch
  • CV engine : Composant aval qui résout les objets CV (ADSR, LFO, ramp) en bus audio ou messages CC
  • Contrôles résolus : Les () runtime, rangés par le moteur sur chaque terminal, lus tels quels par le dispatcher
  • Transport : Protocole de sortie pour les données horodatées (Web Audio, MIDI, OSC)
  • Parité : Comparaison de la sortie au moteur natif de Bernard Bel, via un encodage en texte BP3

Liens dans la série

  • S3 — Types, acteurs et rattachements — comment les acteurs sont déclarés
  • S4 — Les backticks — init, playback, résolution
  • S9 — Le système de hauteurs — ce que le resolver fait
  • S11 — En live — remplacement à chaud et mode boucle
  • S12 — L’EBNF — la grammaire que le parser implémente
  • S13 — L’AST — ce que le parser produit
  • B13 — PolyMake — l’algorithme d’expansion polymétrique

Prérequis : S3, S9 Temps de lecture : 14 min Tags : #BPscript #architecture #compilateur #BPx #dispatcher #acteurs


Prochain article : S11 — En live : modifier pendant que ça joue


← Retour à l’index