title:: SynthDef and NRT examples for ATK
summary:: Introduction to the Ambisonic Toolkit
categories:: Libraries>Ambisonic Toolkit
keyword::Atk

section:: Usage with SynthDef and Synth

For a more in-depth overview of the paradigm of ATK and a complete presentation of its capabilities, see link::Guides/Intro-to-the-ATK::. These examples show a limited set of the ATK's functionality, but illustrate how to work with the library when using SynthDefs, a single decoder that reads the Ambisonic signal through audio bus routing as well as examples for NRT processing.

code::

// ATK SynthDef / Synth examples
Server.default = s = Server.internal.boot;


/* ENCODERS */
/* FoaPanB - similar to PanB - theta and phi can change dynamically */

SynthDef(\foaEncode1, {
	var src, theta, phi, foa, decoderMatrix, out;
	src = PinkNoise.ar(-6.dbamp);
	theta = MouseX.kr(-pi, pi);
	phi = 0;
	// Encode into our foa signal
	foa = FoaPanB.ar(src, theta, phi);
	// Cardioids at 131 deg
	decoderMatrix = FoaDecoderMatrix.newStereo(131/2 * pi/180, 0.5);
	out = FoaDecode.ar(foa, decoderMatrix);
 	Out.ar(0, out);
}).add;

a = Synth(\foaEncode1);
a.free;

/* Encoding signals as plane waves that are then manipulated through transforms using the FoaTransform UGen wrapper */

SynthDef(\foaEncode2, {
	var src, angle, azim, encoderMatrix, foa, decoderMatrix, out;
	src = PinkNoise.ar(-6.dbamp);
	// for the 'push' transform later
	// angle ---> top           = push to plane wave
	//            bottom        = omni-directional
	angle = MouseY.kr(pi/2, 0);
	// azimuth -> hard left     = back
	//            centre        = centre
	//            hard right    = back
	azim = MouseX.kr(pi, -pi);
	// a matrix for an omni image
	encoderMatrix = FoaEncoderMatrix.newOmni;
	// Encode into our foa signal
	foa = FoaEncode.ar(src, encoderMatrix);
	// transform for angle and azim
	foa = FoaTransform.ar(foa, 'pushX', angle);
//	foa = FoaPushX.ar(foa, angle);
	// Cardioids at 131 deg
	decoderMatrix = FoaDecoderMatrix.newStereo(131/2 * pi/180, 0.5);
	out = FoaDecode.ar(foa, decoderMatrix);
 	Out.ar(0, out);
}).add;

a = Synth(\foaEncode2);
a.free;

/* encoding a plane wave, and routing to a single decoder: */

a = Bus.audio(s, 4); // allocate four channels for routing

SynthDef(\foaEncode3, {arg outBus, duration = 0.05, theta, phi;
    var src, foa;
    src = PinkNoise.ar(-6.dbamp) *
        EnvGen.kr(
            Env([0, 1, 0], [0.5, 0.5], \sin),
            timeScale: duration,
            doneAction: 2);
    // Encode into our foa signal
    foa = FoaPanB.ar(src, theta, phi);
     Out.ar(outBus, foa);
}).add;

SynthDef(\foaDecode, {arg inBus;
	var foa, decoderMatrix, out;
	foa = In.ar(inBus, 4);
	// Cardioids at 131 deg
	decoderMatrix = FoaDecoderMatrix.newStereo(131/2 * pi/180, 0.5);
	out = FoaDecode.ar(foa, decoderMatrix);
 	Out.ar(0, out);
}).add;

// start the decoder, reading bus 'a' at the \tail
b = Synth(\foaDecode, [\inBus, a], 1, \addToTail);

Routine.run({
	20.do({
		Synth(\foaEncode3, [\outBus, a, \theta, pi.rand2, \phi, 0]);
		0.1.wait;
	})
});

b.free; // free the decoder
a.free; // free the audio bus


/* using the kernel encoder and decoder methods */

// decoding B-format file through an HRTF decoder
(
var cond;
s.boot;
cond = Condition.new;
s.waitForBoot({
	Routine.run({
		~decoder = FoaDecoderKernel.newListen(1013);
		~sndbuf = Buffer.read(s, Atk.userSoundsDir ++ "/b-format/Pampin-On_Space.wav");
		s.sync(cond);
		SynthDef(\kernelDecode, {arg buffer;
			var out, src;
			src = PlayBuf.ar(4, buffer);
			out = FoaDecode.ar(src, ~decoder);
			Out.ar(0, out);
		}).add;
		s.sync(cond);
		Synth(\kernelDecode, [\buffer, ~sndbuf]);
		// press command period when done
		CmdPeriod.add({~decoder.free; ~sndbuf.free});
	})
})
)

// encoding a UHJ file to B-format, then decoding through an HRTF decoder
(
var cond;
s.boot;
cond = Condition.new;
s.waitForBoot({
	Routine.run({
		~encoder = FoaEncoderKernel.newUHJ;
		~decoder = FoaDecoderKernel.newListen(1013);
		~sndbuf = Buffer.read(s, Atk.userSoundsDir ++ "/uhj/Palestrina-O_Bone.wav");
		s.sync(cond);
		SynthDef(\kernelEncodeDecode, {arg buffer;
			var out, src, encode;
			src = PlayBuf.ar(2, buffer);
			encode = FoaEncode.ar(src, ~encoder);
			out = FoaDecode.ar(encode, ~decoder);
			Out.ar(0, out);
		}).add;
		s.sync(cond);
		Synth(\kernelEncodeDecode, [\buffer, ~sndbuf]);
		// press command period when done
		CmdPeriod.add({~encoder.free; ~decoder.free; ~sndbuf.free});
	})
})
)


// examples with Score - trickier because of the need for bundles, etc.
// since the Kernels also pass in hardcoded buffer IDs, we need to make sure those
// are referenced as well

(
var cond, score, sndfile, sndbufBundle, sndbufDuration, noteBundle;
score = Score.new;
s.boot;
cond = Condition.new;
// get some info about the soundfile we are decoding for the Score requirements
sndfile = SoundFile.new;
sndfile.openRead(Atk.userSoundsDir ++ "/uhj/Palestrina-O_Bone.wav");
sndfile.close;
// we still need to boot the Server for now to allocate ids for the kernels
s.waitForBoot({
	sndbufBundle = s.makeBundle(false, {
		~sndbuf = Buffer.read(s, sndfile.path);
		});
	sndbufBundle = sndbufBundle.addFirst(0.0);
	score.add(sndbufBundle);
	~encoder = FoaEncoderKernel.newUHJ;
	~decoder = FoaDecoderKernel.newListen(1013);
	s.sync(cond);
	// the FoaEncoderKernel and FoaDecoderKernel classes will return
	// bundles that can be added to Score
	score.add(~encoder.kernelBundle);
	score.add(~decoder.kernelBundle);
	s.sync(cond);
	SynthDef(\kernelEncodeDecode, {arg buffer;
		var out, src, encode;
		src = PlayBuf.ar(2, buffer);
		encode = FoaEncode.ar(src, ~encoder);
		out = FoaDecode.ar(encode, ~decoder);
		Out.ar(0, out);
	}).load(s);
	s.sync(cond);
	noteBundle = s.makeBundle(false, {Synth(\kernelEncodeDecode, [\buffer, ~sndbuf])});
	noteBundle = noteBundle.addFirst(1.0);
	score.add(noteBundle);
	score.add([2.0 + sndfile.duration, [0]]  ); // add the needed dummy command to stop NRT
	score.recordNRT("/tmp/trashme", "~/Desktop/myDecode.wav".standardizePath,
		sampleRate: 44100,
		headerFormat: "WAVE",
		sampleFormat: "float",
		options: ServerOptions.new.numOutputBusChannels_(2)
		);
	~encoder.free;
	~decoder.free;
})
)

// using the Ctk quark.

(
var cond, score, sndbuf, kernelInfo, sd, encoder, decoder;
score = CtkScore.new;
s.boot;

// we still need to boot the Server for now to allocate ids for the kernels /* FIX THIS */
s.waitForBoot({
	sndbuf = CtkBuffer.playbuf(Atk.userSoundsDir ++ "/uhj/Palestrina-O_Bone.wav");
	score.add(sndbuf);
	encoder = FoaEncoderKernel.newUHJ;
	decoder = FoaDecoderKernel.newListen(1013);
	s.sync(cond);
	// the FoaEncoderKernel and FoaDecoderKernel classes will return info that can be
	// used to make CtkBuffers for the CtkScore. An array of [path, bufnum, channel] is
	// returned that will line up with the kernel info that the FoaEncode and FoaDecode
	// classes expect
	kernelInfo = encoder.kernelInfo ++ decoder.kernelInfo;
	kernelInfo.do({arg thisInfo;
		var path, bufnum, channel, buf;
		#path, bufnum, channel = thisInfo;
		buf = CtkBuffer(path, bufnum: bufnum, channels: channel);
		score.add(buf);
	});
	sd = CtkSynthDef(\kernelEncodeDecode, {arg buffer;
		var out, src, encode;
		src = PlayBuf.ar(2, buffer);
		encode = FoaEncode.ar(src, encoder);
		out = FoaDecode.ar(encode, decoder);
		Out.ar(0, out);
	});
	score.add(sd.note(1.0, sndbuf.duration).buffer_(sndbuf));
	score.write("~/Desktop/myDecode.wav".standardizePath,
		sampleRate: 44100,
		headerFormat: "WAVE",
		sampleFormat: "float",
		options: ServerOptions.new.numOutputBusChannels_(2)
		);
	encoder.free;
	decoder.free;
})
)

::