would it be helpful to see the synthdefs and MIDI code I made?
( // main patch
s.waitForBoot {
s.plotTree;
s.meter;
if (MIDIClient.initialized.not) {
MIDIClient.init;
postln("MIDI has been initialised!");
} {
postln("MIDI already running!")
};
~lumatone = MIDIIn.findPort("from-Lumatone", "from-Lumatone");
//MIDIIn.connect(0, ~lumatone);
//MIDIIn.disconnect(0, ~lumatone);
//~keyboardIn = MIDIIn.findPort("from-Keyboard", "from-Keyboard");
//MIDIIn.connect(0, ~keyboardIn);
//MIDIIn.disconnect(0, ~keyboardIn);
~loopMIDI_Port1 = MIDIIn.findPort("loopMIDI_Port1", "loopMIDI_Port1");
//MIDIIn.connect(0, ~loopMIDI_Port1);
//MIDIIn.disconnect(0, ~loopMIDI_Port1);
~loopMIDI_Port2 = MIDIIn.findPort("loopMIDI_Port2", "loopMIDI_Port2");
MIDIIn.connect(0, ~loopMIDI_Port2);
//MIDIIn.disconnect(0, ~loopMIDI_Port2);
~linnstrument = MIDIIn.findPort("from-Linnstrument", "from-Linnstrument");
MIDIIn.connect(0, ~linnstrument);
//MIDIIn.disconnect(0, ~linnstrument);
// Synth Definitions
////////////////
// //
// SYNTHS //
// //
////////////
SynthDef(\pluck, {|freq,bend=1,on_vel=64,off_vel=64,mod=1,gate=0,vol=0|
var e,f,o,amp;
gate = gate + Impulse.kr(0);
amp = on_vel.linexp(0,127,0.08,1);
off_vel = off_vel.linlin(0,127,0.001,1);
e = EnvGen.ar(Env(levels:[1,1,0.85,0],times:[1,3,5],curve:'sin'),doneAction:Done.freeSelf);
f = EnvGen.ar(Env.asr(attackTime:0.00001, sustainLevel:1.0, releaseTime:(0.005 + (off_vel * 3)), curve:'sin'),gate,doneAction:Done.freeSelf);
o = WhiteNoise.ar(1);
o = Pluck.ar(
in: o,
trig: Impulse.ar(0),
maxdelaytime: 8/freq,
delaytime: 1/((freq*bend).lag(0.3)),
decaytime: on_vel.linlin(0,127,3.5,8),
coef: mod.explin(1,2,0.65,-0.038) * on_vel.linlin(0,127,1,0.3));
o = BHiShelf.ar(o,2000 * ((freq / 330.0) ** 0.65),0.85,-16);
o = DFM1.ar(o,freq*19*((330.0 / freq )**0.35), 0.1, 1.0, 0.0, 0.0003, 0.6);
o = LeakDC.ar(o);
o = o * 0.5 * amp * e * f * Lag2.kr(vol * mod.linexp(1,2,1.075,0.75), 0.3);
Out.ar(0,o!2);
}).add.postln;
SynthDef(\string, {|freq,bend=1,on_vel=64,off_vel=64,mod=1,gate=0,vol=0|
var e,f,o,amp;
gate = gate + Impulse.kr(0);
amp = on_vel.linexp(0,127,0.2,1);
off_vel = off_vel.linlin(0,127,1,0.2);
e = EnvGen.ar(Env([0,1,1,0],[(on_vel.linexp(0,127,2.5,1)),[1,1.25,1.5,1.75,2].choose,[1,1.5,2,2.5].choose * off_vel],'sin'),doneAction:Done.freeSelf);
f = EnvGen.ar(Env.asr(attackTime:0.001, sustainLevel:1.0, releaseTime:(0.5 + (off_vel * 3)),curve:'sin'),gate,doneAction:Done.freeSelf);
o = WhiteNoise.ar(1);
o = Pluck.ar(
in: o,
trig: Impulse.ar(0),
maxdelaytime: 8/freq,
delaytime: 1/((freq*bend).lag(0.3)),
decaytime: 1 + (on_vel.linlin(0,127,20,1) * 19),
coef: freq.explin(110,3300,0.08 * Lag2.kr(mod.linexp(1,2,3.5,0.08),0.03),0.004)
) * e;
o = BHiShelf.ar(o,2000,2,-16);
o = BHiPass.ar(o,freq*0.9);
o = LeakDC.ar(o);
o = o * Lag2.kr(amp * vol * on_vel.linexp(0,127,0.15, 1.0) * freq.explin(88,1100, 0.1, 1) * mod.linexp(1,2,1.0,0.2), 0.2) * e * f;
Out.ar(0,o!2);
}).add.postln;
SynthDef(\formant, {
|freq, bend=1,
formant_freq = #[1,1,1,1,1],
formant_reso = #[1,1,1,1,1],
formant_amp = #[1,1,1,1,1],
on_vel=64,off_vel=64,mod=1,gate=0,vol=0|
var e,f,o,amp;
gate = gate + Impulse.kr(0);
amp = on_vel.linlin(0,127,0,1);
off_vel = off_vel.linlin(0,127,2,1);
e = EnvGen.ar(Env([0,1,0],[[1,2,3,4].choose,0.4 * [3,4,5,6,7].choose],curve: 'sin'),gate,doneAction:Done.freeSelf);
f = EnvGen.ar(Env.asr(attackTime:0.001, sustainLevel:1.0, releaseTime:(0.02 + off_vel),curve:'sin'),gate,doneAction:Done.freeSelf);
o = Saw.ar((freq*bend).lag(0.3));
o = Mix.new(BBandPass.ar(o, formant_freq, formant_reso, formant_amp));
o = BLowPass.ar(o,Lag2.kr(800 * mod.expexp(1,2,1,2), 0.5));
o = BPeakEQ.ar(o,freq,1,-16);
o = HPF.ar(o,freq*0.85);
o = LeakDC.ar(o) * AmpComp.kr(440*440/freq);
o = o * e * f * 2 * Lag2.kr(amp * vol, 0.4);
Out.ar(0,o!2);
}).add.postln;
SynthDef(\tone, {|freq,bend=1,on_vel=64,off_vel=64,gate=1,mod=1,filter=1,vol=0|
var e,o;
var attack_time = on_vel.bilin(70,0,127,0.001,0.005,0);
var rise_time = on_vel.bilin(70,0,127,0.02,0.05,0.005);
var release_time = off_vel.bilin(70,0,127,0.02,0.3,0.005);
var sustain_level = on_vel.bilin(70,0,127,1.65,1.25,2);
gate = gate + Impulse.kr(0);
e = EnvGen.ar(Env.adsr(attackTime:attack_time,decayTime:rise_time,sustainLevel:sustain_level,releaseTime:release_time,peakLevel:0.5,curve:0),gate,doneAction:Done.freeSelf);
o = DFM1.ar(Saw.ar(freq*bend.lag(0.3)),freq*2*mod.linexp(1,2,1,2.4).lag(0.75)*filter.linexp(1,2,1,5).lag2(0.4),0.3);
o = LPF.ar(o,freq * 2 * ((330.0 / freq )**0.9));
o = BPeakEQ.ar(o,freq,1.2,-26);
o = BPeakEQ.ar(o,160,1.5,-4);
o = BPeakEQ.ar(o,800,1,-2);
o = HPF.ar(o,110);
o = o * e * Lag2.kr(vol, 0.2);
o = o * mod.expexp(1,2,1.4,1.1).lag(0.35) * filter.explin(1,2,1,2).lag2(0.04);
Out.ar(0,o!2);
}).add.postln;
postln("Synths loaded !");
// load MTS single-note retuning and MIDI IO for Lumatone
///////////////
// //
// MTS //
// //
///////////
~tuningMap = Array.newClear(128);
(0..127).do {|j| ~tuningMap[j] = Array.fill(128, {|i| i }) };
// array has one slot per possible MIDI note and stores the actual retuned pitches to be triggered by noteon messages as MIDIfloats
~mts = MIDIFunc.sysex(
{|val| if ([val[0], val[1], val[2], val[3], val[4]] == [-16,127,127,8,2]) {
if (val.size > 10) {
"MIDI tuning received: ".post; "Tuning Map ".post; val[5].postln;
"note ".post; val[7].post; " => ".post;
~tuningMap[val[5]][val[7]] = (val[8] + (val[9]/128.0) + (val[10]/16384.0)).postln;
//(0..127).do { |note| // duplicate incoming MTS to tuning map 127, which is where the morphing function is calculated
~tuningMap[127][val[7]] = ~tuningMap[val[5]][val[7]];
};
//};
};
};
); // convert real-time MTS
//~mts.free;
// 53-tone isomorphic odd-partial set (default)
~tuningMap[0] = [ 46.3857421875, 46.533081054688, 46.824035644531, 46.967712402344, 47.391723632812, 47.668701171875, 47.760070800781, 47.941345214844, 48.209777832031, 48.474060058594, 48.604736328125, 48.863159179688, 48.990905761719, 49.24365234375, 49.492736816406, 49.859802246094, 49.98046875, 50.33740234375, 50.610046386719, 50.687194824219, 50.91650390625, 51.142761230469, 51.366149902344, 51.531860351562, 51.804504394531, 52.01953125, 52.337219238281, 52.649169921875, 52.751892089844, 52.95556640625, 53.190185546875, 53.454528808594, 53.552612304688, 53.843566894531, 54.034912109375, 54.22412109375, 54.688232421875, 54.779602050781, 54.960876464844, 55.31787109375, 55.49365234375, 55.667602539062, 55.896911621094, 56.010437011719, 56.346618652344, 56.512268066406, 56.784912109375, 57.0, 57.317687988281, 57.629638671875, 57.706726074219, 57.93603515625, 58.162353515625, 58.3857421875, 58.533081054688, 58.824035644531, 58.967712402344, 59.391723632812, 59.668701171875, 59.760070800781, 59.941345214844, 60.209777832031, 60.474060058594, 60.604736328125, 60.863159179688, 60.990905761719, 61.24365234375, 61.492736816406, 61.859802246094, 61.98046875, 62.33740234375, 62.610046386719, 62.687194824219, 62.91650390625, 63.142761230469, 63.366149902344, 63.531860351562, 63.804504394531, 64.01953125, 64.337219238281, 64.649169921875, 64.751892089844, 64.95556640625, 65.190185546875, 65.454528808594, 65.552612304688, 65.843566894531, 66.034912109375, 66.22412109375, 66.688232421875, 66.779602050781, 66.960876464844, 67.31787109375, 67.49365234375, 67.667602539062, 67.896911621094, 68.010437011719, 68.346618652344, 68.512268066406, 68.784912109375, 69.0, 69.317687988281, 69.629638671875, 69.706726074219, 69.93603515625, 70.162353515625, 70.3857421875, 70.533081054688, 70.824035644531, 70.967712402344, 71.391723632812, 71.668701171875, 71.760070800781, 71.941345214844, 72.209777832031, 72.474060058594, 72.604736328125, 72.863159179688, 72.990905761719, 73.24365234375, 73.492736816406, 73.859802246094, 73.98046875, 74.33740234375, 74.610046386719, 74.687194824219, 74.91650390625, 75.142761230469 ];
~tuningMap[1] = [ 46.3857421875, 46.533081054688, 46.824035644531, 46.967712402344, 47.391723632812, 47.668701171875, 47.760070800781, 47.941345214844, 48.209777832031, 48.474060058594, 48.604736328125, 48.863159179688, 48.990905761719, 49.24365234375, 49.492736816406, 49.859802246094, 49.98046875, 50.33740234375, 50.610046386719, 50.687194824219, 50.91650390625, 51.142761230469, 51.366149902344, 51.531860351562, 51.804504394531, 52.01953125, 52.337219238281, 52.649169921875, 52.751892089844, 52.95556640625, 53.190185546875, 53.454528808594, 53.552612304688, 53.843566894531, 54.034912109375, 54.22412109375, 54.688232421875, 54.779602050781, 54.960876464844, 55.31787109375, 55.49365234375, 55.667602539062, 55.896911621094, 56.010437011719, 56.346618652344, 56.512268066406, 56.784912109375, 57.0, 57.317687988281, 57.629638671875, 57.706726074219, 57.93603515625, 58.162353515625, 58.3857421875, 58.533081054688, 58.824035644531, 58.967712402344, 59.391723632812, 59.668701171875, 59.760070800781, 59.941345214844, 60.209777832031, 60.474060058594, 60.604736328125, 60.863159179688, 60.990905761719, 61.24365234375, 61.492736816406, 61.859802246094, 61.98046875, 62.33740234375, 62.610046386719, 62.687194824219, 62.91650390625, 63.142761230469, 63.366149902344, 63.531860351562, 63.804504394531, 64.01953125, 64.337219238281, 64.649169921875, 64.751892089844, 64.95556640625, 65.190185546875, 65.454528808594, 65.552612304688, 65.843566894531, 66.034912109375, 66.22412109375, 66.688232421875, 66.779602050781, 66.960876464844, 67.31787109375, 67.49365234375, 67.667602539062, 67.896911621094, 68.010437011719, 68.346618652344, 68.512268066406, 68.784912109375, 69.0, 69.317687988281, 69.629638671875, 69.706726074219, 69.93603515625, 70.162353515625, 70.3857421875, 70.533081054688, 70.824035644531, 70.967712402344, 71.391723632812, 71.668701171875, 71.760070800781, 71.941345214844, 72.209777832031, 72.474060058594, 72.604736328125, 72.863159179688, 72.990905761719, 73.24365234375, 73.492736816406, 73.859802246094, 73.98046875, 74.33740234375, 74.610046386719, 74.687194824219, 74.91650390625, 75.142761230469 ];
// active map
~tuningMap[127] = [ 46.3857421875, 46.533081054688, 46.824035644531, 46.967712402344, 47.391723632812, 47.668701171875, 47.760070800781, 47.941345214844, 48.209777832031, 48.474060058594, 48.604736328125, 48.863159179688, 48.990905761719, 49.24365234375, 49.492736816406, 49.859802246094, 49.98046875, 50.33740234375, 50.610046386719, 50.687194824219, 50.91650390625, 51.142761230469, 51.366149902344, 51.531860351562, 51.804504394531, 52.01953125, 52.337219238281, 52.649169921875, 52.751892089844, 52.95556640625, 53.190185546875, 53.454528808594, 53.552612304688, 53.843566894531, 54.034912109375, 54.22412109375, 54.688232421875, 54.779602050781, 54.960876464844, 55.31787109375, 55.49365234375, 55.667602539062, 55.896911621094, 56.010437011719, 56.346618652344, 56.512268066406, 56.784912109375, 57.0, 57.317687988281, 57.629638671875, 57.706726074219, 57.93603515625, 58.162353515625, 58.3857421875, 58.533081054688, 58.824035644531, 58.967712402344, 59.391723632812, 59.668701171875, 59.760070800781, 59.941345214844, 60.209777832031, 60.474060058594, 60.604736328125, 60.863159179688, 60.990905761719, 61.24365234375, 61.492736816406, 61.859802246094, 61.98046875, 62.33740234375, 62.610046386719, 62.687194824219, 62.91650390625, 63.142761230469, 63.366149902344, 63.531860351562, 63.804504394531, 64.01953125, 64.337219238281, 64.649169921875, 64.751892089844, 64.95556640625, 65.190185546875, 65.454528808594, 65.552612304688, 65.843566894531, 66.034912109375, 66.22412109375, 66.688232421875, 66.779602050781, 66.960876464844, 67.31787109375, 67.49365234375, 67.667602539062, 67.896911621094, 68.010437011719, 68.346618652344, 68.512268066406, 68.784912109375, 69.0, 69.317687988281, 69.629638671875, 69.706726074219, 69.93603515625, 70.162353515625, 70.3857421875, 70.533081054688, 70.824035644531, 70.967712402344, 71.391723632812, 71.668701171875, 71.760070800781, 71.941345214844, 72.209777832031, 72.474060058594, 72.604736328125, 72.863159179688, 72.990905761719, 73.24365234375, 73.492736816406, 73.859802246094, 73.98046875, 74.33740234375, 74.610046386719, 74.687194824219, 74.91650390625, 75.142761230469 ];
// Post << ~tuningMap[0]; // complete array in post window
};
// MIDI I/O and GUI
////////////////
// //
// MIDI //
// //
////////////
~levels=[0.0,0.0,0.0,0.0];
~transpose = 1;
// post MIDI to window
~post_note = true;
~post_cc = true;
~post_bend = false;
~post_pressure = false;
~post_polytouch = false;
// change these values for multichannnel tuning (channel in 1-16 format)
~multichannel = true;
~main_channel = 3;
~equave = 2;
// morphing (interpolating) between two maps
~current_map = 127; // choose active tuning map
~map_down = 0; // choose tuning map when controller value is low
~map_up = 1; // choose tuning map when controller value is high
// polyphony array has one slot per possible MIDI note
~notes = Array.fill2D(16, 128, {[nil, nil, nil, nil]});
// sustain pedal array for sustained notes (note-off velocities)
~sustain_pedal = 0;
~sustained = Array.fill2D(16, 128, {-1} );
~velocities = Array.fill2D(16, 128, {0} );
// bend : pitch-bend wheel is applied to most recent note ONLY
~bend = 1; // center value
~bend_range = 28/27; // fixed ratio for maximum pitch bend
~most_recent_note = []; // array to keep track of notes in order played
// cc from any channel
~controllers = Array.fill(128,{0});
~mod_cc = 1; // set cc number for timbral modification
//~morph_cc = nil; // set cc number for crossfading between tuning maps
//~finetuning_cc = 0;
// MIDI functions
// note-on function
~on = MIDIFunc.noteOn({ |on_vel, num, chan, src|
var formants;
o = 0; // set number of equaves transposition (default to no transposition)
if (~multichannel) { // only transpose when multichannel is set to true
o = (chan % 8) - ~main_channel + 1;
while { o < -4 } { o = o + 8 };
while { o >= 4 } { o = o - 8 };
};
m = ~equave ** o;// determine multiplier
f = ~tuningMap[~current_map][num].midicps * ~transpose * m;
if (~post_note) { "noteon: ".post;[chan + 1, num, on_vel].post; " transposed by ".post; o.post; " equave(s)".postln;
};
if (on_vel > 0) {
~most_recent_note = ~most_recent_note.removeEvery([num + (chan * 128)]);
~most_recent_note = ~most_recent_note.addFirst(num + (chan * 128));
~velocities[chan][num] = on_vel;
};
(0..3).do { |synth| if (~notes[chan][num][synth].isPlaying) {
~notes[chan][num][synth].set(\off_vel, on_vel, \gate, 0); // node will disappear by itself
~notes[chan][num][synth] = nil; // clear the slot !
} }; // check to make sure slot is not sounding already, if it is, turn it off before producing a new synth in the slot!
~notes[chan][num][0] = Synth(\pluck, [\freq, f, \bend, ~bend, \on_vel, on_vel, \mod, ~controllers[~mod_cc], \vol, ~levels[0], \gate, 1]);
NodeWatcher.register(~notes[chan][num][0]);
~notes[chan][num][1] = Synth(\string, [\freq, f, \bend, ~bend, \on_vel, on_vel, \mod, ~controllers[~mod_cc], \vol, ~levels[1], \gate, 1]);
NodeWatcher.register(~notes[chan][num][1]);
formants = { FormantTable.rand }.value;
~notes[chan][num][2] = Synth(\formant, [\formant_freq, formants[0], \formant_reso, formants[1], \formant_amp, formants[2], \freq, f, \bend, ~bend, \on_vel, on_vel, \mod, ~controllers[~mod_cc], \vol, ~levels[2], \gate, 1]);
NodeWatcher.register(~notes[chan][num][2]);
~notes[chan][num][3] = Synth(\tone, [\freq, f, \bend, ~bend, \on_vel, on_vel, \mod, ~controllers[~mod_cc], \vol, ~levels[3], \gate, 1]);
NodeWatcher.register(~notes[chan][num][3]);
});
// note-off
~off = MIDIFunc.noteOff({ |off_vel, num, chan, src|
if (off_vel == 0) {
off_vel = ~velocities[chan][num];
};
if (~sustain_pedal == 0)
{ if (~post_note) {
"noteoff: ".post;[chan + 1, num, off_vel].postln;
};
~most_recent_note = ~most_recent_note.removeEvery([num + (chan * 128)]);
(0..3).do {|synth| if (~notes[chan][num][synth].isPlaying)
{ ~notes[chan][num][synth].set(\off_vel, off_vel, \gate, 0); // node will disappear by itself
~notes[chan][num][synth] = nil;
} };
} { ~sustained[chan][num] = off_vel };
});
// polytouch further modifies brightness per note
~keypressure = MIDIFunc.polytouch({ |val,num,chan,src|
~filter = (val+1).linlin(1,128,1,2);
if (~post_polytouch) {
(chan + 1).post; ": polytouch ".post; num.post; " ".post; val.post; " ".post; ~filter.postln;
};
~notes[chan][num][3].set(\filter,~filter)
});
// channel pressure modifies timbre globally
~channelpressure = MIDIFunc.touch({ |val,chan,src|
~filter_global = (val+1).linlin(1,128,1,2);
if (~post_pressure) {
(chan + 1).post; ": touch ".post; val.post; " ".post; ~filter_global.postln;
};
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][3].isPlaying) {
~notes[channel][note][3].set(\filter,~filter_global)
} } };
});
// apply pitch bend to last played note
~pitchbend = MIDIFunc.bend({ |val=8192,chan,src|
~bend = (val).clip(1,16383).linexp(1,16383,1/~bend_range,~bend_range);
if (~post_bend) {
(chan + 1).post; ": bend ".post; val.post; " ".post; ~bend.postln;
};
if (~most_recent_note[0] != nil) {
var channel = ((~most_recent_note[0] / 128).roundUp - 1).asInteger;
var note = ~most_recent_note[0] % 128;
// "bend-target: [".post; channel.post; ", ".post; note.post; "] ".postln;
(0..3).do { |synth| if(~notes[channel][note][synth].isPlaying)
{ ~notes[channel][note][synth].set(\bend, ~bend) }
}; // apply pitchbend to all channels
};
});
// any cc converted to an exponential value between 1 and 2
~cc = MIDIFunc.cc({ |val,num,chan,src|
c = (val+1).linexp(8,119,1,2); // i
~controllers[num] = c;
if (~post_cc) {
(chan + 1).post; ": cc ".post; num.post; " ".post; val.post; " ".post;~controllers[num].postln;
};
if (num == ~mod_cc) { // timbral modulation controller
(0..15).do { |channel|
(0..127).do { |note|
(0..3).do { |synth| if(~notes[channel][note][synth].isPlaying)
{ ~notes[channel][note][synth].set(\mod,~controllers[~mod_cc]) }
} } } };
if (num == 64) { // sustain pedal
~sustain_pedal = val;
if (val > 0)
{ if (~post_cc) {"sustaining!".postln} }
{ if (~post_cc) {"releasing!".postln};
(0..15).do { |channel|
(0..127).do { |note| if (~sustained[channel][note] >= 0) {
if (~post_note) {
"noteoff: ".post;[channel + 1, note, ~sustained[channel][note]].postln;};
~most_recent_note = ~most_recent_note.removeEvery([note + (channel * 128)]);
(0..3).do {|synth| if (~notes[channel][note][synth].isPlaying)
{ ~notes[channel][note][synth].set(\off_vel, ~sustained[channel][note], \gate, 0);
~notes[channel][note][synth] = nil;
};
};
~sustained[channel][note] = -1; // reset the slot after releasing
} } };
};
};
if (num == 2) { // level of pluck
~levels[0] = val.linlin(0,127,0,1);
v.valueAction = ~levels[0];
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][0].isPlaying)
{ ~notes[channel][note][0].set(\vol,~levels[0])}
} };
};
if (num == 3) { // level of buzz
~levels[1] = val.linlin(0,127,0,1);
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][1].isPlaying)
{ ~notes[channel][note][1].set(\vol,~levels[1])}
} };
};
if (num == 5) { // level of formant
~levels[2] = val.linlin(0,127,0,1);
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][2].isPlaying)
{ ~notes[channel][note][2].set(\vol,~levels[2])}
} };
};
if (num == 6) { // level of saw
~levels[3] = val.linlin(0,127,0,1);
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][3].isPlaying)
{ ~notes[channel][note][3].set(\vol,~levels[3])}
} };
};
});
//////////////
// //
// ESC //
// //
//////////
k = UnicodeResponder.new;
k.register( 27 , false, false, false, false,
{ "Killing all!".postln;
(0..3).do
{ |synth| (0..127).do
{ |note| (0..15).do
{|chan| if (~notes[chan][note][synth].isPlaying) {
~notes[chan][note][synth].set(\off_vel, 64, \gate, 0);
~notes[chan][note][synth] = nil;} } } }; // all ~notes off
});
View.globalKeyDownAction = k;
//////////////
// //
// GUI //
// //
//////////
w = Window.new("Electric Harpsichord Tanpura Organ",Rect((Window.screenBounds.width)*3/4,(Window.screenBounds.height/2)-((Window.screenBounds.width/4-200)/4),
Window.screenBounds.width/4,(Window.screenBounds.height/2)+((Window.screenBounds.width/4-200)/4)-60)); // GUI window
w.front;
v = GUI.slider.new(w,Rect(40,40,(Window.screenBounds.width/4-200)/4,(Window.screenBounds.height/2)-180)); // slider 1
v.action = { ~levels[0]=v.value;
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][0].isPlaying)
{ ~notes[channel][note][0].set(\vol,v.value)}
} };
};
u = GUI.slider.new(w,Rect(80+((Window.screenBounds.width/4-200)/4),40,(Window.screenBounds.width/4-200)/4,(Window.screenBounds.height/2)-180)); // slider 2
u.action = { ~levels[1]=u.value;
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][1].isPlaying)
{ ~notes[channel][note][1].set(\vol,u.value)}
} };
};
q = GUI.slider.new(w,Rect(120+(2*(Window.screenBounds.width/4-200)/4),40,(Window.screenBounds.width/4-200)/4,(Window.screenBounds.height/2)-180)); // slider 3
q.action = { ~levels[2]=q.value;
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][2].isPlaying)
{ ~notes[channel][note][2].set(\vol,q.value)}
} };
};
z = GUI.slider.new(w,Rect(160+(3*(Window.screenBounds.width/4-200)/4),40,(Window.screenBounds.width/4-200)/4,(Window.screenBounds.height/2)-180)); // slider 4
z.action = { ~levels[3]=z.value;
(0..15).do { |channel|
(0..127).do { |note| if (~notes[channel][note][3].isPlaying)
{ ~notes[channel][note][3].set(\vol,z.value)}
} };
};
CmdPeriod.add({ w.close; });
"Use faders to mix the Synths!";
)