Wie jede gespielte Note einen von ihrer Haltedauer abhängigen Nachhall erhalten kann
Um jeder gespielten Note einen eigenen, von ihrer Dauer abhängigen Nachhall zu geben, messen Sie die Zeit zwischen Note-on und Note-off, ordnen Sie dieser Dauer eine Abklingzeit zu und wenden Sie das Ergebnis auf einen dieser Note zugeordneten Hall an. Lassen Sie den Hall der Note weiterlaufen, nachdem ihre Klangquelle ausklingt.
Dieses Tutorial setzt dieses Verhalten in SuperCollider um, einschließlich MIDI-Eingabe und eines Tests mit überlappenden Noten. Das gewünschte Ergebnis ist einfach: Eine kurz gehaltene Note hinterlässt eine kurze Hallfahne, während eine länger gehaltene Note eine längere hinterlässt – selbst wenn sich ihre Hallfahnen überlagern.
Warum braucht jede Note einen eigenen Hall?
Betrachten wir zwei überlappende Noten. Sie halten die erste zwei Sekunden lang und tippen die zweite eine Viertelsekunde lang an. Wenn beide in denselben Hall gespeist werden, verändert eine Anpassung seiner Abklingzeit für die zweite Note auch die Verarbeitung des verbleibenden Nachhalls der ersten Note. Eine gemeinsame Abklingzeitsteuerung kann nicht zwei unabhängige Einstellungen innerhalb desselben Hallzustands erhalten.
Die Signalführung muss daher so aussehen: Die Klangquelle jeder Note speist ihren eigenen Hall, und die fertigen Stimmenausgänge werden zusammengemischt. Das ist eine architektonische Schlussfolgerung: Unabhängige Hallfahnen erfordern einen unabhängigen Verarbeitungszustand, sofern das Instrument oder der Effekt nicht bereits eine gleichwertige interne Trennung bietet.
Ein polyphoner Patch bietet dafür einen nützlichen Rahmen. Cycling ’74 dokumentiert, wie Max mit poly~ Stimmeninstanzen erzeugt und mit thispoly~ jede Instanz als belegt oder verfügbar kennzeichnet. Für diese Aufgabe muss eine Stimme während ihrer Hallfahne belegt bleiben, statt direkt bei Note-off wieder verfügbar zu werden. Cycling ’74: Polyphonie
Hinzu kommt eine zeitliche Einschränkung: Bei einer Live-Performance ist die endgültige Haltedauer erst bekannt, wenn die Note endet. Die folgende Implementierung verwendet eine vorläufige Abklingzeit, solange die Taste gedrückt ist, und weist beim Loslassen die gemessene Abklingzeit zu. Wenn die endgültige Abklingzeit von Anfang an gelten soll, verwenden Sie bereits in einer Sequenz verfügbare Notendauern oder rendern Sie einzelne Noten anschließend separat.
Was sollte die Haltedauer steuern?
Halten Sie drei Steuerungen getrennt: das Amplitudenausklingen des Instruments, die Abklingzeit des Halls und den Effektanteil am Ausgang. Ein längeres Ausklingen des Instruments speist weiterhin Klang in den Hall. Ein höherer Effektanteil macht den Effekt lauter. Keine der beiden Maßnahmen verlängert für sich genommen die Abklingzeit des Halls.
Abklingzeit bezeichnet normalerweise auch keinen exakten Endzeitpunkt. In der Reverb-Dokumentation von Ableton ist die Decay-Steuerung als die Zeit definiert, die die Hallfahne benötigt, um auf ein Tausendstel ihrer Anfangsamplitude beziehungsweise auf −60 dB abzufallen. Dieser Unterschied ist wichtig, wenn Sie eine Abklingzeiteinstellung mit dem Moment vergleichen, in dem eine Hallfahne unhörbar wird. Ableton: Referenz zu Live-Audioeffekten, Reverb
Verwenden Sie für eine erste Implementierung diese ausdrücklich beispielhafte Zuordnung, bei der alle Werte in Sekunden angegeben sind:
decay = min(6.0, max(0.25, 0.25 + 1.5 × heldDuration))
| Haltedauer | Zugewiesene Abklingzeit |
|---|---|
| 0,10 Sekunden | 0,40 Sekunden |
| 0,25 Sekunden | 0,625 Sekunden |
| 1,00 Sekunde | 1,75 Sekunden |
| 2,00 Sekunden | 3,25 Sekunden |
| 4,00 Sekunden | 6,00 Sekunden, gedeckelt |
Diese Werte sind Gestaltungsentscheidungen und keine gemessenen akustischen Ergebnisse. Das Minimum sorgt dafür, dass kurze Anschläge eine hörbare Hallfahne erhalten; der Multiplikator legt fest, wie stark die Haltedauer den Effekt verändert; der Höchstwert begrenzt die angeforderte Abklingzeit. Beginnen Sie mit dieser Zuordnung und passen Sie dann diese drei Werte an, während Anschlagstärke und Effektanteil konstant bleiben.
Erstellen Sie eine Stimme, die Note-off überdauert
Das Beispiel verwendet SuperColliders integriertes GVerb, das ein Monosignal annimmt, zwei Ausgangskanäle erzeugt und revtime in Sekunden bereitstellt. Die Dokumentation weist außerdem auf einen CPU-Spitzenwert beim Instanziieren und auf Artefakte bei Änderungen der Raumgröße hin. Daher bleibt die Raumgröße in diesem Beispiel fest; für den Einstieg empfiehlt es sich, nur wenige gleichzeitige Noten zu verwenden. SuperCollider: GVerb
Jede Stimme enthält eine kleine Klangquelle aus zwei Oszillatoren, ein kurzes Amplitudenausklingen und einen Hall. Eine separate Hüllkurve für die Lebensdauer gibt die Stimme erst frei, nachdem die Hallfahne ausreichend lange ausgeklungen ist. SuperColliders EnvGen unterstützt gehaltene Hüllkurven, die freigegeben werden, wenn ihr Gate den Wert null erreicht, sowie eine Abschlussaktion, die den umschließenden Synth freigibt. SuperCollider: EnvGen
Verwenden Sie eine neue SuperCollider-Sitzung mit funktionierendem Stereo-Audioausgang. Führen Sie den folgenden vollständigen Einrichtungsblock aus und warten Sie dann im Post-Fenster auf Held-note reverb ready. Samples oder zusätzliche Bibliotheken sind nicht erforderlich. Der Code wurde anhand der Dokumentation geprüft, hier jedoch nicht ausgeführt, da der SuperCollider-Interpreter nicht verfügbar ist.
(
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;
};
)
Die Reihenfolge beim Loslassen ist wichtig: Die Stimme erhält gleichzeitig ihre Abklingzeiteinstellung und die Gate-Änderung ihrer Klangquelle. Der Hall verarbeitet das Signal weiter, während die Quelle innerhalb von 20 Millisekunden ausklingt. Nach ungefähr dem Doppelten der zugewiesenen Abklingzeit plus einem kleinen Zuschlag klingt die gesamte Stimme aus und gibt sich selbst frei.
Dieser letzte Zuschlag dient der Ressourcenverwaltung und ist keine Messung der Stille. Erhöhen Sie ihn, wenn die verbleibende Hallfahne beim Aufräumen noch hörbar ist. Bei einem größeren Instrument sollten Sie den festen Zuschlag durch eine Überwachung des Ausgangspegels mit einer maximalen Lebensdauer ersetzen. Das Beispiel behält außerdem Verweise auf abgeschlossene Jobs bis zum Aufräumen bei und ist daher eine Demonstration, kein Instrument für den Dauerbetrieb.
Ordnen Sie gespielte MIDI-Noten der richtigen Stimme zu
Eine Dauermessung gehört zu einer bestimmten Noteninstanz. Wenn Sie nur den Zeitstempel des letzten Note-on erfassen, schlägt das fehl, sobald sich Noten überlappen. Die Einrichtung speichert stattdessen jede Stimme und ihre Startzeit unter einem Schlüssel aus Quelle, Kanal und Tonhöhe.
SuperColliders MIDI-Leitfaden zeigt, wie MIDI initialisiert, Eingänge verbunden und Synth-Referenzen gespeichert werden, damit Note-off den passenden Klang freigeben kann. Die Referenz zu MIDIdef gibt die Callback-Argumente in der Reihenfolge Wert, Notennummer, Kanal und Quelle an. SuperCollider: MIDI verwenden, SuperCollider: MIDIdef
Sobald die Einrichtung „ready“ meldet, führen Sie diesen Block aus, um das Instrument über angeschlossene MIDI-Eingänge zu spielen:
(
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);
});
)
Das Beispiel definiert „gehalten“ als das Intervall zwischen den empfangenen Notenereignissen. Ein Sustain-Pedal wird nicht unterstützt. Wenn Sie eine durch das Pedal verlängerte Dauer wünschen, verschieben Sie die betreffenden Freigabevorgänge bis zum Loslassen des Pedals und messen Sie anschließend das längere Intervall.
Wiederholte Anschläge derselben Tonhöhe werden in einer Warteschlange gespeichert und in der Reihenfolge ihres Eingangs zuerst die ältesten freigegeben. Dies ist eine festgelegte Zuordnungsregel für überlappende Ereignisse derselben Tonhöhe auf einer Quelle und einem Kanal. Nicht zugeordnete Note-off-Ereignisse bewirken nichts; ein nicht zugeordnetes Note-on bleibt bis zum Aufräumen aktiv. Prüfen Sie die Ereignispaare, bevor Sie das Instrument erweitern.
Testen Sie überlappende Hallfahnen, bevor Sie den Klang ändern
Testen Sie zunächst das Verhalten ohne Keyboard. Die folgende Sequenz verwendet dieselben Ereignisfunktionen wie die MIDI-Eingabe: Note 60 dauert zwei Sekunden, während Note 67 nach der Hälfte dieser Zeit beginnt und eine Viertelsekunde dauert. Die erwarteten zugewiesenen Abklingzeiten betragen ungefähr 3,25 beziehungsweise 0,625 Sekunden.
(
~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 ermöglicht die Zeitplanung in Sekunden, wodurch dieser Test unabhängig von einer musikalischen Tempoeinstellung bleibt. Im tatsächlichen Betrieb kann es zu kleinen Abweichungen zwischen den ausgegebenen und den angeforderten Dauern kommen. SuperCollider: SystemClock
Achten Sie auf die kurze Hallfahne der höheren Note, während die tiefere Note weiterklingt, gefolgt von der längeren Hallfahne der tieferen Note. Kehren Sie dann die Dauern um. Dieser zweite Durchlauf hilft zu bestätigen, dass die Dauer das Ergebnis bestimmt und nicht die Tonhöhe. Lesen Sie die ausgegebenen Paare aus Haltedauer und Abklingzeit parallel zum Audio.
Um dieses Beispiel nach der Einrichtung der MIDI-Handler zu stoppen und zu entfernen, führen Sie den folgenden Aufräumblock aus. Er bricht auch ausstehende Hallfahnen-Jobs ab, bevor ihre Stimmen entfernt werden:
(
MIDIdef(\heldVerbOn).free;
MIDIdef(\heldVerbOff).free;
if(~heldTest.notNil) { ~heldTest.stop };
~tailJobs.do { |job| job.stop };
~tailJobs.clear;
~voiceGroup.free;
~heldVoices.clear;
)
Wie lassen sich falsch reagierende Hallfahnen diagnostizieren?
Wenn sich jede Hallfahne ändert, sobald eine Note losgelassen wird, prüfen Sie die Signalführung. Selbst ein Zeitstempel pro Note führt zu gemeinsamem Verhalten, wenn sein Ergebnis einen einzigen Hall hinter der Stimmenmischung steuert. Verschieben Sie den Hall in jede Stimme oder leiten Sie isoliertes Notenaudio an separate Verarbeitungseinheiten.
Wenn die Hallfahne genau bei Note-off verschwindet, prüfen Sie die Lebensdauer der Stimme. Die Amplitudenhüllkurve sollte die Klangquelle stoppen, ohne den Hall freizugeben, der diese Quelle enthält. Bewahren Sie in einer Max-Implementierung den Belegtstatus der Stimme, bis ihr Effektausgang verstummt ist, und folgen Sie dabei dem dokumentierten Zuweisungsmodell von poly~. Cycling ’74: Polyphonie
Wenn die Abklingzeitwerte nicht stimmen, geben Sie den Ereignisschlüssel und die Zeitstempel aus, bevor Sie den Hall ändern. In Max ist timer das entsprechende Zeitmessungs-Bauteil. Es misst die verstrichene Zeit zwischen Ereignissen und verwendet standardmäßig Millisekunden. Rechnen Sie Einheiten ausdrücklich um, wenn das Ziel Sekunden erwartet. Cycling ’74: timer
Wenn die Leistung ungleichmäßig wird, verringern Sie die Zahl gleichzeitiger Stimmen und prüfen Sie die Hallauswahl, bevor Sie den Patch erweitern. Diese Demonstration begrenzt die Zahl der Stimmen nicht, und jede neue Note erzeugt einen weiteren Hall. Ein produktionsreifes Instrument benötigt eine ausdrückliche Zuweisungsregel, die sowohl gehaltene Noten als auch noch nachhallende Stimmen berücksichtigt.
Sobald die Zeitmessungs- und Überlappungstests funktionieren, ersetzen Sie den Oszillator durch Ihre gewünschte Klangquelle pro Stimme. Behalten Sie dieselbe Abfolge bei: eine Stimme zuweisen, ihre Startzeit erfassen, beim Loslassen messen, ihre Abklingzeit festlegen und ihren Hall beibehalten, bis die Hallfahne verklungen ist. So wird die Haltedauer zu einem reproduzierbaren Ausdrucksmittel.
Quellen
- Polyphonie — Cycling ’74-Dokumentation
- timer — Max-Referenz — Cycling ’74-Dokumentation
- Referenz zu Live-Audioeffekten — Ableton-Referenzhandbuch Version 12
- GVerb — SuperCollider-Hilfe
- EnvGen — SuperCollider-Hilfe
- MIDI verwenden — SuperCollider-Hilfe
- MIDIdef — SuperCollider-Hilfe
- SystemClock — SuperCollider-Hilfe
