SuperCollider

De Pontão Nós Digitais
Ir para navegaçãoIr para pesquisar

Tutorial de Introdução à SC

Instalando e configurando

Atualmente SC possui pacotes para a versão 3.4. É recomendado que você comece por usar a versão para Ubuntu/Debian e depois se aventure na versão instável disponível no repositório Git (para tal, veja o Anexo A no final desse tutorial).

Primeiro adicione o PPA seguindo as instruções em https://launchpad.net/~supercollider/+archive/ppa:

   sudo add-apt-repository ppa:supercollider/ppa
   sudo aptitude update

e depois:

   sudo aptitude install supercollider supercollider-doc supercollider-quarks supercollider-gedit

Agora abra o Gedit e vá em Editar -> Preferências -> Plugins e habilite o SCED (SuperCollider EDitor). Assim você poderá usar o Gedit para controlar o SuperCollider, enviando para ele comandos na linguagem do SuperCollider.

Para começar a editar código em SC, vá em Ferramentas e depois Supercollider Mode. Pronto, o Supercollider estará executando em um painel do Gedit. É no painel de cima que começaremos a escrever o código.

Antes de começar a programar... entendendo a arquitetura de SuperCollider

SC é baseado em uma arquitetura cliente-servidor. O servidor é o responsável por acessar o dispositivo de áudio e fazer todo o trabalho de processamento de áudio, e é chamado de scsynth. Como cliente podemos usar qualquer aplicativo que suporte o protocolo OSC. O SC vem com um cliente padrão, chamado sclang. É com ele que iremos programar o scsynth, usando uma linguagem específica, a linguagem SuperCollider.

Portanto, temos o seguinte cenário:

   scsynth  <----OSC----> sclang
      ^---.
          `------OSC----> qualquer programa que suporte OSC (Python, Processing, PD, ...)

A Linguagem SuperCollider

<source lang="c"> // // Tutorial de SC //

// Iniciando o servidor s = Server.local; s.boot;

// Um hello world (no Gedit, para avaliar, Ctrl+E. No Emacs C-c C-c) "Hello, world!".postln;

// Fazendo barulho {[SinOsc.ar(660, 0, 0.2), SinOsc.ar(662, 0, 0.2)]}.play(s);

// Para parar todos os sons ESC, no Gedit.

// // Objetos //

// Tudo em SC é um objeto. Todo objeto tem um valor. 2.value; (2.5).value; { "foo".postln; }.value; "foo".value;

// // Funções //

f = { "foo bar".postln; }; f; f.value;

// Uma função anônima sendo avaliada: { "foo bar".post; }.value;

// Avaliar uma função, é, como o nome sugere, saber seu valor

// Funções podem ter argumentos

f = { arg foo;

     foo.value * 2;

};

f.value(3);

// Podemos definir argumentos de modo alternativo, usando | var |

f = { | a, b |

     a + b;

};

f.value(2, 3);

// Keywords g = { arg a, b, c;

     a * b + c;

};

g.value(b: 4, a: 3, c: 2);

// Valores de argumentos padrão h = { arg foo, bar = 1;

     foo / bar;

};

h.value(2); h.value(2, 1);

// Variáveis dentro de funções

f = { arg foo;

     var bar = 2;
     foo + bar;

};

f.value(2);

// // Variáveis padrões //

// Perceba que todas as variáveis que usamos até agora (f, g, h, s, ...) // não foram previamente declaradas por nós (usando var). Porquê?! // Quando SC é inicializado, ele declara as variáveis de a-z para testes. // Existem variáveis definidas também por motivo especial, como o caso de s, // que aponta sempre para o servidor local de scsynth.

// Tente avaliar: a; b; c; s; foo; // somente essa não é declarada ainda...

// // Blocos de código //

// Para facilitar a avaliação (sem ter que ficar selecionando blocos de código) // é uma prática comum em SC delimitar blocos que iremos avaliar durante // a execução do programa com parênteses

// É muito mais prático avaliar isso:

( // <- sempre deixe o cursor no início do bloco para avaliar f = { | a, b |

     (a * b) / 10;

}; )

f.value(2,3)

// ... do que isso:

f = { |a, b|

     (a * b) / 10;

}

// // Arrays //

a = [2, 3.3, "foo"]; a[0]; a[1] = 3.4; a[2]; a.at(2); a.choose;

// // Fazendo Barulho //

{ SinOsc.ar(222, 0, 0.5); }.play;

( { var ampOsc;

  ampOsc = SinOsc.kr(0.5, 1.5pi, 0.5, 0.5);
  SinOsc.ar(440, 0, ampOsc);

}.play; )

// Podemos usar arrays para especificar múltiplos canais de áudio

{ [SinOsc.ar(440, 0, 0.2), SinOsc.ar(441, 0, 0.2)] }.play;

// ou...

{ SinOsc.ar([440, 442], 0, 0.2) }.play;

// Alguns UGens se aproveitam disso

{ Pan2.ar(PinkNoise.ar(0.2), SinOsc.kr(0.5)) }.play;

// Podemos também somar ondas (mixing)

{ SinOsc.ar(440, 0, 0.3) + SinOsc.ar(441, 0, 0.3) }.play;

// o que é equivalente a mixá-as, ou usar Mix:

{ Mix([SinOsc.ar(440, 0, 0.3), SinOsc.ar(441, 0, 0.3)]) }.play;

// É interessante plotar as ondas para ter uma ideia de sua forma

{ Mix([SinOsc.ar(60, 0, 0.2), Saw.ar(80, 0.2)]) }.plot;

// Usando MouseX

(

        { var input, kernel;                         input=AudioIn.ar(1);             kernel= Mix.ar(LFSaw.ar([300,500,800,1000]*MouseX.kr(1.0,2.0),0,1.0));

        //must have power of two framesize         Out.ar(0,Convolution.ar(input,kernel, 1024, 0.5));          }.play;

)

( { var freq;

freq = [[660, 880], [440, 660], 1320, 880].choose;

SinOsc.ar(freq, 0, 0.2) * SinOsc.ar(20, 0, 0.2);

}.plot;

)

{ Pan2.ar(PinkNoise.ar(0.2), MouseX.kr(-1.0, 1.0)) }.play; a = SynthDef(\sinetest, {arg out = 1, freq = 440; Out.ar(out, SinOsc.ar(freq))}) ; z = SynthDefAutogui(\sinetest, scopeOn:false) ;

// // Classes que abstraem o server (scsynth) //

{ SinOsc.ar(440, 0, 0.5) }.play;

SynthDef("meuSinOsc", { Out.ar(0, SinOsc.ar(440, 0, 0.5)) }).play;

SynthDef("meuSinOsc", { Out.ar(0, SinOsc.ar(440, 0, 0.5)) }).send(s); x = Synth("meuSinOsc"); y = Synth("meuSinOsc"); x.free; y.free;

( SynthDef.new("meuSinOscStereo", { var outArray;

     outArray = [SinOsc.ar(440, 0, 0.5), SinOsc.ar(440, 0, 0.5)];
     Out.ar(0, outArray);

}).play; )

// Quando trabalhamos com SynthDef, é importante definir o segundo argumento, // o 'grafo de UGens' como sendo ele todo composto por UGens // Veja que até a geração de números aleatórios é dada por um UGen: Rand

SynthDef("meuSinOscRandomico", { Out.ar(0, SinOsc.ar(Rand(440, 660), 0, 0.5)) }).send; x = Synth("meuSinOscRandomico") x.free

// Outro exemplo de SynthDef, agora com argumentos (muito mais flexível!)

( SynthDef("sin-param", { | freq=440, out=0 |

   Out.ar(out, SinOsc.ar(freq, 0, 0.5))

}).send; )

x = Synth("sin-param"); // pega os argumentos padrões x = Synth("sin-param", ["freq", 660, "out", 0]); // especificando parâmetros x.free

// Podemos também usar o método set() para alterar argumentos // Muito interessante para livecoding!

x.set("freq", 1000); x.set("out", 1) x.set("out", 0) x.set("freq", 660);

// // Símbolos versus Strings //

// Podemos usar símbolos no lugar de strings

// Repare no símbolo usado: 'meu-sin' ao invés da string "meu-sin"

( SynthDef('meu-sin', { Out.ar(0, SinOsc.ar(440, 0, 0.5)) }).send; )

// ou... (repare no símbolo usado: \meuSin)

( SynthDef(\meuSin, { Out.ar(0, SinOsc.ar(440, 0, 0.5)) }).send; )

// Símbolos portanto são representados como 'meu simbolo' ou \meuSimbolo

// // Busses //

// Pense em um bus como uma forma de acessar o IO de áudio e rotear // o áudio entre bus e synths

// Bus de saída Out.ar(0, SinOsc.ar(440, 0, 0.5)); // Bus de entrada In.ar(0, 1);

// Bus genérico Bus.control(s, 2) // cria dois busses de controle Bus.audio(s) // cria um bus de áudio (1 é padrão)

// Exemplo mais completo de uso de Busses

( // the arg direct will control the proportion of direct to processed signal SynthDef("tutorial-DecayPink", { arg outBus = 0, effectBus, direct = 0.5;

   var source;
   // Decaying pulses of PinkNoise. We'll add reverb later.
   source = Decay2.ar(Impulse.ar(1, 0.25), 0.01, 0.2, PinkNoise.ar);
   // this will be our main output
   Out.ar(outBus, source * direct);
   // this will be our effects output
   Out.ar(effectBus, source * (1 - direct));

}).send(s);

SynthDef("tutorial-DecaySin", { arg outBus = 0, effectBus, direct = 0.5;

   var source;
   // Decaying pulses of a modulating Sine wave. We'll add reverb later.
   source = Decay2.ar(Impulse.ar(0.3, 0.25), 0.3, 1, SinOsc.ar(SinOsc.kr(0.2, 0, 110, 440)));
   // this will be our main output
   Out.ar(outBus, source * direct);
   // this will be our effects output
   Out.ar(effectBus, source * (1 - direct));

}).send(s);

SynthDef("tutorial-Reverb", { arg outBus = 0, inBus;

   var input;
   input = In.ar(inBus, 1);
   // a low rent reverb
   // aNumber.do will evaluate it's function argument a corresponding number of times
   // {}.dup(n) will evaluate the function n times, and return an Array of the results
   // The default for n is 2, so this makes a stereo reverb
   16.do({ input = AllpassC.ar(input, 0.04, { Rand(0.001,0.04) }.dup, 3)});
   Out.ar(outBus, input);

}).send(s);

b = Bus.audio(s,1); // this will be our effects bus )

( x = Synth.new("tutorial-Reverb", [\inBus, b]); y = Synth.before(x, "tutorial-DecayPink", [\effectBus, b]); z = Synth.before(x, "tutorial-DecaySin", [\effectBus, b, \outBus, 1]); )

// Change the balance of wet to dry

y.set(\direct, 1); // only direct PinkNoise z.set(\direct, 1); // only direct Sine wave y.set(\direct, 0); // only reverberated PinkNoise z.set(\direct, 0); // only reverberated Sine wave x.free; y.free; z.free; b.free;

// // Groups //

// // Buffers //

// Criamos um buffer de 100 frames e 2 canais b = Buffer.alloc(s, 100, 2); // Liberamos memória b.free;

// Podemos ler um arquivo para um Buffer b = Buffer.read(s, "/home/vilson/Samples/drums1.wav");

// E tocá-lo ( x = SynthDef("player", { | out=0, bufnum |

       Out.ar(out,
              PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum))
       )

}).play(s, [\bufnum, b]); ) x.free; b.free;

// ou simplesmente: b.play; b.play(true); // toca em loop b.free; // e também visualizá-lo: b.plot;

// Podemos usar buffer com qualquer tipo de dado (útil para waveshapping):

b = Buffer.alloc(s, 512, 1);

b.cheby([1,0,1,1,0,1]);

( x = play({ Shaper.ar(b, SinOsc.ar(300, 0, Line.kr(0,1,6)), 0.5) }); )

x.free; b.free;

// // Agendando eventos //

// A música só acontece no tempo. Em SC, controlamos o "quando" agendando // coisas em relógios.

// Existem 2 relógios importantes em SC: TempoClock, SystemClock // TempoClock é mais interessante para eventos musicais (respeitam compasso) // SystemClock é mais interessante para eventos mundanos (em segundos)

// Agendamos para mostrar Hello daqui a 5 segundos SystemClock.sched(5, { "Hello".postln }) // Quantos segundos até agora? SystemClock.seconds; // Quantas batidas até agora? (do TempoClock padrão, podem haver quantos TempoClocks desejarmos) TempoClock.default.beats; // Podemos usar schedAbs() para especificar quando exatamente (absoluto) queremos que algo seja executado ( TempoClock.default.tempo = 2; var timeNow = TempoClock.default.beats; TempoClock.default.schedAbs(timeNow + 5, { "foo".postln; }); )

// // Agengando eventos com Routines e Tasks //

// // Sequenciando com Patterns //


p = Pseq([60, 72, 71, 67, 69, 71, 72, 60, 69, 67], 1); r = p.asStream; while { (m = r.next).notNil } { m.postln };

( var midi, dur; midi = Pseq([60, 72, 71, 67, 69, 71, 72, 60, 69, 67], 1).asStream; dur = Pseq([2, 2, 1, 0.5, 0.5, 1, 1, 2, 2, 3], 1).asStream;

SynthDef(\smooth, { |freq = 440, sustain = 1, amp = 0.5|

   var sig;
   sig = SinOsc.ar(freq, 0, amp) * EnvGen.kr(Env.linen(0.05, sustain, 0.1), doneAction: 2);
   Out.ar(0, sig ! 2)

}).send(s);

r = Task({

   var delta;
   while {
       delta = dur.next;
       delta.notNil
   } {
       Synth(\smooth, [freq: midi.next.midicps, sustain: delta]);
       delta.yield;
   }

}).play(quant: TempoClock.default.beats + 1.0); )

(

SynthDef(\bass, { |freq = 440, gate = 1, amp = 0.5, slideTime = 0.17, ffreq = 1100, width = 0.15,

detune = 1.005, preamp = 4|

var sig,

env = Env.adsr(0.01, 0.3, 0.4, 0.1);

freq = Lag.kr(freq, slideTime);

sig = Mix(VarSaw.ar([freq, freq * detune], 0, width, preamp)).distort * amp

  • EnvGen.kr(env, gate, doneAction: 2);

sig = LPF.ar(sig, ffreq);

Out.ar(0, sig ! 2)

}).add;


TempoClock.default.tempo = 132/60;


p = Pxrand([

Pbind(

\instrument, \bass,

\midinote, 36,

\dur, Pseq([0.75, 0.25, 0.25, 0.25, 0.5], 1),

\legato, Pseq([0.9, 0.3, 0.3, 0.3, 0.3], 1),

\amp, 0.5, \detune, 1.005

),

Pmono(\bass,

\midinote, Pseq([36, 48, 36], 1),

\dur, Pseq([0.25, 0.25, 0.5], 1),

\amp, 0.5, \detune, 1.005

),

Pmono(\bass,

\midinote, Pseq([36, 42, 41, 33], 1),

\dur, Pseq([0.25, 0.25, 0.25, 0.75], 1),

\amp, 0.5, \detune, 1.005

),

Pmono(\bass,

\midinote, Pseq([36, 39, 36, 42], 1),

\dur, Pseq([0.25, 0.5, 0.25, 0.5], 1),

\amp, 0.5, \detune, 1.005

)

], inf).play(quant: 1);

)


// totally cheesy, but who could resist?

(

SynthDef(\kik, { |preamp = 1, amp = 1|

var freq = EnvGen.kr(Env([400, 66], [0.08], -3)),

sig = SinOsc.ar(freq, 0.5pi, preamp).distort * amp

  • EnvGen.kr(Env([0, 1, 0.8, 0], [0.01, 0.1, 0.2]), doneAction: 2);

Out.ar(0, sig ! 2);

}).add;


// before you play:

// what do you anticipate '\delta, 1' will do?

k = Pbind(\instrument, \kik, \delta, 1, \preamp, 4.5, \amp, 0.32).play(quant: 1);

)


p.stop;

k.stop;

// // Usando MIDI // MIDIClient.init; MIDIClient.sources.at(3); MIDIClient.destinations; MIDIIn.connect(0, MIDIClient.sources.at(3)); MIDIIn.noteOn = { |port, chan, note, vel| [port, chan, note, vel].postln };

( SynthDef("sik-goo", { arg freq=440,formfreq=100,gate=0.0,bwfreq=800;         var x;         x = Formant.ar(                 SinOsc.kr(0.2, 0, 1, freq),                 formfreq,                 bwfreq         );         x = EnvGen.kr(Env.adsr, gate,Latch.kr(gate,gate)) * x;         Out.ar(0, x); }).send(s); )

x = Synth("sik-goo");

//set the action: ( MIDIIn.noteOn = {arg src, chan, num, vel;         x.set(\freq, num.midicps / 4.0);         x.set(\gate, vel / 200 );         x.set(\formfreq, vel / 127 * 1000); }; MIDIIn.noteOff = { arg src,chan,num,vel;         x.set(\gate, 0.0); }; MIDIIn.bend = { arg src,chan,val;         //(val * 0.048828125).postln;         x.set(\bwfreq, val * 0.048828125 ); }; )

(

{Pluck.ar(WhiteNoise.ar(0.1), Impulse.kr(1), 440.reciprocal, 440.reciprocal, 10,

coef:MouseX.kr(-0.999, 0.999))

}.play(s)

)

// // Outros exemplos interessantes //

{o=0; 15.do{|i| o=o+LFTri.ar(138.59*(1.0595**(3*i)), 0, LFPulse.kr((51+i)/60, 0, 0.05, 0.1))}; o;}.play;

fork{{var t=0.2,x=t.rand;play{PMOsc.ar(100+400.rand,400+400.rand,1+4.0.rand,0,0.2)*EnvGen.kr(Env.linen(0,x,0,1),1,1,0,1,2)};(t-x).wait}!200};

(

{var n;

n=34;


Resonz.ar(

Mix.arFill(n,{

var freq, numcps;


freq= rrand(50,560.3);

numcps= rrand(2,20);

Pan2.ar(Gendy1.ar(6.rand,6.rand,1.0.rand,1.0.rand,freq ,freq, 1.0.rand, 1.0.rand, numcps, SinOsc.kr(exprand(0.02,0.2), 0, numcps/2, numcps/2), 0.5/(n.sqrt)), 1.0.rand2)

})

,MouseX.kr(100,2000), MouseY.kr(0.01,1.0))

}.play

)


(


{

var freq,time, ex, delay, filter, local;


freq= 440;

time= freq.reciprocal;


ex= WhiteNoise.ar(EnvGen.kr(Env([1.0,1.0,0.0,0.0], [time,0,100])));


freq= SinOsc.ar(6, 0, 10, freq);

time= freq.reciprocal;


local= LocalIn.ar(1);


filter= LPZ1.ar(ex+local); //apply filter


//maximum delay time is 440-10

delay= DelayN.ar(filter, 430.reciprocal, time-ControlDur.ir);


LocalOut.ar(delay*0.99);


Out.ar(0, Pan2.ar(filter,0.0))

}.play


)


</source>

Anexo A: Instalando a partir do GIT

Se quiser as últimas atualizações é recomendável usar a versão de desenvolvimento:

   git clone --recursive git://supercollider.git.sourceforge.net/gitroot/supercollider/supercollider
   cd supercollider
   mkdir build
   cd build
   cmake ..
   make
   sudo make install 

E mantenha-se atualizado:

   git pull
   git submodule update


Referências