La boite à déclenchement

Proposition formelle d’une interface universelle dans un contexte in vivo


La boite à déclenchement from Sébastien Clara on Vimeo.

Genèse

Durant mes études de musicologie et plus particulièrement durant ma seconde année de Master, spécialisé dans l’informatique musicale, j’ai eu l’occasion de tester plusieurs logiciels destinés à la création sonore. Un grand nombre de logiciel repose sur le paradigme de la programmation modulaire dont Max Mathews, en 1960, nous proposa le premier logiciel pour l’informatique musicale, MUSIC III, sur ce concept. Les principes de la programmation musicale sont les mêmes sur les logiciels actuels ; les plus importants sont Max/Msp, PureData, OpenMusic, Csound ou SuperCollider. Certes, ils ont des spécificités et des usages particuliers, mais ils reposent sur le même concept et utiliser l’un d’eux reste lié à notre apprentissage ou à une démarche idéologique.

La fin de mes études annonce mon indépendance et il devient alors important d’affirmer mon choix d’outil. Cette appropriation passe par la réalisation de projets concrets, mais aussi par l’élaboration d’une boite à outils spécifiques. Ce raisonnement m’a conduit à concevoir un système pour accompagner la musique vivante, sous le logiciel SuperCollider. Pour assister une performance musicale, l’informatique est généralement structurée par séquence que l’on commande par des déclenchements. Ils sont générés par une personne, un capteur autonome ou un autre outil. De fait, disposer d’une boite à déclenchement me permet d’assurer l’interface entre le contrôle et l’action à exécuter, ou plus particulièrement, entre le musicien et l’instrument électronique.

Nous avons donc formalisé notre boite par des couches spécifiques, permettant une construction versatile, afin de répondre à n’importe quelle volonté artistique. Pour étayer notre propos, nous l’avons développée à un usage particulier, la lecture d’échantillons. Cependant, l’adaptation de la boite à déclenchement est un acte pour spécialistes. Pour se plier à certaine contrainte, elle demande un développement sous SuperCollider. La boite à déclenchement est une proposition d’une organisation structurelle pour accompagner les musiques vivantes ou une autre activité artistique in vivo. En tant que Réalisateur en Informatique Musicale, notre système est un outil spécifique permettant une réalisation formalisée sur une courte période de temps.

Principe

La boite à déclenchement dispose de trois couches distinctes pour la préparer à un usage caractéristique. La première couche nous sert à configurer notre système. Cette étape s’effectue par l’utilisation de fichier texte que notre boite consultera lors de son initialisation. Ce moyen donne l’opportunité de personnaliser sa boite, dont le développement final produit un instrument spécifique. Notre réalisation, une variation de la boite à déclenchement, lit des échantillons sonores. Pour cet emploi, nous configurons notre boite à l’aide de deux fichiers. L’un désigne les échantillons et leur emplacement dans notre système de fichiers. Le second permet d’élaborer notre séquence d’actions, composée de l’ordre d’apparition des échantillons, mais nous pouvons aussi exécuter d’autres actions. Par exemple, nous pouvons commander par un déclenchement un arrêt progressif de tous les échantillons en cours de lecture ou un arrêt brutal ; ces actions sont déterminées par les possibilités de l’instrument en aval de notre système. Notre boite à déclenchement pour lire des échantillons n’effectue que cette tâche, et ses fichiers de configuration permettent de répondre à l’expressivité musicale de chaque individu suivant ses personnalisations. Le but de cette première étape est donc de planifier le comportement du système avec l’aide de fichiers texte.

La deuxième couche est notre interface homme-machine. Ici nous l’adaptons à répondre à un geste, à un croisement d’informations de plusieurs capteurs autonomes ou aux données délivrées par un autre logiciel. Ces propositions ne sont pas du ressort de la boite à déclenchement, mais de celles de l’artiste et du Réalisateur en Informatique Musicale. Notre boite attend seulement un stimulus pour effectuer l’action appropriée. Les protocoles de communication MIDI et OSC permettent de sérialiser notre boite à des équipements de tous genres, une percussion électronique, un capteur de mouvement ou un logiciel de programmation multimédia tel que Processing ou openFrameworks.

Cette étape est primordiale et demande un certain investissement afin d’établir un taux de corrélation satisfaisant entre le geste et son capteur, et d’établir un ambitus suffisamment large d’expression musicale. Lorsque les modalités d’interactions sont établies, nous les codons dans notre programme, cette activité demande une certaine compétence et connaissance de l’informatique mais reste triviale pour un spécialiste. La phase délicate de cette étape est de trouver le capteur approprié à un type d’expression et de déterminer un lien entre eux suffisamment éloquent. Après, lier la boite à cette interaction est juste un acte de routage. Pour notre réalisation, nous sommes restés frugal et nous avons utilisé une interface graphique et le clavier de l’ordinateur pour piloter notre boite à déclenchement spécialisée dans la lecture d’échantillons sonores.

La dernière couche de notre système exécute des actions, fournies durant la phase d’initialisation, en réponse à des déclenchements. Cette dernière étape reste aussi large et elle dépend du projet artistique et des caractéristiques de l’instrument utilisé. Toutefois, la modularité de SuperCollider nous permet d’élaborer des instruments synthétiques variés ou d’utiliser les protocoles de communication MIDI ou OSC pour attaquer un instrument physique ou pour réaliser des actions non musicales comme commander des lumières, un logiciel multimédia ou un automate quelconque. Pour notre démonstration, nous utilisons un instrument de lecture d’échantillon écrit sous SuperCollider.

La formalisation de la boite à déclenchement nous permet de structurer la conception d’un système d’interface capable de s’adapter à toutes les démarches individuelles. Chaque usage spécifique demande un développement singulier, toutefois après cette phase, le système devient un instrument particulier que l’on peut s’approprier grâce à l’étape de configuration. Les diagrammes suivants montrent la séquence du fonctionnement de la boite à déclenchement, nous avons décrit le principe général et celui de notre réalisation spécifique.


Réalisation

La première partie de cette publication nous a permis d’exposer la formalisation d’une interface du point de vue artistique. Cette seconde partie poursuit l’analyse, mais elle se focalise sur la technique. Cela s’effectuera par la réalisation d’une facette de notre boite de déclenchement pour la lecture d’échantillons. Néanmoins, cette réalisation structura la partie technique qui pourra servir à un autre développement. L’instrument électronique de lecture d’échantillons est une librairie conçut durant mon stage au GMEM. Celle-ci est commandée par des messages OSC dont voici la spécification :

/preload intFile fileName
/play intLaunching intFile <floatfadeout(ms)>
/gain intLaunching floatDB <floatfade(ms)>
/stop intLaunching <floatfadeout(ms)>
/stopall <floatfadeout(ms)>
/maingain floatGainDB

Le contrôle s’effectuera par l’intermédiaire d’une interface graphique (une Gui, Graphical user interface) et par le clavier.

Gui dynamique de la boite à déclenchement pour la lecture d’échantillons

L’interface nous permet de suivre le déroulement des actions. Un curseur ou une boite de numéro règle le volume global. L’action de l’un met à jour l’autre. Nous pouvons choisir la prochaine action à exécuter grâce au champ Next de l’interface. Le bouton Go ou la barre d’espace déclenche la prochaine action prévue. Lorsque l’action est de lire un échantillon, des commandes apparaissent dynamiquement dans l’interface. Ils donnent le nom de l’action, dans notre exemple ce nom correspond à l’index de déclenchement, mais nous aurions pu mettre le numéro d’une mesure ou un autre discriminant. Ces commandes dynamiques offrent aussi la possibilité de régler le temps de fondu et l’arrêt de l’échantillon. De même, le bouton StopAll ou la touche Échap arrêtent tous les échantillons en cours de lecture suivant un temps de fondu aussi paramétrable.

La configuration du système s’appuie sur deux fichiers de configuration. L’un permet de donner le chemin absolu de nos échantillons et l’autre établit la séquence de nos actions. Ces deux fichiers seront importés et stockés dans des tableaux. La première ligne des fichiers me sert à donner le format des fichiers. Si nécessaire, nous utilisons le symbole arobase comme séparateur entre les champs d’une même action.

configPrelaod.txt :

fonctionPreload
{m.sendMsg(« /preload », 1, « /home/Documents/soundfiles/01-Son.aif »)}
{m.sendMsg(« /preload », 2, « /home/Documents/soundfiles/02-Son.aif »)}
{m.sendMsg(« /preload », 3, « /home/Documents/soundfiles/03-Son.aif »)}

configAction.txt :

fonctionAction@intLaunching@intFile@floatDB@floatFadeOut(ms)
{~actionPlay.value; }@1@1@0@10
{~actionPlay.value; }@2@2@0@20
{ m.sendMsg(« /stopall », 3000); }
{~actionPlay.value; }@3@3@0@30

Ainsi, l’indice du tableau des actions correspond à notre séquence d’action. Cela nous permettra de gérer dynamiquement l’affichage notre Gui. Effectivement, une action de lecture nous engendra des commandes, l’arrêt et le temps de fondu, dans la Gui. L’indice des actions correspondra à l’indice du tableau des objets dynamique. Il fait donc le lien entre les objets de la Gui et les échantillons.

La structure de la boite à déclenchement se divise en deux parties. La première partie est la phase d’initialisation. Elle importe les fichiers de configuration, précharge les échantillons. À la fin de cette partie, la boite est prêt à répondre aux stimulus. La seconde partie contient les réponses aux déclenchements et gère la Gui. Le diagramme des flux de données ci-dessous, nous montre ces deux parties. Pour alléger ce diagramme, nous n’avons pas mis toutes les interactions entre la tâche Gui et le tâche Action.

Analyse globale de la boite à déclenchement pour la lecture d’échantillon

La phase d’initialisation se trouve en haut du diagramme et elle se termine à la création de la Gui. La seconde partie réagit aux actions de l’utilisateur. L’objet startButton permet d’exécuter l’action prévue dans le fichier de configuration, configAction.txt, suivant le numéro d’action choisi par l’objet nextNum. Cela met à jour l’indicateur de l’action courante (currentTextValue), exécute l’action et ajout un à l’objet nextNum. Toutefois, cette action est l’une des actions qu’effectue la tâche action. Celle-ci regroupe toutes les actions possibles de la boite à déclenchement, le diagramme suivant énumère celles-ci.

Tâche Action de la boite à déclenchement pour la lecture d’échantillons

Cette tâche nous permet de configurer notre boite à nos besoins, ici la lecture d’échantillons. Continuons à détailler les différentes sous-tâches d’Action.

La tâche StartButtonAction

Cette tâche répond aux stimulus de l’utilisateur par l’interface ou la barre d’espace du clavier. Elle déclenche l’action prévue par le champ nextNum et met à jour la Gui. À ce stade du développement, la tâche startButtonAction interprète les fonctions du fichier de configuration configAction.txt, par l’intermédiaire de la variable actionTab. Ces actions ont deux types : l’un correspond à l’envoi d’un message OSC, on peut ainsi prévoir un arrêt global par un déclenchement ou gérer la progression du gain d’un échantillon. Et l’autre type d’action est la fonction actionPlay qui exécute la lecture des échantillons sonores, nous la détaillons par la suite.

Diagramme de la tâche StartButtonAction

Tâche startButtonAction de la boite à déclenchement pour la lecture d’échantillons

Séquence de la tâche StartButtonAction

Le numéro de l’action prochaine est-il valide ? nextNum est-il correcte ?
Oui
Mise à jour de currentTextValue (currentTextValuenextNum).
Interprétation de la fonction action.
Mise à jour de nextNum ( nextNumnextNum + 1).
Gestion de la couleur de la Gui : en cours de lecture, nextNum en noir.
Non
Message d’avertissement sur la console.
Gestion de la couleur de la Gui : nextNum en rouge.

Code de la tâche StartButtonAction

startButton.action = {
if( nextNum.value < actionTab.size, {
currentTextValue.string = nextNum.value.asString;
actionTab[nextNum.value][0].compile.value.value;
nextNum.value = nextNum.value+1;
if (nextNum.stringColor != nextNum.normalColor, {nextNum.stringColor = nextNum.normalColor});
}, {
("\n\tThe maximum action is reached.\n").postln;
nextNum.stringColor = nextNum.typingColor;
}); };

La fonction actionPlay

Cette fonction nous permet de lancer la lecture d’un échantillon. La tâche de lecture est complexe car elle gère aussi l’interface graphique. De fait, pour simplifier son utilisation, nous avons écrit une fonction dédiée à cet usage.

Séquence de la fonction actionPlay

L’échantillon est-il déjà joué ?
Oui
On n’effectue aucune action.
Non
Envoi d’un message OSC /play.
Création de l’interface utilisateur dynamique (nom, fondu, bouton d’arrêt).
Mise à jour de la Gui.

Code de la fonction actionPlay

~actionPlay = {
if ( dynamiqueObjetTab[nextNum.value, 0].isNil, {
m.sendMsg("/play", actionTab[nextNum.value][1].asInteger, actionTab[nextNum.value][2].asInteger, actionTab[nextNum.value][3].asInteger, actionTab[nextNum.value][4].asInteger);
dynamiqueObjetTab.put(nextNum.value, 0, Button(fenetre).states_([["Stop"++currentTextValue.string]]).action_({
arg bouton;
var index = bouton.states[0][0].copyToEnd("Stop".size).asInteger;
m.sendMsg("/stop", actionTab[index][1].asInteger, dynamiqueObjetTab[index, 1].value);
dynamiqueObjetTab.rowAt(index).do(_.destroy);
dynamiqueObjetTab[index, 0]=nil; dynamiqueObjetTab[index, 1]=nil; dynamiqueObjetTab[index, 2]=nil;
}) );

dynamiqueObjetTab.put(nextNum.value, 1, NumberBox(fenetre).align_(\center).clipLo_(0).step_(100).scroll_step_(100).value_(actionTab[nextNum.value][4].asFloat) );
dynamiqueObjetTab.put(nextNum.value, 2, StaticText(fenetre).string_(actionTab[nextNum.value][1].asString).align_(\center) );
dynamiqueObjetTab.rowsDo({|objet|
if(objet[0].notNil, {
dynamiqueStopLayout.add(objet[0]);
dynamiqueFadeLayout.add(objet[1]);
dynamiqueTextLayout.add(objet[2]);
})
});
}); };

La tâche stopAllButtonAction

Cette tâche stoppe tous les échantillons en cours de lecture. On déclenche son action à l’aide d’un bouton sur la Gui ou de la touche Échap. De plus, cette tâche utilise une boite numérique pour régler le temps de l’extinction progressive du son.

Séquence de la tâche stopAllButtonAction

Envoi d’un message OSC /stopall.
Mise à jour de la Gui.
Remise à zéro du tableau dynamiqueObjetTab.

Code de la tâche stopAllButtonAction

stopAllButton.action = {
m.sendMsg("/stopall", fadeTimeNum.value);
dynamiqueObjetTab.rowsDo({|objet| if(objet[0].notNil,{objet.do(_.destroy)}) });
dynamiqueObjetTab = Array2D(actionTab.size, 3);
};

La tâche objetDynamiqueAction

Avec cette tâche, nous pouvons sélectionner l’arrêt d’un échantillon en cours de lecture et de choisir un temps de fondu pour cette action. De plus, elle met à jour la Gui en effaçant les commandes liées à cet échantillon.

Diagramme de la Tâche objetDynamiqueAction

Tâche objetDynamiqueAction de la boite à déclenchement pour la lecture d’échantillons.

Séquence de la tâche objetDynamiqueAction

Déterminer l’index de l’objet dynamique sélectionné.
Envoi d’un message OSC /stop.
Mise à jour de la Gui.
Mise à jour du tableau dynamiqueObjetTab.

Code de la Tâche objetDynamiqueAction

Son code est inclus dans la fonction actionPlay. En effet, cela permet de créer des objets dynamiquement, au moment de l’exécution de la fonction de lecture. L’action du bouton dynamique trouve son lien avec l’échantillon à stopper grâce à son nom. Effectivement, lors de la création du bouton nous utilisons comme nom le chaîne de caractère Stop, et nous concaténons à celle-ci l’index du tableau des actions auquel correspond cette action. Par conséquence, cet index nous permettra de connaître le numéro de déclenchement de l’échantillon. Son code source est le suivant :

dynamiqueObjetTab.put(nextNum.value, 0, Button(fenetre).states_([["Stop"+ currentTextValue.string]]).action_({
arg bouton;
var index = bouton.states[0][0].copyToEnd("Stop".size).asInteger;
m.sendMsg("/stop", actionTab[index][1].asInteger, dynamiqueObjetTab[index, 1].value);
dynamiqueObjetTab.rowAt(index).do(_.destroy);
dynamiqueObjetTab[index, 0]=nil; dynamiqueObjetTab[index, 1]=nil; dynamiqueObjetTab[index, 2]=nil;
}) );

La tâche d’interruption deadNodeAction

Cette tâche fonctionne en tâche de fond. Elle permet de s’apercevoir de la fin naturelle de la lecture d’un échantillon. Alors, elle met à jour la Gui en effaçant les commandes liées à cet échantillon.

Diagramme de la tâche d’interruption deadNodeAction

Tâche deadNodeAction de la boite à déclenchement pour la lecture d’échantillons.

Séquence de la tâche d’interruption deadNodeAction

Déterminer l’index de l’objet
Si l’index existe :
Mettre à jour la Gui.
Mettre à jour le tableau dynamiqueObjetTab.
Sinon on n’effectue rien.

Code de la tâche d’interruption deadNodeAction

rechercheIndex = {arg tab, searchString;
var retourneIndex;
tab.do({|item, index| if(searchString == item[1], {retourneIndex=index}) });
retourneIndex;
};
OSCFunc({|msg|
var index = rechercheIndex.value(actionTab, ~centralisationActions.colAt(2).find([msg[1]]).asString );
if( dynamiqueObjetTab.rowAt(index)[0].notNil,
{ { dynamiqueObjetTab.rowAt(index).do(_.destroy);

dynamiqueObjetTab[index, 0]=nil;
dynamiqueObjetTab[index, 1]=nil;
dynamiqueObjetTab[index, 2]=nil;
}.defer;
});
}, '/deadNode' );

La tâche volumeAction

Pour modifier le gain général de la boite à déclenchement, l’utilisateur dispose de deux contrôles, un curseur vertical et d’une boite numérique. La modification d’un des objets graphique du volume à pour conséquence l’envoi du message OSC /maingain et de mettre à jour l’autre objet.

Diagramme de la tâche volumeAction

Tâche volumeAction de la boite à déclenchement pour la lecture d’échantillons.

Code de la tâche volumeAction

volumeSlider.action = {
volumeNum.value = dbControl.map(volumeSlider.value);
m.sendMsg("/maingain", volumeNum.value);
};
volumeNum.action = {
m.sendMsg("/maingain", volumeNum.value);
volumeSlider.value = dbControl.unmap(volumeNum.value);
};

Cet exercice est une application de la boite à déclenchement. Le diagramme de flux de données principale comporte trois tâches que nous pouvons réutiliser pour un autre usage. Ces trois tâches sont :

  • L’importation qui permet de configurer son système grâce à des fichiers texte.
  • Les actions qui répondent à un stimulus quelconque suivant les fichiers de configurations. Nous adaptons les spécifications de la boite à déclenchement dans cette tâche.
  • Et l’interface graphique qui nous donne le moyen d’agir et de contrôler l’évolution des déclenchements.

Cette boite est suffisamment versatile pour répondre à n’importe quelle exigence. Effectivement, SuperCollider nous offre la possibilité d’utiliser les protocoles de communication MIDI et OSC. Cela nous permet d’interfacer les systèmes utilisant l’un de ces protocoles. De plus, nous pouvons exécuter des scripts exogènes à SuperCollider. De fait, ce système est limité uniquement par l’imagination de son utilisateur.

Sébastien Clara – Septembre 2012

Publicités