Image Image Image Image Image
Scroll to Top

To Top

supercollider

16

Jan
2014

7 Comments

In code

By admin

SuperCollider linux standalones

On 16, Jan 2014 | 7 Comments | In code | By admin

There isn’t a program for playing sound files in Linux which has the functionality that I want: fast, light, play files with any number of channels and 32bit floating-point encoded aiffs or wavs, so I decided to just code my own file player in SuperCollider. I didn’t want to have to start up SuperCollder to use the file player, I was in need of a “standalone”. Standalone is concept from OSX, which is not really use much in Linux. On OSX apps are just a folder with all the files and data inside (except some configuration files in ~/Library) while in Linux apps are usually installed system wide. Since the introduction in SuperCollider of the language configuration system via a yaml file, it’s possible to override which paths get searched for class files, and so kind of emulate standalones in linux too.

A SuperCollider standalone can then be created using the language config file, with the caveat that it has to be generated from a script in order to determine the location of the home folder, in order to disable loading the default extensions folder and class library folder. I have pushed a demo of this to github.

For quick scripts I just place

#!/usr/local/bin/sclang -l/home/miguel/bin/langconf.yaml

on top of the file and either run it from terminal or create a .desktop file for it:

[Desktop Entry]
Version=0.1
Name=FilePlayer
Comment=Play files
Exec=/home/miguel/bin/fileplayer.scd
Icon=/media/miguel/internal2/Imagens/fileplayer.png
Terminal=true
Type=Application
Categories=Utility;Application;

So now I have a file player app ! 🙂

fileplayer-example1-1

fileplayer-example2

Tags |

09

Jan
2014

No Comments

In code

By admin

supercollider async actions with completion messages

On 09, Jan 2014 | No Comments | In code | By admin

Usually async actions in SuperCollider are dealt with the sync message. A sync message is placed in between each group of commands containing an async command which needs to run before some other future command. An alternative to the sync message is to use the completion messages. This is the mechanism used by the Request monad from hsc3-server.

Today I needed to quickly get some code going to record from the input buses of a server to a file on disk, and I decided to chain the commands via completion messages:

//rec
(
// this will record to the disk
SynthDef("rec-ins", {arg bufnum;
DiskOut.ar(bufnum, In.ar(NumOutputBuses.ir,32) );
}).send(s);
)
(
~recServer = s;
~recSynth = Synth.basicNew("rec-ins", ~recServer);
~recBuf = Buffer.new(~recServer, 65536, 32);
~recBuf.alloc(
~recBuf.writeMsg("/tmp/test1.aiff", "aiff", "int24", 0, 0, true,
completionMessage:
~recSynth.newMsg(~recServer, ["bufnum", ~recBuf], 'addToTail')
)
)
)
(
~recServer = s;
~recServer.bind{
~recSynth.free;
~recBuf.close(completionMessage:~recBuf.freeMsg);
}
)

Tags |

16

Dec
2013

No Comments

In code

By admin

FPLib FRP – new mouse and keyboard event sources and signals

On 16, Dec 2013 | No Comments | In code | By admin

Inspired by the signals provided by elm for mouse and keyboard interaction I’ve revised the corresponding event sources and signals in FPLib and updated them to be usable with ENDef.

Mouse:

  • mouseClicksENInputES – EventSource – fires when mouse clicks come in
  • mousePosENInput – FPSignal – tracks mouse position. Needs  .acceptsMouseOver_(true) on the view.
  • mouseIsDownENInput – FPSignal – tracks if mouse is down.

Examples:

//mouse position
(
w = Window().acceptsMouseOver_(true).front;
x = EventNetwork(ENDef({
var pos = w.view.mousePosEnIn;
pos.collect{ |p| putStrLn("mouse pos is: "++p) }.enOut;
}));
x.start
)
// is down
(
t = StaticText();
w = Window().layout_(HLayout(t)).front;
x = EventNetwork(ENDef({
var pos = w.view.mouseIsDownEnIn;
pos.collect{ |b| IO{ t.string_("mouse is down: "++b) } }.enOut;
}));
x.start
)
//track mouse position
(
w = UserView().minWidth_(500).minHeight_(500).acceptsMouseOver_(true).front;
x = EventNetwork(ENDef({
var pos = w.mousePosEnIn;
var cx = w.bounds.width/2;
var cy = w.bounds.height/2;
var c = Point(cx,cy);
var drawing = pos.collect{ |p|
var points = [Point(-50, 0),Point(0, 25),Point(0,-25)].collect{ |x|
x.rotate( (c-p).angle ) + c
};
var shape1 = PenStepShape.polygon(points);
var shape2 = PenWedge(p,10,0,2*pi);
PenDrawing( [ PenDrawedShapes([shape1], \fill, Color.blue, Color.red),
PenDrawedShapes([shape2], \fill, Color.green, Color.red)
] )
};
drawing.collect(w.setDrawing(_)).enOut
}));
x.start
)
//stamp circles
(
w = UserView().minWidth_(500).minHeight_(500).acceptsMouseOver_(true).front;
x = EventNetwork(ENDef({
var pos = w.mousePosEnIn;
var clicks = w.mouseClicksEnInES;
var accumPos = pos.sampleOn(clicks).inject([], { |state,x| state.addI(T(x,Color.rand)) });
accumPos.collect{ |positions|
var drawings = positions.collect{ |tup|
PenDrawedShapes([PenWedge(tup.at1,10,0,2*pi)], \fill, tup.at2, Color.red)
};
var final = PenDrawing( drawings );
w.setDrawing(final)
}.enOut
}));
x.start
)

Keyboard

  • keyDownENInputES – EventSource – fires when keyboard keys are pressed
  • keysDownENInput – FPSignal -tracks the currently pressed keys.

Examples:

//keyboard
//keysDown
(
t = StaticText();
w = Window().layout_(HLayout(t)).front;
x = EventNetwork(ENDef({
var pos = w.view.keysDownEnIn;
pos.collect{ |b| IO{ t.string_("keys down: "++b) } }.enOut;
}));
x.start
)
//keyDown
(
t = StaticText();
w = Window().layout_(HLayout(t)).front;
x = EventNetwork(ENDef({
var pos = w.view.keyDownEnInES;
pos.collect{ |b| IO{ t.string_("key down: "++b) } }.enOut;
}));
x.start
)
(
t = StaticText();
w = Window().layout_(HLayout(t)).front;
w.view.keyDownAction_{ |a,b,c| ModKey(c).postln };
w.view.keyUpAction_{ |a,b,c| ModKey(c).postln }
)

Tags |

14

Dec
2013

No Comments

In code

By admin

RCUK project showcase – game controllers and computer vision in the sonic lab

On 14, Dec 2013 | No Comments | In code | By admin

On the 5th of past June a bunch of kids came to the sonic lab of SARC in Belfast for a presentation showcasing some of what we do at SARC. For my presentation I prepared two patches using Unit Lib. One would loop a sample, with a game controller controlling the sample duration and position in the file and panning it in 3D space using VBAP and the 32 speakers of the sonic lab. The other patch used a webcam to track the movement of sphero glowing ball, it would then play a sound whenever the ball “collided” with a virtual object. Also a sound of footsteps would move together with the ball, positioning the sound in the same physical location as the ball. Both patches showcase nicely the interaction of Unit Lib, VBAPLib and FRP from FPLib. The computer vision part was done in processing via the scala language and is available in github. You can see the code below:

/*
A matrix of positions in x,y space each is assigned a random (but fixed) synth, when the ball enters this area it triggers the synth associated with that point.
more interisting sounds
*/
U.loadDef = true
NetAddr.langPort
"netstat -aenp | grep udp".unixCmd;
"java -jar /home/miguel/Development/Processing/detectBallRCUK/out/artifacts/detectBallRCUK_jar/detectBallRCUK.jar".runInTerminal;
(
Udef(\trigBuf,{
var b = \soundFile.asSymbol.kr( [ 0, 1, 0 ] )[0];
var trig = \trig.tr(0);
var audio = PlayBuf.ar( 1, b, 1, trig );
var gate = Peak.kr( trig, Done.kr(audio) );
var out = audio * gate;
UOut.ar(0, out * 0.5 );
},
[ [ \soundFile, nil, BufSndFileSpec(nil) ] ]
);
Udef(\trigBufCont,{
var b = \soundFile.asSymbol.kr( [ 0, 1, 0 ] )[0];
var trig = \trig.tr(0);
var gate = EnvGen.ar( Env.perc(0.01,0.5), trig) > 0.0;
var phasor = Phasor.ar(1, BufRateScale.kr(b) * gate, 0, BufFrames.kr(b));
var out = BufRd.ar(1, b, phasor);
UOut.ar(0, out * 1.5 );
},
[ [ \soundFile, nil, BufSndFileSpec(nil) ] ]
);
)
(
q = ();
q.paths = ("/Volumes/12-13/miguelN/rcuk/grid-sounds/*").pathMatch.scramble;
q.contPath = "/Volumes/12-13/miguelN/rcuk/155858__rutgermuller__footsteps-in-factory-hall-on-wood-and-concrete.wav";
//init vals
q.n = 4;
q.sonicLab = true;
//helper functions
q.makeGrid2D = { |n|
var d = n + 2;
var points = (1..n)/d;
Do(
x<-points;
y<-points;
return RealVector2D[x, y]
)
};
q.squareDiam = 0.705;//0.99 * 0.5.sqrt;
q.makeGrid3D = { |n|
var d = q.squareDiam;
var xs = (0.0,1/(n-1)..1.0).collect{ |x| x.linlin(0.0,1.0, d.neg, d) };
Do(
x<-xs;
y<-xs;
return RealVector3D[x, y, sqrt( 1 - (x**2) - (y**2) ) ].asUnitSpherical360
)
};
q.grid = q[\makeGrid2D].(q.n);
q.points = q[\makeGrid3D].(q.n);
q.detectPoint = { |v, grid, n|
//detection distance
var d = (1 / ( n + 2 ) / 2);
//get first point that is within distance
grid
.collect{ |w,i|
T( v.dist(w) < d, i )
}
.select(_.at1)[0]
//converts nils into None() and values into Some(value)
.asOption
.collect(_.at2)
};
//chains
q.chains = [q.paths, q.points].flopWith{ |p, point|
UChain(
[\trigBuf, [\soundFile, BufSndFile(p)]],
['vbap3D_Simple_Panner', [ 'angles', point, 'lag', 2.0 ] ]
)
};
q.contChain = UChain(
[\trigBufCont, [\soundFile, BufSndFile(q.contPath)]],
['vbap3D_Simple_Panner', [ 'angles', UnitSpherical(0.0,0.0), 'lag', 2.0 ] ]
);
q.session = USession.performList(\new,q.chains++[q.contChain]);
q.en = EventNetwork(
ENDef{
//osc messages are /blob, x, y area
var oscSig = OSCFunc.enIn('/blob');
//extract position
var pos = oscSig.changes.collect{ |x| RealVector2D[x[1], x[2]] };
//this will fire every time the tracked position hits one point
//in the grid
var hits = pos
//store last detection and current detection
//state is T(Option[Integer],Option[Integer])
.inject( T(None(),None()), { |state,x|
T( state.at2, q[\detectPoint].(x, q.grid, q.n) )
})
//triger unless last detection was the same as current detection
.select{ |tup|
(Do(
a <- tup.at1;
b <- tup.at2;
return (a != b)
)).getOrElse( tup.at2.isDefined )
}
//get detected index
//start unit on each hit event
.collect{ |t|
var index = t.at2.get;
IO{ q.chains[index].units[0].set(\trig, 1) }
}.enOut;
//detect if blob is moving
//if current value is farther away from previous value then some ammount
//fire an event
pos
.storePrevious( RealVector2D[0.5,0.5] )
.select{ |t|
t.at1.dist(t.at2) > 0.005
}.collect{ |v|
IO{ q.contChain.units[0].set(\trig, 1) }
}.enOut;
//pan walking sound based on x/y of blob
pos.collect{ |v|
var x = v[0].linlin( 0.0, 1.0, q.squareDiam.neg, q.squareDiam);
var y = v[1].linlin( 0.0, 1.0, q.squareDiam.neg, q.squareDiam);
IO{
q.contChain.units[1].set(
\angles,
RealVector3D[ x, y, sqrt( 1 - (x**2) - (y**2) ) ].asUnitSpherical360
)
}
}.enOut;
//debug
pos.debug("pos");
}, disableOnCmdPeriod: true);
q.session.prepareAndStart;
q.en.start;
)
q.session.gui
q.en.pause

//RUN THIS FIRST
(
Udef(\scratcher, {
var bufnum, rate, loop, startFrame, phasor, start, end, out, out2, delay, pos, tr, tr2;
#bufnum, rate, loop = \soundFile.kr( [ 0, 1, 0 ] );
start = \st.kr(0.0)*BufFrames.kr(bufnum);
phasor = Phasor.ar(
0,
BufRateScale.kr(bufnum),
start,
start + (\lenght.kr(1.0)*SampleRate.ir)
);
out = BufRd.ar(1, bufnum , phasor );
delay = AllpassN.ar(
out * \mixDel.kr,
0.3,
0.25,
6
);
out2 = out + delay;
UOut.ar(0, out2 * \amp.ar(0.0) );
})
.setSpec(\soundFile, BufSndFileSpec(nil) )
.setSpec(\lenght, [0.05, 1.0])
.setSpec(\st, [0.0, 1.0] )
.setSpec(\amp, [0.0,2.0,\lin]);
Udef(\circle, {
var x = LFSaw.kr( \freq.kr(0.5), LinRand(0, 2*pi) ).range(0, 360);
var y = \y.kr(0.0);
UOut.kr(0, [x,y])
})
.setSpec(\y, [-70,70])
.setSpec(\freq, [1/10,1])
)
//RUN THIS FIRST
HIDMKtl.find
//***************
//TEST TEST
MKtl('drgn0').reset
MKtl('zrpl0').reset
MKtl('zrpl0').verbose = true;
MKtl('drgn0').verbose = true;
MKtl('mgwr0').verbose = true;
MKtl('mgwr0').explore
//MAIN CODE
(
q = ();
q.sonicLab = true;
q.desc1 = (
controllerName: 'mgwr0',
elementsLeft: [\joy_L_X, \joy_L_Y, \lfTop_5],
elementsRight: [\joy_R_X, \joy_R_Y, \rfTop_7],
elementsStartStop: [\bt_R_1],
pathLeft: "/Volumes/12-13/miguelN/rcuk/77199__spol__hip-hop-beat-loop7.wav",
pathRight: "/Volumes/12-13/miguelN/rcuk/148675__tr4ck3r__dubstep-beat.wav",
amp:0.25
);
q.desc2 = (
controllerName:'zrpl0',
elementsLeft: [\joy_L_X, \joy_L_Y, \lf],
elementsRight: [\joy_R_X, \joy_R_Y, \rf],
elementsStartStop: [\bt_B],
pathLeft: "/Volumes/12-13/miguelN/rcuk/77199__spol__hip-hop-beat-loop7.wav",
pathRight: "/Volumes/12-13/miguelN/rcuk/148675__tr4ck3r__dubstep-beat.wav",
amp: 0.5
);
q.makeScratchMod = { |elements, controllerName, amp|
UENTModDef({
//get the controller
var ktl = MKtl.basicNew(controllerName);
//get the signals for the controls
var xSig = ktl.elements.at(elements[0]).enIn;
var ySig = ktl.elements.at(elements[1]).enIn;
var but1Sig = ktl.elements.at(elements[2]).enIn;
var but2Sig = ktl.elements.at(elements[3]).enIn;
var amp = but2Sig
.changes //convert to EventSource
.select( _ == 1) //only fire on 1s
.hold(1.0) //convert back into signal
.inject(1.0,{ |st,x| (1.0 - st).abs }) //alternate between 1 and 0
.collect(_*amp);
(
\st : USpecArg( xSig ),
\lenght: USpecArg( ySig ),
\mixDel: USpecArg( but1Sig ),
\amp: USpecArg( amp )
)
});
};
q.makeCircleMod = { |elements, controllerName|
UENModDef({
var ktl = MKtl(controllerName);
var xSig = ktl.elements.at(elements[0]).enIn;
var ySig = ktl.elements.at(elements[1]).enIn;
var invertedY = ySig.collect{ |x| 1.0 - x };
( \freq : USpecArg(xSig), \y: USpecArg(invertedY) )
});
};
q.makeChain = { |elements, path, controllerName, amp|
var mod = q[\makeScratchMod ].(elements, controllerName, amp);
var circleMod = q[\makeCircleMod].(elements, controllerName);
UChain(
[\scratcher,
[ 'soundFile', BufSndFile(path), \amp, amp]
, mod],
[\circle, nil, circleMod],
[
if(q.sonicLab){ 'vbap3D_Simple_Panner' } {\stereoOutput},
['lag', 0.0, \anglesFromBus, true ]
]
);
};
q.makeChains = { |desc|
[
q[\makeChain].(desc.elementsLeft++desc.elementsStartStop, desc.pathLeft, desc.controllerName, desc.amp),
q[\makeChain].(desc.elementsRight++desc.elementsStartStop, desc.pathRight, desc.controllerName, desc.amp)
]
};
q.chains = q[\makeChains].(q.desc1) ++ q[\makeChains].(q.desc2);
q.session = USession(*q.chains);
q.session.prepareAndStart;
q.session.gui
)

Tags |

12

Dec
2013

No Comments

In code

By admin

UnitLib controls with arrays

On 12, Dec 2013 | No Comments | In code | By admin

After fixing a bug earlier this week, it’s now really nice to manipulate controls with arrays in a Udef.  Just use .ukr and specify a spec for the second argument.

Udef(\sines, {
UOut.ar(0, SinOsc.ar( \freqs.ukr(50.collect{ |i| 100 * (i+1) }, \freq ) ).sum / 25 )
})
UChain(\sines, \stereoOutput).gui

unitlib arg array

Tags |

27

Sep
2013

No Comments

In code

By admin

From string to loaded buffer in less then a second

On 27, Sep 2013 | No Comments | In code | By admin

One of the goals of Unit Lib is to make using SuperCollider easier.  The set of abstractions provided by the main class library of SuperCollider are quite low level (Synth, Group, etc).  They require a lot of work to manage the server/client architecture, buses, node ordering, loading buffers, etc. Its possible to simplify these tasks by building higher level abstractions on top of the built in classes. There are many such higher level abstractions, such as JITLib, but the design space is still being explored. With Unit Lib we have quite simplified resource loading, specifically loading buffers, to the point that it has now become a one liner:

(
Udef(\test, {
Out.ar(0, PlayBuf.ar(1, (String.scDir +/+ "sounds/a11wlk01-44_1.aiff").asBufKr ) )
})
)
(
Udef(\test, {
var buf = (String.scDir +/+ "sounds/a11wlk01-44_1.aiff").asBufKr(key: \buf1, numChannels:1, startFrame:0, endFrame:44100 );
Out.ar(0, PlayBuf.ar(1, buf ) )
})
)
UChain(\test).gui
//or
UChain(\test).prepareAndStart

Starting the synth will automatically load the buffer. Nifty isn’t it ? 🙂

Unit Lib is shaping up to be quite nice for live coding or prototyping, it’s very easy to build guis, specify the specs for controls and connect controllers to it.

Tags |