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


Supercollider etude 1

Когда я понял, что программа Supercollider слишком сложна и у меня разбегаются глаза от её возможностей, я понял, что ничего не смогу сделать без системного самоограничения. Я решил написать ряд электронных этюдов, в каждом из которых будет стоять очень конкретная техническая задача. Для первого этюда я решил ограничиться набором средств настолько минимальным, насколько это только возможно, то есть одной голой синусоидой без каких-либо иных форм звука. Задача оказалась сложной и интересной.
Прежде всего, поскольку музыка здесь пишется особым образом, то есть в виде программного кода, получился достаточно интересный принцип композиции. Этюд делится на 9 различных эпизодов/событий, каждое из которых происходит в своё заданное время, которое может пересекаться или не пересекаться с другими. Более того, продолжительность и сложность события может быть очень разной - от одной ноты до длинного или даже потенциально бесконечного эпизода. Все они пронумерованы в тексте программы и частично прокомментированы.
Первые три эпизода - одинокие ноты, последняя с небольшим глиссандо в конце. Эпизод 4 - унисон из 5 синусоид, постепенно расходящихся по высоте и положению в пространстве. Эпизод 5 - длинный путь 12-тоновой серии, которая постепенно ускоряется и превращается в единый звук, после чего замедляется обратно до различимой серии (уже в ракоходе).
Эпизод 6 - самое удивительное. Я решил добавить аддитивный синтез, но совершил 2 ошибки - забыл превратить числа из герц в номера нот и... замкнуть цикл. В результате произошло удивительное - аккорд из скучного визгливого 4-звучия, каким он (я потом проверял) должен был получиться, превратился в красивый кластер в низком и среднем регистре, весьма интересно и богато пульсирующий во времени. В конце же случился и вовсе трансцендентный опыт - как раз к окончанию приключений 12-тоновой серии в программе переполняется счётчик количества голосов, и она начинает осыпать дисплей сообщениями об ошибках и случайным образом отрубать большинство голосов, но почему-то не все, и вся последняя часть композиции посвящена этому мистическому процессу, образующему неожиданные и красивые гармонии, которые я немного дополнил ещё тремя одинокими нотами.

SCE1.mp3

Программный код этого сочинения выглядит следующим образом:

// Supercollider etude 1
// Viva la Sinus!
// Sinusoid forever! ~:@)



Server.default=s=Server.internal
s.boot



(
SynthDef("sine", {arg freq=440, pan=0, gate=1, done=0, add1, add2, time1, time2, curve;

var env, envctl, envfreq;
env=Env.newClear(8);
// здесь задаётся максимальное количество точек огибающей, лучше с запасом
envctl=Control.names ([\env]).kr( env.asArray );// этот трюк позволяет задавать огибающую извне через аргумент env
envfreq=EnvGen.kr(Env.new([freq, freq+add1, freq+add2], [time1, time2], curve)); // возможность делать глиссандо
Out.ar(0, Pan2.ar(SinOsc.ar(envfreq, 0) * EnvGen.kr(envctl, gate, doneAction:done),pan))}).send(s) // и, наконец, сама синусоида
)


(
c=0;
// здесь можно задать отступ в секундах, чтобы не проигрывать каждый раз композицию целиком
d=260; // пригодится для эпизода 4

e=Env([ 0.0001, 0.3, 0.0001], [ 6, 5], \exp);
// 1
Synth("sine", [\gate, 1, \done, 2 ], 1).setn( \env, e.asArray );
// вторая часть трюка с заданием огибающей

e=Env([ 0.0001, 0.4, 0.3, 0.0001], [3, 2, 2], \exp); // 2
Synth("sine", [\gate, 1, \done, 2, \freq, 66.midicps ], 1).setn( \env, e.asArray );
// midicps переводит номер ноты в герцы

e=Env([ 0.0001, 0.001, 0.15, 0.10, 0.0001], [4.8, 2.4, 2, 2.5], \exp); // 3
Synth("sine", [\gate, 1, \done, 2, \pan, 0.25, \freq, 72.midicps, \add2, 94, \time1, 5.75, \time2, 5.75, \curve, 2 ], 1).setn( \env, e.asArray );
// с небольшим глиссандо в конце

e=Env([ 0.00001, 0.00001, 0.02, 0.15, 0.1, 0.01, 0.000001], [8-c, 3, 4, 2, 3, 2]); // 4
5.do({ arg i;

Synth("sine", [\done, 2, \freq, 83.midicps, \pan, i-1/2-1, \add2, d, \time1, 10-c, \time2, 12, \curve, 4], 1).setn( \env, e.asArray); // цикл создаёт 5 звуков, разъезжающихся от общей частоты в разные стороны
d=d-130;
});

x=Array.fill(2048, {arg i; i=2048/i; i=i/192; i=i.atan; i.exp-1}); // 5 // заполнение массива формулой ритмической редукции (подбирал методом научного тыка и метода .plot)
a=[48, 59, 54, 69, 65, 49, 76, 62, 39, 55, 68, 81]; // 12-тоновая серия в разных регистрах
y=List [[48, 59, 54, 69, 65, 49, 76, 62, 39, 55, 68, 81]]; // Лист для формирования порядка проведений серии с различными преобразованиями
171.do({ arg i;
a=[a.permute(i+1), a.rotate(i), a.rotate(i.neg), a.reverse, a.scramble, a].wchoose([0.44, 0.17, 0.17, 0.05, 0.05, 0.12]); // преобразования серии, пропорциональный случайный выбор между пермутацией, ротацией, ракоходом и случайной перестановкой звуков
while ( { a.first == y.flat.last }, { a=[a.permute(i+1), a.rotate(i), a.rotate(i.neg), a.reverse, a.scramble, a].wchoose([0.44, 0.17, 0.17, 0.05, 0.05, 0.12]); }); // проверка на совпадение последнего звука предыдущей серии с первым звуком следующей
y=y.add(a);
});
y=y.flat; // раскрытие вложенных массивов внутри листа
SystemClock.sched (19.5-c, { // (здесь удобнее использовать просто таймер)
r=Routine.new({
2048.do({ arg i; var dur, freq;
dur=x.at(i)/3; // перемещение по списку длительностей с разбиванием каждой на три равных фазы
e=Env([0.0001, 0.4, 0.4, 0.0001], [dur, dur, dur]);
freq=y.at(i).midicps;
// перемещение по звукам серии
Synth("sine", [\done, 2, \freq, freq, \pan, [0.0001.exprand(0.7), 0.0001.neg.exprand(0.7.neg)].choose], 1).setn( \env, e.asArray ); // pan пока ближе к центру
w=x.at(i);
w.wait;
});
2048.do({ arg i; var dur, freq, procent;
dur=x.at(2047-i)/3; // а теперь задом наперёд
e=Env([0.0001, 0.4, 0.4, 0.0001], [dur, dur, dur]);
freq=y.at(2047-i);
procent=i/2047;
freq=[freq-[12,24].wchoose([0.85,0.15]), freq+[12,24].wchoose([0.8, 0.2]), freq].wchoose([1-procent/2, 1-procent/2, procent]).midicps;
// в первой половине могут появляться более экстремальные регистры
Synth("sine", [\done, 2, \freq, freq, \pan, 1.0.bilinrand], 1).setn( \env, e.asArray ); // другой алгоритм рандомизации pan, теперь по всему диапазону
w=x.at(2047-i);
w.wait;
});
});
r.play;
});
// m=0.2;
// была нужна для постепенного роста громкости эпизода 6

(
SystemClock.sched (29-c, {
// 6 // цикл, давший неожиданный эффект
// if (m // 4 основные частоты аккорда (предполагались как номера нот)
4.do({ arg i; var freq, ii, pan;
// pan=i/2.5-0.8; // варианты превращения аккорда из моно в стерео, также оказавшиеся ненужными
// pan=0.7.rand2;
pan=0;
ii=8-i;
freq=z.at(i); //.midicps
// тот самый момент, где вместо планируемых нот получились частоты в герцах из-за отсутствия .midicps
8.do({ arg i;

{Pan2.ar(SinOsc.ar(i+1*freq, 0, SinOsc.ar(SinOsc.ar(ii.rand, 0.5, i+1/10), 0.5, m)) * EnvGen.kr(Env([0.001, m, m, 0.001], [3, 6, 2.5]), doneAction:2), pan)}.play; // аддитивный синтез - от каждой частоты образуется 7 обертонов путём умножения. Дополнительные синусоиды дают колебания громкости.
});
});
// nil // здесь цикл надлежало тем или иным образом завершить с помощью этого сообщения. Без него он получается бесконечным и ведёт себя непредсказуемым образом.
});
);

e=Env([ 0.00001, 0.00001, 0.4, 0.3, 0.0001], [ 220, 10, 2, 5], \exp);
// 7
Synth("sine", [\gate, 1, \done, 2, \freq, 68.midicps], 1).setn( \env, e.asArray );

e=Env([ 0.00001, 0.00001, 0.3, 0.0001], [226, 10, 8], \exp);
// 8
Synth("sine", [\gate, 1, \done, 2, \freq, 66.midicps ], 1).setn( \env, e.asArray );

e=Env([ 0.00001, 0.00001, 0.3, 0.0001], [250, 10, 10], \exp);
// 9
Synth("sine", [\gate, 1, \done, 2, \freq, 440 ], 1).setn( \env, e.asArray );
)