Comment mettre en pause une position de modulation et la reprendre en sens inverse au déclenchement suivant dans SuperCollider
Pour mettre en pause une position de modulation et la reprendre en sens inverse, laissez son générateur de phase fonctionner avec un incrément nul, puis attribuez à cet incrément une valeur négative au déclenchement suivant. Laissez l’entrée de réinitialisation de phase déconnectée de ces déclenchements afin que le mouvement reprenne depuis la position maintenue.
Ce tutoriel met en œuvre ce comportement dans SuperCollider : un balayage ascendant de la hauteur s’arrête au premier déclenchement et retrace son parcours au deuxième. Il s’adresse aux concepteurs sonores qui souhaitent créer un geste de modulation répétable contrôlé par des événements successifs. Le même signal de phase pourra ensuite piloter un autre paramètre.
Que faut-il préserver pendant la pause ?
Considérez la modulation comme deux opérations liées : une position évolue au fil d’un cycle, puis une fonction de correspondance convertit cette position en une valeur utile pour un paramètre. Ici, la position va de zéro à un, et la fonction de correspondance la convertit en fréquence d’oscillateur.
Dans SuperCollider, Phasor fournit la position. Son argument rate définit l’incrément par échantillon ; son entrée de déclenchement fait plutôt passer la position à resetPos. Il s’agit de deux commandes distinctes : un déclenchement utilisé pour changer de direction ne doit pas aussi réinitialiser la position. Documentation de Phasor
L’implémentation conserve un niveau interne et lui ajoute le taux. Il en découle qu’un taux nul maintient le niveau, tandis qu’un taux négatif le fait diminuer. Le patch ci-dessous applique directement ce principe et préserve l’état du générateur pendant toute la pause. Implémentation de TriggerUGens.cpp dans SuperCollider 3.14.0
Que doit faire chaque déclenchement successif ?
Définissez la séquence d’événements avant de connecter le son. Dans ce patch, le mouvement commence immédiatement vers l’avant. Chaque déclenchement suivant fait passer à l’état suivant :
| Déclenchements reçus | État | Multiplicateur de direction |
|---|---|---|
| 0 | Avancer | +1 |
| 1 | Maintenir la position actuelle | 0 |
| 2 | Reprendre en sens inverse | −1 |
| 3 | Maintenir la position actuelle | 0 |
| 4 | Reprendre vers l’avant | +1 |
Ce cycle à quatre états distingue les deux états de maintien : l’un précède le mouvement en sens inverse, l’autre le mouvement vers l’avant. Les gestes répétés sont ainsi prévisibles, sans nécessiter de commande de direction supplémentaire.
PulseCount s’incrémente lorsque son entrée passe d’une valeur non positive à une valeur positive. Le modulo quatre appliqué au compteur produit les indices répétitifs de 0 à 3. Select choisit ensuite le multiplicateur correspondant dans [1, 0, -1, 0]. Voici les rôles documentés de PulseCount et de Select.
Comment construire le patch complet ?
Utilisez une installation locale de SuperCollider avec une sortie audio fonctionnelle ; l’exemple suit la documentation de la version 3.14.0. Il utilise des générateurs intégrés et produit son propre son : aucun fichier d’échantillons ni aucune extension ne sont nécessaires. Évaluez le bloc entier une fois, puis attendez le message indiquant que tout est prêt.
waitForBoot démarre le serveur si nécessaire. La définition est envoyée avant la création du synthé, et s.sync attend la fin des opérations en attente sur le serveur. Consultez la documentation de Server et la documentation de SynthDef.
(
s = Server.default;
s.waitForBoot({
SynthDef(\pauseReverseMod, {
|out = 0, t_step = 0, cycleSeconds = 8, amp = 0.08|
var state, direction, increment, phase, frequency, sound;
state = PulseCount.kr(t_step) % 4;
direction = Select.kr(state, [1, 0, -1, 0]);
// A full 0..1 traversal takes cycleSeconds seconds.
increment = direction * SampleDur.ir
/ cycleSeconds.max(0.01);
// State-changing triggers never reset this phase.
phase = Phasor.ar(
trig: 0,
rate: increment,
start: 0,
end: 1,
resetPos: 0
);
frequency = LinExp.ar(phase, 0, 1, 220, 880);
sound = SinOsc.ar(frequency, 0, amp);
Out.ar(out, sound ! 2);
Poll.kr(Impulse.kr(2), state, "state");
Poll.kr(Impulse.kr(2), A2K.kr(phase), "phase");
}).add;
s.sync;
~pauseReverseSynth = Synth(\pauseReverseMod);
"Ready: evaluate ~pauseReverseSynth.set(\\t_step, 1);".postln;
});
)
L’incrément correspond à la direction multipliée par la durée d’un échantillon et divisée par la durée du cycle. SampleDur renvoie l’inverse de la fréquence d’échantillonnage du serveur ; la durée de huit secondes ne suppose donc pas une fréquence d’échantillonnage particulière. Documentation de SampleDur
La fonction de correspondance de hauteur couvre la plage de 220 à 880 Hz. LinExp convertit une plage d’entrée linéaire en une plage de sortie exponentielle ; les valeurs de début et de fin de la plage de destination doivent être non nulles et avoir le même signe. Les deux valeurs satisfont cette condition. Documentation de LinExp
J’ai vérifié l’exemple SuperCollider à l’aide de la documentation consultée. L’environnement de recherche ne disposait pas du moteur d’exécution SuperCollider ; je n’ai donc pas exécuté ni écouté ce patch. Une vérification arithmétique déterministe distincte a confirmé la séquence de directions et le maintien lorsque l’incrément est nul.
Comment mettre en pause et reprendre en sens inverse ?
Après le message indiquant que tout est prêt, laissez la hauteur monter pendant environ deux secondes. Évaluez ensuite une fois cette commande d’interaction :
~pauseReverseSynth.set(\t_step, 1);
L’état doit passer à 1, et la phase affichée doit se stabiliser sur une valeur. Attendez un instant, puis évaluez de nouveau la même commande. L’état doit passer à 2, et la phase doit diminuer à partir de la position maintenue. Une troisième évaluation met en pause le balayage descendant ; une quatrième le relance vers l’avant.
Le préfixe t_ fait de t_step une commande de déclenchement. La valeur qui lui est attribuée génère une impulsion à la fréquence de contrôle, ce qui permet de réutiliser la même commande sans envoyer manuellement une valeur zéro entre des évaluations ordinaires espacées. Documentation sur les commandes de déclenchement dans SynthDef
À titre d’exemple de calcul temporel, un cycle de huit secondes parcourt 0,125 de sa plage par seconde. Une pause après deux secondes maintient donc la phase à environ 0,25. Après n’importe quelle durée de pause, une seconde de mouvement en sens inverse la ramène à environ 0,125. Il s’agit de valeurs attendues calculées ; le moment où vous exécutez la commande détermine la position réelle de l’arrêt.
Pour terminer, évaluez la commande de nettoyage :
~pauseReverseSynth.free;
Comment vérifier que la position est bien restée immobile ?
Utilisez la phase affichée comme principal indicateur. Elle doit augmenter pendant le mouvement vers l’avant, rester constante dans l’un ou l’autre état de maintien, puis diminuer pendant le mouvement en sens inverse jusqu’à la limite du cycle.
Le patch affiche une valeur deux fois par seconde : l’affichage échantillonne donc le comportement sans montrer chaque échantillon. Observez plusieurs valeurs consécutives pendant la pause, puis comparez-les aux premières valeurs descendantes après la reprise. Poll sert au débogage des valeurs des générateurs ; retirez les deux lignes d’affichage lors de l’adaptation de la définition pour un usage courant. Documentation de Poll
Ce test est plus instructif qu’une écoute seule. Un son stable indique que la valeur de destination ne change plus, tandis qu’une phase affichée stable confirme que la position sous-jacente s’est elle aussi arrêtée. Pour faciliter l’interprétation du résultat, ne modifiez pas la fonction de correspondance pendant le test.
Pourquoi le patch redémarre-t-il, saute-t-il ou manque-t-il un déclenchement ?
Il redémarre à la reprise. Vérifiez si l’événement atteint aussi l’entrée de déclenchement de Phasor. Cette entrée réinitialise explicitement la position. Dans ce patch, elle reste à zéro ; les événements alimentent uniquement le compteur d’états. Documentation de Phasor
La phase saute après avoir atteint une extrémité. Cet exemple fonctionne délibérément en boucle. L’implémentation ramène la phase dans la plage configurée, y compris pendant son déplacement négatif. Une fois que le mouvement en sens inverse passe sous zéro, la phase revient près de la limite supérieure, ce qui provoque un saut de hauteur avec cette fonction de correspondance en rampe. Commencez par tester la pause et l’inversion loin de cette limite. Implémentation de Phasor
Plusieurs commandes rapides ne produisent pas plusieurs états. Le compteur a besoin de transitions montantes distinctes. Les commandes de déclenchement fonctionnent à la fréquence de contrôle : ne supposez donc pas que plusieurs commandes reçues pendant un même bloc de contrôle deviennent des événements distincts. Commencez par utiliser des commandes espacées ; pour les séquences très rapprochées, concevez la source d’événements de façon à produire des impulsions distinctes. Documentation de PulseCount, documentation de SynthDef
Le son change alors que la phase est maintenue. Vérifiez si vous modifiez aussi la fonction de correspondance de destination ou un autre paramètre sonore. Pour ce test, gardez les limites de hauteur fixes et utilisez la phase affichée pour isoler le transport de modulation.
Le geste terminé consiste à avancer, maintenir, repartir en sens inverse, maintenir, puis repartir vers l’avant. Une fois que la séquence fonctionne correctement, conservez le compteur d’états et le générateur de phase comme source de modulation réutilisable, puis adaptez la fonction de correspondance de destination au son que vous créez.
Sources
- Phasor — Aide de SuperCollider 3.14.0
- TriggerUGens.cpp — SuperCollider, version 3.14.0
- PulseCount — Aide de SuperCollider 3.14.0
- Select — Aide de SuperCollider 3.14.0
- Server — Aide de SuperCollider 3.14.0
- SynthDef — Aide de SuperCollider 3.14.0
- SampleDur — Aide de SuperCollider 3.14.0
- LinExp — Aide de SuperCollider 3.14.0
- Poll — Aide de SuperCollider 3.14.0
