Comment donner à chaque note jouée une réverbération dont la décroissance dépend de sa durée de maintien
Pour donner à chaque note jouée sa propre réverbération dépendant de sa durée, mesurez le temps entre son note-on et son note-off, associez cette durée à un temps de décroissance, puis appliquez le résultat à une réverbération dédiée à cette note. Laissez la réverbération de la note continuer après la coupure de sa source sonore.
Ce tutoriel met en place ce comportement dans SuperCollider, avec une entrée MIDI et un test de notes qui se chevauchent. Le résultat attendu est simple : une note maintenue brièvement laisse une courte traîne, tandis qu’une note maintenue plus longtemps laisse une traîne plus longue, même lorsque leurs réverbérations se chevauchent.
Pourquoi chaque note a-t-elle besoin de sa propre réverbération ?
Considérez deux notes qui se chevauchent. Vous maintenez la première pendant deux secondes et jouez la seconde pendant un quart de seconde. Si les deux alimentent une même réverbération, modifier son temps de décroissance pour la seconde note change aussi le traitement de la réverbération restante de la première. Un contrôle de décroissance partagé ne peut pas préserver deux réglages indépendants dans un même état de réverbération.
Le routage doit donc être le suivant : la source sonore de chaque note alimente sa propre réverbération, puis les sorties de ces voix traitées sont mixées. Il s’agit d’une déduction architecturale : des traînes indépendantes nécessitent des états de traitement indépendants, à moins que l’instrument ou l’effet n’assure déjà une séparation interne équivalente.\n\nUn patch polyphonique offre un cadre utile pour cela. Cycling ’74 explique comment poly~ de Max crée des instances de voix et utilise thispoly~ pour marquer chaque instance comme occupée ou disponible. Pour cette tâche, une voix doit rester occupée pendant toute sa traîne de réverbération, au lieu de redevenir disponible immédiatement au note-off. Cycling ’74 : Polyphonie
Il faut aussi tenir compte d’une contrainte temporelle : pendant une performance en direct, la durée finale de maintien est inconnue jusqu’à la fin de la note. La mise en œuvre ci-dessous utilise une décroissance provisoire pendant que la touche est enfoncée, puis attribue la décroissance mesurée à la libération. Si vous devez appliquer la décroissance finale dès le début, utilisez des durées de notes déjà disponibles dans une séquence ou effectuez ensuite le rendu des notes individuellement.
Que doit contrôler la durée de maintien ?
Distinguez trois contrôles : le relâchement d’amplitude de l’instrument, la décroissance de la réverbération et le niveau de sortie du signal traité. Un relâchement plus long de l’instrument continue d’envoyer du son dans la réverbération. Un niveau de signal traité plus élevé rend l’effet plus fort. Aucune de ces opérations, à elle seule, ne prolonge la décroissance de la réverbération.
La décroissance ne désigne pas non plus, en général, un temps d’arrêt exact. La documentation de la réverbération d’Ableton définit le contrôle Decay comme le temps nécessaire à la traîne pour atteindre un millième de son amplitude initiale, soit −60 dB. Cette distinction compte lorsque l’on compare un réglage de décroissance au moment où une traîne devient inaudible. Ableton : Référence des effets audio de Live, Reverb
Pour une première mise en œuvre, utilisez cette correspondance explicitement illustrative, avec toutes les valeurs en secondes :
decay = min(6.0, max(0.25, 0.25 + 1.5 × heldDuration))
| Durée de maintien | Décroissance attribuée |
|---|---|
| 0,10 seconde | 0,40 seconde |
| 0,25 seconde | 0,625 seconde |
| 1,00 seconde | 1,75 seconde |
| 2,00 secondes | 3,25 secondes |
| 4,00 secondes | 6,00 secondes, plafonnées |
Ces valeurs sont des choix de conception, et non des résultats acoustiques mesurés. Le minimum donne aux notes brèves une traîne audible ; le multiplicateur détermine l’influence du maintien sur l’effet ; le maximum limite la décroissance demandée. Commencez avec cette correspondance, puis ajustez ces trois paramètres en maintenant constants la vélocité des notes et le niveau du signal traité.
Créer une voix qui survit au note-off
L’exemple utilise GVerb, intégré à SuperCollider. Il accepte une entrée mono, produit deux canaux de sortie et expose revtime en secondes. Sa documentation signale également un pic de charge processeur à l’instanciation et des artefacts lorsque la taille de la pièce change. En conséquence, cet exemple conserve une taille de pièce fixe et il est préférable de commencer avec quelques notes simultanées. SuperCollider : GVerb
Chaque voix comprend une petite source sonore à deux oscillateurs, un court relâchement d’amplitude et une réverbération. Une enveloppe de durée de vie distincte ne libère la voix qu’une fois écoulé le délai prévu pour sa traîne. EnvGen de SuperCollider prend en charge les enveloppes de maintien qui se relâchent lorsque leur gate passe à zéro, ainsi qu’une action de fin qui libère le synthé englobant. SuperCollider : EnvGen
Utilisez une nouvelle session SuperCollider avec une sortie audio stéréo fonctionnelle. Évaluez le bloc de configuration complet ci-dessous, puis attendez le message Held-note reverb ready dans la fenêtre Post. Aucun échantillon ni bibliothèque supplémentaire n’est nécessaire. Le code a été vérifié au regard de la documentation ; il n’a pas été exécuté ici, car l’interpréteur SuperCollider n’est pas disponible.
(
s.waitForBoot {
SynthDef(\heldNoteVerb, {
|out = 0, freq = 440, amp = 0.035,
gate = 1, decay = 1, alive = 1|
var sourceEnv, lifetime, source, wet;
sourceEnv = EnvGen.kr(
Env.asr(0.01, 1, 0.02), gate
);
lifetime = EnvGen.kr(
Env.asr(0.01, 1, 0.03), alive,
doneAction: 2
);
source = (
SinOsc.ar(freq) + (0.25 * SinOsc.ar(freq * 2))
) * amp * sourceEnv;
wet = GVerb.ar(
source,
roomsize: 20,
revtime: Lag.kr(decay, 0.03),
damping: 0.5,
drylevel: 0,
earlyreflevel: 0.2,
taillevel: 0.5
);
Out.ar(out, ((source ! 2) + wet) * lifetime);
}).add;
s.sync;
~heldVoices = Dictionary.new;
~tailJobs = List.new;
~voiceGroup = Group.head(s);
~voiceKey = { |num, chan, src|
src.asString ++ ":" ++ chan.asString ++ ":" ++ num.asString
};
~decayFor = { |held|
(0.25 + (1.5 * held)).clip(0.25, 6.0)
};
~releaseHeld = { |num, chan = 0, src = 0|
var key, queue, entry, held, decay, node;
key = ~voiceKey.value(num, chan, src);
queue = ~heldVoices[key];
if(queue.notNil) {
if(queue.notEmpty) {
entry = queue.removeAt(0);
node = entry[0];
held = (Main.elapsedTime - entry[1]).max(0);
decay = ~decayFor.value(held);
node.set(\decay, decay, \gate, 0);
[\note, num, \held, held, \decay, decay].postln;
~tailJobs.add(Routine {
((2 * decay) + 0.1).wait;
node.set(\alive, 0);
}.play(SystemClock));
};
if(queue.isEmpty) { ~heldVoices.removeAt(key) };
};
};
~startHeld = { |vel, num, chan = 0, src = 0|
var key, node;
if(vel <= 0) {
~releaseHeld.value(num, chan, src);
} {
key = ~voiceKey.value(num, chan, src);
if(~heldVoices[key].isNil) {
~heldVoices[key] = List.new;
};
node = Synth(\heldNoteVerb, [
\freq, num.midicps,
\amp, (vel / 127) * 0.035
], ~voiceGroup);
~heldVoices[key].add([node, Main.elapsedTime]);
};
};
"Held-note reverb ready".postln;
};
)
L’ordre des opérations à la libération compte : la voix reçoit simultanément son réglage de décroissance et le changement de gate de la source. La réverbération continue de traiter le son tandis que la source s’estompe sur 20 millisecondes. Après environ deux fois la décroissance attribuée, plus une petite marge, toute la voix s’estompe et se libère.
Cette marge finale est un choix de gestion des ressources, et non une mesure du silence. Augmentez-la si la traîne restante est audible au moment du nettoyage. Pour un instrument plus grand, remplacez la marge fixe par une surveillance du niveau de sortie assortie d’une durée de vie maximale. L’exemple conserve aussi les références des tâches terminées jusqu’au nettoyage : il s’agit donc d’une démonstration, et non d’un instrument destiné à fonctionner indéfiniment.
Associer les notes MIDI jouées à la bonne voix
Une mesure de durée correspond à une instance de note précise. Suivre uniquement l’horodatage du note-on le plus récent échoue dès que des notes se chevauchent. La configuration stocke plutôt chaque voix et son heure de début sous une clé contenant la source, le canal et la hauteur.
Le guide MIDI de SuperCollider montre comment initialiser le MIDI, connecter des entrées et enregistrer des références aux synthés afin que le note-off puisse libérer le son approprié. La référence de MIDIdef indique que les arguments du rappel sont, dans l’ordre, la valeur, le numéro de note, le canal et la source. SuperCollider : Utilisation du MIDI, SuperCollider : MIDIdef
Une fois que la configuration indique qu’elle est prête, évaluez ce bloc pour jouer de l’instrument à partir des entrées MIDI connectées :
(
MIDIClient.init;
MIDIIn.connectAll;
MIDIdef.noteOn(\heldVerbOn, { |vel, num, chan, src|
~startHeld.value(vel, num, chan, src);
});
MIDIdef.noteOff(\heldVerbOff, { |vel, num, chan, src|
~releaseHeld.value(num, chan, src);
});
)
L’exemple définit la durée de « maintien » comme l’intervalle entre les événements de note reçus. Il ne gère pas le comportement de la pédale de sustain. Si vous souhaitez que la pédale prolonge la durée, différez les opérations de relâchement concernées jusqu’au relâchement de la pédale, puis mesurez cet intervalle plus long.
Les pressions répétées sur une même hauteur sont stockées dans une file et relâchées dans l’ordre d’arrivée. Il s’agit d’une politique explicite d’appariement pour les événements qui se chevauchent sur une même hauteur, une même source et un même canal. Les note-off sans note correspondante ne font rien ; un note-on sans note correspondante reste actif jusqu’au nettoyage. Vérifiez l’appariement des événements avant d’étendre l’instrument.
Tester les traînes qui se chevauchent avant de modifier le son
Commencez par tester le comportement sans clavier. La séquence suivante utilise les mêmes fonctions d’événement que l’entrée MIDI : la note 60 dure deux secondes, tandis que la note 67 commence à mi-parcours et dure un quart de seconde. Les décroissances attribuées attendues sont d’environ 3,25 et 0,625 seconde.
(
~heldTest = Routine {
~startHeld.value(100, 60);
1.0.wait;
~startHeld.value(100, 67);
0.25.wait;
~releaseHeld.value(67);
0.75.wait;
~releaseHeld.value(60);
}.play(SystemClock);
)
SystemClock permet une programmation en secondes, ce qui rend ce test indépendant du réglage de tempo musical. En fonctionnement réel, de légères différences peuvent apparaître entre les durées affichées et celles demandées. SuperCollider : SystemClock
Écoutez la courte traîne de la note aiguë tandis que la note grave continue, puis la traîne plus longue de la note grave. Inversez ensuite les durées. Ce deuxième passage aide à vérifier que le résultat dépend de la durée, et non de la hauteur. Comparez les paires durée de maintien et décroissance affichées au son.
Pour arrêter et supprimer cet exemple, évaluez le bloc de nettoyage suivant après avoir installé les gestionnaires MIDI. Il annule aussi les tâches de traîne en attente avant de supprimer leurs voix :
(
MIDIdef(\heldVerbOn).free;
MIDIdef(\heldVerbOff).free;
if(~heldTest.notNil) { ~heldTest.stop };
~tailJobs.do { |job| job.stop };
~tailJobs.clear;
~voiceGroup.free;
~heldVoices.clear;
)
Comment diagnostiquer des traînes qui se comportent mal ?
Si chaque traîne change lorsqu’une note est relâchée, inspectez le routage audio. Même avec un horodatage par note, le comportement reste partagé si le résultat pilote une seule réverbération placée après le mixage des voix. Placez la réverbération dans chaque voix ou routez le son isolé de chaque note vers des instances de traitement distinctes.
Si la traîne disparaît exactement au note-off, inspectez la durée de vie de la voix. L’enveloppe d’amplitude doit arrêter la source sonore sans libérer la réverbération qui la contient. Dans une implémentation Max, maintenez l’état occupé de la voix jusqu’à la fin de sa sortie traitée, conformément au modèle d’allocation documenté de poly~. Cycling ’74 : Polyphonie
Si les valeurs de décroissance sont incorrectes, affichez la clé de l’événement et les horodatages avant de modifier la réverbération. Dans Max, l’élément de base correspondant pour le chronométrage est timer, qui mesure le temps écoulé entre des événements et utilise les millisecondes par défaut. Convertissez explicitement les unités lorsque la destination attend des secondes. Cycling ’74 : timer
Si les performances deviennent irrégulières, réduisez le nombre de voix simultanées et examinez le choix de réverbération avant d’agrandir le patch. Cette démonstration ne limite pas le nombre de voix, et chaque nouvelle note crée une autre réverbération. Un instrument de production nécessite une politique d’allocation explicite qui tient compte des notes maintenues et des traînes encore audibles.
Une fois les tests de timing et de chevauchement réussis, remplacez l’oscillateur par la source sonore de votre choix pour chaque voix. Gardez la même séquence : allouez une voix, enregistrez son heure de début, mesurez la durée à la libération, attribuez la décroissance et conservez sa réverbération jusqu’à la fin de la traîne. Cette séquence fait de la durée de maintien un contrôle de performance reproductible.
Sources
- Polyphonie — Documentation Cycling ’74
- timer — Référence Max — Documentation Cycling ’74
- Référence des effets audio de Live — Manuel de référence Ableton Live version 12
- GVerb — Aide SuperCollider
- EnvGen — Aide SuperCollider
- Utilisation du MIDI — Aide SuperCollider
- MIDIdef — Aide SuperCollider
- SystemClock — Aide SuperCollider
