Blog Metlivi

Jak nadać każdej zagranej nucie czas wybrzmiewania pogłosu zależny od czasu jej przytrzymania

Aby nadać każdej zagranej nucie własny pogłos zależny od jej czasu trwania, zmierz czas między komunikatem note-on a note-off, przelicz ten czas na czas wybrzmiewania i zastosuj wynik do pogłosu przypisanego do tej nuty. Pogłos powinien wybrzmiewać także po tym, jak źródło dźwięku nuty przestanie grać.

W tym samouczku pokazano, jak uzyskać takie działanie w SuperColliderze, z uwzględnieniem wejścia MIDI i testu nakładających się nut. Zamierzony efekt jest prosty: krótko przytrzymana nuta zostawia krótki pogłos, a dłużej przytrzymana — dłuższy, nawet gdy ich pogłosy nakładają się na siebie.

29 października 20268 min czytaniaAutor: Metlivi Editorial Team
Sekcja 1

Dlaczego każda nuta potrzebuje osobnego pogłosu?

Rozważmy dwie nakładające się nuty. Pierwszą przytrzymujesz przez dwie sekundy, a drugą naciskasz na ćwierć sekundy. Jeśli obie trafiają do jednego pogłosu, zmiana jego czasu wybrzmiewania dla drugiej nuty zmienia też przetwarzanie pozostałego pogłosu pierwszej. Jeden wspólny regulator czasu wybrzmiewania nie może zachować dwóch niezależnych ustawień w tym samym stanie pogłosu.

Dlatego każda nuta powinna kierować dźwięk ze swojego źródła do własnego pogłosu, a wyjścia gotowych głosów należy zmiksować. To wniosek dotyczący architektury: niezależne ogony pogłosu wymagają niezależnego przetwarzania, chyba że instrument lub efekt zapewnia równoważną separację wewnętrzną.

Przydatną podstawę stanowi łata polifoniczna. Dokumentacja Cycling ’74 opisuje, jak obiekt Maxa poly~ tworzy instancje głosów, a thispoly~ oznacza każdą instancję jako zajętą lub dostępną. W tym przypadku głos musi pozostać zajęty przez cały czas wybrzmiewania pogłosu, zamiast zwalniać się od razu po note-off. Cycling ’74: Polifonia

Jest też ograniczenie czasowe: podczas występu na żywo ostateczny czas przytrzymania jest nieznany, dopóki nuta się nie skończy. Poniższa implementacja stosuje tymczasowy czas wybrzmiewania, gdy klawisz jest wciśnięty, a po jego zwolnieniu ustawia wartość zmierzoną. Jeśli ostateczny czas wybrzmiewania ma obowiązywać od początku, skorzystaj z czasów trwania nut dostępnych już w sekwencji albo później renderuj pojedyncze nuty.

Sekcja 2

Na co powinien wpływać czas przytrzymania?

Oddziel od siebie trzy parametry: czas zwalniania amplitudy instrumentu, czas wybrzmiewania pogłosu i poziom sygnału przetworzonego. Dłuższe zwalnianie instrumentu oznacza, że dźwięk dłużej trafia do pogłosu. Wyższy poziom sygnału przetworzonego zwiększa głośność efektu. Żadne z tych ustawień samo w sobie nie wydłuża czasu wybrzmiewania pogłosu.

Czas wybrzmiewania zwykle nie oznacza też dokładnego momentu, w którym dźwięk ustaje. Dokumentacja pogłosu Abletona definiuje parametr Decay jako czas, w którym ogon pogłosu opada do jednej tysięcznej początkowej amplitudy, czyli −60 dB. To rozróżnienie ma znaczenie przy porównywaniu ustawionego czasu wybrzmiewania z chwilą, w której ogon staje się niesłyszalny. Ableton: Dokumentacja efektów audio Live, Reverb

Na początek zastosuj poniższe, wyraźnie przykładowe przeliczenie; wszystkie wartości podano w sekundach:

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

Czas przytrzymania Ustawiony czas wybrzmiewania
0.10 sekundy 0.40 sekundy
0.25 sekundy 0.625 sekundy
1.00 sekunda 1.75 sekundy
2.00 sekundy 3.25 sekundy
4.00 sekundy 6.00 sekundy, wartość ograniczona

To wartości przyjęte na potrzeby projektu, a nie wyniki pomiarów akustycznych. Minimalna wartość daje krótkim naciśnięciom słyszalny ogon; mnożnik określa, jak mocno dłuższe przytrzymanie zmienia efekt; maksimum ogranicza żądany czas wybrzmiewania. Zacznij od tego przeliczenia, a potem dostosuj te trzy wartości, utrzymując stałą prędkość nut i poziom sygnału przetworzonego.

Sekcja 3

Zbuduj głos, który przetrwa note-off

W przykładzie użyto wbudowanego efektu SuperCollidera GVerb, który przyjmuje sygnał mono, generuje dwa kanały wyjściowe i udostępnia parametr revtime wyrażony w sekundach. Dokumentacja wskazuje także na skok użycia procesora podczas tworzenia instancji oraz artefakty przy zmianie rozmiaru pomieszczenia. Dlatego w przykładzie rozmiar pomieszczenia pozostaje stały. Na początek najlepiej użyć zaledwie kilku jednoczesnych nut. SuperCollider: GVerb

Każdy głos zawiera proste źródło dźwięku z dwoma oscylatorami, krótki czas zwalniania amplitudy i jeden pogłos. Osobna obwiednia czasu życia zwalnia głos dopiero po upływie czasu przeznaczonego na wybrzmiewanie ogona. SuperColliderowy EnvGen obsługuje obwiednie podtrzymywane, które przechodzą w fazę zwalniania po ustawieniu bramki na zero, a także akcję końcową zwalniającą nadrzędny syntezator. SuperCollider: EnvGen

Uruchom nową sesję SuperCollidera z działającym stereofonicznym wyjściem audio. Wykonaj cały poniższy blok konfiguracji, a następnie zaczekaj, aż w oknie Post pojawi się komunikat Held-note reverb ready. Nie są potrzebne próbki ani dodatkowe biblioteki. Kod został sprawdzony pod kątem zgodności z dokumentacją, ale nie został tutaj uruchomiony, ponieważ interpreter SuperCollidera jest niedostępny.

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

Kolejność działań przy zwolnieniu nuty ma znaczenie: głos otrzymuje ustawienie czasu wybrzmiewania i zmianę bramki źródła jednocześnie. Pogłos przetwarza sygnał dalej, podczas gdy źródło wycisza się przez 20 milisekund. Po upływie około dwukrotności ustawionego czasu wybrzmiewania, z niewielkim zapasem, cały głos wycisza się i zwalnia.

Ten końcowy zapas to decyzja dotycząca zarządzania zasobami, a nie pomiar ciszy. Zwiększ go, jeśli przy zwalnianiu głosu nadal słychać ogon. W większym instrumencie zastąp stały zapas monitorowaniem poziomu wyjściowego z maksymalnym czasem życia. Przykład zachowuje też odwołania do zakończonych zadań aż do sprzątania, więc jest demonstracją, a nie instrumentem przeznaczonym do pracy bez końca.

Sekcja 4

Przypisz zagrane nuty MIDI do właściwego głosu

Pomiar czasu trwania dotyczy konkretnego wystąpienia nuty. Śledzenie tylko ostatniego znacznika czasu note-on zawodzi, gdy tylko nuty zaczną się nakładać. W tej konfiguracji każdy głos i jego czas rozpoczęcia są zapisywane pod kluczem zawierającym źródło, kanał i wysokość nuty.

Przewodnik MIDI SuperCollidera pokazuje, jak zainicjować MIDI, podłączyć wejścia i zapisać odwołania do syntezatorów, aby komunikat note-off mógł zwolnić właściwy dźwięk. Dokumentacja MIDIdef podaje argumenty funkcji zwrotnej w kolejności: wartość, numer nuty, kanał i źródło. SuperCollider: Korzystanie z MIDI, SuperCollider: MIDIdef

Gdy konfiguracja zgłosi gotowość, wykonaj ten blok, aby grać na instrumencie z podłączonych wejść MIDI:

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

W przykładzie za czas „przytrzymania” uznaje się odstęp między odebranymi zdarzeniami nut. Nie zaimplementowano w nim działania pedału sustain. Jeśli czas trwania ma uwzględniać przedłużenie pedałem, odłóż odpowiadające mu operacje zwolnienia do chwili podniesienia pedału, a następnie zmierz wydłużony odstęp.

Powtórne naciśnięcia tej samej wysokości nuty są przechowywane w kolejce i zwalniane od najstarszego. To jawnie przyjęta zasada dopasowywania nakładających się zdarzeń tej samej wysokości z jednego źródła i kanału. Niedopasowane komunikaty note-off są ignorowane, a niedopasowane note-on pozostaje aktywne do sprzątania. Przed rozbudową instrumentu sprawdź parowanie zdarzeń.

Sekcja 5

Przetestuj nakładające się ogony przed zmianą brzmienia

Najpierw przetestuj działanie bez klawiatury. Poniższa sekwencja używa tych samych funkcji zdarzeń co wejście MIDI: nuta 60 trwa dwie sekundy, a nuta 67 zaczyna się w połowie jej trwania i trwa ćwierć sekundy. Oczekiwane czasy wybrzmiewania wynoszą odpowiednio około 3.25 i 0.625 sekundy.

(
~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 umożliwia planowanie zdarzeń w sekundach, dzięki czemu test nie zależy od ustawionego tempa muzycznego. Podczas rzeczywistego działania mogą wystąpić niewielkie różnice między wypisanymi a żądanymi czasami trwania. SuperCollider: SystemClock

Posłuchaj krótkiego ogona wyższej nuty, gdy niższa nadal trwa, a następnie dłuższego ogona niższej nuty. Potem odwróć czasy trwania. Drugi przebieg pomaga potwierdzić, że o wyniku decyduje czas trwania, a nie wysokość nuty. Porównuj wartości wypisanego czasu przytrzymania i czasu wybrzmiewania z dźwiękiem.

Aby zatrzymać i usunąć ten przykład, po zainstalowaniu obsługi MIDI wykonaj poniższy blok sprzątający. Anuluje on też oczekujące zadania wybrzmiewania przed usunięciem odpowiadających im głosów:

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

Jak diagnozować nieprawidłowe działanie ogonów pogłosu?

Jeśli po zwolnieniu jednej nuty zmienia się każdy ogon, sprawdź trasę sygnału audio. Nawet przy osobnym znaczniku czasu na nutę działanie pozostanie wspólne, jeśli wynik steruje jednym pogłosem umieszczonym za mikserem głosów. Przenieś pogłos do każdego głosu albo skieruj odizolowany sygnał każdej nuty do osobnej instancji przetwarzania.

Jeśli ogon znika dokładnie przy note-off, sprawdź czas życia głosu. Obwiednia amplitudy powinna zatrzymać źródło dźwięku, nie zwalniając pogłosu, do którego źródło kierowało sygnał. W implementacji Maxa utrzymuj stan zajętości głosu do zakończenia sygnału przetworzonego, zgodnie z udokumentowanym modelem przydzielania głosów poly~. Cycling ’74: Polifonia

Jeśli wartości czasu wybrzmiewania są nieprawidłowe, przed zmianą pogłosu wypisz klucz zdarzenia i znaczniki czasu. W Maxie odpowiednim elementem do pomiaru czasu jest timer, który mierzy czas między zdarzeniami, domyślnie w milisekundach. Jawnie przelicz jednostki, gdy odbiornik oczekuje sekund. Cycling ’74: timer

Jeśli wydajność staje się nierówna, ogranicz liczbę jednoczesnych głosów i sprawdź wybór pogłosu, zanim rozbudujesz łatę. Ta demonstracja nie narzuca limitu głosów, a każda nowa nuta tworzy kolejny pogłos. Instrument przeznaczony do produkcji wymaga jawnej zasady przydzielania zasobów, która uwzględnia zarówno przytrzymane nuty, jak i nadal wybrzmiewające ogony.

Gdy testy czasu i nakładania się nut działają, zastąp oscylator wybranym źródłem dźwięku dla każdego głosu. Zachowaj tę samą kolejność: przydziel głos, zapisz czas rozpoczęcia, zmierz czas przy zwolnieniu, ustaw czas wybrzmiewania i utrzymuj pogłos, aż ogon wybrzmi. Dzięki temu czas przytrzymania staje się powtarzalnym parametrem wykonawczym.

Sekcja 7

Źródła

Warto przeczytać

Eksploruj dalej ten temat