Oh I think the guides are great - I’ve read them several times!
But I can’t remember them all off the top of my head, and no longer regularly refer to them as they are quite difficult to search through in a hurry. Essentially, if it doesn’t come up as a result of the search in the help browser when looking for specific terms (Event, Event Types, Pbind, callback…), it might as well not exist - and I don’t think my laziness is unique there. This might be something fixable, where the heading of the document appear in the search results, its just markdown right(?) so should be possible? Same goes for a lot of programming languages, if the IDE doesn’t auto complete or at least let one jump to source, does it even exist?
I’m not really fussed where they are listed, as long as one can search for it and get an exhaustive list. Currently, the only place they all appear is in Event.sc
. Honestly, I’ve rather have each \type be an actually type (class), that way all the methods(keys) are listed and searchable.
eg…
Pbind(PTypeNote(
\instrument, \synthA,
\freq, Pwhite(200, 400)
...
)).play
Where \instrument, \synthA
is short for PTypeNode().instrument_(\synthA)
. That way all the keys are listed, including inherited ones.
Its worth noting, that you are the author of the guide to patterns, and the \callback Classlib: Move default Event's ~callback into the ~play function · supercollider/supercollider@0cf63b8 · GitHub. If it requires the author to verbally tell everyone about it, something is broken (probably the code and generated documentation as the guides really are great!).
I wouldn’t mind helping out document things … actually, I’ve just looked at the source (or at least, what I think is the source for this), and since I don’t already know what is going on, there is no way I’m gonna read this implementation… its 1000 lines long.
I’m gonna post it without the back ticks just to emphasise that… [okay that was a little much…]
*makeParentEvents {
// define useful event subsets.
partialEvents = (
pitchEvent: (
mtranspose: 0,
gtranspose: 0.0,
ctranspose: 0.0,
octave: 5.0,
root: 0.0, // root of the scale
degree: 0,
scale: #[0, 2, 4, 5, 7, 9, 11], // diatonic major scale
stepsPerOctave: 12.0,
detune: 0.0, // detune in Hertz
harmonic: 1.0, // harmonic ratio
octaveRatio: 2.0,
note: #{
(~degree + ~mtranspose).degreeToKey(
~scale,
~scale.respondsTo(\stepsPerOctave).if(
{ ~scale.stepsPerOctave },
~stepsPerOctave
)
);
},
midinote: #{
(((~note.value + ~gtranspose + ~root) /
~scale.respondsTo(\stepsPerOctave).if(
{ ~scale.stepsPerOctave },
~stepsPerOctave) + ~octave - 5.0) *
(12.0 * ~scale.respondsTo(\octaveRatio).if
({ ~scale.octaveRatio }, ~octaveRatio).log2) + 60.0);
},
detunedFreq: #{
~freq.value + ~detune
},
freq: #{
(~midinote.value + ~ctranspose).midicps * ~harmonic;
},
freqToNote: #{ arg self, freq; // conversion from frequency to note value
self.use {
var midinote;
var steps = ~scale.respondsTo(\stepsPerOctave).if(
{ ~scale.stepsPerOctave }, ~stepsPerOctave
);
midinote = cpsmidi((freq / ~harmonic) - ~ctranspose);
midinote / 12.0 - ~octave * steps - ~root - ~gtranspose
}
},
freqToScale: #{ arg self, freq;
// conversion from frequency to scale value.
self.use {
var steps = ~scale.respondsTo(\stepsPerOctave).if(
{ ~scale.stepsPerOctave }, ~stepsPerOctave
);
var degree = self.freqToNote(freq).keyToDegree(~scale, steps)
- ~mtranspose;
degree.asArray.collect {|x, i|
x = x.round(0.01);
if(x.floor != x) {
"could not translate: %\n".postf(freq[i]);
nil
} { x }
}.unbubble;
}
}
),
durEvent: (
tempo: nil,
dur: 1.0,
stretch: 1.0,
legato: 0.8,
sustain: #{ ~dur * ~legato * ~stretch },
lag: 0.0,
strum: 0.0,
strumEndsTogether: false
),
ampEvent: (
amp: #{ ~db.dbamp },
db: -20.0,
velocity: 64, // MIDI units 0-127
pan: 0.0, // pan center
trig: 0.5
),
serverEvent: (
server: nil,
synthLib: nil,
group: { ~server.defaultGroup.nodeID },
out: 0,
addAction: 0,
instrument: \default,
variant: nil,
// this function should return a msgFunc: a Function that
// assembles a synth control list from event values
getMsgFunc: { |instrument|
var synthLib, desc;
// if user specifies a msgFunc, prefer user's choice
if(~msgFunc.isNil) {
instrument = ~instrument = instrument.asDefName;
synthLib = ~synthLib ?? { SynthDescLib.global };
desc = synthLib.at(instrument);
if (desc.notNil) {
~hasGate = desc.hasGate;
~msgFunc = desc.msgFunc;
} {
~msgFunc = ~defaultMsgFunc;
};
} { ~msgFunc };
},
synthDefName: { |instrument, variant, synthDesc|
// allow `nil to cancel a variant in a pattern
instrument = instrument.asDefName;
variant = variant.dereference;
if(variant.notNil and: { synthDesc.notNil and: { synthDesc.hasVariants } })
{ "%.%".format(instrument, variant).asSymbol }
{ instrument.asSymbol };
},
getBundleArgs: { |instrument|
~getMsgFunc.valueEnvir(instrument).valueEnvir;
}.flop,
hasGate: true, // assume SynthDef has gate
sendGate: nil, // false => event does not specify note release
defaultMsgFunc: #{|freq = 440, amp = 0.1, pan = 0, out = 0|
[\freq, freq, \amp, amp, \pan, pan, \out, out] },
// for \type \set
args: #[\freq, \amp, \pan, \trig],
timingOffset: 0,
// it is more efficient to directly use schedBundleArrayOnClock
// we keep these for compatibility.
schedBundle: #{ |lag, offset, server ... bundle |
schedBundleArrayOnClock(offset, thisThread.clock, bundle, lag, server);
},
schedBundleArray: #{ | lag, offset, server, bundleArray, latency |
schedBundleArrayOnClock(offset, thisThread.clock, bundleArray, lag, server, latency);
},
schedStrummedNote: {| lag, strumTime, sustain, server, msg, sendGate |
var dur, schedBundle = ~schedBundle;
schedBundle.value(lag, strumTime + ~timingOffset, server, msg);
if(sendGate) {
if (~strumEndsTogether) {
dur = sustain ;
} {
dur = sustain + strumTime
};
schedBundle.value(lag, dur + ~timingOffset, server,
[15 /* \n_set */, msg[2], \gate, 0])
}
}
),
bufferEvent: (
bufnum: 0,
filename: "",
frame: 0,
numframes: 0,
numchannels: 1,
gencmd: \sine1,
genflags: 7,
genarray: [1],
bufpos: 0,
leaveOpen: 0
),
midiEvent: (
midiEventFunctions: (
noteOn: #{ arg chan=0, midinote=60, amp=0.1;
[chan, midinote, asInteger((amp * 127).clip(0, 127)) ] },
noteOff: #{ arg chan=0, midinote=60, amp=0.1;
[ chan, midinote, asInteger((amp * 127).clip(0, 127)) ] },
polyTouch: #{ arg chan=0, midinote=60, polyTouch=125;
[ chan, midinote, polyTouch ] },
control: #{ arg chan=0, ctlNum, control=125;
[chan, ctlNum, control ] },
program: #{ arg chan=0, progNum=1; [ chan, progNum ] },
touch: #{ arg chan=0, val=125; [ chan, val ] },
bend: #{ arg chan=0, val=125; [ chan, val ] },
allNotesOff: #{ arg chan=0; [chan] },
smpte: #{ arg frames=0, seconds=0, minutes=0, hours=0, frameRate=25;
[frames, seconds, minutes, hours, frameRate] },
songPtr: #{ arg songPtr; [songPtr] },
sysex: #{ arg uid, array; [array] } // Int8Array
),
midicmd: \noteOn
),
nodeEvent: (
delta: 0,
addAction: 0,
group: { ~server.defaultGroup.nodeID },
latency: 0.2,
instrument: \default,
hasGate: true,
stopServerNode: #{
if (~hasGate == true)
{currentEnvironment.set(\gate, 0) }
{currentEnvironment.sendOSC([11, ~id]) };
~isPlaying = false;
},
freeServerNode: #{
currentEnvironment.sendOSC([11, ~id]);
~isPlaying = false;
},
releaseServerNode: #{
currentEnvironment.set(\gate, 0);
~isPlaying = false;
},
pauseServerNode: #{ currentEnvironment.sendOSC([12, ~id, false]); },
resumeServerNode: #{ currentEnvironment.sendOSC([12, ~id, true]); },
// perhaps these should be changed into mapServerNode etc.
map: #{ | ev, key, busIndex | ev.sendOSC([14, ev[\id], key, busIndex]) },
before: #{ | ev,target |
ev.sendOSC([18, ev[\id], target]);
ev[\group] = target; ev[\addAction] = 2;
},
after: #{ | ev, target |
ev.sendOSC([19, ev[\id], target]);
ev[\group] = target; ev[\addAction] = 3;
},
headOf: #{ | ev, group |
ev.sendOSC([22, group, ev[\id]]);
ev[\group] = group; ev[\addAction] = 0;
},
tailOf: #{ |ev, group |
ev.sendOSC([23, group, ev[\id]]);
ev[\group] = group; ev[\addAction] = 1;
},
isPlaying: #{ |ev| ^(ev[\isPlaying] == true) },
isPlaying_: #{ | ev, flag | ev[\isPlaying] = flag; },
nodeID: #{ |ev| ^ev[\id].asArray.last },
asEventStreamPlayer: #{|ev| ev }
),
playerEvent: (
type: \note,
play: #{
var tempo, server, eventTypes, parentType;
parentType = ~parentTypes[~type];
parentType !? { currentEnvironment.parent = parentType };
server = ~server = ~server ? Server.default;
~finish.value(currentEnvironment);
tempo = ~tempo;
tempo !? { thisThread.clock.tempo = tempo };
if(currentEnvironment.isRest.not) {
eventTypes = ~eventTypes;
(eventTypes[~type] ?? { eventTypes[\note] }).value(server)
};
~callback.value(currentEnvironment);
},
// synth / node interface
// this may be better moved into the cleanup events, but for now
// it avoids confusion.
// this is a preliminary implementation and does not recalculate dependent
// values like degree, octave etc.
freeServerNode: #{
if(~id.notNil) {
~server.sendBundle(~server.latency, ["/n_free"] ++ ~id);
~isPlaying = false;
};
},
// for some yet unknown reason, this function is uncommented,
// it breaks the play method for gatelesss synths
releaseServerNode: #{ |releaseTime|
var sendGate, msg;
if(~id.notNil) {
releaseTime = if(releaseTime.isNil) { 0.0 } { -1.0 - releaseTime };
sendGate = ~sendGate ? ~hasGate;
if(sendGate) {
~server.sendBundle(~server.latency,
*["/n_set", ~id, "gate", releaseTime].flop);
} {
~server.sendBundle(~server.latency, ["/n_free"] ++ ~id);
};
~isPlaying = false;
};
},
// the event types
parentTypes: (),
eventTypes: (
rest: #{},
note: #{|server|
var freqs, lag, strum, sustain;
var bndl, addAction, sendGate, ids;
var msgFunc, instrumentName, offset, strumOffset;
// var schedBundleArray;
freqs = ~detunedFreq.value;
// msgFunc gets the synth's control values from the Event
msgFunc = ~getMsgFunc.valueEnvir;
instrumentName = ~synthDefName.valueEnvir;
// determine how to send those commands
// sendGate == false turns off releases
sendGate = ~sendGate ? ~hasGate;
// update values in the Event that may be determined by functions
~freq = freqs;
~amp = ~amp.value;
~sustain = sustain = ~sustain.value;
lag = ~lag;
offset = ~timingOffset;
strum = ~strum;
~server = server;
~isPlaying = true;
addAction = Node.actionNumberFor(~addAction);
// compute the control values and generate OSC commands
bndl = msgFunc.valueEnvir;
bndl = [9 /* \s_new */, instrumentName, ids, addAction, ~group] ++ bndl;
if(strum == 0 and: { (sendGate and: { sustain.isArray })
or: { offset.isArray } or: { lag.isArray } }) {
bndl = flopTogether(
bndl,
[sustain, lag, offset]
);
#sustain, lag, offset = bndl[1].flop;
bndl = bndl[0];
} {
bndl = bndl.flop
};
// produce a node id for each synth
~id = ids = Array.fill(bndl.size, { server.nextNodeID });
bndl = bndl.collect { | msg, i |
msg[2] = ids[i];
msg.asOSCArgArray
};
// schedule when the bundles are sent
if (strum == 0) {
~schedBundleArray.(lag, offset, server, bndl, ~latency);
if (sendGate) {
~schedBundleArray.(
lag,
sustain + offset,
server,
[15 /* \n_set */, ids, \gate, 0].flop,
~latency
);
}
} {
if (strum < 0) {
bndl = bndl.reverse;
ids = ids.reverse
};
strumOffset = offset + Array.series(bndl.size, 0, strum.abs);
~schedBundleArray.(
lag, strumOffset, server, bndl, ~latency
);
if (sendGate) {
if (~strumEndsTogether) {
strumOffset = sustain + offset
} {
strumOffset = sustain + strumOffset
};
~schedBundleArray.(
lag, strumOffset, server,
[15 /* \n_set */, ids, \gate, 0].flop,
~latency
);
}
}
},
// optimized version of type \note, about double as efficient.
// Synth must have no gate and free itself after sustain.
// Event supports no strum, no conversion of argument objects to controls
grain: #{|server|
var freqs, lag, instr;
var bndl, addAction, sendGate, ids;
var msgFunc, instrumentName, offset;
freqs = ~detunedFreq.value;
// msgFunc gets the synth's control values from the Event
instr = ( ~synthLib ?? { SynthDescLib.global } ).at(~instrument);
if(instr.isNil) {
"Event: instrument % not found in SynthDescLib"
.format(~instrument).warn;
^this
};
msgFunc = instr.msgFunc;
instrumentName = ~synthDefName.valueEnvir;
// update values in the Event that may be determined by functions
~freq = freqs;
~amp = ~amp.value;
~sustain = ~sustain.value;
addAction = Node.actionNumberFor(~addAction);
// compute the control values and generate OSC commands
bndl = msgFunc.valueEnvir;
bndl = [9 /* \s_new */, instrumentName, -1, addAction, ~group.asControlInput] ++ bndl;
~schedBundleArray.(
~lag,
~timingOffset,
server,
bndl.flop,
~latency
);
},
on: #{|server|
var freqs;
var bndl, sendGate, ids;
var msgFunc, desc, synthLib, bundle, instrumentName;
freqs = ~detunedFreq.value;
~freq = freqs;
~amp = ~amp.value;
~isPlaying = true;
msgFunc = ~getMsgFunc.valueEnvir;
instrumentName = ~synthDefName.valueEnvir;
bndl = msgFunc.valueEnvir;
bndl = [9 /* \s_new */, instrumentName, ~id,
Node.actionNumberFor(~addAction), ~group] ++ bndl;
bndl = bndl.flop;
if ( (ids = ~id).isNil ) {
ids = Array.fill(bndl.size, {server.nextNodeID });
bndl = bndl.collect { | msg, i |
msg[2] = ids[i];
msg.asOSCArgArray
};
} {
bndl = bndl.asOSCArgBundle;
};
~schedBundleArray.value(~lag, ~timingOffset, server, bndl, ~latency);
~server = server;
~id = ids;
},
set: #{|server|
var freqs, lag, dur, strum, bndl, msgFunc;
freqs = ~freq = ~detunedFreq.value;
~server = server;
~amp = ~amp.value;
if(~args.size == 0) {
msgFunc = ~getMsgFunc.valueEnvir;
bndl = msgFunc.valueEnvir;
} {
bndl = ~args.envirPairs;
};
bndl = ([15 /* \n_set */, ~id] ++ bndl).flop.asOSCArgBundle;
~schedBundleArray.value(~lag, ~timingOffset, server, bndl, ~latency);
},
off: #{|server|
var gate;
if (~hasGate) {
gate = min(0.0, ~gate ? 0.0); // accept release times
~schedBundleArray.value(~lag, ~timingOffset, server,
[15 /* \n_set */, ~id.asControlInput, \gate, gate].flop,
~latency
)
} {
~schedBundleArray.value(~lag, ~timingOffset, server,
[\n_free, ~id.asControlInput].flop, ~latency)
};
~isPlaying = false;
},
kill: #{|server|
~schedBundleArray.value(~lag, ~timingOffset, server,
[\n_free, ~id.asControlInput].flop, ~latency)
},
group: #{|server|
var bundle, cmd;
if (~id.isNil) { ~id = server.nextNodeID };
bundle = [21 /* \g_new */, ~id.asArray, Node.actionNumberFor(~addAction),
~group.asControlInput].flop;
~schedBundleArray.value(~lag, ~timingOffset, server, bundle, ~latency);
},
parGroup: #{|server|
var bundle, cmd;
if (~id.isNil) { ~id = server.nextNodeID };
bundle = [63 /* \p_new */, ~id.asArray, Node.actionNumberFor(~addAction),
~group.asControlInput].flop;
~schedBundleArray.value(~lag, ~timingOffset, server, bundle, ~latency);
},
bus: #{|server|
var array;
array = ~array.asArray;
~schedBundle.value(~lag, ~timingOffset, server,
[\c_setn, ~out.asControlInput, array.size] ++ array);
},
fadeBus: #{ |server|
var bundle, instrument, rate, bus;
var array = ~array.as(Array);
var numChannels = min(~numChannels.value ? 1, array.size);
if(numChannels > SystemSynthDefs.numChannels) {
Error(
"Can't set more than % channels with current setup in SystemSynthDefs."
.format(SystemSynthDefs.numChannels)
).throw;
};
if (~id.isNil) { ~id = server.nextNodeID };
// the instrumentType can be system_setbus or system_setbus_hold
instrument = format(
if(~hold != true) { "system_setbus_%_%" } { "system_setbus_hold_%_%" },
~rate.value ? \control,
numChannels
);
// addToTail, so that old bus value can be overridden:
bundle = [9, instrument, ~id, 1, ~group.asControlInput,
"values", array,
"out", ~out.value,
"fadeTime", ~fadeTime,
"curve", ~curve
].asOSCArgArray;
~schedBundle.value(~lag, ~timingOffset, server, bundle);
if(~rate == \audio) { // control rate synth frees by itself, because bus holds the value
~stopServerNode = { server.sendBundle(server.latency, [\n_set, ~id, \gate, 0]) }
};
},
gen: #{|server|
~schedBundle.value(~lag, ~timingOffset, server,
[\b_gen, ~bufnum.asControlInput, ~gencmd, ~genflags] ++ ~genarray);
},
load: #{|server|
~schedBundle.value(~lag, ~timingOffset, server,
[\b_allocRead, ~bufnum.asControlInput, ~filename,
~frame, ~numframes]);
},
read: #{|server|
~schedBundle.value(~lag, ~timingOffset, server,
[\b_read, ~bufnum.asControlInput, ~filename,
~frame, ~numframes, ~bufpos, ~leaveOpen]);
},
alloc: #{|server|
~schedBundle.value(~lag, ~timingOffset, server,
[\b_alloc, ~bufnum.asControlInput, ~numframes, ~numchannels]);
},
free: #{|server|
~schedBundle.value(~lag, ~timingOffset, server,
[\b_free, ~bufnum.asControlInput]);
},
midi: #{|server|
var freqs, lag, dur, sustain, strum;
var bndl, midiout, hasGate, midicmd;
freqs = ~freq = ~detunedFreq.value;
~amp = ~amp.value;
~midinote = (freqs.cpsmidi).round(1).asInteger;
strum = ~strum;
lag = ~lag;
sustain = ~sustain = ~sustain.value;
midiout = ~midiout.value;
~uid ?? { ~uid = midiout.uid }; // mainly for sysex cmd
hasGate = ~hasGate ? true;
midicmd = ~midicmd;
bndl = ~midiEventFunctions[midicmd].valueEnvir.asCollection;
bndl = bndl.asControlInput.flop;
bndl.do {|msgArgs, i|
var latency;
latency = i * strum + lag;
if(latency == 0.0) {
midiout.performList(midicmd, msgArgs)
} {
thisThread.clock.sched(latency, {
midiout.performList(midicmd, msgArgs);
})
};
if(hasGate and: { midicmd === \noteOn }) {
thisThread.clock.sched(sustain + latency, {
midiout.noteOff(*msgArgs)
});
};
};
},
setProperties: {
var receiver = ~receiver,
go = {
~args.do { |each|
var selector, value = each.envirGet;
if(value.notNil) {
selector = each.asSetter;
if(~doTrace == true) {
postf("%.%_(%)\n",receiver,selector,value)
};
receiver.perform(selector.asSetter, value)
};
}
};
if(~defer ? true) {
// inEnvir is needed
// because we'll no longer be in this Event
// when defer wakes up
go.inEnvir.defer
} {
go.value
};
},
monoOff: #{|server|
if(~hasGate == false) {
~schedBundle.value(~lag, ~timingOffset, server,
[\n_free] ++ ~id.asControlInput);
} {
~schedBundle.value(~lag, ~timingOffset, server,
*([15 /* \n_set */, ~id.asControlInput, \gate, 0].flop) );
};
},
monoSet: #{|server|
var freqs, lag, bndl;
freqs = ~freq = ~detunedFreq.value;
~amp = ~amp.value;
~sstain = ~sustain.value;
bndl = ([15 /* \n_set */, ~id.asControlInput] ++ ~msgFunc.valueEnvir).flop;
bndl = bndl.collect(_.asOSCArgArray);
~schedBundle.value(~lag, ~timingOffset, server, *bndl);
},
monoNote: #{ |server|
var bndl, id, ids, addAction, f;
addAction = Node.actionNumberFor(~addAction);
~freq = ~detunedFreq.value;
f = ~freq;
~amp = ~amp.value;
bndl = ( [9 /* \s_new */, ~instrument, ids, addAction, ~group.asControlInput]
++ ~msgFunc.valueEnvir).flop;
bndl.do { | b |
id = server.nextNodeID;
ids = ids.add(id);
b[2] = id;
};
~id = ids;
if ((addAction == 0) || (addAction == 3)) {
bndl = bndl.reverse;
};
bndl = bndl.collect(_.asOSCArgArray);
~schedBundle.value(~lag, ~timingOffset, server, *bndl);
~updatePmono.value(ids, server);
},
Synth: #{ |server|
var instrumentName, desc, msgFunc, sustain;
var bndl, synthLib, addAction, group, latency, ids, id, groupControls;
~server = server;
addAction = Node.actionNumberFor(~addAction);
group = ~group.asControlInput;
~freq = ~detunedFreq.value;
~amp = ~amp.value;
~sustain = sustain = ~sustain.value;
ids = ~id;
msgFunc = ~getMsgFunc.valueEnvir;
instrumentName = ~synthDefName.valueEnvir;
bndl = [9 /* \s_new */, instrumentName, ids, addAction, group]
++ msgFunc.valueEnvir;
if ((addAction == 0) || (addAction == 3)) {
bndl = bndl.reverse;
};
bndl = bndl.collect(_.asOSCArgArray);
server.sendBundle(server.latency, *bndl);
~id = ids;
~isPlaying = true;
NodeWatcher.register(currentEnvironment);
},
Group: #{|server|
var ids, group, addAction, bundle;
ids = ~id = (~id ?? { server.nextNodeID }).asArray;
addAction = Node.actionNumberFor(~addAction);
group = ~group.asControlInput;
~server = server;
if ((addAction == 0) || (addAction == 3) ) {
ids = ids.reverse;
};
bundle = ids.collect {|id, i|
[21 /* \g_new */, id, addAction, group];
};
server.sendBundle(server.latency, *bundle);
~isPlaying = true;
NodeWatcher.register(currentEnvironment);
},
tree: #{ |server|
var doTree = { |tree, currentNode, addAction=1|
if(tree.isKindOf(Association)) {
~bundle = ~bundle.add([21 /* \g_new */,
tree.key.asControlInput, Node.actionNumberFor(addAction),
currentNode.asControlInput]);
currentNode = tree.key;
tree = tree.value;
};
if(tree.isSequenceableCollection) {
tree.do { |x, i|
x ?? { tree[i] = x = server.nextNodeID };
doTree.(x, currentNode)
};
} {
~bundle = ~bundle.add([21 /* \g_new */,
tree.asControlInput, Node.actionNumberFor(addAction),
currentNode.asControlInput]);
};
};
~bundle = nil;
~treeGroups = ~treeGroups ?? { ~tree.deepCopy };
~treeGroups !? {
doTree.(~treeGroups, ~group, ~addAction);
CmdPeriod.doOnce { ~treeGroups = nil };
};
~bundle !? {
server.sendBundle(server.latency, *~bundle);
};
~bundle = nil;
},
composite: { |server|
~resultEvents = ~types.collect { |type|
if(type != \composite) {
currentEnvironment.copy.put(\type, type).play;
};
};
}
)
)
);
parentEvents = (
default: ().putAll(
partialEvents.pitchEvent,
partialEvents.ampEvent,
partialEvents.durEvent,
partialEvents.bufferEvent,
partialEvents.serverEvent,
partialEvents.playerEvent,
partialEvents.midiEvent
),
groupEvent: (
lag: 0,
play: #{
var server, group, addAction, ids, bundle;
~finish.value;
group = ~group.asControlInput;
addAction = Node.actionNumberFor(~addAction);
~server = server= ~server ?? {Server.default};
ids = Event.checkIDs(~id, server);
if (ids.isNil) { ids = ~id = server.nextNodeID };
if ((addAction == 0) || (addAction == 3) ) {
ids = ids.asArray.reverse;
};
bundle = ids.collect {|id, i|
[21 /* \g_new */, id, addAction, group];
};
server.sendBundle(server.latency, *bundle);
~isPlaying = true;
~isRunning = true;
NodeWatcher.register(currentEnvironment);
}).putAll(partialEvents.nodeEvent),
synthEvent: (
lag: 0,
play: #{
var server, latency, group, addAction;
var instrumentName, synthLib, desc, msgFunc;
var msgs, cvs;
var bndl, ids;
~finish.value;
~server = server = ~server ?? { Server.default };
~sustain = ~sustain.value;
group = ~group.asControlInput;
addAction = Node.actionNumberFor(~addAction);
synthLib = ~synthLib ?? { SynthDescLib.global };
instrumentName = ~instrument.asDefName;
desc = synthLib.synthDescs[instrumentName];
if (desc.notNil) {
msgFunc = desc.msgFunc;
~hasGate = desc.hasGate;
} {
msgFunc = ~defaultMsgFunc;
};
msgs = msgFunc.valueEnvir.flop;
ids = Event.checkIDs(~id, server);
if (ids.isNil) { ids = msgs.collect { server.nextNodeID } };
bndl = ids.collect { |id, i|
[9 /* \s_new */, instrumentName, id, addAction, group]
++ msgs[i]
};
if ((addAction == 0) || (addAction == 3)) {
bndl = bndl.reverse;
};
bndl = bndl.asOSCArgBundle;
if (~lag !=0) {
server.sendBundle(server.latency ? 0 + ~lag, *bndl);
} {
server.sendBundle(server.latency, *bndl);
};
~id = ids;
~isPlaying = true;
~isRunning = true;
NodeWatcher.register(currentEnvironment);
},
defaultMsgFunc: #{|freq = 440, amp = 0.1, pan = 0, out = 0|
[\freq, freq, \amp, amp, \pan, pan, \out, out] }
).putAll(partialEvents.nodeEvent)
);
defaultParentEvent = parentEvents.default;
}