SuperCollider
Transkrypt
SuperCollider
SuperCollider 1 dzień 1/ SuperCollider–podstawy SuperCollider 1 SUPERCOLLIDER –PODSTAWY 1.1 DLACZEGO SUPERCOLLIDER? SuperCollider jest środowiskiem i językiem programowania oryginalnie stworzonym w 1996 roku przez Jamesa McCartney'a dla real-time audio syntezy i kompozycji algorytmicznej. Od tamtego czasu ewoluował w stronę systemu rozwijanego zarówno przez naukowców jak i artystów pracujących z dźwiękiem. Jest to wydajny i ekspresywny język programowania, co czyni go ciekawą strukturą dla poszukiwań akustycznych, muzyki algorytmicznej i programowania interaktywnego. ( { // 10 voices of a random sine percussion sound: s = Mix.ar(Array.fill(10, { Resonz.ar(Dust.ar(0.2, 50), 200 + 3000.0.rand, 0.003)}) ); // reverb predelay time: z = DelayN.ar(s, 0.048); // 7 length modulated comb delays in parallel: y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, LFNoise1.kr(0.1.rand, 0.04, 0.05), 15) })); // two parallel chains of 4 allpass delays (8 total): 4.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) }); // add original sound to reverb and play it: s+(0.2*y) }.play ) Minusy • użytkownik jest zmuszony do napisania poprawnych składniowo twierdzeń w tym języku. • musisz stać się świadomy podstawowych mechanizmów języka programowania (iteracja, conditional execution, komunikowanie, kontener, itd.), co wymaga sporo wysiłku. • jeśli masz na myśli określone muzyczne zadanie, posługiwanie się tym językiem programowania jest frustrujące. Plusy • możesz odkrywać muzyczne możliwości, które standardowe programy nie byłyby w stanie eksplorować 2 SuperCollider dzień 1/ SuperCollider–podstawy SuperCollider 2 • możesz nauczyć się języka programowania „przy okazji" kiedy piszesz w tym języku, słysząc interesujące wyniki już od pierwszych monetów użytkowania • wpracujesz na darmowym i elastycznym programie SuperCollider to w zasadzie dwa oddzielne programy: język i serwer syntezy dźwiękowej: sclang i scserver. To co możemy oglądać to sclang, scserver pracuje natomiast w tle – lokalnie lub na innym komputerze. Sclang jest nowoczesnym językiem oop z pomysłami z smalltalk, lisp, j, itd., podczas gdy scserver jest to program brute force napisany w c, który odczytuje tylko ograniczoną ilość poleceń. Serwer może być używany niezależnie lub przyjmować instrukcje od innych klientów takich jak: haskel, scheme, python. Większość zadań, które będziemy wykonywać na sc dotyczy dualizmu sclang/scserver: zaprogramujemy i zdefiniujemy syntezator w tym języku a potem wyślemy go do serwera sc w celu odtworzenia. informacja dodatkowa / functional families of modern music and multimedia software and the place of sc in it 1.2 UGENS I ICH PARAMETRY W muzyce komputerowej dźwięk może być zdefiniowany jako funkcja okresowa lub nieokresowa, reprezentowana przez ciąg liczb. Te liczby określają podstawowe parametry źródła dźwięku. Funkcje okresowe: SinOsc.ar(freq, phase, mul, add) freq – częstotliwość w hercach (ilość drgań na sekundę) phase – zmiana fazy fali lub modulator w radianach (0..2pi) mul i add – wspólne parametry dla wielu ugens. Dostarczają sposobu odpowiedniego skalowania sygnałów, tak by mogły być one później użyte jako wejście dla innych ugens- ponieważ wyjście audio systemu oczekuje liczb w skali od -1 do 1, natomiast częstotliwość liczb wyrażana jest w skali od 20 do 20000, argumenty mul i add godzą te różne skale. W sclang wszystko jest obiektem, czy jest to liczba, tekst, ugen, Task czy funkcja. Obiekty te mają atrybuty i metody. Aby usłyszeć falę sinusoidalną, musimy wpisać SinOsc w funkcję i wykonać jej metodę .play { SinOsc.ar() }.play jest podobne do { SinOsc.ar( freq: 440, phase: 0, mul: 1, add: 0 ) }.play Możesz też użyć .scope do pokazania sygnału w czasie jego grania i .plot do wizualizacji wyników funkcji podczas gdy UGen pracuje i .plot(czas w sekundach) do prostej wizualizacji (rekomendowany .plot2, zobacz Plotter). Jest wiele innych ugens z wszystkimi rodzajami fali, szumu, impulsów i quasi-przypadkowych generatorów, filtrów, distortion, panoram, pogłosów, delay, ugens dla syntezy granularnej... / Pobaw się parametrami / Dodaj kontrolę myszką do freq informacja dodatkowa / zobacz [Tour_of_UGens ] 3 SuperCollider dzień 1/ SuperCollider–podstawy 1.3 SZYBKIE WPROWADZENIE DO SCLANG Syntax 4+8 12.245+1 // no need to declare types 12.5+"base" // in case of strings + means concatenation 3+2*10 // operators have no priority, always left-to-right order! 3+(2*10) // use parenthesis for changing the order of execution 1/5+(1/5)+(1/5) uwaga / Zmienne globalne powinny być stosowane wyłącznie do szybkiego testowania. W bardziej złożonych programach rekomendowana jest definicja zmiennych 2.sqrt // square root 60.round(80) // rounding 38.round(80) 60.773542618.round(0.01) 33.sqrt.round // everything is an object - result of one operation os a subject for another one done via method 5.pow(2).clip(0, 20).div(5).cubed.cpsmidi.ceil a= 100 // simple global variable a= a+1 a b= a/200 b= b.ampdb b (//scope of variables var a; a= 10; a + b; // note: local a + global b ) ( var uno= 33; uno+100; // the last statement in the () is the result ) a= 'asymbol' a= "astring" a= \alsoASymbol a= 22 // back to a number "this string"+a // becomes a string [1, 2, \asdf, "qwer"] //an array [1, 2, 3]+10 [1, 2, 3]*10 [1, 2, 3].pow(10) [1, 2, 3]+[4, 5, 6] [1, 2, 3]+[1.1, 2.2] // wraps - more advanced see [Adverbs ] dzień 1/ SuperCollider–podstawy [1, 2, 3].scramble // loads of methods to work [11, 22, 33].rotate [12, 24, 36].sum [3, 2, 1].sort [1, 2, 3].indexOf(2) //testing [1, 2, 3] > 2 Array.series(5, 10, 2) // parameters are: size, start, step SuperCollider 4 see / a= { |x| x.sqrt } // a function a.value(64) // call it with argument 10 [Array ] [SequenceableCollection ] a= { arg x= 2; x + 3 } // set default for 1st argument to 5 a.value a.value([1, 2, 3]) // anything that understands +10 a.value("default value") // string as argument a= {|x, y, z| x*y+z} // multiple arguments a.value(1, 2, 3) a.value(z:1, y:2, x:3) // swap order with keywords 10.do{|x| x.postln} // simple iteration 5.do{|x| x.pow(2).postln} [1, 2, 3, 4].collect{|x| x+10} // iterate and create new array [11, 22, 33, 44].select{|x| x.even} // test and create new from result Array.fill(10, {|x| x.pow(2)}) // create array from scratch Array.fill(100, {|x| 40.rrand(80)}) // 100 random midinotes? Array.fill(16, {|x| 1.0.linrand.round(0.5)}) // 16step amplitudes? a= Array.fill(16, {|x| x}) a= a.rotate(1) a= a.rotate(2) a= a.invert a= a.normalize(-1, 1) (a+100*2).round(0.5) 1.4 ŁĄCZENIE Skoro sygnał jest falą (stąd, dynamicznym ciągiem), większość powyższych operacji będzie mieć zastosowanie również w przypadku sygnałów: {SinOsc.ar.round(0.25)}.scope // -1 to 1 for most signals {SinOsc.ar.pow(2)}.scope {SinOsc.ar.sqrt.abs.round(0.1)-0.5}.scope {Saw.ar.clip(-0.2, 0.8)}.scope [Collection ] 5 SuperCollider dzień 1/ SuperCollider–podstawy {Saw.ar.abs*0.3+(Saw.ar.round(0.2))}.scope {(SinOsc.ar*0.4)+(BrownNoise.ar*0.2)}.scope //elementary mixing {SinOsc.ar.wrap(-0.5, 0.5)}.scope {SinOsc.ar.fold(-0.5, 0.5)}.scope {SinOsc.ar.clip(-0.5, 0.5)}.scope {(SinOsc.ar*0.4).round(BrownNoise.ar)}.scope {WhiteNoise.ar.max(SinOsc.ar*0.1)}.scope {ClipNoise.ar*Pulse.ar.max(SinOsc.ar/3)}.scope {SinOsc.ar.lag(Saw.ar.fold(0, 0.2))}.scope {Pulse.ar.hypot(LFNoise0.ar)%0.5}.scope {Pulse.ar.fold2(SinOsc.ar)*LFNoise1.ar*0.5}.scope {(Pulse.ar+SinOsc.ar*PinkNoise.ar).thresh(Pulse.ar)}.scope wypróbuj / UGens: SinOsc, Pulse, lfpulse, Saw, lfsaw, lftri, Phasor, Dust, Impulse, lfnoise0, lfnoise1, lfnoise2, BrownNoise, PinkNoise, GrayNoise, ClipNoise, WhiteNoise Methods: round, fold, clip, max, min, thresh, hypot, dist, lag, lag2, fold2, wrap, sqrt, pow, abs, +, -, *, /, % references / 1. http://www. fredrikolofsson. com/software/ writingAlgorithmsThatSound.html 2. http://www. informatics.sussex.ac.uk/users/ nc81/courses/cm1/ sccourse.zip (parts 1.1, 1.2, 1.3, 2.2) 1.5 INNA METODA WRAPPING UP PARAMETRÓW are: Line and Xline - linear and exponential enveloping {Pulse.ar.fold2(SinOsc.ar(Line.kr(0.5, 20, 5)))*LFNoise1.ar*0.5}.scope {Pulse.ar.fold2(SinOsc.ar(XLine.kr(0.5, 20, 5)))*LFNoise1.ar*0.5}.scope Envelopes {SinOsc.ar(EnvGen.kr(Env.new([125, 12000, 500, 225], [4, 3, 1], [‚exp’, ‚lin’, ‚sine’])))}.scope //use doneAction of EnvGen.kr to extinguish the enclosing synth Env.new([125, 12000, 500, 225], [4, 3, 1], [‚exp’, ‚lin’, ‚sine’]).plot2 {SinOsc.ar(EnvGen.kr(Env.new([125, 12000, 500, 225], [4, 3, 1], [‚exp’, ‚lin’, ‚sine’])))}.scope //dozens of ready-to-use envelopes, see [Env ] //gating and releasing synths (we will soon learn what SynthDef is) ( SynthDef(\env_help, { | out, gate = 0 | var z; z = EnvGen.kr(Env.adsr, gate) * SinOsc.ar(440, 0, 0.1); Out.ar(out, z) }).send(s); ) a = Synth(\env_help); a.set(\gate, 1); //turn on a.set(\gate, 0); //turn off a.set(\gate, 2); //no matter what value we send to the gate, as long as it is > 0 a.free; dzień 1/ synteza dźwięku SYNTEZA DŹWIĘKU 2.1 SYNTEZA SUBSTRAKTYWNA Server.default=s=Server.internal; //oscilloscope available via internal server only s.boot; W syntezie substraktywnej zaczynamy od skomplikowanego źródła i odejmujemy części od tego surowego dźwięku aby stworzyć bardziej wyrzeźbiony dźwięk. Inaczej określa się to jako model źródło+filter. { WhiteNoise.ar(0.1) }.scope //this line will make a pure white noise source, equal energy at all spectral frequencies Przefiltruj go aby stworzyć mniej surowy dźwięk { LPF.ar(WhiteNoise.ar(0.1),1000) }.scope Przypuśćmy, że potrzebujemy filter, którego cutoff zmienia się w czasie. Możemy użyć generatora funkcji liniowej: Line.kr(10000,1000,10) //takes 10sec to go from 10000 to 1000 Zamiast określonej wartości 1000, wpisujemy Line ugen { LPF.ar(WhiteNoise.ar(0.1),Line.kr(10000,1000,10)) }.scope Zamiast filtra Butterworth (lpf), możemy użyć filtra rezonansu, otrzymując harmoniczny dźwięk z szumu (przypadkowy sygnał) { Resonz.ar(WhiteNoise.ar(0.5), XLine.kr(1000, 10000,10), 0.05) }.scope Możliwe jest zrobienie tego periodycznie: { Resonz.ar(WhiteNoise.ar(0.5), SinOsc.kr(0.025, 0, 10000, 1000).abs, 0.05) }.scope Z filtrem rezonującym możemy osiągnąć efekt nieprzerwanie poszerzającego się dźwięku poprzez modulowanie centralnych częstotliwości: { Resonz.ar(LFNoise0.ar(400),Line.kr(10000,1000,10),0.1) }.scope Szum biały jest najlepszy do syntezy substraktywnej ponieważ jego widmowa gęstość mocy (dystrybucja siły w widmie częstotliwościowym) jest zupełnie płaska, stad bardziej elastyczna. Odnieś się do koloru szumu. Zauważ, że implementacja większości oscylatorów ugens jest czytaniem przykładowych wartości z wavetable. Wavetable jest czytana raz za razem w pętli, powodując okresowość (stały ton). Te tabele są stale używane tyle razy na sekundę ile określa to częstotliwość: { SinOsc.ar(100) }.plot2(0.05) //plot 5 cycles of a SinOsc sine oscillator: reads through the sine wavetable 5 times SuperCollider 6 7 SuperCollider patrz również / Oscillators: [Saw] [Blip] Noise Sources: [PinkNoise] [LFNoise0] dzień 11/ synteza dźwięku Czy jest możliwe określenie zwyczajnego kształtu fali? Zajmiemy się Buffers szczegółowo w przyszłości, ale są one dogodne do przechowywania danych takich jak wavetable.sc ma specjalny skuteczny format wavetable aby spakować buffer: b = Buffer.alloc(s, 512, 1); //make a Buffer storage area b.sine1(1.0 / [1, 2, 3, 4, 5, 6], true, false, true); //fill the Buffer with wavetable data b.plot2; //stored shape (not in special SuperCollider Wavetable format, for clarity) Filters: { PlayBuf.ar(1, b.bufnum, 1, Impulse.kr(1), 0, 1) }.scope [HPF] [BPF] [Resonz] { LPF.ar( PlayBuf.ar(1, b.bufnum, 1, Impulse.kr(1), 0, 1), SinOsc.kr(0.1, 0, 100, 10).abs) }.scope b.free; 2.2 SYNTEZA ADDYWNA Zamiast zaczynać od czegoś skomplikowanego i odejmować energię by wyrzeźbić dźwięk, możemy rozpocząć od prostego budowania bloków i dodać wiele ich razem aby stworzyć bardziej skomplikowany dźwięk. Klasycznym blokiem budowanym w muzyce komputerowej jest sygnał sinusoidalny: { SinOsc.ar }.scope //defaults to A3 (440Hz) Zauważ: wysokość tonu (nutę) można łatwo otrzymać jeśli zna się częstotliwość: 440.cpsmidi.midinotename i z powrotem: \A3.namemidi.midicps Najprostszy sposób na uzyskanie dwóch tonów jednocześnie: { SinOsc.ar(400,0,0.1) + SinOsc.ar(660,0,0.1) }.scope Poniższy sposób jest łatwiejszy: { SinOsc.ar([400,660],0,0.1) }.scope //note stereo field expansion Właściwą drogą do mieszania sygnałów jest panning: { Pan2.ar(WhiteNoise.ar(0.1), MouseX.kr(-1,1)) }.scope Mix ugen: {Mix(SinOsc.ar([400,660],0,0.1))}.scope //a two channel signal put through Mix turns into mono Stosowanie Pan2 pozwala na umiejscowienie tego w polu stereo: {Pan2.ar(Mix(SinOsc.ar([400,660],0,0.1)),MouseX.kr(-1,1))}.scope //a two channel signal put through Mix turns into mono W syntezie addytwnej, jeśli znamy przepis na zawartość częstotliwości dźwięku, możemy syntezować go dodając tony sinusoidalne do każdej składowej częstotliwości. Dla przykładu, przepis na składowe minor third bell, w częstotliwości bazowej 500 500 * [0.5,1,1.19,1.56,2,2.51,2.66,3.01,4.1] { Mix(SinOsc.ar(500*[0.5,1,1.19,1.56,2,2.51,2.66,3.01,4.1],0,0.1)) }.scope //bell spectra, all partials the same volume Zamiast nadawać każdej części w miksie amplitudę domyślną 0.1, możliwe jest nadanie dzień 11/ synteza dźwięku im własnej wartości, co sprawia, że dźwięk brzmi bardziej naturalnie { Mix(SinOsc.ar(500*[0.5,1,1.19,1.56,2,2.51,2.66,3.01,4.1],0,0.1*[0.25,1,0.8,0. 5,0.9,0.4,0.3,0.6,0.1])) }.scope //bell spectra, different volumes for partials Częstotliwość i modulacja fazy Koncept źródła i modulatora, lfo: { SinOsc.ar(SinOsc.ar(XLine.kr(1, 1000, 9), 0, 200, 800), 0, 0.25) }.play; //modulate freq { SinOsc.ar(800, SinOsc.ar(XLine.kr(1, 1000, 9), 0, 2pi), 0.25) }.play; //modulate phase Dodawanie tonów sinusoidalnych znaczy mieszanie ich, mnożenie znaczy modulowanie: ( { SinOsc.ar(100, 0, 0.5) + SinOsc.ar(400, 0, 0.4) + SinOsc.ar(900, 0, 0.3) * SinOsc.kr(3, 0, 1) //no need to run these 2 in audiorate * SinOsc.kr(1.03, 0, 1) }.play ) Osobny lfo dla każdego tonu sinusoidalnego (zwróć uwagę na nawiasy) ( { (SinOsc.ar(100, 0, 0.5)*SinOsc.kr(1, 0, 1)) + (SinOsc.ar(400, 0, 0.4)*SinOsc.kr(0.5, 0, 1)) + (SinOsc.ar(900, 0, 0.3)*SinOsc.kr(0.25, 0, 1)) }.play ) Możliwe jest osiągnięcie skomplikowanej szkatułkowej struktury: { SinOsc.ar(SinOsc.ar(25, 0, 500, SinOsc.kr(0.2, 0, 500, 1000)), 0, SinOsc. ar(201, 0, 0.25, 0.25)) }.play Różne generatory: { SinOsc.ar(Saw.ar(LFPulse.kr(0.5, 0.3, 5, LFNoise0.kr(4, 6, 8)).round(2), 500).abs) }.play ( { var out, amp= 1, freq= \F2.namemidi.midicps; out= RLPF.ar( LFSaw.ar( DegreeToKey.kr( LocalBuf.newFrom([0, 2, 3.2, 5, 7, 9, 10]), MouseX. SuperCollider 8 / syntezator fm/pm stworzony przez Fredrika Olofssona http://www.fredrikolofsson.com/ kr(0,15), 12, 1, 88) + Impulse.ar( XLine.kr([48,55].midicps, [48,55].midicps + 100, f0blog/?q=node/521 9 SuperCollider dzień 11/ synteza dźwięku 1.5), 0.0, 0.7.rand) + WhiteNoise.ar(0.8.rand) ).midicps, LFNoise1.kr(1, 38, 115).midicps, 0.1 ); out= [out, DelayN.ar(out, 0.04, 0.04) ]; 4.do({ out= AllpassN.ar(out, 0.05, [0.05.rand, 0.05.rand], 4, amp/4) }); Pan2.ar( out, MouseX.kr(-1,1) ); }.scope ) dobre Źródła do poszukiwań / [Tour of ugens ] [ Mark Polishook tutorial ] 2.3 SYNTHDEFS {...}.play jest prostą ale nie preferowaną drogą pracowania z syntezatorami w sc, ponieważ można je tylko uruchomić/zatrzymać ale nie zapisać i użyć ponownie. SynthDef (Definicja Syntezatora) zamiast tego definiuje sieć jednostek generatorów. Wiele syntezatorów może więc być stworzonych z pojedynczej definicji, co czyni możliwym tworzenie chmur podobnych dźwięków i innych pojedyńczych lub zespołowych procesów. ( SynthDef(\synrand, { var out, gens= [SinOsc, Saw, LFTri]; out= gens.choose.ar(freq: Rand(200, 2000), mul:0.1); Out.ar(0, Pan2.ar(out, 0)) }).send(s) ) Pełne wytłumaczenie Out ugen jest odłożone do momentu kiedy będziemy dyskutować Buses (szyny danych), ale możecie o tym pomyśleć teraz jako instrukcja ‚wyślij dźwięk do pierwszego wyjścia audio komputera’. Teraz możemy stworzyć indywidualny syntezator z specyfikacją opisaną powyżej: Synth(\synrand); Albo może on być używany tak wiele razy jak tego sobie życzysz: a=Synth(\synrand); b=Synth(\synrand); c=Synth(\synrand); Te komendy zatrzymają indywidualnie każdy syntezator: a.free; b.free; c.free; Zwróć uwagę, że Rand pracuje nad stworzeniem indywidulanego przykładu syntezatora, podczas gdy .choose – tylko nad wysyłaniem Syntezatora do serwera. (ugen vs. metoda obiektu) dzień 11/ synteza dźwięku Poza .send jest wiele innych metod: .load(s), .store, .writeDefFile, .writeOnce, .tail, a nawet shortcut .play; Wszystkie one określają tymczasowy charakter syntezatora - czy sc powinien go użyć tylko przed opuszczeniem programu czy mieć dostęp do zdefiniowanego syntezatora zawsze (i wtedy musi być zapisany na dysku twardym i być uruchomiony kiedy włączamy sc); Parametry syntezatorów: ( SynthDef(\sine, { arg freq= 440, amp= 0.3, pan= 0; var out; out= SinOsc.ar(freq, mul: amp); out= SinOsc.ar(freq, mul: amp); Out.ar(0, Pan2.ar(out, pan)) }).send(s) ) SuperCollider 10 więcej szczegółów / [SynthDef ] a=Synth(\sine); b=Synth(\sine,[\freq,550]); c=Synth(\sine,[\freq,660, \amp,0.5, \pan,-0.8]); a.free; b.free; c.free; Aby zapobiec nagłemu zatrzymaniu dźwięku podczas wyłączania syntezatora, zaleca się użycia envelope do kontroli parametrów mul syntezatora: ( SynthDef(\sine, { arg freq= 440, amp= 0.3, pan= 0, gate= 0; var env, out; env= EnvGen.kr(Env.asr(0.1, 1, 1, \sin), gate: gate, doneAction: 2); out= SinOsc.ar(freq, mul: amp) * env; Out.ar(0, Pan2.ar(out, pan)) }).send(s) ) //starting and stopping synths also changes: a=Synth(\sine); b=Synth(\sine,[\freq,550, \gate, 1]); c=Synth(\sine,[\freq,660, \amp,0.5, \pan,-0.8]); zwróć uwagę na / parametr doneAction z EnvGen i liczbę syntezatorów pracujących na serwerze uwaga / Sygnał .kr nie może być użyty jako parametr SynthDef: a.set(\gate, 1); b.set(\gate, 1); b.set(\gate, 1); a.set(\gate, 0); b.set(\gate, 0); c.set(\gate, 0); a.set(\freq, MouseX.kr(600, 6000)); //crashes 11 SuperCollider dzień 111 DZIEŃ III 3.1 SYNTEZA GRANULARNA Techniki, które zamierzamy badać, były po raz pierwszy opracowane i tworzone w muzyce instrumentalnej i elektronicznej przez Iannisa Xenakisa (późne lata 50-te) i później analizowane w komputerach przez Curtisa Roadsa i Barry’ego Truaxa, poczynając od wczesnych lat 70-tych. Systemy czasu rzeczywistego stały się wiarygodne w latach 80-tych. W syntezie granularnej dźwięki są modelowane z mikroskopijnych cząsteczek dźwięku, małych granulek rzędu 10-100 milisekund: b = Buffer.read(s,”sounds/a11wlk01.wav”); //three different possible grains ( { var singrain1, singrain2, sfgrain; singrain1= SinOsc.ar(440, 0, XLine.kr(1.0, 0.0001, 0.05)); singrain2= FSinOsc.ar(800, 0.0, Line.kr(1.0,0,0.05).squared); sfgrain= (PlayBuf.ar(1, b.bufnum, BufRateScale.kr(b.bufnum))) * EnvGen.kr(Env([0,1,1,0],[0.01,0.01,0.01], -4)); [singrain1, singrain2, sfgrain] }.plot2(0.1) ) Miriady tych mikrodźwięków mogą być łączone w duże chmury granularne, w ten sposób tworząc makroskopowe pejzaże muzyczne. Chmura granularna może być kontrolowana poprzez szeregowanie w pętli: //simple sine grain synthdef (the importance of doneAction!) ( SynthDef(\sinegrain, {arg pan, freq, amp; var grain; grain= SinOsc.ar(freq, 0, amp)*(XLine. kr(1.001,0.001,0.1,doneAction:2)-0.001); Out.ar(0,Pan2.ar(grain, pan))}).add; ) //single grain; Synth(\sinegrain,[\freq,rrand(100,10000),\amp, exprand(0.05,0.1), \pan, //100 random grains over 1 second in a schedule dzień 111 ( { 100.do{arg i; Synth(\sinegrain,[\freq,rrand(100,10000), \amp,exprand(0.05,0.1), \pan, 1.0.rand2]); }; }.fork ) W miarę rozwoju, możemy zmienić cechy chmury, na przykład, zmieniając jego gęstość i kontrolować indywidulane parametry granulek wewnątrz tendency masks lub podążając określonymi ścieżkami //schedule 200 random grains over time, decreasing the range of allowed random frequencies and lowering the density over time ( { 200.do{arg i; var timeprop = (i/199.0)**3; Synth(\sinegrain,[\freq,exprand(100,5000-(20*i)), \ amp,exprand(0.05,0.1), \pan,1.0.rand2]); rrand((timeprop*0.1).max(0.01),timeprop*0.3).wait }; }.fork ) Teraz przedstawimy to w formie syntezatora, aby mieć lepszą kontrolę nad parametrami ( SynthDef(\sfgrain, {arg bufnum=0, pan=0.0, startPos=0.0, amp=0.1, dur=0.04; var grain; grain= PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), 1, BufFrames. ir(bufnum)*startPos, 0) *(EnvGen.kr(Env.perc(0.01, dur), doneAction:2)-0.001); Out.ar(0,Pan2.ar(grain, pan)) }).add; ) b = Buffer.read(s,”sounds/a11wlk01.wav”); //individual grain Synth(\sfgrain,[\bufnum, b.bufnum, \startPos,rrand(0.0,1.0), \amp, exprand(0.005,0.1), \pan, 1.0.rand2]); //schedule 200 random soundfile playback grains over time, with random offset positions into the soundfile and lowering the density over time ( { 200.do{arg i; SuperCollider 12 13 SuperCollider dzień 111 }; }.fork ) var timeprop = (i/199.0)**3; Synth(\sfgrain,[\bufnum, b.bufnum, \startPos,rrand(0.0,timeprop),\amp, exprand(0.005,0.1), \pan, 1.0.rand2]); rrand((timeprop*0.1).max(0.01),timeprop*0.4).wait Każda granulka może mieć inne parametry przypisane do siebie; w przypadku cichych może być to wysokość dźwięku, trwanie obwiednicy, pozycja w polu stereo i amplituda. Całkowita chmura granularna może też mieć pewnego rodzaju sposób dystrybucji tych parametrów np. zasięg częstotliwości cząsteczek dozwolonych w różnych momentach czasu lub kontrola nad rozwijającą się gęstością chmury granularnej. Zadaniem kompozytora jest zarówno określenie granulek, jak również tego jak są wykorzystywane w czasie aby stworzyć interesującą strukturę kompozycyjną. Ponieważ jesteśmy w stanie operować mikroskopijnymi porcjami dźwięku, przetwarzanie granularne pozwala na wykonywanie dramatycznych transformacji na źródłach dźwięku. Dźwięk może zniknąć w strumieniu kwantów i pojawić się ponownie łącząc się z różnych cząsteczek: ( var w, slid, lastval; lastval=0.0; w=Window(„My Window”, Rect(100,500,200,200)); slid=Slider(w,Rect(10,10,150,40)); //a basic slider object slid.action_({lastval= slid.value;}); //this is the callback- the function is called whenever you move the slider { inf.do{ |i| var prop, timestart, timeend; prop= (i%300)/300; pewne klasz do timestart= prop*0.8; timeend= prop*(0.8+(0.1*lastval)); zbadania/ [PitchShift ] //A granular pitchshifter [TGrains ] //efficient granular synthesis on a buffer, from a single UGen Synth(\sfgrain,[\bufnum, b.bufnum, \startPos,rrand(timestart,timeend), \amp, exprand(0.005,0.1), \pan, lastval.rand2, \dur, 0.1+(lastval*0.5)]); //max in this to avoid ever going near 0.0 wait time, which would crash the computer! (((lastval*0.2)+0.01).max(0.01)).wai}; }; }.fork; w.front; dzień 111 ) Pewne klasy do zbadania: [PitchShift ] //a granular pitchshifter [TGrains ] //efficient granular synthesis on a buffer, from a single UGen 3.2 KIEROWANIE AUDIO – SZYNA DANYCH BUSES Szyny mogą być opisane jako mikser kanałów lub ścieżek wewnątrz, które mogą grać audio niezależne od siebie. scsynth serwer posiada domyślnie 128 szyn danych. Wejścia i wyjścia twojej karty dźwiękowej zawsze rezerwują pierwsze sąsiednie zakresy tych szyn. Klasa ServerOptions pozwala na redefinicję liczby wejść i wyjść, zależnie od możliwości karty dźwiękowej: //default Server.local.options.numOutputBusChannels = 8; Server.local.options.numInputBusChannels = 8; //changing this to 2 in 2 out means straight stereo setup ServerOptions musi być ustawiony zanim uruchomisz wspomniany serwer – to nie zmieni nic w obecnie pracującym serwerze, tylko następnym razem jak ponownie go uruchomisz. Zakładając, że mamy 8 wyjść, 8 wejść, wiem o 128 szynach danych, wskaźniki: 0-7 to 8 wyjść 8-15 to 8 wejść 16-127 są dla innych trasowań / celów renderowania (symulacji) To są liczby, które będą używane w wyjściach i wejściach w jednostkach generatora w SynthDefs. //mono sound, just plays in left channel because that’s the first audio out on the soundcard { Out.ar(0, SinOsc.ar(440,0,0.1)) }.play //mono sound in right channel { Out.ar(1, SinOsc.ar(440,0,0.1)) }.play //its playing, but can’t hear it on stereo out capability { Out.ar(2, SinOsc.ar(440,0,0.1)) }.play //get the first audio input, route to the left channel (warning! feedback! also re-switch channels in startup.rtf to default) { Out.ar(0, In.ar(8,1)) }.play //compare to SoundIn (wrapper for In, which uses audio-in buses starting from index 0) { Out.ar(0, SoundIn.ar(0,1)) }.play N-kanałowy dźwięk włożony na szynę danych x pokryje się z szyną danych od x do x+n-1. SuperCollider 14 15 SuperCollider dzień 111 //stereo sound put onto bus 0 plays first channel on 0, second on 1: so stereo out {Out.ar(0,SinOsc.ar([440,880],0,0.1))}.play //all 8 inputs routed to all 8 outputs {Out.ar(0,In.ar(8,8))}.play //output 16 channel sound to buses 16-31 (silence) {Out.ar(16,SinOsc.ar(Array.series(16,400,100),0,0.1))}.play //this gets them back to hearing, mixing to central field {Out.ar(0,Pan2.ar(Mix.ar(InFeedback.ar(16,16)),0.0))}.play 3.3 KIEROWANIE LICZB – CONTROL BUSES zwróć uwagę na / Podobne do pomysłu .ar i .kr ugena, są również szyny Audio i szyny danych. a= Bus.audio(s,1) //makes a 1-channel (mono) virtual audio bus c= Bus.control(s,1) //makes a 1-channel (mono) virtual control bus a.index //index of this bus c.index //and of the control bus c.set(9); //set current value to 9 Out.kr(c.index, SinOsc.kr) //any other .kr UGen other than SinOsc could go here Zminiejając liczby control rate na szynie danych w ciągu czasu, możemy to przeczytać gdzie indziej: In.kr(c.index, 1) //read 1 channel from this control bus location To jest standardowa metoda inter-węzłowej komunikacji – różne syntezatory mogą czytać lub pisać na tych samych szynach danych. Każdy argument syntezatora może być kontrolowany przez szynę danych: ( SynthDef(\mapexpl,{arg freq=440; Out.ar(0,SinOsc.ar(freq,0,0.1)) }).send(s) ) parametr doneAction z EnvGen i liczbę syntezatorów pracujących na serwerze g= Synth(\mapexpl); c.set(660); g.map(\freq, c.index) c.set(770); h= { Out.kr(c.index, SinOsc.kr(2,0,100,1000)) }.play; l= { Out.kr(c.index, MouseX.kr(100,1000)) }.play; h.free; l.free; g.set(\freq, 550); g.free; dzień 111 Przykład z PlayBuf ( // load the default sound into a buffer (read brings in the whole sound at once) b = Buffer.read(s,”sounds/a11wlk01.wav”); SynthDef(„playbuf”,{ arg out=0,bufnum=0, rate=1, trigger=1, startPos=0, loop=1; Out.ar(out, Pan2.ar( PlayBuf.ar(1,bufnum, BufRateScale.kr(bufnum)*rate, trigger, BufFrames.ir(b.bufnum)*startPos, loop) ,0.0) ) }).send(s); ) //make a new control Synth, playing on control buses ( SynthDef(„playbufctrls”,{ Out.kr(0,Impulse.kr(LFNoise0.kr(0.5, 5, 6))); //control for retrigger (impulse with modulated rate) Out.kr(1,LFNoise0.kr(0.25,0.5,0.5)); //control for jump position in sample (any random frame) Out.kr(2,LFNoise0.kr(3,0.5,0.5)); //control for rate in sample }).play(s); ) a=Synth(\playbuf, [\out, 0, \bufnum, b.bufnum, \rate, 1, \trigger, „c0”, \a.set(\rate, "c2"); // to unmap signal from a control bus, simply use c without index (here - unmap the modulation of jump position when retriggered) a.set(\startPos,”c”) //set fixed chosen start frame a.set(\startPos, 30000) a.free; 3.4 SERVER NODES Podczas tworzenia syntezatorów, przyjmowaliśmy pewne wartości domyślne. W szczególności nie martwiliśmy się porządkiem renderowania dla syntezy (znanym jako porządek wykonania). SuperCollider 16 17 SuperCollider dzień 111 Może być to kontrolowane przez określenie Węzłów Nodes na serwerze. Serwer posiada wykres wszystkich działających syntezatorów, które mogą być zorganizowane w Grupy dla wygody. Możesz zobaczyć tworzące się syntezatory i Grupy patrząc tylko na grafikę serwera. Węzeł oznacza Syntezator lub Grupę. Za każdym razem gdy naciskasz Cmd+. resetujesz wykres, usuwając wszystkie syntezatory i grupy, które dodałeś tzn. usuwasz wszystkie węzły. s.queryAllNodes //the Nodes on the Server (keyboard shortcut - [n] on server window) Kiedy serwer determinuje wyjście Audio, musi działać poprzez wszystkie syntezatory w wykresie, renderując je wszystkie. Kolejność, w której to wykonuje odnosi się do porządku wykonania. To może spowodować problem jeśli nie jesteś ostrożny z porządkiem! Ponieważ wejścia niektórych syntezatorów zależą od obliczenia w pierwszej kolejności innych syntezatorów: wyobraź sobie jednostkę pogłosu, która musi podążać pewnymi spawned falami sinusoidalnych granulek. Jeśli pogłos jest najpierw obliczony, nie ma żadnego obecnego wejścia aby nad nim pracować. ( s= Server.default; SynthDef(„reverb”,{Out.ar(0,CombN.ar(In.ar(0,2),0.1,0.1,4))}).send(s); SynthDef(„impulses”,{Out.ar(0,Impulse.ar([0.9,1.19],0,0.3))}).send(s); ) ( //no reverb Synth(„impulses”); Synth(„reverb”); ) Kiedy tworzysz syntezatory, możesz polegać na wartościach domyślnych pod względem porządku wykonania. To jest niebezpieczne i to jest dobre ćwiczenie na bycie eksplicytnym względem wykresu, który tworzysz. Stanowi to dla Ciebie dodatkowy wysiłek; jedyną pociechą jest to, że sc3 jest bardziej wydajny, ponieważ nie musi brać odpowiedzialności za porządek wykonania. Początkowy stan wykresu Węzła na serwerze: s.queryAllNodes //run me to see the Nodes on the Server Dwie wartości domyślne węzłów są dogodnymi grupami do umieszczenia w nich syntezatorów: Grupa(0) jest absolutnym korzeniem drzewa. Wszystkie nowe syntezatory są umieszczane gdzieś w tej grupie (mogą być w podgrupie ale będą się znajdować w wierzchołku grupy RootNode na szczycie hierarchii). r= RootNode.new; //this gets a reference to Group(0) Grupa(1) została dodana jako dodatkowa wartość domyślna aby otrzymywać wszystkie stworzone syntezatory w celu uniknięcia zaśmiecania bazy drzewa. Group.basicNew(s, 1); //this gets a reference to Group(1) Rozpocznij nowy syntezator: { SinOsc.ar(440,0,0.1) }.play Jeśli teraz zadasz pytanie węzłom, zobaczysz, że syntezator został dodany do Grupy(1). dzień 111 Teraz uruchom: { SinOsc.ar(880,0,0.1) }.play Query all nodes/ Zadaj pytanie wszystkim węzłom ponownie. Zobacz jak buduje się porządek wykonania dla renderowania. Zauważ teraz, że możemy naprawić wczesny przykład impulsu i pogłosu w poniższy sposób: ( Synth(„reverb”); Synth(„impulses”); ) To działa tylko dlatego, ponieważ wartości domyślne są po naszej stronie. Inna naprawa wykorzystałaby InFeedback ugen. InFeedback pobiera stare wartości z szyny danych zanim zacznie się cykl obliczeń, pozwalając ci na ustawienie cykli feedbacku. Jednak zazwyczaj programy są bardziej skomplikowane i zaleca się bardziej branie odpowiedzialności za wykres węzłów na serwerze niż za zaakceptowanie wartości domyślnych. W zasadzie branie tej odpowiedzialności zawsze będzie znaczyło, że nie będziesz miał problemów i jest to dobry zwyczaj. Za każdym razem gdy tworzysz syntezator określasz gdzie go umieścić w Node drzewa. Możesz ustawić nowe węzły przed lub po innych węzłach i na czele lub końcu grupy (lista syntezatorów). ( //controlled execution g= Group.basicNew(s, 1); Synth.tail(g, „reverb”); Synth.head(g, „impulses”); ) ) ) Teraz nie jest ważne w jakim porządku działa kod, ponieważ umiejscowienie jest kontrolowane: ( //controlled execution g= Group.basicNew(s,1); Synth.head(g,”impulses”); Synth.tail(g,”reverb”); ) Inne sposoby na osiągnięcie tego samego efektu: ( a= Synth(„impulses”); Synth.after(a,”reverb”); ) or ( a= Synth(„reverb”); Synth.before(a,”impulses”); ) Wszystkie syntezatory dostają przypisaną liczbę (zaczynając od 1000) i wszystkie grupy dostają liczbę (zaczynając od 0). Maksymalna liczba węzłów jest ustawiana w ServerOptions i jej wartość domyślna to 1024. SuperCollider 18 19 SuperCollider dzień 111 Łączenie używania węzłów i szyn danych: możesz używać węzłów do kontrolowania porządku wykonania i szyn danych (poprzez wejścia i wyjścia) do przekazywania danych audio pomiędzy syntezatorami – więc efekty mogą operować na szynie danych, na której inny syntezator już coś napisał i porządek wykonania zapewni, że wszystko będzie policzone we właściwym porządku. 3.5 EFEKTY Zaczynając od przykładu: ( SynthDef(\impulse, { Out.ar(0,Pan2.ar(Saw.ar(440,Decay2.ar(Impulse. ar(1),0.001,0.1,0.5)),0.0)); }).add; SynthDef(\continuous, { Out.ar(0,Pan2.ar(WhiteNoise.ar(0.1),0.0)); }).add; ) a= Group.basicNew(s,1); //get Group 1 x= Synth.head(a, \impulse); s.scope Dodając delay: ( SynthDef(\fxexampledelay, {arg delaytime=0.1; var input, effect; input=In.ar(0,2); //get two channels of input starting (and ending) on bus 0 effect= DelayN.ar(input, 1,delaytime); //max delay of one second Out.ar(0,effect); //adds to bus 0 }).add; ) y= Synth.tail(a, \fxexampledelay); y.free; y= Synth.tail(a, \fxexampledelay, [\delaytime, 0.4]); z= Synth.tail(a, \fxexampledelay, [\delaytime, 0.08]); y.free; dzień 111 SuperCollider 20 z.free; x.free; Vibrato ( { var source; var fx; source= Saw.ar(440,0.1); fx= DelayC.ar(source, 0.01, SinOsc.ar(Rand(5,10),0,0.0025,0.0075)); fx }.play ) inne UGens do zbadania: / DelayN DelayL DelayC Delay1 Tap MultiTap Chorus ( { var source; var fx; var n=10; source= EnvGen.ar(Env([0,1,0],[0.1,0.5]), Impulse.kr(2)) * Saw.ar(440,0.5); fx= Mix.fill(n, { var maxdelaytime= rrand(0.01, 0.03); var half= maxdelaytime*0.5; var quarter= maxdelaytime*0.25; //%half+(quarter*LPF.ar(WhiteNoise.ar,rrand(1.0,10))) DelayC.ar(source, maxdelaytime, LFNoise1. kr(Rand(5,10),0.01,0.02) ) }); fx }.play ) Pogłos sygnału wchodzącego ( SynthDef(\fxexamplereverb, {arg delaytime=0.01, decaytime=1; var input; var numc,numa,temp; input= SoundIn.ar(0,2); //get two channels of input starting (and ending) on bus 0 numc = 4; //number of comb delays numa = 6; //number of allpass delays temp = DelayN.ar(input, 0.048,0.048); //reverb predelay time 21 SuperCollider readymade reverbs in SC3 / FreeVerb for custom built reverbs useful UGens are / CombN CombL CombC AllpassN AllpassL AllpassC dzień 111 temp=Mix.fill(numc, { CombL.ar(temp,0.1,rrand(0.01, 0.1),5) }); //chain of 4 allpass delays on each of two channels (8 total): numa.do({ temp = AllpassN.ar(temp, 0.051, [rrand(0.01, 0.05),rrand(0.01, 0.05)], 1) }); //add original sound to reverb and play it: Out.ar(0, (0.2*temp)); }).add; ) y= Synth.tail(a, \fxexamplereverb); y.free; x.free; Okresowość i Flanging Phaser odtwarza sygnał w połączeniu z przesuniętą fazą swojej kopii (używając filtra allpass); zmień czas opóźnienia poniżej 20 msec ( SynthDef(\fxexamplephasing, {arg freq=0.2; var input, effect; input= SoundIn.ar(0,2); //get two channels of input starting (and ending) on bus 0 effect= AllpassN.ar(input,0.02,SinOsc.kr(freq,0,0.01,0.01)); //max delay of 20msec Out.ar(0,effect); //adds to bus 0 where original signal is already playing }).add; ) x= Synth.head(a, \continuous); y= Synth.tail(a, \fxexamplephasing); y.set(\freq, 0.1) y.set(\freq, 1) y.free; x.free; Flanger odtwarza sygnał w połączeniu ze swoją opóźnioną kopią; zmień opóźnienie ok 10 msec; Flanging zazwyczaj zawiera trochę feedback, tutaj używając LocalIn i LocalOut: ( SynthDef(\fxexampleflanging, {arg flangefreq= 0.1, fdback= 0.1; var input, effect; input= SoundIn.ar(0,2); //get two channels of input starting (and ending) on bus 0 input= input + LocalIn.ar(2); //add some feedback effect= DelayN.ar(input,0.02,SinOsc.kr(flangefreq,0,0.005,0.005)); //max delay of 20msec dzień 111 LocalOut.ar(fdback*effect); // localOut.ar(fdback*BPF.ar(effect,MouseX.kr(1000,10000),0.1)); // alternative with filter in the feedback loop Out.ar(0, effect); //adds to bus 0 where original signal is already playing }).send(s); ) x= Synth.head(a, \continuous); y= Synth.tail(a, \fxexampleflanging); y.set(\flangefreq,0.4); y.set(\fdback, 0.95); y.free; x.free; Dynamics Processing Skompresuj lub rozszerz rozpiętość tonalną (wariacja amplitudy) sygnału ( SynthDef(\fxexamplecompression, {arg gain=1.5, threshold=0.5; var input, effect; input= SoundIn.ar(0,2); //get two channels of input starting (and ending) on bus 0 effect= CompanderD.ar(gain*input,threshold,1,0.5); ReplaceOut.ar(0,effect); //replaces bus 0 where original signal is already playing }).add; ) x= Synth.head(a, \impulse); x= Synth.head(a, \impulse); y= Synth.tail(a, \fxexamplecompression); y.free; y= Synth.tail(a, \fxexamplecompression,[\gain,2, \threshold,0.1]); y.free; x.free; Ogranicz siły do absolutnego limitu by uniknąć przeciążenia (czasem bardzo przydatne na końcowym etapie routing audio) SuperCollider 22 23 SuperCollider dzień 111 ( SynthDef(\fxexamplelimiter, {arg gain=1; var input, effect; input= SoundIn.ar(0,2); //get two channels of input starting (and ending) on bus 0 effect= Limiter.ar(gain*input,0.99, 0.01); ReplaceOut.ar(0,effect); //replaces bus 0 where original signal is already playing }).add; ) x= Synth.head(a, \impulse); y= Synth.tail(a, \fxexamplelimiter); czytaj więcej / y.set(\gain, 10) //careful!!! Compander Normalizer Limiter y.free; x.free; Distortion Dodawanie nowych komponentów do sygnału by go wzbogacić, skutki uboczne modulowania są tego przykładem. { SinOsc.ar(440,0,0.5) }.scope { SinOsc.ar(440,0,0.5).distort }.scope { SinOsc.ar(440,0,0.5).cubed }.scope //squared would put it an octave up { SinOsc.ar(440,0,0.5).cubed }.scope //squared would put it an octave up { SinOsc.ar(440,0,0.1).pow(MouseX.kr(0.1,1.0)) }.scope { SinOsc.ar(440,0,0.5).clip(-0.2,0.3) }.scope Użyj Shaper dla techniki waveshaping; każdy argument x ma przypisaną wartość y w tabelce b = Buffer.alloc(s, 1024, 1); //arbitrary transfer function, create the data at 1/2 buffer size + 1 t = Signal.fill(513, { |i| i.linlin(0.0, 512.0, -1.0, 1.0) }); t.plot // linear function //t.asWavetable will convert it to the official Wavetable format at twice the size b.sendCollection(t.asWavetableNoWrap); //may also use loadCollection here //shaper has no effect because of the linear transfer function ( { var sig = Shaper.ar(b, SinOsc.ar(440, 0, 0.4)); sig ! 2 dzień 111 SuperCollider 24 }.scope; ) //now for a twist ( a = Signal.fill(256, { |i| var t = i/255.0; t + (0.1 * (max(t, 0.1) - 0.1) * sin(2pi * t * 80 + sin(2pi * 25.6 * t))) }) ); a.plot d = (a.copy.reverse.neg) ++(Signal[0])++ a; d.plot d.size //must be buffer size/2 + 1, so 513 is fine b.sendCollection(d.asWavetableNoWrap); // may also use loadCollection here b.plot //wavetable format! //test shaper ( { Shaper.ar( b, SinOsc.ar(440, 0.5, Line.kr(0,0.9,6)) ) }.scope Shaper może być także używany aby celowo zniekształcić falę sinusoidalną w kontrolowany sposób czyli jako metoda syntezy. [Shaper ] wiele przykladów różnych efektów / [Tour_of_UGens ] 25 SuperCollider dzień 1v/ interaktywność INTERAKTYWNOŚĆ 4.1 MYSZKA MouseX.kr(leftval, rightval, warp) MouseY.kr(topscreenval, bottomscreenval, warp) Warp może być linearlny lub eksponencjalny { SinOsc.ar(MouseX.kr(20, 20000, ‚linear’),0,0.1) }.play { SinOsc.ar(MouseY.kr(20,20000, ‚exponential’),0,0.1) }.play Jest możliwe ograniczenie kontrolera do dyskretnego zakresu za pomocą Index ugen. Sygnał indeksowany zostaje ograniczony aby utrzymać go w zakresie. Rząd musi być typu FloatArray, aby mógł zbudować go jako [0,1,2].asSignal Index.kr(array, indexing signal) Przykład: ( //three states at left, middle and right sides of the screen var vals, buf, s; s= Server.internal; vals= [100, 200, 300, 500]; buf= Buffer(s, vals.size, 1); s.listSendMsg( buf.allocMsg( buf.setnMsg(0, vals) )); //alloc and set the values { SinOsc.ar(Index.ar(buf.bufnum, MouseX.kr(0, vals.size+1)),0,0.2) }.play ) Przykłady: ( var vals, buf, s; var numharm, basefreq; s= Server.internal; numharm= 11; //number of harmonics basefreq= 66; //base frequency of series vals= basefreq * (Array.series(numharm, 1, 1)); buf= Buffer(s, vals.size, 1); s.listSendMsg( buf.allocMsg( buf.setnMsg(0, vals) )); //alloc and set the values dzień 1v/ interaktywność { SinOsc.ar(Index.kr(buf.bufnum,MouseX.kr(0,numharm)),0,0.1) }.play ) Zauważ: Lepszy sposób na skalę - DegreeToKey → patrz przykład Myszka może też być użyta jako trigger: ( { var trig, mx; mx= MouseX.kr(0.0, 1.0); trig= mx > 0.5; //this is a UGen which compares mx to the constant signal 0.5 at krate SinOsc.ar(440, 0, 0.1*trig) }.play; ) ( //trigger in a given region { var trig, mx, my; mx= MouseX.kr(0.0, 1.0); trig= if((mx>0.3) * (mx<0.5) * (my>0.3) * (my<0.7), 1, 0); //if is a UGen here, * is equivalent to logical AND SinOsc.ar(440, 0, 0.1 * trig) }.play; ) Przechodzące jedne w drugie dźwięki trąbek przy użyciu myszki – James McCartney (patrz SC2-examples_2): ( { var mousex, out; mousex = MouseX.kr; out = Mix.arFill(8, { arg i; var trigger, pluck, period, metal, z, n=15, freq; // place trigger points from 0.25 to 0.75 trigger = HPZ1.kr(mousex > (0.25 + (i * 0.07))).abs; pluck = PinkNoise.ar(Lag.kr(Trig.kr(trigger, 1), 0.2, 0.01)); freq = (#[0, 3, 5, 7, 10, 12, 15, 17].at(i) + 60).midicps; z = `[ // filter bank specification : y = Array.fill(n, { arg j; (j+1) * freq } ), // frequencies nil, //amplitudes default to 1.0 Array.fill(n, { 0.2.rand }) //ring times ]; metal = Klank.ar(z, pluck); //metal = SinOsc.ar(800, 0, 0.1); Pan2.ar(metal, i * 0.2 - 0.5); SuperCollider 26 27 SuperCollider dzień 1v/ interaktywność }); LPF.ar(out, 12000); out = LeakDC.ar(out); 6.do({ out = AllpassN.ar(out, 0.1, [0.05.rand,0.05.rand], 4) }); out }.play; ) 4.2 KLAWIATURA uwaga / Klawiatura może być użyta by spowodować wydarzenia poprzez ustawienie metody .action_(). Jest to zazwyczaj robione z GUIs, ale poniżej są przykłady wywołania funkcji z obecnego dokumentu: ( var doc; wyłącz: Document. SynthDef(„typeofsound”,{ Out.ar(0, Line.kr(1, 0, 0.1, doneAction:2) * VarSaw.ar(Rand(100,1000), 0, Rand(0.1, 0.8), 0.1)) }).add; doc = Document.current; //this text window we’re reading from! doc.keyDownAction_({ arg ...args; [ args[1], args[3] ].postln; Synth(„typeofsound”); }); ) current.keyDownAction_(nil); patrz document help file dla innych przykładów: / [Document ] 4.3 MIDI Informacje midi mają 7-bit (2**7) zakres wartości, a więc biorą pod uwagę liczy całkowite od 0 do 127. Aby uzyskać dostęp do urządzeń midi najpierw zainicjuj: MIDIClient.init; //posts a list of available devices Aby reagować na przychodzące komunikaty midi, użytkownik ustawia funkcje zwrotne: MIDIIn.connect(0, MIDIClient.sources[0]) //first number is port number, second is device from sources list dzień 1v/ interaktywność MIDIIn.noteOn= { arg src, chan, num, vel; [chan, num, vel/127].postln; }; //set up callback for MIDI Note On message Vel/127 konwertuje od zasięgu 0 .. 127 do zakresu 0.0 .. 1.0 przystosowując się do kontroli amplitudy. Inny sposób aby to zrobić: vel.linlin (0, 127, 0, 1) W rzeczywistości może istnieć więcej niż jedno źródło i urządzenie docelowe, każde zawierające różne wejścia i wyjścia. Aby zawrzeć je wszystkie: n= 3; n.do({ |i| MIDIIn.connect(i, MIDIClient.sources.at(i)); }); ( c = CCResponder({ |src,chan,num,value| [src,chan,num,value].postln; }, nil, //any source nil, //any channel nil, //any CC number nil //any value ) ); Pracując z midi, czasami konieczne jest nauczenie sc rozumienia wiadomości: ( c = CCResponder({ |src, chan, num, value| [src, chan, num, value].postln; }); c.learn; //wait for the first controller ) Kiedy kończy się program nie zapomnij usunąć ccresponder, w przeciwnym razie midi-wiadomości mogą być krzyżowane w kilku Responders, co może spowodować bałagan: c.remove; lub CCResponder.removeAll; W systemie Mac pierwszy kanał midi jest domyślnie używany do Inter-Application Communication Driver (iac), wirtualnego urządzenia midi, które jest domyślnie instalowane na wszystkich systemach Mac os. Podstawowa funkcjonalność sterownika iac (iac Driver) jest niezwykle przydatna w połączeniu z osc-midiBridge; pozwoli osc-midi bridge do przekazania wiadomości osc wewnętrznie do aplikacji midi. SuperCollider 28 29 SuperCollider dzień v/ kompozycja algorytmiczna KOMPOZYCJA ALGORYTMICZNA 5.1 PLANOWANIE WYDARZEŃ W CZASIE – CLOCKS ( SynthDef( \helpsched, { arg out= 0, note= 60, dur=1, amp=0.2; var freq, env, signal; freq= note.midicps; env= EnvGen.kr( Env.perc, levelScale: 0.3, timeScale: dur, doneAction: 2 ); out= env * RLPF.ar( LFSaw.ar( freq ) + Impulse.ar( XLine.kr(freq, freq + 100,1.5), 0.0, 0.7.rand) + WhiteNoise.ar(0.8.rand), LFNoise1.kr(1, 38, 115).midicps, 0.1 ); signal = [out, DelayN.ar(out, 0.04, 0.04) ]; 4.do({ out = AllpassN.ar(out, 0.05, [0.05.rand, 0.05.rand], 4, amp) }); Out.ar( out, signal ); } ).send(s); ) Konfigurowanie funkcji powtarzanej w czasie Uwaga: Jest jeden globalny wirtualny zegar [SystemClock],który liczy czas w sekundach. SystemClock jest najbardziej wiarygodnym i dokładnym zegarem. Pozostałe zegary to [TempoClock] i [AppClock]. ( SystemClock.sched(0.0, //start at 0.0 sec from now, i.e. immediately { //a function which states what you wish to schedule Synth(\helpsched); 1 //repeat every second }); ) Ostatnią rzeczą, którą funkcja zwraca jest czas, dopóki nie zostanie ponownie wywołana. Konieczne jest, aby zwrócić jakikolwiek rezultat: zwrócenie liczby większej niż zero N planuje nowe wydarzenie za N-sekund, zwórcenie zera zatrzyma planowanie. dzień v/ kompozycja algorytmiczna Losowe wartości: ( SystemClock.sched(1.0, { //start in 1.0 sec Synth(\helpsched, [ //passing in arguments to the Synth \note, (#[0, 2, 4, 5, 7, 9] + 48).choose, \dur, 1.0.rand2 ] ); [0.25, 0.3, 0.7, 0.1].choose //random choice of repeat time }) ) TempoClock SuperCollider mierzy tempo w uderzeniach na sekundę (bps) a nie w uderzeniach na minutę jak zwykło się przyjmować 1 bps = 60 bpm 1.6666667 bps = 100 bpm 2 bps = 120 bpm 2.4 bps = 144 bpm 3 bps = 180 bpm (pomnóż to przez 60 aby z liczby bps otrzymać uderzenia na minutę, podziel przez 60 aby otrzymać odwrotny rezultat) Dla planowania opartego na uderzeniach używamy TempoClock: ( var t= TempoClock(2); //make a new tempoclock at tempo 120 bpm = 2 beats per second t.schedAbs(0, { arg ... args; //start at absolute beat 0 immediately args.postln; //post the input arguments to our event function //(will post logical time in beats, elapsed time //in seconds of enclosing thread and this clock) Synth(\helpsched); //make sound 1.0 //reschedules every beat }) ) TempoClock jest ustawiony aby reagować właściwie na zmiany tempa lub sygnatury czasu (.wchoose pozwala na wyważony wybór wartości w jednej tablicy w odniesieniu do obciążenia w drugiej tablicy) t = TempoClock.default; ( t.schedAbs(t.elapsedBeats.ceil, //start at next whole beat { Synth(\helpsched, [\note, [36,40,43].choose, \dur, exprand(0.625, 2)]); SuperCollider 30 31 SuperCollider dzień v/ kompozycja algorytmiczna [0.25, 0.5, 1.0, nil].wchoose([0.5, 0.4, 0.09, 0.01]); //weighted choice repeat at some number of beats from the array, nil means stop }) ) t.tempo_(4); //new tempo t.tempo_(1); //back to default 5.2 PLANOWANIE Z UŻYCIEM ROUTINES I TASKS Procedury (Routines) i zadania (Tasks) pomagają planować wydarzenia z możliwością przejścia przez różne etapy programu. Nie jest to określane od razu poprzez .value, ale może „produkować” swoją wartość na każdym z wielu etapów. ( r = Routine({ 1.yield; Synth(\helpsched); 2.yield; 1.yield; Synth(\helpsched); 1.yield; }); ) r.next; //r.value (or r.next) return the yield value r.next; r.next; r.next; Procedura może być odtwarzana na pewnych zegarach Clocks: ( r = Routine({ 0.5.yield; Synth(\helpsched); 1.yield; 0.5.yield; Synth(\helpsched, [\note, 43]); 0.5.yield; }); ) r.play(SystemClock); dzień v/ kompozycja algorytmiczna r.reset; //back into its original state r.play(TempoClock(3)); wait jest bardziej znaczące niż .yield (mimo, że znaczy to samo): ( var r; r = Routine.new({ „I just began!”.postln; 1.0.wait; „1 second later”.postln; 2.0.wait; „finished”.postln; }); r.play; ) inf.do może pracować ciągle i nieprzerwanie, ale powinien być stosowany z ostrożnością, aby nigdy nie przegapić pewnego pozytywnego czasu oczekiwania .wait. W przeciwnym razie, pętla będzie podążać coraz szybciej i scw końcu się zawiesi. ( var r; r = Routine.new({ inf.do({ |i| Synth(\helpsched, [ \note, 36 + (3 * (i % 10)) ]); //added %10 to stop it going up forever 0.25.wait; //IMPORTANT!!! }); }); r.play; ) Task jest Procedurą, która może być wstrzymana i wznowiona: ( t = Task.new({ inf.do({ arg i; //keep going forever (until stopped externally) Synth(\helpsched, [\note, 36 + (2.3 * (i % 17))]); 0.25.wait; }); }); ) t.play(TempoClock(1.4)); //start the Task going t.pause; //pause t.resume; //unpause Istnieje specjalny skrót na Procedurę {}.fork, który może być przydatny dla automatycznego przetwarzania funkcji w procedurę i automatycznego jej grania .fork wymaga zegara jako argumentu: SuperCollider 32 33 SuperCollider dzień v/ kompozycja algorytmiczna ( var note, amp, waittime, dur= #[2, 1, 0.5, 0.25, 0.125, 0.625]; { 16.do { |i| note= rrand(48, 84); amp= rrand(0.0, 0.125); waittime= dur.choose; Synth(\helpsched, [\note, note, \amp, amp]); [i, note.midinotename, round(amp, 0.01)].postln; waittime.wait; } }.fork(TempoClock(0.5)) ) help files / [Routine ] [Task ] 5.3 PATTERNS Patterns jest narzędziem do pisania muzyki generatywnej/ kompozycji algorytmicznej. Zamiast tworzenia pojedynczych wydarzeń, jest to obiekt, który jest w stanie zaplanować wiele wydarzeń w czasie. a = Pbind.new.play(quant:1.0); a.stop; Pbind(\freq, 440).play(quant:1.0); Parametr quant pozwala na opóźnienie harmonogramu do następnego uderzenia, tak, że Patterns rozpoczynają się w różnych czasach zamykając się w sobie. ( Pbind( \dur, Pseq([1, 0.75, 0.5, 0.25, 0.125, 0.625, 0.75], inf), \midinote, Pseq([0, 4, 0, 7, 4, 0, 0] + 60, inf), \amp, Prand([0.125, 0.2, 0.25], inf) ).play(quant:1.0) ) [Streams ] Patterns są generatorami Streams. Przykładem Stream jest Routine. Pattern może być zmieniony w Stream poprzez .asStream: a = Pseq([1, 3, 400], 2); //make Pattern, a simple sequence x = a.asStream; //turn this Pattern into a specific Stream x.next; //ask for the next value in the Stream Generowanie niezależnych Streams jest łatwe z jednego Pattern: ( var a, x, y; dzień v/ kompozycja algorytmiczna a= Pshuf([1, 2, 3], inf); x= a.asStream; //this creates a Routine from the Pattern. y= a.asStream; x.nextN(10).postln; y.nextN(10); ) Patterns mogą występować jako generatory liczb dla parametrów innych Patterns: ( Pbind( \freq, Pseq([Pseq([100, 200, 300], 2), 400, 500, 600],inf) ).play; ) ( Pbind( \freq, Pseq([Prand([440, 442, 445, 448]), Pxrand([840, 741, 642], 2)], inf) ).play; ) Patterns mają wiele parametrów. Można stworzyć własne parametry a nawet zagrać Pattern na własnym syntezatorze, wskazując im nazwę syntezatora jako parametr \instrument: ( 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; ) Uwaga: nie używaj .send lub .load dla syntezatorów dostępnych dla Patterns .add zapewnia, że syntezator może być używany jako \instrument, ale .store zapisuje je na dysku, co zapewnia jego załadowanie za każdym razem kiedy scserver jest uruchamiany. ( var clock; clock = TempoClock(1.5); //tempoclock at 90 bpm p= Pbind( \midinote, Pxrand([ Pseq([ 36, 38, 39, 41, 43, 46 ]), Pseq([ 31, 34, 36, 38, SuperCollider 34 35 SuperCollider uwaga: / nie używaj .send lub .load dla syntezatorów dostępnych dla Patterns .add zapewnia, że syntezator może być używany jako \instrument, ale .store zapisuje je na dysku, co zapewnia jego załadowanie za każdym razem kiedy SCserver jest uruchamiany. najlepsze wprowadzenie do patterns: / [A Practical Guide to Patterns ] by H. James Harkins uwaga / Łączenie Patterns i efektów: [Pfx ] dzień v/ kompozycja algorytmiczna 39, 41 ]) ], inf), //midi note \dur, Pseq([1.0, 0.5], inf), //duration of event in beats \legato, Prand([ Pseq([1.0, 0.25]), Pseq([0.65, 0.125]) ], inf), //proportion of inter onset time to play \pan, Pseq([0.5, -0.5],inf), \instrument, \bass ); p.play(clock); ) Klasa Pbind zawsze dobiera pary argumentów, dosłowną \property i związane z nimi wartości. Właściwości mogą być od siebie wzajemnie zależne (w tym przypadku częstotliwość zależy od amplitudy): ( Pbind( //co-binding of properties \instrument, \bass, [\freq, \amp], Pseq([ [150, 0.07], [75, 0.5], Pfuncn({ [rrand(120, 360), 0.1.rand] }, 1) ], inf) ).play ) Polifonia może być osiągnięta poprzez Ppar: ( var melodypat, basspat; melodypat = Pbind( \instrument, \help_mdapiano, [\midinote, \dur], Pseq([ [60, 0.75],[64, 0.5],[66, 0.5],[69, 0.25], [67,0.75],[64,0.5],[60,0.5],[57,0.25] ],inf) ); SuperCollider Tematy do samodzielnego zbadania • Extensions: [Quarks ] • GUI: [GUI-Overview ], menu UI -> New SC Window • OSC: [OSC_communication ] • Grafika, animacja i procesy fizyczne w SC: [Pen ], [ ] • Modelowanie dźwięków akustycznych - http://en.wikibooks.org/wiki/Designing_ Sound_in_SuperCollider • FFT • Live coding (ixi lang) 36