« 16 / 30 »

redUniverse: Clean-up and some New Features

2010-12-11 04:01 supercollider

clean-up #26:

My redUniverse quark still has a lot of features missing that I want to put in. It is endless work and I only get to it now and then. But at least after today the help files are in a bit better shape, and examples are changed to use animate to run more smoothly (most now look a lot better!).

Here is the complete SVN diff: sourceforge.net/p/quarks/code/1759/


Vertex

2010-12-10 05:01 supercollider

clean-up #25:

Attached is a little class that draws polygons in SuperCollider. With inspiration from processing.org/reference/beginShape_.html.

If you want to use the \points type of shape it will look better with smoothing set to false and Vertex.pointSize= 0;

(
var win= Window("vertex test", Rect(100, 100, 430, 320), false);
win.drawFunc= {
  Pen.fillColor= Color.white;
  Pen.strokeColor= Color.black;

  Pen.use{
    Pen.translate(10, 10);
    Vertex.beginShape();
    Vertex(Point(30, 20));
    Vertex(Point(85, 20));
    Vertex(Point(85, 75));
    Vertex(Point(30, 75));
    Vertex.endShape(1);  //1 means close
    Pen.fillStroke;  //also try fill and stroke
  };
  Pen.use{
    Pen.translate(110, 10);
    Vertex.beginShape(\points);
    Vertex(Point(30, 20));
    Vertex(Point(85, 20));
    Vertex(Point(85, 75));
    Vertex(Point(30, 75));
    Vertex.endShape();
    Pen.stroke;
  };
  Pen.use{
    Pen.translate(210, 10);
    Vertex.beginShape(\lines);
    Vertex(Point(30, 20));
    Vertex(Point(85, 20));
    Vertex(Point(85, 75));
    Vertex(Point(30, 75));
    Vertex.endShape();
    Pen.stroke;
  };
  Pen.use{
    Pen.translate(310, 10);
    Vertex.beginShape();
    Vertex(Point(30, 20));
    Vertex(Point(85, 20));
    Vertex(Point(85, 75));
    Vertex(Point(30, 75));
    Vertex.endShape(0);  //0 means not close (default)
    Pen.stroke;
  };

  Pen.use{
    Pen.translate(10, 110);
    Vertex.beginShape();
    Vertex(Point(30, 20));
    Vertex(Point(85, 20));
    Vertex(Point(85, 75));
    Vertex(Point(30, 75));
    Vertex.endShape(1);  //1 means close
    Pen.stroke;
  };
  Pen.use{
    Pen.translate(110, 110);
    Vertex.beginShape(\triangles);
    Vertex(Point(30, 75));
    Vertex(Point(40, 20));
    Vertex(Point(50, 75));
    Vertex(Point(60, 20));
    Vertex(Point(70, 75));
    Vertex(Point(80, 20));
    Vertex.endShape();
    Pen.fillStroke;  //also try fill and stroke
  };
  Pen.use{
    Pen.translate(210, 110);
    Vertex.beginShape(\triangleStrip);
    Vertex(Point(30, 75));
    Vertex(Point(40, 20));
    Vertex(Point(50, 75));
    Vertex(Point(60, 20));
    Vertex(Point(70, 75));
    Vertex(Point(80, 20));
    Vertex(Point(90, 75));
    Vertex.endShape();
    Pen.fillStroke;  //also try fill and stroke
  };
  Pen.use{
    Pen.translate(310, 110);
    Vertex.beginShape(\triangleFan);
    Vertex(Point(57.5, 50));
    Vertex(Point(57.5, 15));
    Vertex(Point(92, 50));
    Vertex(Point(57.5, 85));
    Vertex(Point(22, 50));
    Vertex(Point(57.5, 15));
    Vertex.endShape();
    Pen.fillStroke;  //also try fill and stroke
  };

  Pen.use{
    Pen.translate(10, 210);
    Vertex.beginShape(\quads);
    Vertex(Point(30, 20));
    Vertex(Point(30, 75));
    Vertex(Point(50, 75));
    Vertex(Point(50, 20));
    Vertex(Point(65, 20));
    Vertex(Point(65, 75));
    Vertex(Point(85, 75));
    Vertex(Point(85, 20));
    Vertex.endShape();
    Pen.fillStroke;  //also try fill and stroke
  };
  Pen.use{
    Pen.translate(110, 210);
    Vertex.beginShape(\quadStrip);
    Vertex(Point(30, 20));
    Vertex(Point(30, 75));
    Vertex(Point(50, 20));
    Vertex(Point(50, 75));
    Vertex(Point(65, 20));
    Vertex(Point(65, 75));
    Vertex(Point(85, 20));
    Vertex(Point(85, 75));
    Vertex.endShape();
    Pen.fillStroke;  //also try fill and stroke
  };
  Pen.use{
    Pen.translate(210, 210);
    Vertex.beginShape();
    Vertex(Point(20, 20));
    Vertex(Point(40, 20));
    Vertex(Point(40, 40));
    Vertex(Point(60, 40));
    Vertex(Point(60, 60));
    Vertex(Point(20, 60));
    Vertex.endShape(1);  //also try 0 here
    Pen.fillStroke;  //also try fill and stroke
  };
  Pen.use{
    Pen.translate(310, 210);
    Vertex.beginShape();
    14.do{
      Vertex(Point(85.rand, 85.rand));
    };
    Vertex.endShape(1);  //also try 0 here
    Pen.fillStroke;  //also try fill and stroke
  };
};
win.front;
)
//https://processing.org/reference/bezierVertex_.html
(
var win= Window("beziervertex test1", Rect(100, 100, 300, 300), false);
win.drawFunc= {
  Pen.fillColor= Color.white;
  Pen.strokeColor= Color.black;
  BezierVertex.beginShape(Point(30, 20));
  BezierVertex(Point(80, 0), Point(80, 75), Point(30, 75));
  BezierVertex.endShape;
  Pen.stroke;  //also try fillStroke and fill
};
win.front;
)

//https://processing.org/reference/bezierVertex_.html
(
var win= Window("beziervertex test2", Rect(100, 100, 300, 300), false);
win.drawFunc= {
  Pen.fillColor= Color.white;
  Pen.strokeColor= Color.black;
  BezierVertex.beginShape(Point(30, 20));
  BezierVertex(Point(80, 0), Point(80, 75), Point(30, 75));
  BezierVertex(Point(50, 80), Point(60, 25), Point(30, 20));
  BezierVertex.endShape;
  Pen.fillStroke;  //also try stroke and fill
};
win.front;
)

Updates:

Attachments:


K-means

2010-12-09 03:14 supercollider

clean-up #24:

I took some code I had for performing K-means clustering and made it into a proper class. I also found Dan Stowell's KMeans quark and borrowed some concepts from that one. My version works slightly different and uses RedVectors of any dimension.

The class is part of my redUniverse quark and the best way to install it is as always to from within SuperCollider type... Quarks.install("redUniverse"); and then recompile. A help file and simple examples are included but I hope to write more elaborate demonstrations of this later on.

This is a screenshot from the example in the RedKMeans help file. 1000 random points are categorised into 5 groups/clusters. The bigger circles are the 5 mean points/centroids.

Also, see 190-kmeans.scd for an example running in realtime.


anneVideotracking

2010-12-08 04:08 supercollider, visuals

clean-up #23:

The screenshot below is of a simple little mac application I made in MaxMSPJitter. It takes realtime input from a web or DV-camera, chops it into six regions and tracks activity in each of those regions separately. The result is sent over to SuperCollider via OSC.

The analysis/tracking is just a simple average luminescence per frame. That is, it takes the mean of all the red, green and blue values in one frame and then calculate the luma with (0.299*red)+(0.587*green)+(0.114*blue). The luma will be close to 0 for dark frames and close to 1 for very bright video frames.

To tune this system to produce maximal output, one should first make sure the camera sits still and has sufficient and stable light. Then in the software there are colour, brightness and contrast controls to help get even better readings. (Completely inverting all the colours might help in really dark places.)

The luma values (float 0.0-1.0) can be rounded off to nearest number and also smoothed out. This is often necessary to avoid flutter and noise. And most important - there is manual calibration of the luma values - either per region or globally.

To calibrate one should first empty the camera's view of any objects and then press the 'calibrate min' button in the lower-left corner. Then move some objects around to see what is the maximum reading. Last put that number into the 'set max' number box. One can also calibrate each region separately with the min and max buttons next to the preview windows.

Download the mac OSX standalone from /code/apps/#annevideotracking.

and here is SuperCollider code to read the data and make some simple sounds out of it.

//to start: select all + enter
//to stop: cmd + .
(
n= 6;
s.latency= 0.05;
s.waitForBoot{
  var dlast= 0.dup(n);
  d= {Bus.control(s, 1)}.dup(n);
  e= {Bus.control(s, 1)}.dup(n);
  OSCFunc({|m|
    var index= m[1], val= m[2], diff= (val-dlast[index]).abs;
    //m.postln;
    d[index].set(val);
    e[index].set(diff);
    dlast.put(index, val);
  }, \anneVideoTracking);
  CmdPeriod.doOnce({d.do{|x| x.free}; e.do{|x| x.free}});
  SynthDef(\annetest, {
    var src= Mix({|i| SinOsc.ar(i*100+400, 0, LagUD.kr(In.kr(e[i].index), 0.01, 0.1))}.dup(n));
    Out.ar(0, Pan2.ar(src));
  }).add;
  s.sync;
  Synth(\annetest);
};
)

Updates:


Tweets

2010-12-07 03:07 supercollider

clean-up #22:

Here are my best-of twitter tweets so far. See twitter.com/redFrik for the rest. With the hard limitation of 140 characters, it is really challenging to write a piece of SuperCollider code that fits and sounds good _and is interesting to listen to for more than a few seconds.

tweet0001

r{99.do{|i|x={Pan2.ar(SinOsc.ar(i+1,SinOsc.ar((i%9).div(3)*100+(i%9)+500),0.03),1.0.rand2)}.play;2.wait;x.release(25)}}.play//#SuperCollider

99 sine oscillators played one after the other with a two seconds interval. Each oscillator lasts for 27 seconds. So the total duration is 99 * 2 + 27 = 225 seconds. the oscillators are phase modulated with other sine oscillators with frequencies repeating in the number series: 500, 501, 502, 603, 604, 605, 706, 707, 708. The base frequencies of the 99 carrier oscillators slowly raise by one Hertz from 1 to 99. The only random thing is the stereo panning position for each oscillator.

tweet0006

r{loop{x={GVerb.ar(MoogFF.ar(ClipNoise.ar*0.4,LFPar.kr({0.3.rand}!2,0,600,990)),9,9,1)}.play(s,0,19);3.wait;x.release}}.play//#SuperCollider

A clip noise generator runs through a Moog filter and then a reverb. Every third second there is a new noise added and each noise lasts for 22 seconds. Each Moog filter has two unique parabolic LFOs that runs at a random rate between 0 and 0.3 Hertz - each in one channel. At most, there is 8 number of overlapping noises playing at the same time. As there are so many reverbs playing here at once one needs to allocate more memory to SC server. Something like this... Server.local.options.memSize= 32768; and then reboot the localhost server.

tweet0014

play{a=SinOscFB;sum({|i|a.ar(a.ar(a.ar(a.ar(i+1,1/9,999),1/9,a.ar(1/9,1,1/9)),a.ar(0.1,3),i+2*999),a.ar(1/9,1/9),1/9)}!9)!2}//#SuperCollider

This one is completely deterministic although it is hard to believe when hearing it. A lot of nested feedbacking sine oscillators. Nine parallel oscillators are mixed down to 1 and duplicated in left and right channels. Each of the nine oscillators is frequency and feedback modulated but have a static amplitude of 1/9. The frequency modulator consists of yet more modulated feedbacking sine oscillators, while the feedback of the outer is only modulated with a single feedbacking sine oscillator running at 0.1 Hertz. All in all, there are 109 unit generators in total running in this tweet and it peaks at about 9.3% of my computer's CPU.

tweet0016 - tweet0019

These four all work the same way. They only differ in buffer size and what kind of oscillator is used for reading back samples from the buffer. There is not much progress over longer time but they do have character and subtle though deterministic variation in the details.

play{b=LocalBuf(9e4,2).clear;i=Sweep.ar(BufRd.ar(2,b,Saw.ar(12,3e4,4e4)),9e4);BufWr.ar(Saw.ar([8,9]),b,i);BufRd.ar(2,b,i)/2}//#SuperCollider
play{b=LocalBuf(8e4,2).clear;i=Sweep.ar(BufRd.ar(2,b,Saw.ar(3.1,4e4,4e4)),8e4);BufWr.ar(Blip.ar([2,3]),b,i);BufRd.ar(2,b,i)}//#SuperCollider
play{b=LocalBuf(5e3,2).clear;i=Sweep.ar(BufRd.ar(2,b,Saw.ar(50,2e3,5e3)),6e4);BufWr.ar(Saw.ar([4,3]),b,i);BufRd.ar(2,b,i)/6}//#SuperCollider
play{b=LocalBuf(1e4,2).clear;i=Sweep.ar(BufRd.ar(2,b,Saw.ar(1,2e3,5e3)),5e5);BufWr.ar(Saw.ar([8,50]),b,i);BufRd.ar(2,b,i)/3}//#SuperCollider

tweet0020

play{a=LFPulse;b=(1..4);Mix(a.ar(a.ar(a.ar(a.ar(b/32)+1/8)+1*b)+(Mix(a.ar(b/64))+a.ar(4/b)*(a.ar(a.ar(b/8))*2+b))*100))/8!2}//#SuperCollider

This tweet is also totally deterministic and without any randomness. Here a lot of nested square wave oscillators creates the complexity. Basically, there are 4 channels/voices mixed down to one and then duplicated in left and right channel. There are three levels deep nesting of frequency modulation with another set of square waves mixed and added.

tweet0021

r{{|j|a=play{sin(Decay.ar(Duty.ar(1/50,0,Dseq(flat({|i|asBinaryDigits(j+1*i)}!8),4),2),j+1*0.008))/2!2};5.12.wait}!256}.play//#SuperCollider

Binary numbers from 0 to 255 form the rhythmic patterns in this tweet. Each number first repeats 8 times - each time all the bits are shifted one position to the left. That creates an array of 64 ones and zeros. This array is then repeated four times. This is what makes it sound like a 4 x 4/4 bar theme (i.e. 4 bars per number). The total 64 * 4 * 256 binary digits are played in sequence and each digit lasts for 1/50th of a second. The total duration becomes 64 * 4 * 256 / 50 = 1310.72 seconds. The sound is generated by the ones and the zeros directly. There is an exponential decay for these flipflop pulses that slowly increases throughout the 256 numbers. It starts with a decay time of 0.008 seconds and ends with 2.04 seconds. In the MP3 below only the numbers 0 - 31 are played.

tweet0024

{|j|r{{|i|x=sin(i/5+(j*5));Ndef(i%5+(j*5),{Pan2.ar(LFCub.ar(j*2+x*40+400+i)/15,i%5/2-1)}).play;wait(x.abs+0.5)}!500}.play}!5//#SuperCollider

Another tweet without any randomness. There are five parallel routines and all do something 500 times. What they do is to define or redefine a node proxy. There are 25 proxies in total and each one contains a sine shaped oscillator panned to one out of four positions in the stereo field. The frequencies are climbing upwards in a slightly jagged curve. The exact length is a bit complicated to calculate but it is around 575 seconds. In the end, the proxies do not stop to play but just keeps the last assigned frequency and the whole soundscape becomes static.


Klee

2010-12-06 03:45 supercollider

clean-up #21:

Attached is a Klee step-sequencer as a SuperCollider list-pattern class. The included help file should explain how it works.

There are a couple of ways to make a Klee sequencer on the server-side as well. In the below examples I used argument lists, but one can also do it using buffers together with the Index UGen.

(
SynthDef(\klee8, {|freq= 400, rate= 0.5,
  list= #[0, 0, 0, 0, 0, 0, 0, 0],
  mul= #[0, 0, 0, 0, 0, 0, 0, 0]|
  var i= LFSaw.kr(rate, 1, 0.5, 0.5)*list.size;
  var sum= (mul*Select.kr(i+(0..list.size-1)%list.size, list)).sum;
  var src= SinOsc.ar(freq+sum, 0, Decay2.kr(Impulse.kr(list.size*rate), 0.01, 0.1, 0.5));
  Out.ar(0, Pan2.ar(Mix(src)));
}).add;
)
a= Synth(\klee8)
a.set(\list, #[4, 0, 0, 2, 0, 0, 0, 0]*100)  //set the values
a.set(\mul, #[1, 0, 0, 0, 0, 0, 0, 0])  //activate 1 read head
a.set(\mul, #[1, 1, 0, 0, 0, 0, 0, 0])  //activate 2 read heads
a.set(\mul, #[1, 1, 1, 0, 0, 0, 0, 0])
a.set(\mul, #[1, 1, 1, 1, 0, 0, 0, 0])
a.set(\mul, #[1, 0.5, 0.25, 0, 0, 0, 0, 0])
a.set(\list, #[4, 0, -4, 2, 0, 0, 0, 0]*100)
a.set(\freq, 500)
a.set(\rate, 1.5)
a.free

And similar but with 16 steps. It is only to change the number of zeros in list and mul arguments.

(
SynthDef(\klee16, {|freq= 400, rate= 0.5,
  list= #[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  mul= #[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]|
  var i= LFSaw.kr(rate, 1, 0.5, 0.5)*list.size;
  var sum= (mul*Select.kr(i+(0..list.size-1)%list.size, list)).sum;
  var src= SinOsc.ar(freq+sum, 0, Decay2.kr(Impulse.kr(list.size*rate), 0.01, 0.1, 0.5));
  Out.ar(0, Pan2.ar(Mix(src)));
}).add;
)
a= Synth(\klee16)
a.set(\list, #[4, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0]*100)  //set the values
a.set(\mul, #[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])  //activate 1 read head
a.set(\mul, #[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])  //activate 2 read heads
a.set(\mul, #[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0.25, 0, 0, 0, 0, 0])
a.set(\list, #[4, 0, -4, 2, 0, 0, 0, 0, 3, 0, -3, 2, 0, 0, 0, 0]*100)
a.set(\freq, 500)
a.set(\rate, 1.5)
a.free

Updates:

Attachments:


2 Max Ports

2010-12-05 03:56 supercollider

clean-up #20:

Here are two SuperCollider documents made years ago to resemble two MaxMSP patches by Katsuhiro Chiba. His patches are wonderfully constructed with playful and nice GUI design plus they sounded good to me. So I was curious how he made them and to learn more I ported parts of his code to SuperCollider. The Max4 patches are still available here along with some screenshots.

All credit for the attached code should go to Mr Chiba.

Updates:

Attachments:


Alarm

2010-12-04 02:43 supercollider

clean-up #19:

There was a sleeping-bag concert as part of the Sound of Stockholm festival this year. Music for sleeping - what a great concept!

First I was assigned to play last and would thereby have had the honour/responsibility of waking everybody up for breakfast at 7:00 in the morning. Both fun and daunting. How to let your audience sleep in as long as possible and then wake them up gently but firmly?

I assumed most people would be asleep when it was my turn and that I also would be very tired. So I prepared my solo set (using redThermoKontroll) to be extremely simple and static in nature. Hardly any variation - only a couple of sounds sources (4 SuperCollider patches I think it was) with looong transitions between settings that I could control manually.

Then there was the waking up part and for that, I wrote the very simple sounding code below. It starts slowly with one little alarm going off. Then a second alarm, then more and more until complete chaos. I made it so that this patch could be mixed in with my drones and with separate control over volume.

Unfortunately, when I got to Stockholm they had changed the playing order and I did do my performance around midnight instead. And I never set the alarms off.

//--set alarms
(
~num= 25;  //number of alarms
~fadeTime= 10;  //fade in time in seconds
~num.do{|i|
  Ndef(("alarm"++i).asSymbol, {|amp= 0, lag= 5|
    var src= Pulse.ar(
      ExpRand(300, 3000)+LFPulse.kr(ExpRand(3, 30), 0, Rand(0.4, 0.6), Rand(-100, 100)),
      Rand(0.4, 0.6),
      LFPulse.kr(LinRand(0.05, 2), 0, LinRand(0.1, 0.5), 0.5)
    );
    Pan2.ar(src, Rand(-0.95, 0.95), amp.lag(lag));
  });
};
Ndef.all;  //list them
)
//--start alarms
(
~task.stop;
~task= Task({
  ~num.do{|i|
    Ndef(("alarm"++i).asSymbol).play(fadeTime: ~fadeTime).set(\amp, 7/~num).postln;
    i.linlin(0, ~num-1, ~fadeTime*2, ~fadeTime/2).wait;
  };
}).play;
)
//--stop alarms
(
~task.stop;
~task= Task({
  ~num.do{|i|
    Ndef(("alarm"++i).asSymbol).stop(fadeTime: 1/~fadeTime);
    i.linlin(0, ~num-1, 1/~fadeTime, 10/~fadeTime).wait;
  };
}).play;
)

« 16 / 30 »