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