Hey guys still kinda new to Supercollider but I am capable of coding a few things. I didn’t think this is a problem that would even appear and I wouldnt be able to solve honestly.
Launchpad pro runs multiple buffers and I’m trying to use the bcr 20000 control gain of each of these buffers. No matter what I do, even when I see the printed data of the adjusted gain, I can’t hear the difference in intensity.
- X** is the option to adjust the gain before I run the synth buffers. Now for some reason in the Gain Chan frame, the message - nan ID keeps appearing to the most recently affected Chan.
** Y** works with the option of connecting bcr2000 to midi chan dictionary where midi cc is refered to midi chans of Launchpad to control gain but for some reason midi between controllers doesn’t print any data or has no perceivable effect in intensity. It is possible that BCR 2000 reads midi better via MIDI Func and the problem is the Midi Trace format of both controllers but it’s hard to say to for me.
Now why am I using Midi Trace in the first place ? Well…
Another strange thing that happens is that when I do MIDI In for both Launchpad Pro and Bcr2000, only BCR2000 shows data via Midi Def or Midi Func and this doesn’t work either:
(
MIDIIn.connect(0, 2); // Connect Launchpad Pro (source 0)
MIDIIn.connect(1, 3); // Connect BCR2000 (source 3)
)
/// data per device
(
MIDIFunc.cc({ |val, num, chan, src|
("BCR2000 | CC: " + num + " | Value: " + val).postln;
}, nil, nil, 3);
MIDIFunc.cc({ |val, num, chan, src|
("Launchpad Pro | CC: " + num + " | Value: " + val).postln;
}, nil, nil, 0);
The only thing that will fix it is this but then I suspect the midi is in the wrong format but maybe I am mistaken.
(
MIDIClient.init;
MIDIIn.connectAll;
);
MIDIFunc.trace(true);
Sorry for not formatting this in the proper language. I wanted to upload a file but Im a new user. Can anyone enlighten so as to how to solve this problem. I am using Windows 11 HP PC and Supercollider 3.13 if that also helps.
X
(
MIDIClient.init;
MIDIIn.connectAll;
);
MIDIFunc.trace(true);
(
s.waitForBoot {
// Load buffers
~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]);
~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]);
~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]);
~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]);
~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]);
~d6 = Buffer.readChannel(s, "C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/drone 6 cell d6.wav", channels: [0, 1]);
~one = Buffer.readChannel(s, "C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/one bee stereo(double mono).wav", channels: [0, 1]);
~bzz = Buffer.readChannel(s, "C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/zen bees - buzz drone .wav", channels: [0, 1]);
~swarm = Buffer.readChannel(s, "C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/bee swarm.wav", channels: [0, 1]);
~toot = Buffer.readChannel(s, "C:/Users/janda/Downloads/To BEE or not to BE/INTERACTIVE HONEYCOMB/Bee Cells For Sampler/queen bee tooting then quacking.wav", channels: [0, 1]);
// Create groups
~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;
// Create control buses for gain
~gainBus = (0..7).collect { Bus.control(s, 1) } ++ (0..1).collect { Bus.control(s, 1) };
// Define SynthDef
SynthDef.new(\bee, {
arg amp=1, out=0, buf, rate=1, start=0, loop=1, gate=1, da=0, cutoff=1000, gainBus;
var sig, env, gain;
// Envelope
env = EnvGen.kr(Env.adsr(0.1, 0.2, 0.8, 0.5), gate, doneAction: da);
// Read gain from control bus
gain = max(0, In.kr(gainBus, 1) * amp);
// Sound source
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, startPos: start, loop: loop, doneAction: 0) * gain;
// Apply envelope and low-pass filter
sig = LPF.ar(sig * env, cutoff);
// Output
Out.ar(out, sig);
}).add;
// Debugging function to print gain values
~printGains = {
(0..7).do { |i| ~gainBus[i].get({ |val| ("Gain CH " + i + ": " + val).postln }); };
(0..1).do { |i| ~gainBus[8 + i].get({ |val| ("Gain CH " + (8 + i) + ": " + val).postln }); };
};
// MIDI Control for Gain (CC 71 for first 8 buffers, CC 74 for last 2 buffers)
MIDIFunc.cc({ |val, num, chan, src|
var mappedGain = val.linlin(0, 127, 0, 1);
if (num == 71, { ~gainBus[chan].set(mappedGain); });
("Updated Gain CH " + chan + " to " + mappedGain).postln;
~printGains.();
}, ccNum: 71);
MIDIFunc.cc({ |val, num, chan, src|
var mappedGain = val.linlin(0, 127, 0, 1);
if (num == 74, { ~gainBus[8 + chan].set(mappedGain); });
("Updated Gain CH " + (8 + chan) + " to " + mappedGain).postln;
~printGains.();
}, ccNum: 74);
// Function to trigger buffer playback
~triggerBuffer = { |buffer, index|
Synth(\bee, [\buf, buffer, \out, 0, \gainBus, ~gainBus[index].index, \gate, 1]);
};
}
)
(
~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].do { |group|
if (group.notNil) {
group.set(\gate, 0); // Smooth release
};
};
"All groups are releasing synths smoothly.".postln;
};
)
(
~chan = IdentityDictionary.new;
~noFilterBuffers = ~noFilterBuffers ? []; // Ensure ~noFilterBuffers is initialized
~channelToSynthConfig = IdentityDictionary[
71 -> (buf: ~w1.bufnum, group: ~worker1, out: 0),
72 -> (buf: ~w2.bufnum, group: ~worker2, out: 0),
73 -> (buf: ~f3.bufnum, group: ~forager3, out: 0),
74 -> (buf: ~f4.bufnum, group: ~forager4, out: 0),
75 -> (buf: ~q5.bufnum, group: ~queen5, out: 0),
76 -> (buf: ~d6.bufnum, group: ~drone6, out: 0),
66 -> (buf: ~bzz.bufnum, group: ~drone, out: 0),
67 -> (buf: ~one.bufnum, group: ~bee, out: 0),
68 -> (buf: ~swarm.bufnum, group: ~hive, out: 0),
69 -> (buf: ~toot.bufnum, group: ~quack, out: 0)
];
MIDIdef.noteOn(\synth, { |nn, chan, src|
var config, synthArgs;
[chan, src].postln;
if (chan == 43) {
~midiEnabled = true;
MIDIdef(\synthoff).enable;
"MIDI Input Enabled!".postln;
};
if (chan == 42) {
~midiEnabled = false;
MIDIdef(\synthoff).disable;
"MIDI Input Disabled!".postln;
};
if (chan == 77) { ~spawnSynths.(); };
if (chan == 78) { ~freeSynths.(); };
if (chan == 56) {
"Releasing all synths smoothly".postln;
~releaseAllGroups.();
};
// Free groups based on MIDI channel
if (chan == 36) { "Releasing worker1".postln; ~worker1.set(\gate, 0); };
if (chan == 37) { "Releasing worker2".postln; ~worker2.set(\gate, 0); };
if (chan == 38) { "Releasing forager3".postln; ~forager3.set(\gate, 0); };
if (chan == 39) { "Releasing forager4".postln; ~forager4.set(\gate, 0); };
if (chan == 40) { "Releasing queen5".postln; ~queen5.set(\gate, 0); };
if (chan == 41) { "Releasing drone6".postln; ~drone6.set(\gate, 0); };
if (chan == 46) { "Releasing bzz".postln; ~drone.set(\gate, 0); };
if (chan == 47) { "Releasing one".postln; ~bee.set(\gate, 0); };
if (chan == 48) { "Releasing swarm".postln; ~hive.set(\gate, 0); };
if (chan == 49) { "Releasing toot".postln; ~quack.set(\gate, 0); };
// Check if the channel has a synth mapping
if (~channelToSynthConfig[chan].notNil) {
config = ~channelToSynthConfig[chan];
("Playing: buf=" + config[\buf] + ", group=" + config[\group] + ", out=" + config[\out]).postln;
if (~chan.isNil) { ~chan = IdentityDictionary.new; };
// Ensure ~noFilterBuffers is an array before checking
if (~noFilterBuffers.isNil.not and: { (~noFilterBuffers.asArray).includes(config[\buf]).not }) {
config.postln; // Debugging: Show config when condition is met
};
// Base synth arguments
synthArgs = [
\buf, config[\buf],
\loop, 1,
\gate, 1,
\out, config[\out]
];
// If the buffer is NOT in the noFilter list, add a cutoff parameter
if (~noFilterBuffers.includes(config[\buf]).not) {
synthArgs = synthArgs ++ [\cutoff, 1000];
};
~chan[chan] = 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;
};
});
)
// Define a mapping of CC numbers to specific target MIDI channels
(
~ccToChannelMap = IdentityDictionary[
21 -> 71, // CC 20 controls synth on channel 71
22 -> 72, // CC 21 controls synth on channel 61
23 -> 73, // CC 22 controls synth on channel 58
24 -> 74, // CC 23 controls synth on channel 53
25 -> 75, // CC 24 controls synth on channel 78
26 -> 76, // CC 25 controls synth on channel 73
];
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
// Ensure the target synth exists before setting the cutoff
if (~chan.includesKey(targetChan) and: { ~chan[targetChan].isKindOf(Synth) }) {
~chan[targetChan].set(\cutoff, mappedCutoff);
("Updated cutoff for Channel " + targetChan + " to " + mappedCutoff).postln;
} {
("No active synth found for MIDI Channel " + targetChan).postln;
};
} {
("MIDI CC " + num + " is not mapped to any synth").postln;
};
}, (21..28)); // Only respond to CC numbers 20–28
)
// Untrigger buffer synths
MIDIdef.noteOff(\synthoff, {
arg nn, chan, src;
[nn, chan, src].postln;
if (~chan[chan].notNil) {
~chan[chan].set(\gate, 0);
~chan[chan] = nil;
};
});
(
~spawnSynths = {
~cell1 = Group.new;
~cell2 = Group.new;
~cell3 = Group.new;
~cell4 = Group.new;
~cell5 = Group.new;
~cell6 = Group.new;
~synths = (
worker1: Synth.new(\bee, [\buf, ~w1.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell1),
worker2: Synth.new(\bee, [\buf, ~w2.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell2),
forager3: Synth.new(\bee, [\buf, ~f3.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell3),
forager4: Synth.new(\bee, [\buf, ~f4.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell4),
queen5: Synth.new(\bee, [\buf, ~q5.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell5),
drone6: Synth.new(\bee, [\buf, ~d6.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell6)
);
"Synths Spawned!".postln;
};
// Function to smoothly release synths
~freeSynths = {
~synths.do { |synth|
synth.set(\gate, 0); // Smooth release via envelope
};
"Synths Released with Envelope!".postln;
};
)
Y
(
MIDIClient.init;
MIDIIn.connectAll;
);
(
{
~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 cell d6.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 stereo(double mono).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/zen bees - buzz drone .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/bee swarm.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/queen bee tooting then quacking.wav", channels: [0, 1]);
//~toot.play
~flute = Buffer.readChannel(s,"C:/Users/janda/Downloads/To BEE or not to BE/flute sample.wav", channels: [0, 1]);
//~flute.play
//~flute.bufnum.postln;
}.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;
);
s.plotTree
//// 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].do { |group|
if (group.notNil) {
group.set(\gate, 0); // Smooth release
};
};
"All groups are releasing synths smoothly.".postln;
};
)
// Define SynthDef for looping flute with gain control
SynthDef.new(\flute, {
arg amp=1, out=0, buf, rate=1, loop=1, gate=1;
var sig, env, gain;
// 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)
gain = amp.dbamp;
// Apply gain and envelope
sig = sig * gain * env;
// Output to four channels (quadriphonic)
Out.ar(out, sig.dup(2));
}).add;
// Dictionary to track the flute synth
~fluteSynths = IdentityDictionary.new;
// MIDI Note On handling
MIDIdef.noteOn(\flute_control, { |nn, chan, src|
if (chan == 70) { // Channel 70 starts the flute
if (~fluteSynths[chan].isNil) {
~fluteSynths[chan] = Synth(\flute, [
\buf, ~flute.bufnum,
\out, 0,
\amp, 0.dbamp, // Default to 0 dB
\gate, 1
]);
"Flute Synth Started".postln;
};
};
if (chan == 50) { // Channel 71 stops the flute
if (~fluteSynths[70].notNil) {
~fluteSynths[70].set(\gate, 0);
~fluteSynths.removeAt(70);
"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, -24, 24); // Convert CC range to dB
if (~fluteSynths[70].notNil) {
~fluteSynths[70].set(\amp, mappedGain.dbamp);
("Updated Flute Gain to " + mappedGain + " dB").postln;
};
};
}, 27);
)
(
SynthDef.new(\bee, {
arg amp=1, gain=1, out=0, buf, rate=1, start=0, loop=1, gate=1, da=0, cutoff=1000;
var sig, env;
// Envelope
env = EnvGen.kr(Env.adsr(0.1, 0.2, 0.8, 0.5), gate, doneAction: da);
// Sound source
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, startPos: start, loop: loop)* gain;
// Apply envelope
sig = sig * env; // Multiply by gain
// Apply low-pass filter
sig = LPF.ar(sig, cutoff);
// Output sound
Out.ar(out, sig);
}).add;
)
a = Synth.new(\bee, [\buf, ~w1.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0, \cutoff, 1000], ~worker1);
a.set(\gain, 0.1);
a.set(\gain, 2);
a.set(\gain, 0);
b = Synth.new(\bee, [\buf, ~w2.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0, \cutoff, 1000], ~worker2);
b.set(\gate, 0);
c = Synth.new(\bee, [\buf, ~f3.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0, \cutoff, 1000], ~forager3);
c.set(\gate, 0);
d = Synth.new(\bee, [\buf, ~f4.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0, \cutoff, 1000], ~forager4);
d.set(\gate, 0);
e = Synth.new(\bee, [\buf, ~q5.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0, \cutoff, 1000], ~queen5);
e.set(\gate, 0);
f = Synth.new(\bee, [\buf, ~d6.bufnum,\gain, 1, \loop, 1, \gate, 1, \out, 0, \cutoff, 1000], ~drone6);
f.set(\gate, 0);
g = Synth.new(\bee, [\buf, ~one.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0], ~bee);
g.set(\gate, 0);
h = Synth.new(\bee, [\buf, ~bzz.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0], ~drone);
h.set(\gate, 0);
i = Synth.new(\bee, [\buf, ~swarm.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0], ~hive);
i.set(\gate, 0);
j = Synth.new(\bee, [\buf, ~toot.bufnum, \gain, 1, \loop, 1, \gate, 1, \out, 0], ~quack);
j.set(\gate, 0);
k = Synth.new(\flute, [\buf, ~flute.bufnum, \loop, 1, \gate, 1, \out, 0]);
k.set(\gate, 0);
(
~chan = IdentityDictionary.new;
~noFilterBuffers = ~noFilterBuffers ? []; // Ensure ~noFilterBuffers is initialized
~channelToSynthConfig = IdentityDictionary[
71 -> (buf: ~w1.bufnum, group: ~worker1, out: 0),
72 -> (buf: ~w2.bufnum, group: ~worker2, out: 0),
73 -> (buf: ~f3.bufnum, group: ~forager3, out: 0),
74 -> (buf: ~f4.bufnum, group: ~forager4, out: 0),
75 -> (buf: ~q5.bufnum, group: ~queen5, out: 0),
76 -> (buf: ~d6.bufnum, group: ~drone6, out: 0),
66 -> (buf: ~bzz.bufnum, group: ~drone, out: 0),
67 -> (buf: ~one.bufnum, group: ~bee, out: 0),
68 -> (buf: ~swarm.bufnum, group: ~hive, out: 0),
69 -> (buf: ~toot.bufnum, group: ~quack, out: 0)
];
MIDIdef.noteOn(\synth, { |nn, chan, src|
var config, synthArgs;
[chan, src].postln;
if (chan == 43) {
~midiEnabled = true;
MIDIdef(\synthoff).enable;
"MIDI Input Enabled!".postln;
};
if (chan == 42) {
~midiEnabled = false;
MIDIdef(\synthoff).disable;
"MIDI Input Disabled!".postln;
};
if (chan == 77) { ~spawnSynths.(); };
if (chan == 78) { ~freeSynths.(); };
if (chan == 56) {
"Releasing all synths smoothly".postln;
~releaseAllGroups.();
};
// Free groups based on MIDI channel
if (chan == 36) { "Releasing worker1".postln; ~worker1.set(\gate, 0); };
if (chan == 37) { "Releasing worker2".postln; ~worker2.set(\gate, 0); };
if (chan == 38) { "Releasing forager3".postln; ~forager3.set(\gate, 0); };
if (chan == 39) { "Releasing forager4".postln; ~forager4.set(\gate, 0); };
if (chan == 40) { "Releasing queen5".postln; ~queen5.set(\gate, 0); };
if (chan == 41) { "Releasing drone6".postln; ~drone6.set(\gate, 0); };
if (chan == 46) { "Releasing bzz".postln; ~drone.set(\gate, 0); };
if (chan == 47) { "Releasing one".postln; ~bee.set(\gate, 0); };
if (chan == 48) { "Releasing swarm".postln; ~hive.set(\gate, 0); };
if (chan == 49) { "Releasing toot".postln; ~quack.set(\gate, 0); };
// Check if the channel has a synth mapping
if (~channelToSynthConfig[chan].notNil) {
config = ~channelToSynthConfig[chan];
("Playing: buf=" + config[\buf] + ", group=" + config[\group] + ", out=" + config[\out]).postln;
if (~chan.isNil) { ~chan = IdentityDictionary.new; };
// Ensure ~noFilterBuffers is an array before checking
if (~noFilterBuffers.isNil.not and: { (~noFilterBuffers.asArray).includes(config[\buf]).not }) {
config.postln; // Debugging: Show config when condition is met
};
// Base synth arguments
synthArgs = [
\buf, config[\buf],
\loop, 1,
\gate, 1,
\out, config[\out]
];
// If the buffer is NOT in the noFilter list, add a cutoff parameter
if (~noFilterBuffers.includes(config[\buf]).not) {
synthArgs = synthArgs ++ [\cutoff, 1000];
};
~chan[chan] = 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;
};
});
)
// Define a mapping of CC numbers to specific target MIDI channels
(
~ccToChannelMap = IdentityDictionary[
21 -> 71, // CC 20 controls synth on channel 71
22 -> 72, // CC 21 controls synth on channel 61
23 -> 73, // CC 22 controls synth on channel 58
24 -> 74, // CC 23 controls synth on channel 53
25 -> 75, // CC 24 controls synth on channel 78
26 -> 76, // CC 25 controls synth on channel 73
];
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
// Ensure the target synth exists before setting the cutoff
if (~chan.includesKey(targetChan) and: { ~chan[targetChan].isKindOf(Synth) }) {
~chan[targetChan].set(\cutoff, mappedCutoff);
("Updated cutoff for Channel " + targetChan + " to " + mappedCutoff).postln;
} {
("No active synth found for MIDI Channel " + targetChan).postln;
};
} {
("MIDI CC " + num + " is not mapped to any synth").postln;
};
}, (21..28)); // Only respond to CC numbers 20–28
)
// Untrigger buffer synths
MIDIdef.noteOff(\synthoff, {
arg nn, chan, src;
[nn, chan, src].postln;
if (~chan[chan].notNil) {
~chan[chan].set(\gate, 0);
~chan[chan] = nil;
};
});
//// TRIGGER ALL SYNTHS
// Function to create synths
(
~spawnSynths = {
~cell1 = Group.new;
~cell2 = Group.new;
~cell3 = Group.new;
~cell4 = Group.new;
~cell5 = Group.new;
~cell6 = Group.new;
~synths = (
worker1: Synth.new(\bee, [\buf, ~w1.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell1),
worker2: Synth.new(\bee, [\buf, ~w2.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell2),
forager3: Synth.new(\bee, [\buf, ~f3.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell3),
forager4: Synth.new(\bee, [\buf, ~f4.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell4),
queen5: Synth.new(\bee, [\buf, ~q5.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell5),
drone6: Synth.new(\bee, [\buf, ~d6.bufnum, \loop, 1, \gate, 1, \out, 0], ~cell6)
);
"Synths Spawned!".postln;
};
// Function to smoothly release synths
~freeSynths = {
~synths.do { |synth|
synth.set(\gate, 0); // Smooth release via envelope
};
"Synths Released with Envelope!".postln;
};
)
////MIDI
(
MIDIClient.init;
MIDIIn.connectAll;
);
MIDIClient.sources;
MIDIdef(\synth).enable; /// Enable noteon
MIDIdef(\synth).disable;/// disable noteon
MIDIdef(\synth).free;/// delete Midi Def
MIDIdef(\synth).freeAll;// delete all Midi Defs: by default ctrl. will also delete MIDI DEFs unless specified otherwise
MIDIdef(\synthoff).enable; /// Enable noteon
MIDIdef(\synthoff).disable;/// disable noteon
MIDIdef(\synthoff).free;/// delete Midi Def
MIDIdef(\synthoff).freeAll
MIDIdef.noteOn(\synth, {"buffer".postln}).permanent_(true);
MIDIdef.noteOn(\filterControl, {"buffer".postln}).permanent_(true);/// this will make sure ctrl. does not delete the Midi Def
MIDIdef.noteOn(\bcrGainControl, {"buffer".postln}).permanent_(true);
MIDIdef.cc(\bcrGainControl, { |val, num, chan, src|
var lookupKey = [chan, num];
var mappedGain = val.linlin(0, 127, -60, 6).dbamp;
("🎛 MIDI Received: Channel " + chan + " | CC=" + num + " | Value=" + val).postln;
if (~bcrToSynthChannelMap.includesKey(lookupKey)) {
var targetChan = ~bcrToSynthChannelMap[lookupKey];
if (~chan.includesKey(targetChan).not or: (~chan[targetChan].isRunning.not)) {
// 🎹 Start synth only if it doesn’t exist or isn’t running
if (~channelToSynthConfig.includesKey(targetChan)) {
~chan[targetChan] = Synth(\bee, [
\buf, ~channelToSynthConfig[targetChan][\buf],
\loop, 1,
\gate, 1,
\out, ~channelToSynthConfig[targetChan][\out],
\gain, mappedGain // Start with correct gain
], ~channelToSynthConfig[targetChan][\group]);
("🚀 Started Synth on Channel: " + targetChan).postln;
} {
("⚠️ No config found for Channel " + targetChan).postln;
};
} {
// 🎚 If the synth is already running, just update gain
~chan[targetChan].set(\gain, mappedGain);
("✅ Updated gain for Synth " + targetChan + " to " + mappedGain).postln;
};
// 🔎 Check the new gain value
~chan[targetChan].get(\gain, { |val| ("📢 Final Gain for " + targetChan + " -> " + val).postln; });
} {
("❌ MIDI Mapping Failed: " + lookupKey + " is not in `~bcrToSynthChannelMap`").postln;
};
}, (0..127));
~bcrToSynthChannelMap.keys.do { |key|
("🔍 Key in Map: " + key + " -> " + ~bcrToSynthChannelMap[key]).postln;
};
returns:
🔍 Key in Map: [ 3, 71 ] -> 74
🔍 Key in Map: [ 6, 71 ] -> 66
🔍 Key in Map: [ 1, 74 ] -> 69
🔍 Key in Map: [ 1, 71 ] -> 72
🔍 Key in Map: [ 2, 71 ] -> 73
🔍 Key in Map: [ 5, 71 ] -> 76
🔍 Key in Map: [ 0, 71 ] -> 71
🔍 Key in Map: [ 4, 71 ] -> 75
🔍 Key in Map: [ 2, 74 ] -> 70
🔍 Key in Map: [ 7, 71 ] -> 67
🔍 Key in Map: [ 0, 74 ] -> 68
~bcrToSynthChannelMap = IdentityDictionary[
[0, 71] -> 71,
[1, 71] -> 72,
[2, 71] -> 73,
[3, 71] -> 74,
[4, 71] -> 75,
[5, 71] -> 76,
[6, 71] -> 66,
[7, 71] -> 67,
[0, 74] -> 68,
[1, 74] -> 69,
[2, 74] -> 70
];
-> IdentityDictionary[ ([ 1, 71 ] -> 72), ([ 0, 74 ] -> 68), ([ 1, 74 ] -> 69), ([ 2, 71 ] -> 73), ([ 6, 71 ] -> 66),
([ 7, 71 ] -> 67), ([ 4, 71 ] -> 75), ([ 5, 71 ] -> 76), ([ 3, 71 ] -> 74), ([ 0, 71 ] -> 71),
([ 2, 74 ] -> 70) ]
//when gain is updated
🎛 MIDI Received: Channel 0 | CC= 71 | Value= 74
❌ MIDI Mapping Failed: [ 0, 71 ] is not in `~bcrToSynthChannelMap`
🎛 MIDI Received: Channel 0 | CC= 71 | Value= 77