Суперколлайдер: автоматический сустейн в событиях с конвертом

( 

SynthDef(\testEvt,{
    arg out, gate = 1;
    var sint = Blip.ar(440) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
Out.ar(out, Pan2.ar(sint, 0));
}).add();

Synth(\testEvt) 
(instrument: \testEvt, freq:220, sustain: inf).play;
(instrument: \testEvt,freq:220).play;

)

Выполнение первой и второй строки после SynthDef создаст синтезатор, который будет воспроизводиться вечно, тогда как синтезатор третьей строки воспроизводится в течение 0,8 секунды в соответствии со значением по умолчанию для сгенерированного события.

Проблема в том, что я не использую «сустейн» нигде в своем SynthDef, и он используется автоматически только потому, что есть лен.

То же самое не происходит с freq: оба события воспроизводятся на 440, а не на 220, и это только потому, что SynthDef не использует 'freq' в качестве аргумента. Так почему же сустейн не следует тому же правилу?

Кроме того, есть ли способ ссылаться на синтезаторы, созданные событием? Так что, когда у них будет sustain: inf в качестве аргумента, я смогу освободить их позже.


person MrBloom    schedule 22.06.2013    source источник


Ответы (2)


(instrument: \testEvt, freq:220, sustain: inf).play;

а также

(instrument: \testEvt,freq:220).play;

являются событиями. События решают многое за вас. Они вычисляют, когда установить гейт в 0. (Помните, что гейт является одним из аргументов в вашем SynthDef.) В первом примере, поскольку вы поддерживаете бесконечное время, гейт никогда не достигает нуля. Во втором примере он использует продолжительность по умолчанию и устанавливает ворота на ноль после того, как эта продолжительность прошла. Вы можете узнать, какие ключевые слова используются в переменных среды событий, просмотрев исходный файл Event.sc. Если вы ищете сустейн, вы обнаружите другие ключевые слова, которые он использует для тайминга. Один из них — дур. Попробуй это:

(instrument: \testEvt, dur:3).play

Freq также является ключевым словом для событий, но, поскольку у вас нет аргумента freq, он не может повлиять на ваш synthDef. Если вы хотите установить частоту, вам нужно внести изменения:

SynthDef(\testEvt,{
    arg out, gate = 1, freq = 440;
    var sint = Blip.ar(freq) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
    Out.ar(out, Pan2.ar(sint, 0));
}).add();

Для контраста между событиями и прямым управлением синтезатором попробуйте: a = Synth.new(\testEvt, [\out, 0, \gate, 1]) не имеют никакого эффекта, потому что это не аргументы для вашего synthdef. И, в отличие от Event, synth не выполняет никаких вычислений от вашего имени. Если вы хотите, чтобы нота заканчивалась, установите гейт на 0 самостоятельно: a.set(\gate, 0)

Хорошо знать переменные среды событий, потому что они также используются Pbinds, и с их помощью вы можете влиять на другие вещи. Если бы у вас был synthdef, который использовал сустейн в качестве аргумента для чего-то другого, вы могли бы быть удивлены тем, что он изменил ваши длительности.

person les_h    schedule 22.06.2013

Что касается вашего последнего подвопроса,

Кроме того, есть ли способ ссылаться на синтезаторы, созданные событием? Так что, когда у них будет sustain: inf в качестве аргумента, я смогу освободить их позже.

Да, "индексируя" ключ Event по \id. На самом деле это возвращает массив идентификаторов узлов, потому что событие с \strum может запускать более одного узла/синтеза. Кроме того, значение \id равно nil, пока событие не воспроизводится. Но этот метод индексации совершенно не нужен для того, что вы хотите, потому что...

Вы можете закончить (связанный) синтезатор, закончив Event раньше на release, точно так же, как и для самого Synth. То, что это делает, в основном отключает его внутренний синтезатор. (В вашем примере этот вызов release переходит в точку освобождения конверта ASR, сгенерированного Linen, путем понижения gate до 0.). И, конечно же, используйте переменную, чтобы сохранить "ссылку" на синтезатор и/или событие, если не планируете сразу выпускать его в программе (что не даст звука с гейтированной огибающей).

В принципе

fork { var x = Synth(\testEvt); 2.wait; x.release }

делает то же самое, что

fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.release }

за исключением того, что в последнем случае для выпуска есть один уровень косвенности. Первый пример также эквивалентен

fork { var x = Synth(\testEvt); 2.wait; x.set(\gate, 0); }

который выполняет работу release явно. Event также поддерживает set и передает значение соответствующему элементу управления Synth (если последний был правильно added на сервере).

Теперь сложный метод, о котором вы спрашивали (получение идентификаторов узлов для события и отправка им сообщений), тоже возможен... хотя вряд ли это необходимо:

fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait;
    e[\id].do({ arg n; s.sendMsg("/n_set", n, "gate", 0); }) }

Кстати, вы не можете использовать wait вне Routine, поэтому fork было необходимо в приведенных выше примерах. В интерактивном режиме в редакторе вы, конечно, можете «подождать вручную», прежде чем вызывать release либо на Synth, либо на Event.

В качестве тонкого момента в том, как работает гейтирование огибающей, оно фактически не начинает воспроизводиться (технически не начинает переход к конечной точке первого сегмента огибающей [атаки]), пока вы не установите гейт на 1. Т.е. вы можете отложить начало (конверта), как в:

fork { x = Synth(\testEvt, [\gate, 0]); 3.wait; x.set(\gate, 1); 2.wait; x.release }

Имейте в виду, что Event.play по умолчанию не генерирует этот переход gate от 0 до 1, т. е. вы не можете полагаться на него для срабатывания огибающей вашего синтезатора, если вы установите начальное значение gate равным нулю в SynthDef.

Кроме того, я предполагаю, что под «бесплатным» вы подразумеваете «прекратить играть», а не «освободить их память на сервере». Нет необходимости вручную освобождать эти (событийные) синтезаторы в последнем смысле, поскольку у них есть doneAction:2 в конверте, который сделает это за вас, как только они будут выпущены и завершится воспроизведение последнего сегмента конверта. Если вы каким-то образом хотите сразу убить синтезатор (как это делает Ctrl+.), вместо того, чтобы запускать его исчезновение, вы можете заменить сообщение, отправленное во внутренней функции «сложного» примера (выше), на s.sendMsg("/n_free", n). Или гораздо проще

fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.free }

Кроме того, если вас интересует \strum, вот пример:

e = (instrument: \testEvt, sustain: inf, strum: 1, out: #[0, 0]).play

Теперь e[\id] представляет собой массив из двух узлов. Event немного дерзок в том смысле, что он будет создавать несколько узлов только для массивов, переданных фактическим элементам управления Synth, а не для случайных полей, поэтому «наигрывание» \freq (или его предшественников, таких как \degree и т. д.) создает несколько узлов только в том случае, если ваш SynthDesc имеет элемент управления freq .

Увы, "сложный" метод почти бесполезен, когда дело доходит до игры Pbinds (паттернов). Это связано с тем, что Pbind.play возвращает, а EventStreamPlayer... увы, создает частную копию воспроизводимого события-прототипа и воспроизводит эту частную копию, которая недоступна для контекста вызывающей стороны (если только вы не взломаете EventStreamPlayer.prNext). Как ни странно, EventStreamPlayer имеет доступную переменную event, но это только «прототип», а не воспроизводимое событие частной копии... Итак, если p является экземпляром EventStreamPlayer, то p.event[\id] всегда равно нулю (или тому, что вы установили заранее) даже во время игры. Так как редко кто играет Events индивидуально и гораздо чаще по шаблонам...

Просто в качестве сложного хакерского упражнения оказывается, что есть еще более запутанный способ доступа к идентификаторам узлов, которые запускает EventStreamPlayer... Он основан на переопределении события по умолчанию play, которое, к счастью, может быть расширено за пределы наследования класса, потому что значение по умолчанию удобно сохраняется в словаре класса...

(p = Pbind(\instrument, \testEvt, \sustain, Pseq([1, 2]), \play, {
    arg tempo, srv;
    var rv;
    "playhack".postln;
    rv = Event.parentEvents[\default][\play].value(tempo, srv);
    ~id.postln;
    rv;
}).play)

Однако в целом шаблоны явно не предназначены для использования таким образом, то есть путем взлома «слоя ниже», чтобы получить идентификаторы узлов. В качестве «доказательства», хотя приведенное выше работает достаточно хорошо с Pbind (который использует тип Event по умолчанию \note), он не работает надежно с Pmono, который не устанавливает событие \id на его первой ноте (тип события \monoNote), а только на последующие заметки (которые генерируют другой тип события, \monoSet). Pmono хранит внутреннюю копию идентификатора узла, но она совершенно недоступна для первой мононоты; по какой-то причине он копирует его только в Events в последующих заметках (возможно, ошибка, но может быть «по замыслу»). Кроме того, если вы используете Pdef, который расширяет Event с типом \phrase... приведенный выше хак не работает полностью, т.е. \id никогда не устанавливается типом \phrase; возможно, вы сможете добраться до основных подсобытий, сгенерированных каким-то образом... Я не удосужился исследовать дальше.

В документации SC (в руководстве по шаблонам) даже говорится в одном месте

Помните, что потоки, созданные из шаблонов, не раскрывают свои внутренности. Это означает, что вы не можете настроить параметры синтезатора эффектов напрямую, потому что у вас нет возможности узнать, какой у него идентификатор узла.

Это не совсем правильно, учитывая приведенный выше хак, но в некоторых контекстах это правда.

person Fizz    schedule 21.02.2020