Hey everyone,
Here is the code I am using I know out of order and not yet in one code block. For some reason I play the buffer synths with my midi controllers launchpad and bcr 2000 and then out of nowhere the server goes yellow into idle state for no reason and I have no audio . Therefore I have to kill and then reload everything which is super annoying and it was not happening before so i have no idea why the sudden idling. Otherwise everything works besides the fried audio which I am handling with busses and making sure synths are being freed properly. Anyone know what the issue is ???
The server idle mostly happens when I am adjusting the code and not playing. Suddenly it just goes yellow and I do not have any audio.
I set all of these things before booting ( as I need to reduce cpu, get rid of crackles and latency )
s.options.sampleRate = 48000;
s.options.hardwareBufferSize = 1024;
s.options.blockSize = 1024;
s.options.memSize = 131072;
s.options.device = "ASIO : Focusrite USB A";
Server.default.options.numOutputBusChannels = 8;
Here is the full code:
(
{
~w1 = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/Worker Bee Cell 1 w1.wav", channels: [0, 1]);
// Define the downmixing SynthDef
// Boot the server and load the buffer
//~w1.play
//~w1.numFrames; ~w1.query, ~w1.dump, ~w1.numChannels.postln;, ~w1.duration;, (~w1.bufnum.notNil).postln;
~w2 = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/worker 2 cell w2.wav", channels: [0, 1]);
//~w2.play
~f3 = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/Forager Bee Cell 1 f3.wav", channels: [0, 1]);
//~f3.play
//~f3.bufnum.postln;, ~f3.query, ~f3.dump, ~f3.numChannels.postln;, ~f3.duration;
~f4 = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/cell 2 forager f4.wav", channels: [0, 1]);
//~f4.play
~q5 = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/queen 5 cell q5.wav", channels: [0, 1]);
//~q5.play
~d6 = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/drone 6.wav", channels: [0, 1]);
//~d6.play
~one = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/one bee 2ch -18dB.wav", channels: [0, 1]);
//~one.play
~bzz = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/bzz + 6dB.wav", channels: [0, 1]);
//~bzz.play
~swarm = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/swarm - 3,5dB.wav", channels: [0, 1]);
//~swarm.play
~toot = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/toot final.wav", channels: [0, 1]);
//~toot.play
~flute = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/flute final comp.wav", channels: [0, 1]);
//~flute.play
//~flute.bufnum.postln;
~overtone = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/overtone +12dB.wav", channels: [0, 1]);
//~flute.play
~zero = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/zero state honeycomb.wav", channels: [0, 1]);
//~zero.play
~flnd = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/reconcile flute.wav", channels: [0, 1]);
//~flnd.play
~bend = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/bend .wav", channels: [0, 1]);
//~bend.play
}.value;
)
(
~worker1 = Group.new;
~worker2 = Group.new;
~forager3 = Group.new;
~forager4 = Group.new;
~queen5 = Group.new;
~drone6 = Group.new;
~bee = Group.new;
~drone = Group.new;
~hive = Group.new;
~quack = Group.new;
~null = Group.new;
~peace = Group.new;
~reconcile = Group.new;
);
(
MIDIClient.init;
MIDIIn.connectAll;
);
//// release individual groups
(
~releaseGroup = { |group|
group.notNil.if({
group.set(\gate, 0); // Send release signal instead of hard freeAll
("Releasing synths in " + group).postln;
}, {
"Group is nil".postln;
});
};
)
/// release all groups
(
~releaseAllGroups = {
[~worker1, ~worker2, ~forager3, ~forager4, ~queen5, ~drone6, ~bee, ~drone, ~hive, ~quack,].do { |group|
if (group.notNil) {
group.set(\gate, 0); // Smooth release
};
};
"All groups are releasing synths smoothly.".postln;
};
)
(
// SynthDef for flute (4-channel output)
SynthDef.new(\flute, {
arg amp=1, gain=1, out=0, buf, rate=1, loop=1, gate=1;
var sig, env, quad, gainCtrl;
// PlayBuf in stereo, always looping
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, loop: 1, doneAction: 0);
// Envelope for smooth start/stop
env = EnvGen.kr(Env.asr(0.1, 1, 0.5), gate, doneAction: 2);
// Gain control (default 0 dB)
gainCtrl = amp.dbamp * gain;
// Apply gain and envelope
sig = sig * gainCtrl * env;
//sig.postln;
//sig.size.postln;
// Simple quad spread
// Manually route stereo signal to quad (0-3)
quad = [sig[0], sig[1], sig[0], sig[1]];
quad.postln;
// Output to four channels (quadriphonic)
Out.ar(out, quad); // ✅ Removed extra closing parenthesis
}).add;
// Dictionary to track the flute synth
~fluteSynths = IdentityDictionary.new;
/// CONFIGURE MIDI GAIN CONTROL FROM BCR 2000 HERE IN FLUTE MIDI DEF
// MIDI Note On handling
MIDIdef.noteOn(\flute_control, { |nn, chan, src|
if (chan == 75) { // Channel 70 starts the flute
if (~fluteSynths[chan].isNil) {
~fluteSynths[chan] = Synth(\flute, [
\buf, ~flute.bufnum,
\out, 0,
\amp, 0.dbamp,
\gain, (~channelGainDefaults[chan] ? 1),// Default to 0 dB
\gate, 1
]);
"Flute Synth Started".postln;
// ✅ Track it for BCR gain control
if (~chan[75].isNil) { ~chan[75] = List.new; };
~chan[75].add(~fluteSynths[chan]);
};
};
if (chan == 65) {
if (~fluteSynths[75].notNil) {
~fluteSynths[75].set(\gate, 0);
// ⬇ Remove from BCR tracking list
if (~chan[75].notNil) {
~chan[75].remove(~fluteSynths[75]);
};
~fluteSynths.removeAt(75);
"Flute Synth Released".postln;
};
};
});
// MIDI CC Mapping for Gain Control
MIDIdef.cc(\flute_gain, { |val, num, chan, src|
if (num == 27) { // Assign CC 74 for gain control
var mappedGain = val.linlin(0, 127, -12, 24); // Convert CC range to dB
if (~fluteSynths[75].notNil) {
~channelGainDefaults[75] = mappedGain.dbamp;
~fluteSynths[75].set(\amp, mappedGain.dbamp);
("Updated Flute Gain to " + mappedGain + " dB").postln;
};
};
}, 27);
~overtoneSynths = IdentityDictionary.new;
MIDIdef.noteOn(\overtone_control, { |nn, chan, src|
if (chan == 76) {
if (~overtoneSynths[chan].isNil) {
~overtoneSynths[chan] = Synth(\flute, [
\buf, ~overtone.bufnum,
\out, 0,
\amp, 0.dbamp,
\gain, (~channelGainDefaults[chan] ? 1),
\gate, 1
]);
"Overtone Started".postln;
// ✅ Track it for BCR gain control
if (~chan[76].isNil) { ~chan[76] = List.new; };
~chan[76].add(~overtoneSynths[chan]);
};
};
if (chan == 66) {
if (~overtoneSynths[76].notNil) {
~overtoneSynths[76].set(\gate, 0);
if (~chan[76].notNil) {
~chan[76].remove(~overtoneSynths[76]);
};
~overtoneSynths.removeAt(76);
"Overtone Stopped".postln;
};
};
});
MIDIdef.cc(\overtone_gain, { |val, num, chan, src|
if (num == 28) {
var mappedGain = val.linlin(0, 127, -24, 24);
if (~overtoneSynths[76].notNil) {
~channelGainDefaults[76] = mappedGain.dbamp;
~overtoneSynths[76].set(\amp, mappedGain.dbamp);
("Updated Overtone Gain to " + mappedGain + " dB").postln;
};
};
}, 28);
)
//// Organized synth
(
~playBee = { |buf|
var q = ~q5.tryPerform(\bufnum);
var d = ~d6.tryPerform(\bufnum);
var t = ~toot.tryPerform(\bufnum);
var isQueenOrDrone = (buf.bufnum == q) or: { buf.bufnum == d } or: { buf.bufnum == t };
var routeMode = isQueenOrDrone.if(1, 0);
("Playing buffer: %" ++ buf).postln;
("Routing to " ++ (routeMode == 1).if("channels 4–7", "channels 0–7")).postln;
Synth(\bee, [
\buf: buf.bufnum,
\routeMode: routeMode,
\outBus: 0
]);
};
SynthDef(\bee, {
arg amp = 1, gain = 1, outBus = 0, buf = 0, rate = 1, start = 0, loop = 1, gate = 1, da = 2, cutoff = 1000, routeMode = 0, lagTime = 0.05;
var sigStereo, env, outSig, smoothedGain;
smoothedGain = Lag.kr(gain, lagTime);
env = EnvGen.kr(Env.adsr(0.1, 0.2, 0.8, 0.5), gate, doneAction: da);
sigStereo = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, startPos: start, loop: loop) * smoothedGain;
sigStereo = LPF.ar(sigStereo * env * amp, Lag.kr(cutoff, 0.2));
outSig = Array.fill(8, { |i|
Select.ar(routeMode, [
// routeMode = 0: L/R across 0–7
sigStereo[i % 2],
// routeMode = 1: silence on 0–3, L/R on 4–7
(i < 4).if(DC.ar(0), sigStereo[i % 2])
])
});
Out.ar(outBus, outSig);
}).add;
)
(
~releaseAllChannels = {
"Releasing all synths from configured channels...".postln;
~channelToSynthConfig.keysValuesDo { |chan, config|
var group = config[\group];
if (group.notNil and: { group.isKindOf(Group) }) {
group.set(\gate, 0); // Smooth envelope release
("Released group on channel " ++ chan).postln;
} {
("No valid group found for channel " ++ chan).postln;
};
};
// Optionally also release anything in ~chan (dynamic per-channel synths)
~chan.keysValuesDo { |chan, synthList|
if (synthList.notEmpty) {
synthList.do { |s|
if (s.isKindOf(Synth)) {
s.set(\gate, 0);
};
};
("Released all synths in ~chan[" ++ chan ++ "]").postln;
};
};
};
)
// Use Dictionary instead of IdentityDictionary
~bcrToSynthChannelMap = Dictionary[
[0, 71] -> 81,
[1, 71] -> 82,
[2, 71] -> 83,
[3, 71] -> 84,
[4, 71] -> 85,
[5, 71] -> 86,
[6, 71] -> 37,
[7, 71] -> 38,
[0, 74] -> 71,
[1, 74] -> 72,
[2, 74] -> 73,
[3, 74] -> 74,
[4, 74] -> 75,
[5, 74] -> 76,
[6, 74] -> 77,
[7, 74] -> 87,
];
// Define a mapping of CC numbers to specific target MIDI channels
/// No active synth found for MIDI Channel 83!!!!
(
~ccToChannelMap = IdentityDictionary[
21 -> 81, // CC 21 controls synth on channel 81
22 -> 82, // CC 22 controls synth on channel 82
23 -> 83, // CC 23 controls synth on channel 83
24 -> 84, // CC 24 controls synth on channel 84
25 -> 85, // CC 25 controls synth on channel 85
26 -> 86 // CC 26 controls synth on channel 86
];
MIDIdef.cc(\filterControl, {
arg val, num, chan, src;
var mappedCutoff, targetChan;
mappedCutoff = val.linexp(0, 127, 50, 15000); // Map CC values to filter range
// Check if CC number exists in the mapping
if (~ccToChannelMap.includesKey(num)) {
targetChan = ~ccToChannelMap[num]; // Get target MIDI channel
if (~chan.includesKey(targetChan)) {
~chan[targetChan].do { |s|
if (s.isKindOf(Synth)) {
s.set(\cutoff, mappedCutoff);
};
};
("Updated cutoff for Channel " + targetChan + " to " + mappedCutoff).postln;
} {
("No active synths found for MIDI Channel " + targetChan).postln;
};
} {
("MIDI CC " + num + " is not mapped to any synth").postln;
};
}, (21..28)); // Only respond to CC numbers 21–26
)
MIDIdef.noteOff(\synthoff, { |nn, chan, src|
[nn, chan, src].postln;
// 🚫 Skip flute and overtone channels
if (chan == 75 or: { chan == 76 }) {
"Skipping synthoff for channel %".format(chan).postln;
^nil;
};
if (~chan[chan].notNil) {
~chan[chan].do { |s| s.set(\gate, 0) };
~chan[chan] = nil;
};
});
(
~releaseChannel = { |channel|
if (~chan[channel].notNil) {
~chan[channel].do { |synth| synth.set(\gate, 0) };
~chan.removeAt(channel);
("Released all layers on channel " + channel).postln;
};
};
)
(
// ⬅️ Setup gain memory + synth mapping
~chan = IdentityDictionary.new;
~channelGainDefaults = Dictionary.new;
~noFilterBuffers = ~noFilterBuffers ? [];
~channelToSynthConfig = IdentityDictionary[
81 -> (buf: ~w1.bufnum, group: ~worker1, out: 0),
82 -> (buf: ~w2.bufnum, group: ~worker2, out: 0),
83 -> (buf: ~f3.bufnum, group: ~forager3, out: 0),
84 -> (buf: ~f4.bufnum, group: ~forager4, out: 0),
85 -> (buf: ~q5.bufnum, group: ~queen5, out: 0),
86 -> (buf: ~d6.bufnum, group: ~drone6, out: 0),
71 -> (buf: ~bzz.bufnum, group: ~drone, out: 0),
72 -> (buf: ~one.bufnum, group: ~bee, out: 0),
73 -> (buf: ~swarm.bufnum, group: ~hive, out: 0),
74 -> (buf: ~toot.bufnum, group: ~quack, out: 0),
77 -> (buf: ~zero.bufnum, group: ~null, out: 0),
37 -> (buf: ~flnd.bufnum, group: ~peace, out: 0),
38 -> (buf: ~bend.bufnum, group: ~reconcile, out: 0)
];
MIDIdef.noteOn(\synth, { |nn, chan, src|
var config, synthArgs, isSpecial, routeMode, gainToUse, newSynth;
[chan, src].postln;
if (chan == 47) {
"Releasing all synths on channel 87".postln;
if (~chan[87].notNil and: { ~chan[87].notEmpty }) {
~chan[87].do { |s| if (s.isKindOf(Synth)) { s.set(\gate, 0) } };
~chan[87] = nil;
} {
"No synths to release on channel 87".postln;
};
};
if (chan == 88) {
"Releasing one synth on channel 87".postln;
if (~chan[87].notNil and: { ~chan[87].notEmpty }) {
var s = ~chan[87].removeAt(0);
if (s.isKindOf(Synth)) {
s.set(\gate, 0);
};
} {
"No synths to release on channel 87".postln;
};
};
if (chan == 58) {
~midiEnabled = true;
MIDIdef(\synthoff).enable;
"MIDI Input Enabled!".postln;
};
if (chan == 57) {
~midiEnabled = false;
MIDIdef(\synthoff).disable;
"MIDI Input Disabled!".postln;
};
if (chan == 87) { ~spawnSynths.(); };
if (chan == 36) {
~releaseAllChannels.();
};
if (chan == 48) {
"Releasing all synths smoothly".postln;
~releaseAllGroups.();
};
if (chan == 51) { "Releasing worker1".postln; ~worker1.set(\gate, 0); };
if (chan == 52) { "Releasing worker2".postln; ~worker2.set(\gate, 0); };
if (chan == 53) { "Releasing forager3".postln; ~forager3.set(\gate, 0); };
if (chan == 54) { "Releasing forager4".postln; ~forager4.set(\gate, 0); };
if (chan == 55) { "Releasing queen5".postln; ~queen5.set(\gate, 0); };
if (chan == 56) { "Releasing drone6".postln; ~drone6.set(\gate, 0); };
if (chan == 61) { "Releasing bzz".postln; ~drone.set(\gate, 0); };
if (chan == 62) { "Releasing one".postln; ~bee.set(\gate, 0); };
if (chan == 63) { "Releasing swarm".postln; ~hive.set(\gate, 0); };
if (chan == 64) { "Releasing toot".postln; ~quack.set(\gate, 0); };
if (chan == 78) { "Releasing zero".postln; ~null.set(\gate, 0); };
if (chan == 27) { "Releasing flnd".postln; ~peace.set(\gate, 0); };
if (chan == 28) { "Releasing bend".postln; ~reconcile.set(\gate, 0); };
if (chan == 41) { ~releaseChannel.(81); };
if (chan == 42) { ~releaseChannel.(82); };
if (chan == 43) { ~releaseChannel.(83); };
if (chan == 44) { ~releaseChannel.(84); };
if (chan == 45) { ~releaseChannel.(85); };
if (chan == 46) { ~releaseChannel.(86); };
if (chan == 31) { ~releaseChannel.(71); };
if (chan == 32) { ~releaseChannel.(72); };
if (chan == 33) { ~releaseChannel.(73); };
if (chan == 34) { ~releaseChannel.(74); };
if (chan == 35) { ~releaseChannel.(77); };
if (chan == 17) { ~releaseChannel.(37); };
if (chan == 18) { ~releaseChannel.(38); };
if (~channelToSynthConfig[chan].notNil) {
config = ~channelToSynthConfig[chan];
isSpecial = (config[\buf] == ~q5.bufnum) or: { config[\buf] == ~d6.bufnum } or: { config[\buf] == ~toot.bufnum };
routeMode = isSpecial.if(1, 0);
("Playing: buf=" + config[\buf] + ", group=" + config[\group] + ", out=" + config[\out]).postln;
if (~chan.isNil) { ~chan = IdentityDictionary.new; };
if (~chan[chan].isNil) { ~chan[chan] = List.new; };
gainToUse = ~channelGainDefaults[chan] ? 1;
synthArgs = [
\buf, config[\buf],
\loop, 1,
\gate, 1,
\out, config[\out],
\gain, gainToUse,
\da, 2,
\routeMode, routeMode
];
if (~noFilterBuffers.includes(config[\buf]).not) {
synthArgs = synthArgs ++ [\cutoff, 1000];
};
newSynth = Synth(\bee, synthArgs, config[\group]);
~chan[chan].add(newSynth);
("✅ Synth added to channel " + chan + ": " + newSynth).postln;
// ~chan[chan].add(Synth(\bee, synthArgs, config[\group]));
("Triggered Synth on Channel " + chan + " with buffer " + config[\buf] + " on bus " + config[\out]).postln;
} {
("No synth mapped to channel " + chan).postln;
};
});
// 🎛 BCR2000 Gain Control — with default gain fix
~debugMIDI = false;
~lastCCUpdate = 0;
MIDIdef.cc(\bcrGainControl, { |val, num, chan, src|
var now, lookupKey, mappedGain, targetChan, config, existing;
now = Main.elapsedTime;
if (now - ~lastCCUpdate < 0.01) {
if (~debugMIDI) { "⏱ Skipped due to rate limit".postln; };
^nil;
};
~lastCCUpdate = now;
lookupKey = [chan, num];
mappedGain = val.linlin(0, 127, -50, 18).dbamp; // Map MIDI CC value to dB, then to amp
targetChan = ~bcrToSynthChannelMap[lookupKey];
if (~debugMIDI) {
("🎛 MIDI Received: Channel " + chan + " | CC=" + num + " | Value=" + val).postln;
};
if (targetChan.notNil) {
// 💾 Persist gain as the new default for this channel
~channelGainDefaults[targetChan] = mappedGain;
// 🔄 Apply gain to all currently playing synths on this channel
existing = ~chan[targetChan];
if (existing.notNil) {
existing.do { |synth|
if (synth.notNil and: { synth.isKindOf(Synth) }) {
synth.set(\gain, mappedGain);
};
};
};
if (~debugMIDI) {
("✅ Updated and saved gain for Synth " + targetChan + " to " + mappedGain).postln;
};
} {
if (~debugMIDI) {
("❌ No mapping for [chan, num] = " + lookupKey).postln;
};
};
}, (0..127));
if (~chan[87].notNil and: { ~chan[87].notEmpty }) {
~chan[87].do { |s|
if (s.isPlaying) {
s.set(\gate, 0); // let the EnvGen release smoothly
};
};
// Give time for release before spawning new ones
0.6.wait; // match your ADSR release time
};
~spawnSynths = {
var gainFor, spawnSynth;
// Helper to get gain from defaults or fallback to 1
gainFor = { |chan| ~channelGainDefaults[chan] ? 1 };
spawnSynth = { |name, buf, group, out, chan|
var synth, gain;
gain = gainFor.(chan);
// ✅ Step 4: Debug gain before spawning
("🔍 Spawning synth for channel " + chan + " with gain: " + gain).postln;
synth = Synth.new(\bee, [
\buf, buf,
\loop, 1,
\gate, 1,
\gain, gain,
\lagTime, 0, // Make sure lag is disabled at spawn
\out, out
], group);
synth;
};
~cell1 = Group.new;
~cell2 = Group.new;
~cell3 = Group.new;
~cell4 = Group.new;
~cell5 = Group.new;
~cell6 = Group.new;
~synths = (
worker1: spawnSynth.(\worker1, ~w1.bufnum, ~cell1, 0, 81),
worker2: spawnSynth.(\worker2, ~w2.bufnum, ~cell2, 0, 82),
forager3: spawnSynth.(\forager3, ~f3.bufnum, ~cell3, 0, 83),
forager4: spawnSynth.(\forager4, ~f4.bufnum, ~cell4, 0, 84),
queen5: spawnSynth.(\queen5, ~q5.bufnum, ~cell5, 0, 85),
drone6: spawnSynth.(\drone6, ~d6.bufnum, ~cell6, 0, 86)
);
// 🔥 Store them for later release via channel 47 / 88
if (~chan[87].isNil) {
~chan[87] = List.new;
};
~chan[87].addAll([
~synths.worker1,
~synths.worker2,
~synths.forager3,
~synths.forager4,
~synths.queen5,
~synths.drone6
]);
"✅ Synths Spawned with Preserved Gain Levels!".postln;
// ✅ Step 6: Debug gain values from each synth
~chan[87].do { |s|
s.get(\gain, { |val| ("🎚️ Gain reported from " + s + ": " + val).postln; });
};
};
)