VirtualRoom
a convenience class for easy creation and management of virtual audio environments
The rendering is done in a class method (ar) while multiple instances of a VirtualRoom
may be defined to be able to switch between them (its also possible to play them
simultaniously, if that makes sense to some application)
Note: For better performance the following server options should be set before booting the server:
s.options.blockSize_(128) // sets kr blocksize; default = 64
s.options.memSize_(8192 * 16); // more memory for delay lines
p = ProxySpace.push;
First thing is to set the path to the full Kemar HRTFs (dowloadable from http://sound.media.mit.edu/KEMAR/full.tar.Z). Default is "KemarHRTF/" (located in the SC3 application folder)
// set the Kemar path
VirtualRoom.kemarPath = "KemarHRTF/";
Then init the class - that will switch on the rendering (and consumes CPU) and route the output to the standard output with .play
// create instance
v = VirtualRoom.new;
// initialise rendering
v.init;
// set the room properties (reverberation time and gain,
// hf damping on reverb and early reflections gain
(
v.revTime = 0.1;
v.revGain = 0.1;
v.hfDamping = 0.5;
v.refGain = 0.8;
)
Note:
the coordinate system is given according to the listener's head: x-axis (nose), y-axis (left-ear) and z-axis (vertex) and Rooms are defined by the origin and width/depth/height
// a room 8m wide (y), 5m deep(x) and 5m high(z) - nose is always along x
v.room = [0, 0, 0, 5, 8, 5];
// make it play to standard out
v.out.play;
// listener is a NodeProxy.kr, set the source
v.listener.source = { [ MouseY.kr(5,0), MouseX.kr(8,0), 2.5, 0] };
// more static
v.listener.source = { |x=2.5, y=4, z=2.5, o=0| [ x, y, z, o] };
// adding sources to the scene
(
~noisy = { EnvGen.kr(Env.adsr, Impulse.kr(3)) * PinkNoise.ar(1) };
~sinus = { EnvGen.kr(Env.adsr, Impulse.kr(3)) * SinOsc.ar(440,0.8) };
~dust = { Dust.ar(400) };
)
v.addSource( ~noisy, "test1", [1, 2, 2.5]);
v.addSource( ~sinus, "test2", [4, 7, 2.5]);
v.addSource( ~dust, "test3", [2, 5, 2.5]);
// change the source position
v.sources["test1"].setn(\xpos, [2.5, 3, 2.5]); // right
v.sources["test1"].setn(\xpos, [2.5, 5, 2.5]); // left
v.sources["test1"].setn(\xpos, [2, 4, 2.5]); // back
v.sources["test1"].setn(\xpos, [3, 4, 2.5]); // front
// change the listener position
v.listener.setn(\x, [3, 5, 2.5, 0]) // source to the right
v.listener.setn(\x, [3, 5, 2.5, pi/2]) // turn, source to the front
// remove the sources
v.removeSource("test1");
v.removeSource("test2");
v.removeSource("test3");
// the light version of addSource is used if a lot of sources are needed
// consumes 63% CPU on iBook G4 1Ghz
(
x = (1..6).collect ({ |i|
var test, testpos;
test = NodeProxy.audio(s,1);
test.source = { EnvGen.kr(Env.adsr, Impulse.kr(3)) * SinOsc.ar(440.rand,0.6) };
v.addSourceLight(test, i.asString, [1 , 4, 2.5] );
test;
});
)
// remove the sources and free the node proxies
6.do ({ |i| v.removeSource(i.asString); x[i].free; });
// adjust the room properties as you like
v.gui;
// Create a second virtual room
w = VirtualRoom.new;
// initialise rendering
w.init;
// set the room properties (reverberation time and gain,
// hf damping on reverb and early reflections gain
(
w.revTime = 0;
w.revGain = 0;
w.hfDamping = 1;
w.refGain = 0.9;
)
// same listener, same room dimensions
w.listener.source = { |x=2.5, y=4, z=2.5, o=0| [ x, y, z, o] };
w.room = [0, 0, 0, 5, 8, 5];
// play it on channels 2, 3
w.out.playN([2,3]);
~dust = { Dust.ar(400) };
w.addSource( ~dust, "test3", [3, 3, 2.5]);
w.removeSource("test3");
// free the virtual room
v.free;
w.free;
// remove the ProxySpace
p.pop;