S7) L’instant partagé

! et <!

Simultanéité entre runtimes, triggers entrants et composition distribuée

Un seul point dans le temps. Plusieurs événements simultanés — du son, une percussion, un trigger lumineux. C’est !. Et quand on veut attendre un signal venu d’ailleurs, c’est <!.

Note : les exemples entre runtimes (SC + Python simultanés) décrivent l’architecture cible. Aujourd’hui, la simultanéité fonctionne au sein du navigateur (JS + Web Audio).

Où se situe cet article ?

! est l’opérateur de simultanéité de BPscript. Il est exclusivement temporel : il attache des événements (triggers, gates, cv) à un même instant.


! — « à cet instant, aussi ça »

! attache un ou plusieurs éléments secondaires à un point dans le temps. Le premier élément (le primaire) définit la position et la durée. Tout ce qui suit ! se déclenche au même instant.

 

Sa!dha                       // joue Sa (gate:sc) + déclenche dha (trigger:sc)
Sa!visual_glow               // joue Sa (gate:sc) + active visual_glow (gate:processing)
Sa!dha!spotlight             // gate + trigger + trigger, trois runtimes au même instant
-!dha                        // silence + trigger (percussion sur un temps vide)

 

Règles

  • Avant ! : le primaire — doit occuper du temps (gate, cv, ou silence -)
  • Après ! : les secondaires — des symboles (triggers, gates ou cv) qui se déclenchent au même instant :

– Un trigger → durée zéro (impulsion)
– Un gate → hérite la durée du primaire
– Un cv → hérite la durée du primaire

! n’accepte que des symboles après lui — c’est un opérateur purement temporel. Pour changer un flag à cet instant, on place [flag=N] dans le RHS, à la position voulue : c’est un mécanisme distinct, géré par le moteur (voir S6).


Simultanéité entre runtimes

C’est là que ! prend tout son intérêt. Un seul point temporel peut déclencher des événements dans plusieurs runtimes — sans passer par la polymétrie :

 

@actor sitar  alphabet:raga    transport:supercollider
@actor lights alphabet:lights  transport:python

// Un seul point temporel, deux runtimes
S -> sitar.Sa!sitar.dha!lights.spotlight sitar.Re!sitar.ti sitar.Ga!sitar.dha!lights.fadeout sitar.Pa

 

À chaque position :

  • Sa!dha!spotlight → SuperCollider joue Sa et dha, Python déclenche spotlight
  • Re!ti → SuperCollider joue Re et ti
  • Ga!dha!fadeout → SuperCollider joue Ga et dha, Python déclenche fadeout

La synchronisation est exacte parce que c’est BP3 qui calcule le moment — les runtimes reçoivent leurs instructions pour le même instant, sans drift (décalage progressif accumulé) ni latence différentielle.


Macros de groupes simultanés

Si un même ensemble d’événements simultanés revient souvent, une macro le factorise :

 

// Macros — réécriture agnostique
@macro scene_a(x) = x!visual_glow!spotlight
@macro scene_b(x) = x!visual_strobe!flash

// Usage
S -> scene_a(Sa) scene_b(Re) scene_a(Ga)

// Après expansion :
// Sa!visual_glow!spotlight Re!visual_strobe!flash Ga!visual_glow!spotlight

 

La macro ne sait pas que visual_glow est du Processing ni que spotlight est du Python (S2). Elle recopie. Le typage est vérifié après expansion.

C’est le mécanisme naturel pour les scènes multimédia : on définit une palette de combinaisons son+lumière+vidéo, puis on les utilise dans la grammaire comme des symboles ordinaires.


<! — attendre un signal externe

<! est le miroir de ! : au lieu d’envoyer un signal, on attend un signal venu de l’extérieur. C’est un point de synchronisation — la dérivation se met en pause jusqu’à ce que le signal arrive.

 

trigger sync1()                  // déclaré trigger

// Attend en silence, puis joue
S -> -<!sync1 Sa Re Ga

// Joue Sa, attend, puis continue
S -> Sa<!sync1 Re Ga

// Attend seul puis démarre
S -> <!sync1 Sa Re Ga

 

La syntaxe reprend la logique des flèches : ! sort (comme ->, on envoie), <! entre (comme <-, on reçoit).

La source du signal

La source du signal (MIDI, OSC, capteur, autre instance BPscript) est configurée dans le mapping (S9), pas dans le langage. Le mapping est exprimé via la directive @map :

 

@map cc:64 -> <!sync1            // un CC MIDI déclenche sync1
@map osc:/sync/downbeat -> <!sync2  // un message OSC déclenche sync2

 

BPscript ne sait pas d’où vient le signal — il sait juste qu’il doit attendre. La source peut être un clavier MIDI, un capteur, un message OSC (Open Sound Control), ou une autre instance de BPscript.


Composition distribuée

! et <! ouvrent la porte à la composition distribuée : plusieurs instances de BPscript tournant sur des machines différentes, synchronisées par des triggers.

Perspective : la composition distribuée décrite ici est une vision de l’architecture, pas encore implémentée. L’attente d’un signal externe (<!) et la synchronisation entre instances relèvent de la couche runtime/bridge en aval, à venir.

 

// Machine A — la mélodie
S -> Sa Re!sync Ga Pa!sync Dha Ni

// Machine B — attend les syncs de A
S -> <!sync rythme <!sync rythme

// Machine C — les lumières, déclenchées par A
S -> <!sync scene_a <!sync scene_b

 

La machine A envoie !sync (via OSC ou MIDI). Les machines B et C attendent <!sync. Le résultat : trois grammaires indépendantes, synchronisées par des points de rendez-vous.

C’est le même principe que la synchronisation par horloge MIDI dans un studio analogique — mais au niveau des structures grammaticales, pas des battements.


Chaîner ! et <!

Les deux opérateurs sont chaînables :

 

S -> Sa!dha<!sync1 Re Ga
// Joue Sa + déclenche dha, puis attend sync1, puis continue avec Re

 

L’ordre de lecture est gauche à droite : d’abord les triggers sortants !, puis l’attente <!.


Ce qu’il faut retenir

  1. ! = simultanéité temporelle — attache des symboles (triggers, gates, cv) à un point dans le temps
  2. Le primaire (avant !) définit la durée. Les secondaires (après !) se déclenchent au même instant
  3. ! n’accepte que des symboles — les mutations de flag s’écrivent [flag=N] dans le RHS, pas avec !
  4. Entre runtimes : un seul ! peut viser plusieurs runtimes au même instant (SC, Python… — architecture cible ; aujourd’hui en navigateur, JS / Web Audio)
  5. <! = attente — la dérivation se met en pause jusqu’à réception du signal
  6. Les macros de groupes simultanés factorisent les combinaisons récurrentes (son + lumière + vidéo)
  7. Le mapping (@map) définit la source des signaux entrants, pas le langage

Glossaire

  • Primaire : L’élément avant ! — doit occuper du temps (gate, cv ou silence)
  • Secondaire : Le symbole après ! — se déclenche au même instant que le primaire
  • Trigger sortant (!) : Signal envoyé vers un runtime au moment de l’exécution
  • Trigger entrant (<!) : Point de synchronisation — la dérivation attend un signal externe
  • Composition distribuée : Architecture où plusieurs instances BPscript sur des machines différentes se synchronisent par triggers
  • Groupe simultané : ensemble d’événements attachés au même instant, souvent factorisé dans une macro

Liens dans la série

  • S3 — Les types — pourquoi trigger est de durée zéro
  • S6 — Les mutations de flag [flag=N] — distinctes de !
  • S9 — Le mapping — comment configurer les sources de <!
  • S10 — Le runtime aval — comment les événements ! sont envoyés aux runtimes

Prérequis : S3, S6
Temps de lecture : 10 min
Tags : #BPscript #triggers #simultanéité #composition-distribuée #multimédia


Prochain article : S8 (à venir) — Patterns, captures et templates


Retour à l’index