Metlivi-bloggen

Så ger du varje spelad ton en efterklangstid baserad på hur länge den hölls nere

Om du vill ge varje spelad ton en egen efterklangstid som beror på tonens längd mäter du tiden mellan note-on och note-off, mappar den varaktigheten till en avklingningstid och tillämpar resultatet på en efterklang som är dedikerad till tonen. Låt tonens efterklang fortsätta efter att ljudkällan har släppt tonen.

Den här handledningen bygger upp beteendet i SuperCollider, inklusive MIDI-inmatning och ett test med överlappande toner. Det avsedda resultatet är enkelt: en ton som hålls nere en kort stund får en kort efterklang, medan en ton som hålls nere längre får en längre efterklang – även när deras efterklanger överlappar.

29 oktober 20268 min läsningAv Metlivi Editorial Team
Avsnitt 1

Varför behöver varje ton en separat efterklang?

Tänk dig två överlappande toner. Du håller den första nere i två sekunder och trycker kort på den andra i en kvarts sekund. Om båda skickas till samma efterklang ändrar en justering av avklingningen för den andra tonen även behandlingen av den första tonens kvarvarande efterklang. En gemensam avklingningskontroll kan inte bevara två oberoende inställningar i samma efterklangstillstånd.

Signalvägen behöver därför vara: varje tons ljudkälla skickas till sin egen efterklang, och sedan mixas de färdiga rösterna ihop. Det här är en arkitektonisk slutsats: oberoende efterklanger kräver oberoende behandlingstillstånd, såvida instrumentet eller effekten inte redan erbjuder motsvarande intern separation.\n\nEn polyfonisk patch ger ett användbart ramverk för detta. Cycling ’74 dokumenterar hur Max poly~ skapar röstinstanser och använder thispoly~ för att markera varje instans som upptagen eller tillgänglig. För den här uppgiften måste en röst förbli upptagen under hela sin efterklang, i stället för att bli tillgänglig direkt vid note-off. Cycling ’74: Polyfoni

Det finns också en tidsmässig begränsning: under ett liveframträdande är den slutliga tonlängden okänd tills tonen avslutas. Implementationen nedan använder en preliminär avklingning medan tangenten hålls nere och ställer in den uppmätta avklingningen när tangenten släpps. Om du behöver att den slutliga avklingningen gäller från början kan du utgå från tonlängder som redan finns i en sekvens eller rendera enskilda toner i efterhand.

Avsnitt 2

Vad ska tiden som tonen hålls nere styra?

Håll tre kontroller åtskilda: instrumentets amplitudavklingning, efterklangens avklingning och den våta utsignalens nivå. En längre amplitudavklingning fortsätter att mata ljud in i efterklangen. En högre våt nivå gör effekten starkare. Ingen av dessa åtgärder ger i sig efterklangen en längre avklingning.

Avklingning betyder normalt inte en exakt stopptid. Abletons dokumentation för Reverb definierar Decay-kontrollen som tiden det tar för efterklangen att sjunka till en tusendel av sin ursprungliga amplitud, eller −60 dB. Den skillnaden är viktig när du jämför en avklingningsinställning med tidpunkten då efterklangen blir ohörbar. Ableton: Referens för Live-ljudeffekter, Reverb

För en första implementation använder du den här uttryckligen illustrativa mappningen, med alla värden i sekunder:

decay = min(6.0, max(0.25, 0.25 + 1.5 × heldDuration))

Tid som tonen hålls nere Tilldelad avklingning
0.10 sekunder 0.40 sekunder
0.25 sekunder 0.625 sekunder
1.00 sekund 1.75 sekunder
2.00 sekunder 3.25 sekunder
4.00 sekunder 6.00 sekunder, maxgräns

Dessa värden är designval, inte uppmätta akustiska resultat. Minimitiden ger korta tryckningar en hörbar efterklang; multiplikatorn anger hur mycket den längre nedtryckningstiden påverkar effekten; maxgränsen begränsar den begärda avklingningen. Börja med den här mappningen och justera sedan dessa tre val medan du håller tonhastighet och våt nivå konstanta.

Avsnitt 3

Bygg en röst som överlever note-off

Exemplet använder SuperColliders inbyggda GVerb, som tar emot en monosignal, producerar två utgångskanaler och har parametern revtime i sekunder. Dokumentationen nämner också en CPU-topp vid instansiering och artefakter när rumsstorleken ändras. Därför håller det här exemplet rumsstorleken fast och fungerar bäst om du inledningsvis använder några få samtidiga toner. SuperCollider: GVerb

Varje röst innehåller en ljudkälla med två oscillatorer, en kort amplitudavklingning och en efterklang. En separat livslängdsenvelopp frigör rösten först när tiden för efterklangen har löpt ut. SuperColliders EnvGen stöder hållande envelopper som släpper när deras gate går till noll, samt en slutförandeåtgärd som frigör den omslutande synthen. SuperCollider: EnvGen

Använd en ny SuperCollider-session med fungerande stereoljudutgång. Kör följande kompletta inställningsblock och vänta sedan på Held-note reverb ready i Post-fönstret. Inga samplingar eller extra bibliotek behövs. Koden har kontrollerats mot dokumentationen; den har inte körts här eftersom SuperCollider-tolken inte är tillgänglig.

(
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;
};
)

Ordningen vid släppet är viktig: rösten får sin avklingningsinställning och ändringen av källans gate samtidigt. Efterklangen fortsätter att bearbetas medan källan tonar ut under 20 millisekunder. Efter ungefär dubbla den tilldelade avklingningstiden plus en liten marginal tonar hela rösten ut och frigörs.

Den sista marginalen är ett val för resurshantering, inte en mätning av tystnad. Öka den om den återstående efterklangen fortfarande hörs vid upprensningen. För ett större instrument kan du ersätta den fasta marginalen med nivåövervakning av utgången plus en maximal livslängd. Exemplet behåller också referenser till avslutade jobb tills upprensningen sker, så det är en demonstration snarare än ett instrument som kan köras utan tidsbegränsning.

Avsnitt 4

Koppla spelade MIDI-toner till rätt röst

En tidsmätning hör till en viss toninstans. Om du bara håller reda på tidsstämpeln för den senaste note-on-händelsen misslyckas det så snart toner överlappar. I stället lagrar inställningen varje röst och dess starttid under en nyckel som innehåller källa, kanal och tonhöjd.

SuperColliders MIDI-guide visar hur man initierar MIDI, ansluter ingångar och sparar synthreferenser så att note-off kan frigöra rätt ljud. Referensen för MIDIdef anger att callback-argumenten kommer i ordningen värde, tonnummer, kanal och källa. SuperCollider: Använda MIDI, SuperCollider: MIDIdef

När inställningen visar att den är klar kör du det här blocket för att spela instrumentet via anslutna MIDI-ingångar:

(
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);
});
)

I exemplet definieras ”nedtryckt tid” som intervallet mellan mottagna tonhändelser. Pedalhållning är inte implementerad. Om du vill att pedalen ska förlänga tiden skjuter du upp berörda släppåtgärder tills pedalen lyfts och mäter sedan det längre intervallet.

Upprepade tryck på samma ton lagras i en kö och släpps i den ordning de trycktes ned. Det är en uttrycklig matchningsregel för överlappande händelser med samma tonhöjd från samma källa och kanal. Note-off-händelser utan matchning gör ingenting; en note-on-händelse utan matchning förblir aktiv tills upprensningen sker. Kontrollera att händelseparen stämmer innan du bygger ut instrumentet.

Avsnitt 5

Testa överlappande efterklanger innan du ändrar ljudet

Testa först beteendet utan keyboard. Följande sekvens använder samma händelsefunktioner som MIDI-inmatningen: ton 60 varar i två sekunder, medan ton 67 börjar halvvägs in och varar i en kvarts sekund. De förväntade avklingningstiderna är ungefär 3.25 respektive 0.625 sekunder.

(
~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 schemalägger i sekunder, vilket gör testet oberoende av en musikalisk tempoinställning. Små skillnader mellan utskrivna och begärda varaktigheter kan förekomma i faktisk drift. SuperCollider: SystemClock

Lyssna efter den högre tonens korta efterklang medan den lägre tonen fortsätter, följt av den lägre tonens längre efterklang. Byt sedan plats på varaktigheterna. Den andra genomspelningen hjälper dig att avgöra om det är varaktigheten, snarare än tonhöjden, som styr resultatet. Jämför de utskrivna paren med nedtryckt tid och avklingning med ljudet.

Om du vill stoppa och ta bort det här exemplet kör du följande upprensningsblock efter att ha installerat MIDI-hanterarna. Det avbryter även väntande efterklangsjobb innan rösterna tas bort:

(
MIDIdef(\heldVerbOn).free;
MIDIdef(\heldVerbOff).free;
if(~heldTest.notNil) { ~heldTest.stop };
~tailJobs.do { |job| job.stop };
~tailJobs.clear;
~voiceGroup.free;
~heldVoices.clear;
)
Avsnitt 6

Hur felsöker du efterklanger som beter sig fel?

Om varje efterklang ändras när en ton släpps kontrollerar du ljudroutningen. En tidsstämpel per ton ger fortfarande ett gemensamt beteende om resultatet styr en enda efterklang efter röstmixen. Flytta in efterklangen i varje röst eller routa isolerat tonljud till separata behandlinginstanser.

Om efterklangen försvinner exakt vid note-off kontrollerar du röstens livslängd. Amplitudenveloppen ska stoppa ljudkällan utan att frigöra efterklangen som innehåller den källan. I en Max-implementation ska röstens upptagenstatus bevaras tills den våta utsignalen har klingat färdigt, i enlighet med den dokumenterade allokeringsmodellen för poly~. Cycling ’74: Polyfoni

Om avklingningsvärdena är fel skriver du ut händelsenyckeln och tidsstämplarna innan du ändrar efterklangen. I Max är motsvarande tidsmätningsbyggsten timer, som mäter förfluten tid mellan händelser och använder millisekunder som standard. Konvertera enheterna uttryckligen när målet förväntar sig sekunder. Cycling ’74: timer

Om prestandan blir ojämn minskar du antalet samtidiga röster och kontrollerar efterklangsvalet innan du bygger ut patchen. Den här demonstrationen begränsar inte antalet röster, och varje ny ton skapar ytterligare en efterklang. Ett produktionsinstrument behöver en uttrycklig allokeringsregel som tar hänsyn till både nedhållna toner och efterklanger som fortfarande klingar.

När testerna av tidsmätning och överlappning fungerar kan du ersätta oscillatorn med den ljudkälla du vill använda per röst. Behåll samma sekvens: allokera en röst, registrera dess starttid, mät vid släpp, tilldela dess avklingning och behåll dess efterklang tills den klingat färdigt. Den sekvensen gör tiden tonen hålls nere till en repeterbar kontroll under framförandet.

Avsnitt 7

Källor

Relaterad läsning

Fortsätt utforska detta ämne