Когда я понял, что программа для создания электронной музыки Supercollider слишком сложна и у меня разбегаются глаза от возможностей, я понял, что ничего не смогу сделать без системного самоограничения. Я решил написать ряд электронных этюдов, в каждом из которых будет стоять очень конкретная техническая задача. Для первого этюда я решил ограничиться набором средств настолько минимальным, насколько это только возможно, то есть одной голой синусоидой без каких-либо иных форм звука. Задача оказалась сложной и интересной.
Прежде всего, поскольку музыка здесь пишется особым образом, то есть в виде программного кода, получился достаточно интересный принцип композиции. Этюд делится на 9 различных эпизодов/событий, каждое из которых происходит в своё заданное время, которое может пересекаться или не пересекаться с другими. Более того, продолжительность и сложность события может быть очень разной - от одной ноты до длинного или даже потенциально бесконечного эпизода. Все они пронумерованы в тексте программы и частично прокомментированы.
Первые три эпизода - одинокие ноты, последняя с небольшим глиссандо в конце. Эпизод 4 - унисон из 5 синусоид, постепенно расходящихся по высоте и положению в пространстве. Эпизод 5 - длинный путь 12-тоновой серии, которая постепенно ускоряется и превращается в единый звук, после чего замедляется обратно до различимой серии (уже в ракоходе).
Эпизод 6 - самое удивительное. Я решил добавить аддитивный синтез, но совершил 2 ошибки - забыл превратить числа из герц в номера нот и... замкнуть цикл. В результате произошло удивительное - аккорд из скучного визгливого 4-звучия, каким он (я потом проверял) должен был получиться, превратился в красивый кластер в низком и среднем регистре, весьма интересно и богато пульсирующий во времени. В конце же случился и вовсе трансцендентный опыт - как раз к окончанию приключений 12-тоновой серии в программе переполняется счётчик количества голосов, и она начинает осыпать дисплей сообщениями об ошибках и случайным образом отрубать большинство голосов, но почему-то не все, и вся последняя часть композиции посвящена этому мистическому процессу, образующему неожиданные и красивые гармонии, которые я немного дополнил ещё тремя одинокими нотами.
Здесь несколько криво поддерживается HTML, из-за чего страдает форматирование, поэтому лучше смотреть здесь: busidoremikle.livejournal.com/404044.html
// 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 < 0.2, {m=m+0.02}, {m=0.2});    // постепенный рост громкости, от которого я решил отказаться - хуже звучит
         z = [72, 78, 83, 93];    // 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 );
)
*послушать не имел возможности, ибо читал не из дома )