SuperCollider: mudanças entre as edições
Linha 30: | Linha 30: | ||
== A Linguagem SuperCollider == | == A Linguagem SuperCollider == | ||
< | <source lang="c"> | ||
// | // | ||
// Tutorial de SC | // Tutorial de SC | ||
Linha 712: | Linha 712: | ||
</ | </source> | ||
== Anexo A: Instalando a partir do GIT == | == Anexo A: Instalando a partir do GIT == |
Edição das 19h23min de 22 de agosto de 2011
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
- Série de artigos sobre SC 3.5 e o novo livro The SC Book (mit press): http://www.linuxjournal.com/content/super-collision-studio-dave-new-world-supercollider3-part-1
- Tutorial LAPPSO-UEM: http://www.dmu.uem.br/lappso/index.php?title=Tutorial_SuperCollider
- Capítulo do livro The SC Book, Composition with SuperCollider: http://supercolliderbook.net/wilsondescrivanch3.pdf