SuperCollider: mudanças entre as edições
Linha 62: | Linha 62: | ||
Esse é o básico do uso de SC: iniciar o servidor, avaliar as expressões (comandos) com CTRL+E e parar todos os sons com ESC. | Esse é o básico do uso de SC: iniciar o servidor, avaliar as expressões (comandos) com CTRL+E e parar todos os sons com ESC. | ||
=== Objetos == | === Objetos === | ||
Tudo em SC são objetos. Todo objeto tem um valor. Números, strings, até mesmo funções (blocos de código) são tratados como objetos e possuem seus respectivos valores. | |||
Tente avaliar cada comando abaixo e veja o que é retornado (note que '''avaliar''' é exatamente o que o termo sugere: '''saber o valor de algo'''): | |||
<pre> | <pre> | ||
2.value; | 2.value; | ||
(2.5).value; | (2.5).value; | ||
{ "foo".postln; }.value; | { "foo".postln; }.value; | ||
"foo".value; | "foo".value; | ||
</pre> | |||
=== Funções === | |||
<pre> | |||
f = { "foo bar".postln; }; | f = { "foo bar".postln; }; | ||
f; | f; |
Edição das 23h07min 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
Antes de tudo, é necessário sempre iniciar o servidor de SC (SC Server). É importante que durante toda a leitura desse tutorial você vá copiando os trechos de código e colando no seu editor. Comece copiando e colando o código abaixo:
// Iniciando o servidor s = Server.local; s.boot;
Para avaliar esses comandos, digite CTRL + E enquanto seu cursor estiver na linha da expressão. Grave esse comando, pois usaremos ele para executar (avaliar) qualquer comando em SC.
Com isso fizemos o SC Lang conectar no SC Server e inicializamos ele. É importante lembrar que você terá que iniciar o servidor JACK de áudio. Para isso, recomendo instalar o qjackctl (sudo aptitude install qjackctl), executá-lo e clicar em Start.
A sintaxe de SC será familiar para quem já programou em C, Java e Smalltalk. SC Lang é uma linguagem orientada a objetos, fortemente baseada em Smalltalk e com algumas noções de Scheme.
Para não perder o costume, vamos a um "Hello World" em SC:
"Hello, world!".postln;
Note que o que estamos fazendo é chamando a função (ou método) postln do objeto "Hello, world!", uma string. Esses detalhes ficarão claros a seguir.
Vamos tirar um primeiro som de SC, apenas para testar. Novamente, copie o código abaixo no seu Gedit e aperte CTRL+E:
{[SinOsc.ar(660, 0, 0.2), SinOsc.ar(662, 0, 0.2)]}.play(s);
Para para de tocar todos os sons, aperte ESC.
Esse é o básico do uso de SC: iniciar o servidor, avaliar as expressões (comandos) com CTRL+E e parar todos os sons com ESC.
Objetos
Tudo em SC são objetos. Todo objeto tem um valor. Números, strings, até mesmo funções (blocos de código) são tratados como objetos e possuem seus respectivos valores.
Tente avaliar cada comando abaixo e veja o que é retornado (note que avaliar é exatamente o que o termo sugere: saber o valor de algo):
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 )
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