• jon@schemawound.com
Supercollider
The Tunnel

The Tunnel

Today Waxen Wings has released my track ‘The Tunnel’ on the album ’Far Far Away’. The track was produced entirely using Supercollider.  I know one of the issues many people have with systems like Supercollider is figuring out a workflow.  I figured I would take the time to break down how I created this song in hopes it helps someone else.

Since the theme of the compilation was how difficult it is to hear people from far away I knew I needed to start with some reverberation.  I then played with various combinations of oscillators and distortion until I came up with the following loop that produced quite a lot of variation on it’s own.

({FreeVerb.ar(((((SinOsc.ar(7) * SinOsc.ar(LFSaw.ar(0.1, 0, 0.5, 0.5) * 100) * SinOsc.ar(LFSaw.ar(0.25, 0, 0.5, 0.5) * 600)) * 6).distort * SinOsc.ar(500)) * 6).distort * 0.5 + SinOsc.ar(30, 0, 0.5), SinOsc.ar(0.014) * 0.5 + 0.5, 1, 0.5, 1)!2;}).play;

I then decided to work in reverse and start pulling pieces out into well named variables to see what I actually had.  I put the results into a synthdef.

(//----------Tunnel SynthDef----------
	SynthDef(TheTunnel, {
		arg
			out = 0,			amp = 1,
			lfo1Speed = 0.1,	lfo1Depth = 100,
			lfo2Speed = 0.25,	lfo2Depth = 600,
			osc1Freq = 7,
			loSineFreq = 30,	loSineAmp = 0.5,
			clip1Amp = 6,		clipModFreq = 500,		clip2Amp = 6,
			verbLfoSpeed = 0.014, verbLfoDepth = 1, verbRoom = 1, verbDamp = 0.5
		;
		var loSine = SinOsc.ar(loSineFreq, 0, loSineAmp);
		//Osc1
		var osc1 = SinOsc.ar(osc1Freq);
		//Osc2
		var lfo1 = LFSaw.ar(lfo1Speed, 0, 0.5, 0.5); //Make positve only
		var osc2 = SinOsc.ar(lfo1 * lfo1Depth);
		//Osc3
		var lfo2 = LFSaw.ar(lfo2Speed, 0, 0.5, 0.5); //Make positve only
		var osc3 = SinOsc.ar(lfo2 * lfo2Depth);
		//Sum and Clip
		var clip1 = ((osc1 * osc2 * osc3) * clip1Amp).distort * SinOsc.ar(clipModFreq);
		//Output
		var output = (clip1 * clip2Amp).distort * 0.5 + loSine;
		var verbLfo = (SinOsc.ar(verbLfoSpeed) * 0.5 + 0.5) * verbLfoDepth;
		var verb1 = FreeVerb.ar(output, verbLfo, verbRoom, verbDamp, amp);
		Out.ar(out, verb1!2);
	}).store; 
)

This can be played as follows:

~tunnel = Synth(TheTunnel);

Next I revised the synthdef to include lag time for smooth transition between input values. I built knobs to control each parameter of the synth. I created a preset system to store good sets of values. In order to make finding interesting sounding patches easier I eventually added a random button and a button to print all current knob values to the post window. I also added midi responders to allow myself to ‘play’ the presets using a midi keyboard.

(//----------Tunnel SynthDef----------
	SynthDef(TheTunnel, {
		|
			out = 0,								amp = 1,							lfo1Speed = 0.1,				lfo1Depth = 100,			lfo2Speed = 0.25,		
			lfo2Depth = 600,			osc1Freq = 7,				loSineFreq = 30,				loSineAmp = 0.5,			clip1Amp = 6,		
			clipModFreq = 500,		clip2Amp = 6,				verbLfoSpeed = 0.014, 	verbLfoDepth = 1, 		verbRoom = 1, 
			verbDamp = 0.5
		|
		var loSine = SinOsc.ar(loSineFreq, 0, loSineAmp);
		//Osc1
		var osc1 = SinOsc.ar(osc1Freq);
		//Osc2
		var lfo1 = LFSaw.ar(lfo1Speed, 0, 0.5, 0.5); //Make positve only
		var osc2 = SinOsc.ar(lfo1 * lfo1Depth);
		//Osc3
		var lfo2 = LFSaw.ar(lfo2Speed, 0, 0.5, 0.5); //Make positve only
		var osc3 = SinOsc.ar(lfo2 * lfo2Depth);
		//Sum and Clip
		var clip1 = ((osc1 * osc2 * osc3) * clip1Amp).distort * SinOsc.ar(clipModFreq);
		//Output
		var output = (clip1 * clip2Amp).distort * 0.5 + loSine;
		var verbLfo = (SinOsc.ar(verbLfoSpeed) * 0.5 + 0.5) * verbLfoDepth;
		var verb1 = FreeVerb.ar(output, verbLfo, verbRoom, verbDamp, amp);
		Out.ar(out, verb1!2);
	}, [0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]).store; 
)

(//----------Synth Setup----------
	
	//----Start Synth----
	~tunnel = Synth(TheTunnel);
	~presets = 
		[
			//	amp,			lfo1Speed,	lfo1Depth,	lfo2Speed,	lfo2Depth,	osc1Freq,	loSineFreq,	loSineAmp,	clip1Amp,	clipModFreq,	clip2Amp,	verbLfoSpeed,	verbLfoDepth,	verbRoom,	verbDamp
			[		1, 				0.1,				100, 			0.25, 			600, 			7, 					30, 					0.5,				6, 					500, 					6, 					0.014,					1, 							1, 					0.5				],
			[		1, 				030.756,		628.79, 		0.25, 			600, 			35.412, 		11.8, 				0.5,				6, 					500, 					6, 					0.151, 				0.204, 				1, 					1					],
			[		2, 				048.345,		446.41, 		45.021,		694.56, 		14.97, 		42.53, 			0.14,			1.833, 		168.49,				1, 					0.014, 				0.314, 				1, 					0					],
			[		1.5, 		0.507, 		1000, 			6.285, 		317.08, 		34.579, 		31.77, 			0.5, 			3.672, 		44.59, 				3.492, 		0.316, 				0.49, 					0.461, 		1					],
			[		1.5, 		0.507, 		288.03,		0.67, 			317.08, 		10.449, 		88.06, 			0.37, 			2.557, 		44.59, 				3.492, 		0.155, 				1, 							1, 					1					],
			[		0.575,		100, 			1000, 			96.903, 		1000, 			50, 				88.06, 			1.22, 			6, 					127.37, 				6, 					0.167, 				0.493, 				1, 					1					],
			[		1.2, 		1, 					101.3, 		1.5, 			74.79, 		11.691, 		5.2, 				0, 					3.225, 		49.8, 					2.754, 		0.51, 					0.494, 				0.784, 		0.497		],
			[		2.852,		0.001, 		80.79, 		100, 			306.59, 		11.691, 		5.2, 				0, 					3.225, 		49.8, 					3.734, 		0.273, 				0.656, 				0.784, 		0.889		],
			[		1.116,		71.939, 		631.92, 		72.476,		427.99, 		17.475, 		96.68, 			0.56, 			4.298, 		496.15, 				5.649, 		0.804, 				0.694, 				0.303, 		0.461		],
			[		0.979,		34.043, 		650.05, 		29.176,		480.98, 		30.836, 		66.66, 			1.13, 			2.401, 		205.91, 				2.664, 		0.173, 				0.778, 				0.517, 		0.88			],
			[		2.206,		22.558, 		217.5,			91.033,		520.13,		3.484,			84.09,				1.59, 			2.731,			259.07, 				5.919,			0.571, 				0.988, 				0.133, 		0.0				]
		];
)

(//----------Tunnel GUI----------
	
	//Global Variables
	var layout;
	var knobSize = 60@80;
	var buttonSize = 20@20;
	var titleFont = Font( "Times-Bold", 25 );

	//Knob Variables
	var ampControlSpec, ampKnob;
	var lfoSpeedControlSpec, lfoDepthControlSpec, lfo1SpeedKnob, lfo1DepthKnob, lfo2SpeedKnob, lfo2DepthKnob;
	var osc1FreqControlSpec, loSineFreqControlSpec, loSineAmpControlSpec, osc1FreqKnob, loSineFreqKnob, loSineAmpKnob;
	var clipAmpControlSpec, clipModControlSpec, clip1AmpKnob, clipModFreqKnob, clip2AmpKnob;
	var verbControlSpec, verbLfoSpeedKnob, verbLfoDepthKnob, verbRoomKnob, verbDampKnob;

	//Button Variables
	var callPreset, presetButton1, presetButton2, presetButton3, presetButton4, presetButton5, presetButton6, presetButton7, presetButton8, presetButton9, presetButton10, 
			presetButton11, presetButtonRandom, presetButtonShow;

	//Window Setup
	w = Window( "The Tunnel", Rect( 128, 230, 280, 620)); 
	w.view.decorator = layout = FlowLayout( w.view.bounds ); 
	w.front;

	//Master Knobs
	StaticText(w, 150@20).string_("Master").font = titleFont;	layout.nextLine;
	ampControlSpec = ControlSpec(0, 10, lin, 0.001);
	ampKnob = EZKnob(w, knobSize, "Amp",	ampControlSpec, action: {|inValue| ~tunnel.set(amp, inValue.value);}, layout:vert2);
	layout.nextLine;

	//LFO Knobs
	lfoDepthControlSpec = ControlSpec(1, 1000, lin, 0.01);
	lfoSpeedControlSpec = ControlSpec(0.001, 100, lin, 0.001);
	StaticText(w, 150@20).string_("LFO").font = titleFont;	layout.nextLine;
	lfo1SpeedKnob = EZKnob(w, knobSize, "Lfo1Speed",	lfoSpeedControlSpec, action: {|inValue| ~tunnel.set(lfo1Speed, inValue.value);}, layout:vert2);
	lfo1DepthKnob = EZKnob(w, knobSize, "Lfo1Depth",	lfoDepthControlSpec, action: {|inValue| ~tunnel.set(lfo1Depth, inValue.value);}, layout:vert2);
	lfo2SpeedKnob = EZKnob(w, knobSize, "Lfo2Speed",	lfoSpeedControlSpec, action: {|inValue| ~tunnel.set(lfo2Speed, inValue.value);}, layout:vert2);
	lfo2DepthKnob = EZKnob(w, knobSize, "Lfo2Depth",	lfoDepthControlSpec, action: {|inValue| ~tunnel.set(lfo2Depth, inValue.value);}, layout:vert2);
	layout.nextLine;

	//OSC Knobs
	osc1FreqControlSpec = ControlSpec(0.5, 50, lin, 0.001);
	loSineFreqControlSpec = ControlSpec(1, 100, lin, 0.01);
	loSineAmpControlSpec = ControlSpec(0.0, 2, lin, 0.01);
	StaticText(w, 150@20).string_("Osc").font = titleFont;	layout.nextLine;
	osc1FreqKnob = EZKnob(w, knobSize, "osc1Freq",	osc1FreqControlSpec, action: {|inValue| ~tunnel.set(osc1Freq, inValue.value);}, layout:vert2);
	loSineFreqKnob = EZKnob(w, knobSize, "loSineFreq",	loSineFreqControlSpec, action: {|inValue| ~tunnel.set(loSineFreq, inValue.value);}, layout:vert2);
	loSineAmpKnob = EZKnob(w, knobSize, "loSineAmp",	loSineAmpControlSpec, action: {|inValue| ~tunnel.set(loSineAmp, inValue.value);}, layout:vert2);
	layout.nextLine;

	//Clip Knobs
	clipAmpControlSpec = ControlSpec(1, 6, lin, 0.001);
	clipModControlSpec = ControlSpec(10, 500, lin, 0.01);
	StaticText(w, 150@20).string_("Clip").font = titleFont;	layout.nextLine;
	clip1AmpKnob = EZKnob(w, knobSize, "clip1Amp",	clipAmpControlSpec, action: {|inValue| ~tunnel.set(clip1Amp, inValue.value);}, layout:vert2);
	clipModFreqKnob = EZKnob(w, knobSize, "clipModFreq",	clipModControlSpec, action: {|inValue| ~tunnel.set(clipModFreq, inValue.value);}, layout:vert2);
	clip2AmpKnob = EZKnob(w, knobSize, "clip2Amp", clipAmpControlSpec, action: {|inValue| ~tunnel.set(clip2Amp, inValue.value);}, layout:vert2);
		
	//Verb Knobs
	verbControlSpec = ControlSpec(0, 1, lin, 0.0001);
	StaticText(w, 150@20).string_("Reverb").font = titleFont;	layout.nextLine;
	verbLfoSpeedKnob = EZKnob(w, knobSize, "lfoSpeed",	verbControlSpec, action: {|inValue| ~tunnel.set(verbLfoSpeed, inValue.value);}, layout:vert2);
	verbLfoDepthKnob = EZKnob(w, knobSize, "lfoDepth",	verbControlSpec, action: {|inValue| ~tunnel.set(verbLfoDepth, inValue.value);}, layout:vert2);
	verbRoomKnob = EZKnob(w, knobSize, "verbRoom",	verbControlSpec, action: {|inValue| ~tunnel.set(verbRoom, inValue.value);}, layout:vert2);
	verbDampKnob = EZKnob(w, knobSize, "verbDamp",	verbControlSpec, action: {|inValue| ~tunnel.set(verbDamp, inValue.value);}, layout:vert2);
	layout.nextLine;

	//Preset Buttons
	callPreset = { | presetNum |
			ampKnob.valueAction 						= ~presets[presetNum][0];
			lfo1SpeedKnob.valueAction 		= ~presets[presetNum][1];
			lfo1DepthKnob.valueAction			= ~presets[presetNum][2];
			lfo2SpeedKnob.valueAction 		= ~presets[presetNum][3];
			lfo2DepthKnob.valueAction			= ~presets[presetNum][4];
			osc1FreqKnob.valueAction 			= ~presets[presetNum][5];
			loSineFreqKnob.valueAction 		= ~presets[presetNum][6];
			loSineAmpKnob.valueAction 		= ~presets[presetNum][7];
			clip1AmpKnob.valueAction 			= ~presets[presetNum][8];
			clipModFreqKnob.valueAction		= ~presets[presetNum][9];
			clip2AmpKnob.valueAction 			= ~presets[presetNum][10];
			verbLfoSpeedKnob.valueAction	= ~presets[presetNum][11];
			verbLfoDepthKnob.valueAction	= ~presets[presetNum][12];
			verbRoomKnob.valueAction 			= ~presets[presetNum][13];
			verbDampKnob.valueAction 			= ~presets[presetNum][14];
	};

	StaticText(w, 150@20).string_("Presets").font = titleFont;	layout.nextLine;
	presetButton1 			= (Button(w, buttonSize).states = [["1", Color.black]]).action = {callPreset.value(0);};
	presetButton2 			= (Button(w, buttonSize).states = [["2", Color.black]]).action = {callPreset.value(1);};
	presetButton3 			= (Button(w, buttonSize).states = [["3", Color.black]]).action = {callPreset.value(2);};
	presetButton4 			= (Button(w, buttonSize).states = [["4", Color.black]]).action = {callPreset.value(3);};
	presetButton5 			= (Button(w, buttonSize).states = [["5", Color.black]]).action = {callPreset.value(4);};
	presetButton6 			= (Button(w, buttonSize).states = [["6", Color.black]]).action = {callPreset.value(5);};
	presetButton7 			= (Button(w, buttonSize).states = [["7", Color.black]]).action = {callPreset.value(6);};
	presetButton8 			= (Button(w, buttonSize).states = [["8", Color.black]]).action = {callPreset.value(7);};
	presetButton9 			= (Button(w, buttonSize).states = [["9", Color.black]]).action = {callPreset.value(8);};
	presetButton10 			= (Button(w, buttonSize).states = [["10", Color.black]]).action = {callPreset.value(9);};
	presetButton11 			= (Button(w, buttonSize).states = [["11", Color.black]]).action = {callPreset.value(10);};
	presetButtonRandom	= (Button(w, buttonSize).states = [["R", Color.black]]).action = {
			ampKnob.valueAction 						= ampControlSpec.map(1.0.rand);												lfo1SpeedKnob.valueAction 		= lfoSpeedControlSpec.map(1.0.rand);
			lfo1DepthKnob.valueAction 		= lfoDepthControlSpec.map(1.0.rand);									lfo2SpeedKnob.valueAction 		= lfoSpeedControlSpec.map(1.0.rand);
			lfo2DepthKnob.valueAction 		= lfoDepthControlSpec.map(1.0.rand);									osc1FreqKnob.valueAction 			= osc1FreqControlSpec.map(1.0.rand);
			loSineFreqKnob.valueAction 		= loSineFreqControlSpec.map(1.0.rand);								loSineAmpKnob.valueAction 		= loSineAmpControlSpec.map(1.0.rand);
			clip1AmpKnob.valueAction 			= clipAmpControlSpec.map(1.0.rand);									clipModFreqKnob.valueAction		= clipModControlSpec.map(1.0.rand);
			clip2AmpKnob.valueAction 			= clipAmpControlSpec.map(1.0.rand);									verbLfoSpeedKnob.valueAction	= verbControlSpec.map(1.0.rand);
			verbLfoDepthKnob.valueAction 	= verbControlSpec.map(1.0.rand);											verbRoomKnob.valueAction 			= verbControlSpec.map(1.0.rand);
			verbDampKnob.valueAction 			= verbControlSpec.map(1.0.rand);											};
	presetButtonShow 		= (Button(w, buttonSize).states = [["S", Color.black]]).action = {
			~tunnel.get(amp, 						{|val| ("amp:" ++ val.value).postln;});
			~tunnel.get(lfo1Speed,			{|val| ("lfo1Speed:" ++ val.value).postln;});
			~tunnel.get(lfo1Depth,			{|val| ("lfo1Depth:" ++ val.value).postln;});
			~tunnel.get(lfo2Speed,			{|val| ("lfo2Speed:" ++ val.value).postln;});
			~tunnel.get(lfo2Depth,			{|val| ("lfo2Depth:" ++ val.value).postln;});
			~tunnel.get(osc1Freq,				{|val| ("osc1Freq:" ++ val.value).postln;});
			~tunnel.get(loSineFreq,			{|val| ("loSineFreq:" ++ val.value).postln;});
			~tunnel.get(loSineAmp,			{|val| ("loSineAmp:" ++ val.value).postln;});
			~tunnel.get(clip1Amp,				{|val| ("clip1Amp:" ++ val.value).postln;});
			~tunnel.get(clipModFreq,		{|val| ("clipModFreq:" ++ val.value).postln;});
			~tunnel.get(clip2Amp,				{|val| ("clip2Amp:" ++ val.value).postln;});
			~tunnel.get(verbLfoSpeed,	{|val| ("verbLfoSpeed:" ++ val.value).postln;});
			~tunnel.get(verbLfoDepth,	{|val| ("verbLfoDepth:" ++ val.value).postln;});
			~tunnel.get(verbRoom,				{|val| ("verbRoom:" ++ val.value).postln;});
			~tunnel.get(verbDamp,				{|val| ("verbDamp:" ++ val.value).postln;});
	};

	//Set all knobs equal to current bus values so that if the gui is recreated it matches the playing synth
	~tunnel.get(amp, 						{|val| AppClock.sched(0.0,{ ampKnob.value = val.value;});});
	~tunnel.get(lfo1Speed,			{|val| AppClock.sched(0.0,{ lfo1SpeedKnob.value = val.value;});});
	~tunnel.get(lfo1Depth,			{|val| AppClock.sched(0.0,{ lfo1DepthKnob.value = val.value;});});
	~tunnel.get(lfo2Speed,			{|val| AppClock.sched(0.0,{ lfo2SpeedKnob.value = val.value;});});
	~tunnel.get(lfo2Depth,			{|val| AppClock.sched(0.0,{ lfo2DepthKnob.value = val.value;});});
	~tunnel.get(osc1Freq,				{|val| AppClock.sched(0.0,{ osc1FreqKnob.value = val.value;});});
	~tunnel.get(loSineFreq,			{|val| AppClock.sched(0.0,{ loSineFreqKnob.value = val.value;});});
	~tunnel.get(loSineAmp,			{|val| AppClock.sched(0.0,{ loSineAmpKnob.value = val.value;});});
	~tunnel.get(clip1Amp,				{|val| AppClock.sched(0.0,{ clip1AmpKnob.value = val.value;});});
	~tunnel.get(clipModFreq,		{|val| AppClock.sched(0.0,{ clipModFreqKnob.value = val.value;});});
	~tunnel.get(clip2Amp,				{|val| AppClock.sched(0.0,{ clip2AmpKnob.value = val.value;});});
	~tunnel.get(verbLfoSpeed,	{|val| AppClock.sched(0.0,{ verbLfoSpeedKnob.value = val.value;});});
	~tunnel.get(verbLfoDepth,	{|val| AppClock.sched(0.0,{ verbLfoDepthKnob.value = val.value;});});
	~tunnel.get(verbRoom,				{|val| AppClock.sched(0.0,{ verbRoomKnob.value = val.value;});});
	~tunnel.get(verbDamp,				{|val| AppClock.sched(0.0,{ verbDampKnob.value = val.value;});});

//----------Tunnel MIDI----------
	//Init MIDI
	MIDIIn.connect;
	MIDIIn.noteOn = nil;
	NoteOnResponder.removeAll;
	NoteOnResponder({~presetButton1.doAction;}, nil, nil, 64, nil); 
	NoteOnResponder({~presetButton2.doAction;}, nil, nil, 65, nil); 
	NoteOnResponder({~presetButton3.doAction;}, nil, nil, 66, nil); 
	NoteOnResponder({~presetButton4.doAction;}, nil, nil, 67, nil); 
	NoteOnResponder({~presetButton5.doAction;}, nil, nil, 68, nil); 
	NoteOnResponder({~presetButton6.doAction;}, nil, nil, 69, nil); 
	NoteOnResponder({~presetButton7.doAction;}, nil, nil, 70, nil); 
	NoteOnResponder({~presetButton8.doAction;}, nil, nil, 71, nil); 
	NoteOnResponder({~presetButton9.doAction;}, nil, nil, 72, nil);
	NoteOnResponder({~presetButton10.doAction;}, nil, nil, 73, nil);
	NoteOnResponder({~presetButton11.doAction;}, nil, nil, 74, nil);
)                                 


While the knobs were fun to experiment with I eventually realized that individual knob moves were not going to get me to where I needed to go with this track. I scrapped the midi input and scrapped all the GUI code except for the preset system. I increased the lag times on the synthdef to 30 seconds so that each time I chose a preset it would morph from one preset to the other. Then I made a task that posted the elapsed time since the synth started running to the post window. I used this as my guide and made notes of which presets sounded good merging into each other and how long I wanted to let each hold. In the end I only ended up using a handfull of the presets. My final ‘performance’ code starts the recording, executes the patch changes and then stops the recording. If you attempt to run this code you will need to change the directory referance to a valid one on your system.

(//----------Tunnel SynthDef----------
	SynthDef(TheTunnel, {
		|
			out = 0,					amp = 1,					lfo1Speed = 0.1,				lfo1Depth = 100,			lfo2Speed = 0.25,		
			lfo2Depth = 600,			osc1Freq = 7,				loSineFreq = 30,				loSineAmp = 0.5,			clip1Amp = 6,		
			clipModFreq = 500,		clip2Amp = 6,				verbLfoSpeed = 0.014, 		verbLfoDepth = 1, 		verbRoom = 1, 
			verbDamp = 0.5
		|
		var loSine = SinOsc.ar(loSineFreq, 0, loSineAmp);
		//Osc1
		var osc1 = SinOsc.ar(osc1Freq);
		//Osc2
		var lfo1 = LFSaw.ar(lfo1Speed, 0, 0.5, 0.5); //Make positve only
		var osc2 = SinOsc.ar(lfo1 * lfo1Depth);
		//Osc3
		var lfo2 = LFSaw.ar(lfo2Speed, 0, 0.5, 0.5); //Make positve only
		var osc3 = SinOsc.ar(lfo2 * lfo2Depth);
		//Sum and Clip
		var clip1 = ((osc1 * osc2 * osc3) * clip1Amp).distort * SinOsc.ar(clipModFreq);
		//Output
		var output = (clip1 * clip2Amp).distort * 0.5 + loSine;
		var verbLfo = (SinOsc.ar(verbLfoSpeed) * 0.5 + 0.5) * verbLfoDepth;
		var verb1 = FreeVerb.ar(output, verbLfo, verbRoom, verbDamp, amp);
		Out.ar(out, verb1!2 * 0.5);
	}, [0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]).add; 
)

(
	//----Start Synth----
	~tunnelSynth = Synth(TheTunnel);
	
	//----Preset----
	~tunnelPreset = ();
	~tunnelPreset.current = 0;
	~tunnelPreset.list = [
		//			amp,	lfo1Speed,	lfo1Depth,	lfo2Speed,	lfo2Depth,	osc1Freq,	loSineFreq,	loSineAmp,	clip1Amp,	clipModFreq,	clip2Amp,	verbLfoSpeed,	verbLfoDepth,	verbRoom,	verbDamp
		/*00*/	[	1, 		0.1,		100, 		0.25, 		600, 		7, 			30, 			0.5,		6, 			500, 			6, 			0.014,			1, 				1, 			0.5			],
		/*01*/	[	1, 		30.756,	628.79, 	0.25, 		600, 		35.412, 	11.8, 			0.5,		6, 			500, 			6, 			0.151, 		0.204, 		1, 			1			],
		/*02*/	[	1,		48.345,	446.41, 	45.021,	694.56, 	14.97, 	42.53, 		0.14,		1.833, 	168.49,		1, 			0.014, 		0.314, 		1, 			0			],
		/*03*/	[	1.163,	0.507, 	1000, 		6.285, 	317.08, 	34.579, 	31.77, 		0.5, 		3.672, 	44.59, 		3.492, 	0.316, 		0.49, 			0.461, 	1			],
		/*04*/	[	1.392,	0.507, 	288.03,	0.67, 		317.08, 	10.449, 	88.06, 		0.37, 		2.557, 	44.59, 		3.492, 	0.155, 		1, 				1, 			1			],
		/*05*/	[	0.575,	100, 		1000, 		96.903, 	1000, 		50, 		88.06, 		1.22, 		6, 			127.37, 		6, 			0.167, 		0.493, 		1, 			1			],
		/*06*/	[	3.2, 	1, 			101.3, 	1.5, 		74.79, 	11.691, 	5.2, 			0, 			3.225, 	49.8, 			2.754, 	0.51, 			0.494, 		0.784, 	0.497		],
		/*07*/	[	2.505,	0.001, 	80.79, 	100, 		306.59, 	11.691, 	5.2, 			0, 			3.225, 	49.8, 			3.734, 	0.273, 		0.656, 		0.784, 	0.889		],
		/*08*/	[	0.9,	71.939, 	631.92, 	72.476,	427.99, 	17.475, 	96.68, 		0.56, 		4.298, 	496.15, 		5.649, 	0.804, 		0.694, 		0.303, 	0.461		],
		/*09*/	[	0.6,	34.043, 	650.05, 	29.176,	480.98, 	30.836, 	66.66, 		1.13, 		2.401, 	205.91, 		2.664, 	0.173, 		0.778, 		0.517, 	0.88		],
		/*10*/	[	0.54,	22.558, 	217.5,		91.033,	520.13,	3.484,		84.09,			1.59, 		2.731,		259.07, 		5.919,		0.571, 		0.988, 		0.133, 	0.0			]
	];
	~tunnelPreset.load = { |ev| 
		var current = ev.current;
		~tunnelSynth.set(amp, ev.list[current][0]);
		~tunnelSynth.set(lfo1Speed, ev.list[current][1]);
		~tunnelSynth.set(lfo1Depth, ev.list[current][2]);
		~tunnelSynth.set(lfo2Speed, ev.list[current][3]);
		~tunnelSynth.set(lfo2Depth, ev.list[current][4]);
		~tunnelSynth.set(osc1Freq, ev.list[current][5]);
		~tunnelSynth.set(loSineFreq, ev.list[current][6]);
		~tunnelSynth.set(loSineAmp, ev.list[current][7]);
		~tunnelSynth.set(clip1Amp, ev.list[current][8]);
		~tunnelSynth.set(clipModFreq, ev.list[current][9]);
		~tunnelSynth.set(clip2Amp, ev.list[current][10]);
		~tunnelSynth.set(verbLfoSpeed, ev.list[current][11]);
		~tunnelSynth.set(verbLfoDepth, ev.list[current][12]);
		~tunnelSynth.set(verbRoom, ev.list[current][13]);
		~tunnelSynth.set(verbDamp, ev.list[current][14]);
	};

	//----------Tunnel GUI----------
	~tunnelWindow = Window( "The Tunnel", Rect( 128, 230, 110, 140)); 
	~tunnelWindow.view.decorator = FlowLayout( ~tunnelWindow.view.bounds );
	~tunnelPreset.list.do({|each, count| (Button(~tunnelWindow, 30@30).states = [[count]]).action = {(~tunnelPreset.current = count).load;};});
	~tunnelWindow.front;
	~tunnelWindow.onClose = {~tunnelSynth.free;};

	//Kill the gui if the user kills the sound
	CmdPeriod.doOnce({
		~tunnelWindow.close;
	});
)

(//---------Perform Song----------
	~tunnelTask = Task({
		~tunnelSynth.free;
		s.recChannels = 2;
		s.recHeaderFormat = "AIFF";
		s.recSampleFormat = "int24";
		s.prepareForRecord("C:\Users\Jennifer\Jon\Code\thetunnel.aiff");
		s.record;
		~tunnelSynth = Synth(TheTunnel, [amp, 0]); 	1.wait;
		~tunnelSynth.set(amp, 1);						128.wait;
		~tunnelSynth.set(amp, 0.7);
		~tunnelSynth.set(clip1Amp, 100); 
		~tunnelSynth.set(clip2Amp, 100);				30.wait;
		(~tunnelPreset.current = 2).load;				15.wait;
		(~tunnelPreset.current = 6).load;				30.wait;
		~tunnelSynth.set(amp, 0);						35.wait;
		~tunnelSynth.free;
		s.stopRecording;
	});
	~tunnelTask.play;
)