( var genWindow, masterView, faderView, buildGenerator, buildMetronome, buildTransport, buildAuxControls, buildMasterFader, buildTrackFader, updateSection, updateSubsection, clockStringFunc, metronomeStringFunc, metronomeColorFunc, updateTransport, currentSection = 1, currentSubsection = 1; buildGenerator = {arg view; var ranSeed; HLayout( ranSeed = TextField(view, Rect(10, 10, 10, 20)).string_("19800725"), Button(view).states_([["reset seed"]]).action_({ ranSeed.string = "19800725"}.inEnvir), Button(view).states_([["random seed"]]).action_({ ranSeed.string = 50000000.rand.asString}.inEnvir), Button(view).states_([["generate"]]).action_({ {~genAll.value(ranSeed.string.asInteger); ~appStatus.string = "status: ready"}.fork(AppClock); ~appStatus.string = "status: generating"}.inEnvir), Button(view).states_([["transcribe"]]).action_({ {~transcribe.value(~scoreData, ~sectionData); ~appStatus.string = "status: ready"}.fork(AppClock); ~appStatus.string = "status: transcribing"}.inEnvir), [~appStatus = StaticText(view).string_("status: ready"), stretch: 1], nil) }; // these funcs update the elements of the transport panel clockStringFunc = { arg measure, beat; var measureString, beatString, leadSpace; measureString = measure.asInteger.asString; beatString = beat.asInteger.asString; leadSpace = (3 - measureString.size).collect({" "}).join; leadSpace ++ measureString ++ "." ++ beatString }; // [-30, -105, -104] and [-30, -105, -113] are unicode inverse bullet and normal bullet, respectively metronomeStringFunc = { arg beat; if(beat == 1, {[-30, -105, -104].collect({arg int; int.asAscii}).as(String)}, {[-30, -105, -113].collect({arg int; int.asAscii}).as(String)})}; metronomeColorFunc = { arg beat; if(beat == 1, {Color.red},{Color.black})}; updateTransport = {arg clock, metronome, sectionDisplay, measure, beat, section, subsection; sectionDisplay.string = "section: " ++ section.asInteger ++ "." ++ subsection.asInteger; clock.string = clockStringFunc.value(measure, beat); metronome.stringColor = metronomeColorFunc.value(beat); metronome.string = metronomeStringFunc.value(beat); {0.75.wait; {metronome.string = ""}.defer}.fork(~tempoClock, quant: 0); }.inEnvir; buildMetronome = {arg win; var clock, metronome, layout; clock = StaticText(win).string_(" 1.1").font_(Font("Liberation Mono", 200)); metronome = StaticText(win).string_([-30, -105, -104].collect({arg int; int.asAscii}) .as(String)).font_(Font("Liberation Mono", 300)).stringColor_(Color.red); layout = HLayout( clock, StaticText(win).string_("|").font_(Font("Liberation Mono", 200)), metronome ); [clock, metronome, layout] }; updateSection = {arg mod, clock, metronome, sectionDisplay, refresh = true; if(~sectionNavDict[[currentSection + mod, 1]] != nil, { currentSection = currentSection + mod; currentSubsection = 1; if(refresh, { updateTransport.value(clock, metronome, sectionDisplay, ~sectionNavDict[[currentSection, currentSubsection]], 1, currentSection, currentSubsection ); }); }); }; updateSubsection = {arg mod, clock, metronome, sectionDisplay, refresh = true; if(~sectionNavDict[[currentSection, currentSubsection + mod]] != nil, { currentSubsection = currentSubsection + mod; if(refresh, { updateTransport.value(clock, metronome, sectionDisplay, ~sectionNavDict[[currentSection, currentSubsection]], 1, currentSection, currentSubsection ); }); }, {updateSection.value(mod, clock, metronome, sectionDisplay, refresh)}) }; buildTransport = {arg win, view, clock, metronome; var sec, subsec, sectionDisplay, layout, player; sectionDisplay = StaticText(win).string_("section: 1.1").font_(Font("Liberation Mono", 70)); OSCFunc({ arg msg, time; { var measure, beat, section, subsection; # measure, beat, section, subsection = msg[3..]; currentSection = sec = section.asInteger; currentSubsection = subsec = subsection.asInteger; //currentSubsection.postln; updateTransport.value(clock, metronome, sectionDisplay, measure, beat, section, subsection); }.inEnvir.defer; },'/measureClock_' ++ ~hash, s.addr); //OSCdef(\externalAdvance ++ ~hash, {arg msg, time; {updateSection.value(1, clock, metronome, sectionDisplay, false)}.inEnvir.defer},'/nextSubsection_' ++ ~hash, s.addr); layout = HLayout( Button(view).states_([["<<", Color.black]]).action_({arg pState; updateSection.value(-1, clock, metronome, sectionDisplay)}.inEnvir), Button(view).states_([["<", Color.black]]).action_({arg pState; updateSubsection.value(-1, clock, metronome, sectionDisplay)}.inEnvir), Button(view).states_([["play", Color.black], ["stop", Color.black, Color.grey]]).action_({arg pState; if(pState.value == 1, { player = { ~patternProxy.source = Ppar( [Pseq(~patterns[0], 1, ~sectionNavDict[[currentSection, currentSubsection]] - 1)] ++ ~patterns[2].collect({arg pattern, p; Pmono(\amp_curve_ ++ ~hash, \amp, Pseg(Pseq(pattern.slice(nil, 0), 1, (~sectionNavDict[[currentSection, currentSubsection]] - 1) * 4), 4 * ~dUnit), \dur, 4 * ~dUnit, \bus, ~preampBusses[p].index ) }) ++ ~patterns[1].collect({arg pattern, p; Pmono(\bass_mono_ ++ ~hash, \freq, Pseq(pattern.slice(nil, 0), 1, (~sectionNavDict[[currentSection, currentSubsection]] - 1) * 4), \dur, 4 * ~dUnit, \ampBus, ~preampBusses[p + 1].index, \bus, ~postampBusses[4].index ) }) ); Pbind(\instrument, \click_ ++ ~hash, \beat, Pseq([1, 2, 1, 2]), \dur, 1).play(~tempoClock, quant: 0); [1, 2, 1, 2].do({arg beat; { metronome.stringColor = metronomeColorFunc.value(beat); metronome.string = metronomeStringFunc.value(beat); }.defer; 0.75.wait; {metronome.string = ""}.defer; 0.25.wait; }); ~patternProxy.play(~tempoClock, quant: 0) }.fork(~tempoClock, quant: 0) }, { //subsec.postln; ~patternProxy.pause; //player.stop; updateTransport.value(clock, metronome, sectionDisplay, ~sectionNavDict[[currentSection, currentSubsection]], 1, currentSection.postln, currentSubsection.postln); }); }.inEnvir), Button(view).states_([[">", Color.black]]).action_({arg pState; updateSubsection.value(1, clock, metronome, sectionDisplay)}.inEnvir), Button(view).states_([[">>", Color.black]]).action_({arg pState; updateSection.value(1, clock, metronome, sectionDisplay)}.inEnvir), nil, sectionDisplay, nil); [sectionDisplay, layout] }; buildAuxControls = {arg view; var tempo, address, updateSection; HLayout( tempo = TextField(view).string_("60"), Button(view).states_([["set tempo"]]).action_({~tempoClock.tempo = tempo.string.asInteger / 60}.inEnvir), StaticText(view).string_(" | "), Button(view).states_([["auto advance", Color.black], ["auto advance", Color.black, Color.grey]]).action_({ arg v; ~autoAdvance = if(v.value == 0, {false}, {true});~autoAdvance; }.inEnvir).value_(1), Button(view).states_([["interludes", Color.black], ["interludes", Color.black, Color.grey]]).action_({ arg v; ~interludes = if(v.value == 0, {false}, {true}) }.inEnvir), StaticText(view).string_(" | "), address = TextField(view, Rect(10, 10, 10, 20)).string_("127.0.0.1:57120"), Button(view).states_([["set address:port"]]).action_({ var addr, ip, port; addr = address.string.split($:); ip = addr[0]; port = addr[1].asInteger; thisProcess.openUDPPort(port); addr = NetAddr(ip, port); OSCdef(\externalAdvance_ ++ ~hash, {arg msg, time; {updateSection.value(1)}.inEnvir.defer},'/nextSubsection', addr); }.inEnvir), [StaticText(view).string_(" "), stretch: 1]) }; buildMasterFader = {arg view, masterVol, masterMute, masterIndicators; HLayout( VLayout( [HLayout( Slider(view).value_(0.8).action_( {arg v; masterVol = v.value * 1.25; ~play.set(\masterVol, masterVol)}.inEnvir), masterIndicators[0], masterIndicators[1]), stretch: 2], Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_( {arg v; masterMute = (1 - v.value).abs; ~play.set(\masterMute, masterMute)}.inEnvir), StaticText(view).string_(" master ").align_(\center) ), nil) }; buildTrackFader = {arg view, name, index, initVal; var trackIndicator; trackIndicator = LevelIndicator(); OSCFunc.new({arg msg; {trackIndicator.value = msg[3].ampdb.linlin(-50, 0, 0, 1)}.defer}, '/trackLevel_' ++ index ++ "_" ++ ~hash, s.addr); HLayout( VLayout( HLayout( Slider(view).value_(0.8).action_( {arg v; var vol = v.value * 1.25; ~play.set(\vol_ ++ index, vol)}.inEnvir), trackIndicator), Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_( {arg v; var mute = (1 - v.value).abs; ~play.set(\mute_ ++ index, mute)}.inEnvir).value_(initVal), VLayout( StaticText(view).string_("pan").align_(\center), Knob(view).action_({arg v; var pan = v.value * 2 - 1; ~play.set(\pan_ ++ index, pan)}.inEnvir).value_(0.5) ), //[StaticText(view).string_(" "), stretch: 0.1] StaticText(view).string_(name).align_(\center) ), nil) }; masterView = {arg win; var view, generatorLayout, clock, metronome, metronomeLayout, transportLayout, auxControlsLayout, countOff, ranSeed, order, tempo, sectionDisplay, address; view = View(win); generatorLayout = buildGenerator.value(view); # clock, metronome, metronomeLayout = buildMetronome.value(win); # sectionDisplay, transportLayout = buildTransport.value(win, view, clock, metronome); auxControlsLayout = buildAuxControls.value(view); view.layout_( HLayout( [ VLayout( metronomeLayout, [StaticText(view).string_(" "), stretch: 1], //nil transportLayout, [StaticText(view).string_(" "), stretch: 1], auxControlsLayout, [StaticText(view).string_(" "), stretch: 1], generatorLayout), alignment: \top ] ) ) }; faderView = {arg win; var view, masterIndicators, trackIndicators, master, tracks; var partAbbr = ["*", "III", "II", "I", "accomp_I", "accomp_II", "click"]; var trackNames = ["*", "III", "II", "I", "accomp_I", "accomp_II", "click"]; var partVols, partMutes, partPans; var masterMute, masterVol; // set initial mixer values partVols = [1, 1, 1, 1, 1, 1]; partMutes = [0, 1, 1, 1, 1, 0]; partPans = [0, 0, 0, 0, 0, 0]; masterMute = 1; masterVol = 1; view = View(win); masterIndicators = {LevelIndicator()} ! 2; trackIndicators = {LevelIndicator()} ! 6; /* OSCFunc.new({arg msg; { {arg i; masterIndicators[i].value = msg[3 + i].ampdb.linlin(-40, 0, 0, 1)} ! 2}.defer}, '/masterLevels' ++ ~hash, s.addr); OSCFunc.new({arg msg; { {arg i; trackIndicators[i].value = msg[3 + i].ampdb.linlin(-40, 0, 0, 1)} ! 6}.defer}, '/trackLevels' ++ ~hash, s.addr); */ master = buildMasterFader.value(view, masterVol, masterMute, masterIndicators); tracks = {arg part; buildTrackFader.value(view, trackNames[part], part, 0); } ! 7; view.layout_(HLayout(master, nil, *tracks)) }; ~generateGUI = { var win, tabButtonReset, transportButton, mixerButton, helpButton, tabs; win = Window("to kill a monarch", Rect(500, 500, 1100, 575), false).front; tabButtonReset = {transportButton.value = 1; mixerButton.value = 1; helpButton.value = 1}; win.layout = VLayout( HLayout( HLayout( [ transportButton = Button().states_([["transport", Color.white, Color.grey], ["transport", Color.black]]).action_( {tabButtonReset.value; transportButton.value = 0; tabs.index = 0 }.inEnvir).value_(0), stretch: 1 ], [ mixerButton = Button().states_([["mixer", Color.white, Color.grey], ["mixer", Color.black]]).action_( {tabButtonReset.value; mixerButton.value = 0; tabs.index = 1 }.inEnvir).value_(1), stretch: 1 ] ), helpButton = Button().states_([["help", Color.white, Color.grey], ["help", Color.black]]).action_( {tabButtonReset.value; helpButton.value = 0; tabs.index = 2 }.inEnvir).value_(1) ), tabs = StackLayout(masterView.value(win), faderView.value(win)/*, helpView.value*/)); }; //~generateGUI.value //~~FUNCTION THAT GENERATES THE GUI /* ~generateGUI = { var win, clockStringFunc, metronomeStringFunc, metronomeColorFunc, masterView, faderView, helpView, tabs; var tabButtonReset, transportButton, mixerButton, helpButton, startPos = 0; var partAbbr = ["guitar", "accompHigh", "accompLowLower", "accompLowUpper", "interlude", "click"]; var trackNames = ["guitar", "high", "low 1", "low 2", "interlude", "click"]; var partVols, partMutes, partPans; var masterMute, masterVol; // set initial mixer values partVols = [1, 1, 1, 1, 1, 1]; partMutes = [0, 1, 1, 1, 1, 0]; partPans = [0, 0, 0, 0, 0, 0]; masterMute = 1; masterVol = 1; // these funcs update the elements of the transport panel clockStringFunc = { arg measure, beat; var measureString, beatString, leadSpace; measureString = measure.asInteger.asString; beatString = beat.asInteger.asString; leadSpace = (3 - measureString.size).collect({" "}).join; leadSpace ++ measureString ++ "." ++ beatString }; // [-30, -105, -104] and [-30, -105, -113] are unicode inverse bullet and normal bullet, respectively metronomeStringFunc = { arg beat; if(beat == 1, {[-30, -105, -104].collect({arg int; int.asAscii}).as(String)}, {[-30, -105, -113].collect({arg int; int.asAscii}).as(String)})}; metronomeColorFunc = { arg beat; if(beat == 1, {Color.red},{Color.black})}; win = Window("Counterfeiting in Colonial Connecticut", Rect(500, 500, 1100, 575), false).front; masterView = { var updateTransport, updateSection, view, generator, transport, countOff, ranSeed, order, tempo, sectionDisplay, clock, metronome, address; // this func updates the whole transport panel updateTransport = {arg measure, beat; clock.string = clockStringFunc.value(measure, beat); metronome.stringColor = metronomeColorFunc.value(beat); metronome.string = metronomeStringFunc.value(beat); {0.75.wait; {metronome.string = ""}.defer}.fork(~tempoClock, quant: 0); }.inEnvir; // this func handles the movement between sections updateSection = {arg shift, stop = true, manualCall = true; var runThis; runThis = (manualCall || (manualCall.not && ~autoAdvance)); runThis = runThis && ((currentSection + shift) < ~sectionOrder.size); runThis = runThis && (((currentSection % 4) == 3) && ~interludes && manualCall.not).not; if(runThis, { var truncOnly, section, subSection; if(~isPlaying, { if(stop, { ~patterns[~sectionOrder[currentSection]].stop }) }); truncOnly = case {(currentSection + shift) < 0} {true} {(shift < 0) && ~isPlaying} {true} {(shift < -1) && ((currentSection % 4) > 0)} {true} {true} {false}; if(truncOnly.not, { currentSection = (currentSection + shift).trunc(shift.abs); }, { currentSection = currentSection.trunc(shift.abs); }); section = ((~sectionOrder[currentSection] / 4) + 1).asInteger; subSection = ((~sectionOrder[currentSection] % 4) + 1).asInteger; sectionDisplay.string = "section: " ++ section.asString ++ "." ++ subSection.asString; if(~isPlaying, { countOff = { if(~interludes && ((currentSection % 4) == 0), { Pbind( \instrument, \click ++ ~hash, \beat, Pseq([1, 2, 1, 2]), \dur, 1 ).play(~tempoClock, quant: 0); [1, 2, 1, 2].do({arg beat; { metronome.stringColor = metronomeColorFunc.value(beat); metronome.string = metronomeStringFunc.value(beat); }.defer; 0.75.wait; {metronome.string = ""}.defer; 0.25.wait; }); }); ~play.set(\sel, currentSection % 2); ~patterns[~sectionOrder[currentSection]].play(~tempoClock, quant: 0); if(~interludes && ((currentSection % 4) == 3) && (currentSection != (~sectionOrder.size - 1)), { var center, interval, freq1, freq2, tremRate; center = 50 - 12.0.rand; interval = 3.0.rand + 2; freq1 = (center + (interval / 2)).midicps; freq2 = (center - (interval / 2)).midicps; tremRate = 50 + 4.0.rand2; ~interludeTremelo.set(\gate, 1, \amp, 1, \freq1, freq1, \freq2, freq2, \tremRate, tremRate); }); if((currentSection % 4) == 0, { ~interludeTremelo.set(\gate, 0); }); if(((currentSection % 4)) != 0 && ((currentSection % 4) != 3), { ~interludeTremelo.set(\gate, 0, \amp, 0); }); }.fork(~tempoClock, quant: 0); }, { var measure, beat; measure = ~sectionStartMeasure[~sectionOrder[currentSection]]; beat = 1; updateTransport.value(measure, beat); }); }); }.inEnvir; // these funcs receive messages from the synth OSCFunc({ arg msg, time; { var measure, beat; measure = msg[3]; beat = msg[4]; updateTransport.value(measure, beat) }.inEnvir.defer; },'/measureClock' ++ ~hash, s.addr); OSCFunc({ arg msg, time; {updateSection.value(1, false, false)}.inEnvir.defer},'/nextSubsection' ++ ~hash, s.addr); OSCdef(\externalAdvance ++ ~hash, {arg msg, time; {updateSection.value(1)}.inEnvir.defer},'/nextSubsection', s.addr); view = View(win); generator = HLayout( ranSeed = TextField(view, Rect(10, 10, 10, 20)).string_("20200525"), Button(view).states_([["reset seed"]]).action_({ ranSeed.string = "20200525"}.inEnvir), Button(view).states_([["random seed"]]).action_({ ranSeed.string = 50000000.rand.asString}.inEnvir), Button(view).states_([["generate"]]).action_({ {~genAll.value(ranSeed.string.asInteger); ~appStatus.string = "status: ready"}.fork(AppClock); ~appStatus.string = "status: generating"}.inEnvir), Button(view).states_([["transcribe"]]).action_({ {~transcribe.value(~scoreData, ranSeed.string); ~appStatus.string = "status: ready"}.fork(AppClock); ~appStatus.string = "status: transcribing"}.inEnvir), [~appStatus = StaticText(view).string_("status: ready"), stretch: 1], nil); transport = HLayout( Button(view).states_([["<<", Color.black]]).action_({arg pState; updateSection.value(-4)}.inEnvir), Button(view).states_([["<", Color.black]]).action_({arg pState; updateSection.value(-1)}.inEnvir), Button(view).states_([["play", Color.black], ["stop", Color.black, Color.grey]]).action_( {arg pState; if(pState.value == 0, { var measure, beat; countOff.stop; ~isPlaying = false; ~patterns[~sectionOrder[currentSection]].stop; ~interludeTremelo.set(\gate, 0); measure = ~sectionStartMeasure[currentSection]; beat = 1; updateTransport.value(measure, beat); ~interludeTremelo.set(\gate, 0, \amp, 0); },{ countOff = { Pbind( \instrument, \click ++ ~hash, \beat, Pseq([1, 2, 1, 2]), \dur, 1 ).play(~tempoClock, quant: 0); [1, 2, 1, 2].do({arg beat; { metronome.stringColor = metronomeColorFunc.value(beat); metronome.string = metronomeStringFunc.value(beat); }.defer; 0.75.wait; {metronome.string = ""}.defer; 0.25.wait; }); ~isPlaying = true; ~play.set(\sel, currentSection % 2); ~patterns[~sectionOrder[currentSection]].play(~tempoClock, quant: 0); if(~interludes && ((currentSection % 4) == 3) && (currentSection != (~sectionOrder.size - 1)), { var center, interval, freq1, freq2, tremRate; center = 50 - 12.0.rand; interval = 3.0.rand + 2; freq1 = (center + (interval / 2)).midicps; freq2 = (center - (interval / 2)).midicps; tremRate = 50 + 4.0.rand2; ~interludeTremelo.set(\gate, 1, \amp, 1, \freq1, freq1, \freq2, freq2, \tremRate, tremRate); }); }.fork(~tempoClock, quant: 0); }) }.inEnvir ), Button(view).states_([[">", Color.black]]).action_({arg pState; updateSection.value(1)}.inEnvir), Button(view).states_([[">>", Color.black]]).action_({arg pState; updateSection.value(4)}.inEnvir), nil, sectionDisplay = StaticText(win).string_("section: 1.1").font_(Font("Liberation Mono", 70)), nil); view.layout_(HLayout( [VLayout( HLayout(clock = StaticText(win).string_(" 1.1").font_(Font("Liberation Mono", 200)), StaticText(win).string_("|").font_(Font("Liberation Mono", 200)), metronome = StaticText(win).string_([-30, -105, -104].collect({arg int; int.asAscii}).as(String)).font_(Font("Liberation Mono", 300)).stringColor_(Color.red)), nil, transport, nil, HLayout( tempo = TextField(view).string_("90"), Button(view).states_([["set tempo"]]).action_({~tempoClock.tempo = tempo.string.asInteger / 60}.inEnvir), StaticText(view).string_(" | "), Button(view).states_([["auto advance", Color.black], ["auto advance", Color.black, Color.grey]]).action_({ arg v; ~autoAdvance = if(v.value == 0, {false}, {true});~autoAdvance; }.inEnvir).value_(1), Button(view).states_([["interludes", Color.black], ["interludes", Color.black, Color.grey]]).action_({ arg v; ~interludes = if(v.value == 0, {false}, {true}) }.inEnvir), StaticText(view).string_(" | "), address = TextField(view, Rect(10, 10, 10, 20)).string_("127.0.0.1:57120"), Button(view).states_([["set address:port"]]).action_({ var addr, ip, port; addr = address.string.split($:); ip = addr[0]; port = addr[1].asInteger; thisProcess.openUDPPort(port); addr = NetAddr(ip, port); OSCdef(\externalAdvance ++ ~hash, {arg msg, time; {updateSection.value(1)}.inEnvir.defer},'/nextSubsection', addr); }.inEnvir), [StaticText(view).string_(" "), stretch: 1]), [StaticText(view).string_(" "), stretch: 1], HLayout( order = TextField(view).string_("1-16"), Button(view).states_([["set order"]]).action_({ ~patterns[~sectionOrder[currentSection]].stop; ~sectionOrder = order.string.split($,).collect({arg secEntry; var bounds; bounds = secEntry.split($-).collect({arg item; item.asInteger - 1}); ((bounds.minItem)..(bounds.maxItem)).collect({arg sec; (sec.asInteger * 4) + [0, 1, 2, 3] }); }).flat; currentSection = 0; updateSection.value(0); }.inEnvir), [StaticText(view).string_(" "), stretch: 1]), [StaticText(view).string_(" "), stretch: 1], generator ), alignment: \top]))}; faderView = { var view, masterIndicators, trackIndicators, master, tracks; view = View(win); masterIndicators = {LevelIndicator()} ! 2; trackIndicators = {LevelIndicator()} ! 6; OSCFunc.new({arg msg; { {arg i; masterIndicators[i].value = msg[3 + i].ampdb.linlin(-40, 0, 0, 1)} ! 2}.defer}, '/masterLevels' ++ ~hash, s.addr); OSCFunc.new({arg msg; { {arg i; trackIndicators[i].value = msg[3 + i].ampdb.linlin(-40, 0, 0, 1)} ! 6}.defer}, '/trackLevels' ++ ~hash, s.addr); master = HLayout( VLayout( [HLayout( Slider(view).value_(0.8).action_( {arg v; masterVol = v.value * 1.25; ~play.set(\masterVol, masterVol)}.inEnvir), masterIndicators[0], masterIndicators[1]), stretch: 2], Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_( {arg v; masterMute = (1 - v.value).abs; ~play.set(\masterMute, masterMute)}.inEnvir), StaticText(view).string_(" master ").align_(\center) ), nil); tracks = {arg part; HLayout( VLayout( HLayout( Slider(view).value_(0.8).action_( {arg v; partVols[part] = v.value * 1.25; ~play.set(partAbbr[part] ++ "Vol", partVols[part])}.inEnvir), trackIndicators[part]), Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_( {arg v; partMutes[part] = (1 - v.value).abs; ~play.set(partAbbr[part] ++ "Mute", partMutes[part])}.inEnvir).value_( {if((part == 0) || (part == 5), {1}, {0})}.value), StaticText(view).string_("pan").align_(\center), Knob(view).value_(0.5).action_( {arg v; partPans[part] = v.value * 2 - 1; ~play.set(partAbbr[part] ++ "Pan", partPans[part])}.inEnvir), StaticText(view).string_(trackNames[part]).align_(\center) ), nil) } ! 6; view.layout_(HLayout(master, nil, *tracks))}; helpView = { TextView(win).string_(File.readAllString(~dir +/+ "cicc_readme.scd")).editable_(false); }; tabButtonReset = {transportButton.value = 1; mixerButton.value = 1; helpButton.value = 1}; win.layout = VLayout( HLayout( HLayout( [ transportButton = Button().states_([["transport", Color.white, Color.grey], ["transport", Color.black]]).action_( {tabButtonReset.value; transportButton.value = 0; tabs.index = 0 }.inEnvir).value_(0), stretch: 1 ], [ mixerButton = Button().states_([["mixer", Color.white, Color.grey], ["mixer", Color.black]]).action_( {tabButtonReset.value; mixerButton.value = 0; tabs.index = 1 }.inEnvir).value_(1), stretch: 1 ] ), helpButton = Button().states_([["help", Color.white, Color.grey], ["help", Color.black]]).action_( {tabButtonReset.value; helpButton.value = 0; tabs.index = 2 }.inEnvir).value_(1) ), tabs = StackLayout(masterView.value, faderView.value, helpView.value)); }; */ )