Skip to content

Instantly share code, notes, and snippets.

@erstwhile
Created February 3, 2016 00:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erstwhile/cb9a278f4d88528ffe1a to your computer and use it in GitHub Desktop.
Save erstwhile/cb9a278f4d88528ffe1a to your computer and use it in GitHub Desktop.
24 Quaternion Permutations (mostly-uncommented source code)
(
/*
launch 4 servers
since supercollider isn't multi-core aware, do this to load-balance audio - 1 core per server
*/
~servers = (1..4).collect({|x| Server.new("xs"+x,NetAddr("127.0.0.1",9989-x));});
~servers.collect(_.boot);
~servers.collect(_.makeWindow);
)
(
/*
instruments for the piece - one eq / compressor and 1 complex fm synth
*/
/*
eq / compressor. eq settings are hardcoded
*/
~servers.collect({|z|
SynthDef("eq-comp",{|bus=0,in=2,fr=100,thresh=1,above=1,below=1,amp=1,w=1,db=1,d=1|
var dry = In.ar(in,2),out, eq1, eq2;
out = dry * db;
out = Compander.ar(out,out,thresh,below,above,0.1,0.1);
eq1 = MidEQ.ar(out,84*2/3,0.25,6);
eq2 = MidEQ.ar(out, 84*16*2/3,0.5,5);
out = (eq1+eq2);
Out.ar(bus,(out*w)+(dry*d)*amp);
}).load(z);
});
/*
fm synth with 3 modulators (pm1, pm2, pm3) and a modulator on the phase of the carrier wave (phar)
*/
~servers.collect({|z|
SynthDef("fmsynth",{|freq=100, ampp=0.75,par=#[1,1,1,1],atk=1, rel=1, sus=1, amp=0.005, gate=0, p=0,phar,carm=1, pm1=1,pm2=1,pm3=1,mo1=0,mo2=0,mo3=0,out=0|
var xar = XLine.ar(3,1/1024,(sus.abs*atk.sqrt).clip(2/freq,4)),
pn = ((1-(p))+(par*p)),sin, sin1, sin2, sin3,car;
freq = freq *pn;
amp = amp/(freq+11);
phar = SinOsc.ar(2.pow((freq.log2.mod(3)+7)),0,xar*pi);
car = SinOsc.ar(freq,phar,0.5,0.5)*carm;
sin1 = SinOsc.ar(freq *pm1,0, mo1);
sin2 = SinOsc.ar(freq *pm2,0, mo2);
sin3 = SinOsc.ar(freq *pm3,0, mo3);
sin = sin3 + sin2 + sin1 + car;
sin = (sin * 2pi).cos * amp;
sin = LeakDC.ar(sin);
OffsetOut.ar(out,Mix.ar(Pan2.ar(sin,[-0.75,0.25,-0.25,0.75])) * EnvGen.ar(Env.perc(atk,rel,1,sus),gate, doneAction:2) );}).send(z);
});
)
(
//global function to filter out unique vals in an array
~unique = {|a|var t = Array.fill(a.size);
a.collect({|i,j|(t.includes(i)).if({t.put(j,nil)}, {t.put(j,a[j])})}).reject({|i|i==nil});
t.reject({|i|i==nil});
}
//basic proto with a few attributes
~fracP =Proto({
~fr = 110*8/9;
~freq = 100;
~pm = 1;
~w = 4/(~fr);
~x= 2;
~o = (1..8);
~mcheck = [Quaternion(-1,0,0,0)];
~z = Quaternion(2,1,1,1);
~mqz = [Complex(1,1),Complex(1,1),Complex(1,1),Complex(1,1)];
~pqz = [Complex(1,1),Complex(1,1),Complex(1,1),Complex(1,1)];
~qz = [Complex(1,1),Complex(1,1),Complex(1,1),Complex(1,1)];
~c = Quaternion(6,0,0,0);
~cc = Complex(1,0);
~mcount = 0;
~mf = Quaternion(9,0,0,0);
});
//function to initialize an array of empty "Frac" protos
~init = {|n|
~fracs = [];
(0..(n-1)).collect({|i|
var frac = ~fracP.clone();
frac.i = i;
frac.servers = ~servers;
~fracs = ~fracs.add(frac);
});
};
)
(
//"define" function fills out the methods for a "Frac" proto
//this can be tweaked and executed while running
~define = {
~fracs.collect({|f|f.fr = 84; f.w = 1/12});
~fracs.collect({|f|f.mc = {
var x,bailout = false, reason;
x= ~x;
~mf = ~z;
~z = ~m.(~z,~c);
~pqz = ~qz;
~qz = [Complex(~z.a,~z.b),Complex(~z.b,~z.c),Complex(~z.c,~z.d),Complex(~z.d,~z.a)].rotate(~i);
~mqz = ~qz - ~pqz;
~mcount = ~mcount + 1;
(Quaternion(0,0,0,0).distance(~z).abs > 64).if ({bailout = true;});
(~mcheck.collect(_.norm).sum > 128).if ({bailout = true;});
(~mcheck.detect({|n|n==~z}).isNil==false).if ({bailout = true;});
(bailout==true).if({
~mcount = 0;
~c = ~cFunc.();
~cc = Complex(~c.a,~c.b);
~z = ~c;
~qc = [Complex(~z.a,~z.b),Complex(~z.b,~z.c),Complex(~z.c,~z.d),Complex(~z.d,~z.a)];
~mcheck= [~z];
});
~mcheck = ~mcheck.add(~z);
~mcheck
}});
~frac = {
var s,freq,o,m,m3,oct,sus,x,player;
x = ~x.asInt;
oct = ~octave.();
~freq = oct * ~fr * ~ot.();
sus = ~susFunc.();
player = {|freq|
s = Synth("fmsynth",[\out,2],~servers.wrapAt((~x/~servers.size).floor));
~ampf.(sus,freq,1,s);
{
var p = (freq.log2 > (8+freq.log2.mod(1))).if({-1},{1});
s.set(\pm1,2.pow(p), \pm2,3.pow(p), \pm3,7.pow(p));
}.();
~setMods.(s);
s.set(\sus,sus);
s.set(\rel,64* ~w);
s.set(\atk,(2/freq));
//amplitude of the carrier wave can be changed
s.set(\carm,-2/3);
s.set(\freq,freq);
s
};
player.(~freq);
~mc.();
~x = ~x+1;
s
};
~fracs.collect({|f|f.frac = ~frac});
~fracs.collect({|f|f.susFunc = {((~mqz[2] - ~mqz[3]).theta.sin *3).cubed }});
~fracs.collect({|f|f.setMods = {|s|
s.set(\mo1,((~qz[1]-~pqz[1]).theta.cos).abs.squared/3,
\mo2,((~qz[2]-~pqz[2]).theta.cos).abs.squared/3,
\mo3,((~qz[3]-~pqz[3]).theta.cos).abs.squared/3);
}});
~fracs.collect({|f|f.unique = ~unique});
~fracs.collect({|f|f.ampf ={|sus,freq,pm,s|
s.set(\amp,1);
s.set(\p,(~w.reciprocal/(2.pow(freq.log2.mod(1)+13))));
s.setn(\par,([2,1,0,-2]).permute((~mcount*Complex(~c.c,~c.d).rho).asInt));
}});
~ot ={
var lo, ot, mx, cq;
ot = (6..32).reject({|n|n.factors.last>7}).reject({|n|n.mod(5)==0});
mx = {
var a = [1], c = ~c.coordinates.collect(_.sign);
3.do({|x|
var b = a.last;
b = b *(c.at(x+1)==1).if({2/3},{3/4});
a=a.add(b);
});
a.reverse;
}.();
ot= ~unique.(ot * mx);
ot = ~unique.([2,3,4,ot].flat.sort);
ot = [ot,ot.reverse].flat;
ot = ot / 1.5;
lo = ot.wrapAt((ot.size * (((~mqz[2].rho-~mqz[3].rho) /(~mqz[1].rho- ~mqz[2].rho)).reciprocal.atan*2/pi)).floor);
lo;
};
~fracs.collect({|f|f.ot=~ot});
~fracs.collect(_.octave = {
var out, qz = ~qz - ~pqz, q1 = qz[0], q2 = qz[1], q3 = qz[2], q4 =qz[3];
out = (q1-q2).theta.cos * (q3-q4).theta.cos;
out = (15*out.abs).round.mod(3);
out = 1.5.pow(out-1);
};);
~fracs.collect({|f,i|f.cFunc ={
var c = ~mcheck.reverse.wrapAt(~x),xl2 = ~x.mod(2.pow(~x.log2.floor))/2.pow(~x.log2.floor);
o = ((~endpoint-~offset) * xl2);
((~mcheck.detect({|n|n==~z}).isNil==false).or(c.distance(~mcheck.first) > 32).or(c.norm.abs > 8).or(c.coordinates.abs.sort.last>4).or(~mcheck.size==2)).if({
~offset = Quaternion(~offset.a,~offset.b,~offset.c,~offset.d);
(~offset + o);
},{
Quaternion(c.a.clip(-4,4),c.b.clip(-4,4),c.c.clip(-4,4),c.d.clip(-4,4)).postln;
});
}});
//here's the function that performs the fractal iteration
~m = {|z,c|
((z * z) + c)};
~fracs.collect({|f| f.m = ~m});
~q1= [0.38,-1/3,0.125,-0.125].permute(~permute);
~q2 = [-0.5,0.56,0,0].permute(~permute);
~fracs.collect({|f|f.permute = ~permute});
~fracs.collect({|f|f.offset = Quaternion(~q1[0],~q1[1],~q1[2],~q1[3])});
~fracs.collect({|f|f.endpoint = Quaternion(~q2[0],~q2[1],~q2[2],~q2[3])});
};
~define.();
//the main sequence
~run = {
~ws = [~fracs.first.w];
~fracs.collect(_.x = ~startPoint);
~fracs.collect({|f|f.c=f.offset});
~fracs.collect({|f|f.mc.()});
3.wait;
~cycle.do({|x|
var a = ~fracs.collect({|f|f.frac.()});
a.collect(_.set(\gate,1));
(~ws.wrapAt(x)).wait;
});
};
)
(
//execute this to run all 24 permutations in sequence
~permute = 0;
~servers.collect(_.freeAll);
Routine {
24.do({|x|
~cycle = 2.pow(13).asInt;
~startPoint = 2.pow(13);
~init.(2);
"Permutation: ".post;
x.postln;
~permute = (x).asInt;
[1,2,3,4].permute(~permute).postln;
~define.();
~run.();
10.wait;
});
}.play;
//initialize the eq / compressor for each channel
Routine {
0.1.wait;
~dreq = ~servers.collect({|d|Synth("eq-comp",[\in,2,\bus,0],d)});
~dreq.collect{|d|d.set(\d,0,\w,1,\db,1,\thresh,0.7,\above,0.7,\below,1,\amp,1)};
}.play;
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment