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; )