Hi
I've made a class to add some common used functions of supercollider in
a menu using SCMenuItem.
It allows to choose soundcard for each server or all at the same time,
EQ, record window, auto sintax colorize on current document, color
picker, open quarks, Init Midi, check current midi messages, run a file
with cmd^r .
Finally it adds a menu tree with all files in predetermined folders
for easy access. When working on a project I'm always editing the same
files, closing and opening the files, and I got fed up and decided to
just put them in the menu bar for easy access.
I hope this might be useful for someone else, I think specially
newbies will appreciate being able to change the sound card (thanks for
someone who posted the code in the list) and record from a gui (thanks
wouter for ServerRecordWindow !).
http://www.friendlyvirus.org/artists/zlb/code/ToolsMenu.zip
Also included here is a nice minimal black gui for the server window I
got a from a friend and that I refined a bit. It puts all server guis as
a black strip in the bottom of the screen plus a big fader that changes
the volume of all servers. To use this addon one must call
Server.makeWindows each time a server is created and in the startup.rtf.
It uses the gui version of queryAllNodes by Scott Wilson that I added as
the method queryAllNodesGui.
I hope this is useful.
--
Miguel Negrão // ZLB
http://www.friendlyvirus.org/artists/zlb/
+ Server {
// shortcut server record
rek {|chans=1, filename="test.aif", format="int24"|
var path, sched;
path = "~/Desktop/".standardizePath ++ filename;
this.recChannels = chans;
this.recSampleFormat = format;
this.prepareForRecord( path );
AppClock.sched(1.0, { this.record; nil });
}
stoprek { this.stopRecording }
*makeWindows{
var window,w;
var bgColor= Color.grey(0.0, 0.9);
var stringColor= Color.grey(0.8);
var runningColor= Color.magenta;
var bootingColor= Color.yellow(0.9);
var bundlingColor= Color.new255(237, 157, 196);
var width, font;
Server.set.do({|se| if(se.window != nil)
{
Routine({
se.window.close;
0.1.wait;
se.makeWindow;
}).play(AppClock)
}{
se.makeWindow
}});
width = SCWindow.screenBounds.width - 2;
w = window = Window( "Main Volume",
Rect(1, 1, width, 19),
resizable: false, border: false);
w.view.background_(bgColor);
w.view.decorator = FlowLayout(w.view.bounds);
w.front;
Slider.new(w,Rect(0, 0, width-2, 16))
.action_{|v| Server.set.do{ |server|
server.volume.volume_([server.volume.min,server.volume.max,
\db].asSpec.map(v.value))
}
}
.canFocus_(false);
}
// server window customize
makeWindow { arg w;
var active, booter, killer, makeDefault, running, booting, stopped,
bundling;
var scoper;
var countsViews, ctlr;
var dumping=false, label, gui, volumeNum;
var bgColor= Color.grey(0.0, 0.9);
var stringColor= Color.grey(0.8);
var runningColor= Color.magenta;
var bootingColor= Color.yellow(0.9);
var bundlingColor= Color.new255(237, 157, 196);
var hideLocal= false;
var hideInternal= false;
var hideButt;
var width, font;
gui = GUI.current;
if (window.notNil, { ^window.front });
width = SCWindow.screenBounds.width - 2;
font = Font("Monaco", 9);
if(w.isNil) {
label = name.asString + "server";
w = window = Window( label,
Rect(1, ((named.values.size-1)-named.values.indexOf(this)+1)*20+1,
width, 19),
resizable: false, border: false);
w.view.background_(bgColor);
w.view.decorator = FlowLayout(w.view.bounds);
} { label = w.name };
active = StaticText(w, Rect(0, 0, 78, 24));
active.string = this.name.asString;
active.align = \center;
active.font = font;
active.background = Color.clear;
if(serverRunning,running,stopped);
hideButt= Button(w, Rect(0, 0, 19, 19));
hideButt.states= [["x", stringColor, Color.clear], ["", stringColor,
Color.clear]];
hideButt.action_({|view|
if(view.value==1, {
w.bounds_(Rect(1,
((named.values.size-1)-named.values.indexOf(this)+1)*20+1, 108, 19));
w.view.children.do{|x|
if([active, hideButt].includes(x).not, {
x.visible= false;
});
};
}, {
w.bounds_(Rect(1,
((named.values.size-1)-named.values.indexOf(this)+1)*20+1, width, 19));
w.view.children.do{|x| x.visible= true};
});
});
if(isLocal&&hideLocal, { hideButt.valueAction_(1) });
if(this.name==\internal&&hideInternal, { hideButt.valueAction_(1) });
if(isLocal,{
booter = Button(w, Rect(0, 0, 48, 24));
booter.states = [["Boot", stringColor, Color.clear],
["Quit", stringColor, Color.clear]];
booter.action = { arg view;
if(view.value == 1, {
booting.value;
this.boot;
});
if(view.value == 0,{
this.quit;
});
};
booter.setProperty(\value,serverRunning.binaryValue);
killer = Button(w, Rect(0,0, 24, 24));
killer.states = [["K", stringColor, Color.clear]];
killer.action = { Server.killAll };
});
makeDefault = Button(w, Rect(0, 0, 80, 24));
makeDefault.states = [["-> default", stringColor, Color.clear]];
makeDefault.action = {
thisProcess.interpreter.s = this;
Server.default = this;
};
w.view.keyDownAction = { arg ascii, char;
var startDump, stopDump, stillRunning;
case
{char === $n} { this.queryAllNodesGui }
{char === $l} { this.meter }
{char === $N} { this.queryAllNodes(true) }
{char === $ } { if(serverRunning.not) { this.boot } }
{char === $s and: { Stethoscope.isValidServer( this )}} { this.scope }
{char == $d} {
if(this.isLocal or: { this.inProcess }) {
stillRunning = {
SystemClock.sched(0.2, { this.stopAliveThread });
};
startDump = {
this.dumpOSC(1);
this.stopAliveThread;
dumping = true;
w.name = "dumping osc: " ++ name.asString;
CmdPeriod.add(stillRunning);
};
stopDump = {
this.dumpOSC(0);
this.startAliveThread;
dumping = false;
w.name = label;
CmdPeriod.remove(stillRunning);
};
if(dumping, stopDump, startDump)
} {
"cannot dump a remote server's messages".inform
}
};
};
if (isLocal, {
running = {
active.stringColor_(runningColor);
booter.setProperty(\value,1);
};
stopped = {
active.stringColor_(stringColor);
booter.setProperty(\value,0);
};
booting = {
active.stringColor_(bootingColor);
};
bundling = {
active.stringColor_(bundlingColor);
booter.setProperty(\value,1);
};
w.onClose = {
window = nil;
ctlr.remove;
};
},{
running = {
active.stringColor = runningColor;
active.background = Color.red;
};
stopped = {
active.stringColor = stringColor;
active.background = Color.black;
};
booting = {
active.stringColor = bootingColor;
active.background = Color.yellow;
};
bundling = {
active.stringColor = bundlingColor;
active.background = Color.red(0.5);
booter.setProperty(\value,1);
};
w.onClose = {
// but do not remove other responders
this.stopAliveThread;
ctlr.remove;
};
});
if(serverRunning,running,stopped);
countsViews =
#[
"Avg CPU :", "Peak CPU :",
"UGens :", "Synths :", "Groups :", "SynthDefs :"
].collect({ arg name, i;
var label,numView, pctView;
label = StaticText(w, Rect(0,0, 80, 14));
label.stringColor_(stringColor);
label.string = name;
label.align = \right;
if (i < 2, {
numView = StaticText(w, Rect(0,0, 38, 14));
numView.stringColor_(stringColor);
numView.string = "?";
numView.align = \left;
pctView = StaticText(w, Rect(0,0, 12, 14));
pctView.stringColor_(stringColor);
pctView.string = "%";
pctView.align = \left;
},{
numView = StaticText(w, Rect(0,0, 50, 14));
numView.stringColor_(stringColor);
numView.string = "?";
numView.align = \left;
});
numView
});
if(isLocal or: { options.remoteControlVolume }) {
{
var volSpec, cpVol;
var volumeSlider, muteButton, muteActions, volController;
muteActions = [{this.unmute}, {this.mute}];
volSpec = [volume.min, volume.max, \db].asSpec;
StaticText.new(w, Rect(0,0, 44, 18))
.font_(font)
.stringColor_(stringColor)
.string_(" volume :");
muteButton = gui.button.new(w, Rect(0, 0, 20, 16))
.font_(font)
.canFocus_(false)
.states_([
["M", stringColor, Color.clear],
["M", Color.black, Color.red.alpha_(0.3)]
])
.action_({arg me;
this.serverRunning.if({
muteActions[me.value].value;
}, {
"The server must be booted to mute it".warn;
me.value_(0);
})
});
volumeNum = gui.numberBox.new(w, Rect(0, 0, 28, 15))
.font_(font)
.value_(0.0)
.align_(\center)
.stringColor_(stringColor)
.action_({arg me;
var newdb;
newdb = me.value.clip(-90, 6);
this.volume_(newdb);
volumeSlider.value_(volSpec.unmap(newdb));
});
volumeSlider = gui.slider.new(w, Rect(0, 0, 172, 16))
.value_(volSpec.unmap(0))
.onClose_{volController.remove}
.action_({arg me;
var newdb;
newdb = volSpec.map(me.value).round(0.1);
this.volume_(newdb);
volumeNum.value_(newdb);
})
.keyDownAction_({arg slider, char, modifiers, unicode, keycode;
if (char == $], { slider.increment; });
if (char == $[, { slider.decrement; });
if (unicode == 16rF700, { slider.increment; });
if (unicode == 16rF703, { slider.increment; });
if (unicode == 16rF701, { slider.decrement; });
if (unicode == 16rF702, { slider.decrement; }); nil;
});
volController = SimpleController(volume)
.put(\amp, {|changer, what, vol|
{
volumeNum.value_(vol.round(0.01));
volumeSlider.value_(volSpec.unmap(vol));
}.defer
})
.put(\mute, {|changer, what, flag|
{
muteButton.value_(flag.binaryValue);
}.defer
})
.put(\ampRange, {|changer, what, min, max|
volSpec = [min, max, \db].asSpec;
volumeSlider.value_(volSpec.unmap(volume.volume));
})
}.value;
};
w.front;
ctlr = SimpleController(this)
.put(\serverRunning, { if(serverRunning,running,stopped) })
.put(\counts,{
countsViews.at(0).string = avgCPU.round(0.1);
countsViews.at(1).string = peakCPU.round(0.1);
countsViews.at(2).string = numUGens;
countsViews.at(3).string = numSynths;
countsViews.at(4).string = numGroups;
countsViews.at(5).string = numSynthDefs;
})
.put(\bundling, bundling);
this.startAliveThread;
w.view.children.do{|view|
view.bounds_(Rect(view.bounds.left, view.bounds.top, view.bounds.width,
14));
if(view.respondsTo(\font)) { view.font_(font) };
view.canFocus_(false);
};
}
queryAllNodesGui {
var resp, done = false;
// msg[1] controls included
// msg[2] nodeID of queried group
// initial number of children
resp = OSCresponderNode(this.addr, '/g_queryTree.reply', { arg time,
responder, msg;
//var finalEvent;
var i = 2, j, controls, printControls = false, dumpFunc;
if(msg[1] != 0, {printControls = true});
dumpFunc = {|numChildren|
var event, children;
event = ().group;
event.id = msg[i];
event.instrument = nil; // need to know it's a group
i = i + 2;
children = Array.fill(numChildren, {
var id, child;
// i = id
// i + 1 = numChildren
// i + 2 = def (if synth)
id = msg[i];
if(msg[i+1] >=0, {
child = dumpFunc.value(msg[i+1]);
}, {
j = 4;
child = ().synth.instrument_(msg[i+2]);
if(printControls, {
controls = ();
msg[i+3].do({
controls[msg[i + j]] = msg[i + j + 1];
j = j + 2;
});
child.controls = controls;
i = i + 4 + (2 * controls.size);
}, {i = i + 3 });
});
child.id = id;
});
event.children = children;
event;
};
~finalEvent = dumpFunc.value(msg[3]);
done = true;
{
var collectChildren, levels, countSize;
var window, view, bounds;
var tabSize = 25;
collectChildren = {|group|
group.children.collect({|child|
if(child.children.notNil,{
child.id -> collectChildren.value(child);
}, {
child.id -> child.instrument;
});
});
};
levels = collectChildren.value(~finalEvent);
countSize = {|array|
var size = 0;
array.do({|elem|
if(elem.value.isArray, { size = size +
countSize.value(elem.value) + 2}, {size = size + 1;});
});
size
};
//countSize.value(levels);
window = Window.new(this.asString,scroll:true).front;
window.view.hasHorizontalScroller_(false).background_(Color.black);
bounds = Rect(0, 0, 400, tabSize * (countSize.value(levels) +
2));
view = UserView.new(window, bounds);
view.drawFunc = {
var xtabs = 0, ytabs = 0, drawFunc;
drawFunc = {|group|
var thisSize, rect, endYTabs;
xtabs = xtabs + 1;
ytabs = ytabs + 1;
group.do({|node|
if(node.value.isArray, {
thisSize = countSize.value(node);
endYTabs = ytabs + thisSize + 0.2;
rect = Rect(xtabs * tabSize,
ytabs * tabSize,
window.view.bounds.width - (xtabs * tabSize
* 2),
thisSize * tabSize;
);
Pen.fillColor = Color.green.alpha_(0.5);
Pen.fillRect(rect);
Pen.strokeRect(rect);
(" Group" + node.key.asString + (node.key ==
1).if("- default group", "")).drawInRect(rect,
Font("Helvetica", 11),
Color.black);
drawFunc.value(node.value);
ytabs = endYTabs;
//ytabs.postln;
},{
rect = Rect(xtabs * tabSize,
ytabs * tabSize,
7 * tabSize,
0.8 * tabSize
);
//rect.postln;
Pen.fillColor = Color.red;
Pen.fillRect(rect);
Pen.strokeRect(rect);
(" " ++ node.key.asString +
node.value.asString).drawInRect(rect, Font("Helvetica", 11), Color.black);
ytabs = ytabs + 1;
});
});
xtabs = xtabs - 1;
};
drawFunc.value(levels);
};
}.defer
}).add.removeWhenDone;
this.sendMsg("/g_queryTree", 0, 0);
SystemClock.sched(3, {
done.not.if({
resp.remove;
"Server failed to respond to Group:queryTree!".warn;
});
});
}
}