( var clockStringFunc, metronomeStringFunc, metronomeColorFunc, updateTransport, updateSubsection, buildGenerator, buildMetronome, updateSection, buildTransport, buildTempoControl, buildMasterFader, buildTrackFader, buildMasterView, buildFaderView, buildHelpView, currentSection = 1, currentSubsection = 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})}; 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; 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]][0], 1, currentSection, currentSubsection ); }); }, { updateSection.value(mod, clock, metronome, sectionDisplay, refresh, true) }) }; buildGenerator = {arg view; var ranSeed; HLayout( ranSeed = TextField(view).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), [~appStatus = StaticText(view).string_("status: ready"), stretch: 1], Button(view).states_([["transcribe"]]).action_({ {~transcribe.value(~scoreData, ~sectionData, ranSeed.value); ~appStatus.string = "status: ready"}.fork(AppClock); ~appStatus.string = "status: transcribing"}.inEnvir), Button(view).states_([["bounce audio"]]).action_({ {~bounceAudio.value(ranSeed.value); ~appStatus.string = "status: ready"}.fork(AppClock); ~appStatus.string = "status: bouncing audio"}.inEnvir), nil) }; 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, indirect = false; var changeSection; case {(currentSubsection > 1) && (mod < 0)} { currentSubsection = 1; } {(currentSubsection <= 1) && (mod < 0) && (currentSection > 1)} { currentSection = currentSection + mod; if(indirect, { currentSubsection = ~sectionNavDict[[currentSection, 1]][1] }, { currentSubsection = 1; }) } {(mod > 0) && (~sectionNavDict[[currentSection + mod, 1]] != nil)} { currentSection = currentSection + mod; currentSubsection = 1; }; if(refresh, { updateTransport.value(clock, metronome, sectionDisplay, ~sectionNavDict[[currentSection, currentSubsection]][0], 1, currentSection, currentSubsection ); }); }; buildTransport = {arg win, view, clock, metronome, preampBusses, accompBusses, postampBusses; 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; updateTransport.value(clock, metronome, sectionDisplay, measure, beat, section, subsection); }.inEnvir.defer; },'/measureClock_' ++ ~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 = { var startMeasure = ~sectionNavDict[[currentSection, currentSubsection]][0] - 1; ~patternProxy.source = ~genPlayablePatterns.value(startMeasure, ~patterns, preampBusses, accompBusses, postampBusses); 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) }, { ~patternProxy.pause; //player.stop; updateTransport.value(clock, metronome, sectionDisplay, ~sectionNavDict[[currentSection, currentSubsection]][0], 1, currentSection, currentSubsection); }); }.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] }; buildTempoControl = {arg view; var layout, tempoField, address, updateSection; layout = HLayout( tempoField = TextField(view).string_("60").action_({arg v; var tempo = v.value.asInteger; ~tempoClock.tempo = tempo / 60}.inEnvir), Button(view).states_([["set tempo"]]).action_({arg v; ~tempoClock.tempo = tempoField.string.asInteger / 60}.inEnvir), [StaticText(view).string_(" "), stretch: 1]); [layout, tempoField] }; buildMasterFader = {arg view; var trackIndicators, layout, volSlider, muteButton, outMenu; trackIndicators = {LevelIndicator()} ! 2; OSCFunc.new({arg msg; {trackIndicators[0].value = msg[3].ampdb.linlin(-50, 0, 0, 1)}.defer; {trackIndicators[1].value = msg[4].ampdb.linlin(-50, 0, 0, 1)}.defer }, '/masterLevels_' ++ ~hash, s.addr); layout = HLayout([ VLayout( HLayout( volSlider = Slider(view).value_(0.8).action_( {arg v; var masterVol = v.value * 1.25; ~play.set(\masterVol, masterVol)}.inEnvir), trackIndicators[0], trackIndicators[1]), muteButton = Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_( {arg v; var masterMute = (1 - v.value).abs; ~play.set(\masterMute, masterMute)}.inEnvir), StaticText(view).string_("out").align_(\center), outMenu = PopUpMenu(view).items_((1..16).collect({arg o; o + "-" + (o + 1)})).action_( {arg v; var out = v.value.postln; ~play.set(\masterOut, out)}.inEnvir), StaticText(view).string_("master").align_(\center) ), stretch: 2], nil); [layout, volSlider, muteButton, outMenu] }; buildTrackFader = {arg view, name, index; var trackIndicator, netAddr, layout, volSlider, soloButton, muteButton, panKnob, outMenu; netAddr = NetAddr("127.0.0.1", NetAddr.langPort); trackIndicator = LevelIndicator(); OSCFunc.new({arg msg; {trackIndicator.value = msg[3].ampdb.linlin(-50, 0, 0, 1)}.defer}, '/trackLevel_' ++ index ++ "_" ++ ~hash, s.addr); layout = HLayout( VLayout( HLayout( volSlider = Slider(view).value_(0.8).action_( {arg v; var vol = v.value * 1.25; ~play.set(\vol_ ++ index, vol)}.inEnvir), trackIndicator), soloButton = Button(view).states_([["solo", Color.black], ["solo", Color.black, Color.grey]]).action_( {netAddr.sendMsg("/soloer_" ++ ~hash, index)}.inEnvir).value_(0), muteButton = Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_( {arg v; var mute = (1 - v.value).abs; //netAddr.sendMsg("/muter_" ++ ~hash, index); ~play.set(\mute_ ++ index, mute)}.inEnvir).valueAction_(if(index < 4, {1}, {0})), VLayout( StaticText(view).string_("pan").align_(\center), panKnob = Knob(view).action_({arg v; var pan = v.value * 2 - 1; ~play.set(\pan_ ++ index, pan)}.inEnvir).valueAction_(0.5) ), StaticText(view).string_("out").align_(\center), outMenu = PopUpMenu(view).items_(["master"] ++ (1..16)).action_( {arg v; var out = v.value; ~play.set(\out_ ++ index, out)}.inEnvir).valueAction_(if(index < 6, {0}, {3})), StaticText(view).string_(name).align_(\center) //StaticText(view).string_("output").align_(\center), ), nil); [layout, volSlider, soloButton, muteButton, panKnob, outMenu] }; buildMasterView = {arg win, preampBusses, accompBusses, postampBusses; var view, generatorLayout, clock, metronome, metronomeLayout, transportLayout, tempoContol, 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, preampBusses, accompBusses, postampBusses); tempoContol = buildTempoControl.value(view); auxControlsLayout = tempoContol[0]; view.layout_( HLayout( [ VLayout( metronomeLayout, [StaticText(view).string_(" "), stretch: 1], transportLayout, [StaticText(view).string_(" "), stretch: 1], auxControlsLayout, [StaticText(view).string_(" "), stretch: 1], generatorLayout), alignment: \top ] ) ); [view, tempoContol[1]] }; buildFaderView = {arg win, tempoField; var view, masterIndicators, trackIndicators, master, tracks, openButton, basePath, saveButton; var partAbbr = ["*", "III", "II", "I", "accomp_II", "accomp_I", "click"]; var trackNames = ["*", "III", "II", "I", "accomp_II", "accomp_I", "click"]; var partVols, partMutes, partPans; var masterMute, masterVol; var netAddr = NetAddr("127.0.0.1", NetAddr.langPort); var player = ~play; // 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; master = buildMasterFader.value(view); tracks = {arg part; buildTrackFader.value(view, trackNames[part], part); } ! 7; OSCFunc.new({arg msg; { tracks.slice(nil, 3).do({arg mute, m; if(tracks[msg[1]][2].value == 1, { mute.valueAction = if(msg[1] == m, {0}, {1}); tracks[m][2].value = if(msg[1] != m, {0}, {1}) }, { mute.valueAction = 0 }); }); }.defer}, '/soloer_' ++ ~hash, netAddr); //OSCFunc.new({arg msg; { //tracks.slice(nil, 2).do({arg solo, m; solo.value.postln; solo.value = 0}); //}.defer}, '/muter_' ++ ~hash, netAddr); basePath = ~dir +/+ ".." +/+ "mixer_settings"; openButton = Button(view).states_([["open", Color.black]]).action_({ Dialog.openPanel({ arg path; var settings; settings = File.readAllString(path).parseJSON; tempoField.valueAction = settings["tempo"]; master[1].valueAction = settings["master_volume"]; master[2].valueAction = settings["master_pan"]; master[3].valueAction = settings["master_out"]; settings["track_volumes"].do({arg val, v; tracks[v][1].valueAction = val}); settings["track_solos"].do({arg val, v; tracks[v][2].valueAction = val}); settings["track_mutes"].do({arg val, v; tracks[v][3].valueAction = val}); settings["track_pans"].do({arg val, v; tracks[v][4].valueAction = val}); settings["track_outs"].do({arg val, v; tracks[v][5].valueAction = val}); },{}, false, basePath); }); saveButton = Button(view).states_([["save", Color.black]]).action_({ Dialog.savePanel({ arg path; var settings, file; settings = "{\n"; settings = settings ++ "\"tempo\": " ++ tempoField.string ++ ",\n"; settings = settings ++ "\"master_volume\": " ++ master[1].value ++ ",\n"; settings = settings ++ "\"master_mute\": " ++ master[2].value ++ ",\n"; settings = settings ++ "\"master_out\": " ++ master[3].value ++ ",\n"; settings = settings ++ "\"track_volumes\": [" ++ tracks.collect({arg track; track[1].value}).join(",") ++ "],\n"; settings = settings ++ "\"track_solos\": [" ++ tracks.collect({arg track; track[2].value}).join(",") ++ "],\n"; settings = settings ++ "\"track_mutes\": [" ++ tracks.collect({arg track; track[3].value}).join(",") ++ "],\n"; settings = settings ++ "\"track_pans\": [" ++ tracks.collect({arg track; track[4].value}).join(",") ++ "],\n"; settings = settings ++ "\"track_outs\": [" ++ tracks.collect({arg track; track[5].value}).join(",") ++ "]\n"; settings = settings ++ "}"; file = File(path, "w"); file.write(settings); file.close; },{}, basePath); }); view.layout_(HLayout(HLayout(master[0], nil, *tracks.slice(nil, 0)), VLayout(nil, saveButton, openButton))) }; buildHelpView = {arg win; TextView(win).string_(File.readAllString(~dir +/+ "tkam_readme.scd")).editable_(false); }; ~generateGUI = {arg preampBusses, accompBusses, postampBusses; var win, tabButtonReset, transportButton, mixerButton, helpButton, masterConrol, tempoControl, masterView, faderView, helpView, tabs; win = Window("to kill a monarch", Rect(500, 500, 1100, 575), false).front; tabButtonReset = {transportButton.value = 1; mixerButton.value = 1; helpButton.value = 1}; masterConrol = buildMasterView.value(win, preampBusses, accompBusses, postampBusses); masterView = masterConrol[0]; tempoControl = masterConrol[1]; faderView = buildFaderView.value(win, tempoControl); helpView = buildHelpView.value(win); 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, faderView, helpView)); }; )