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

Podobne dokumenty