( /////////////////////////////// // Korg nanokontrol2 Clouds interface /////////////////////////////// /*
By Mads Kjeldgaard, 08-11-2020
Dependencies:
- mi ugens: https://github.com/v7b1/mi-UGens
- Modality toolkit: Quarks.install("Modality")
*/
// Options ~inputchannel = 6; ~patchInSeries = true;
// Sound card options ~numchannelsin = 8; ~numchannelsout = 2;
// Boot s.options.numOutputBusChannels_(~numchannelsout); s.options.numInputBusChannels_(~numchannelsin); s.boot;
) ( // The input Ndef(\clouds_input, {|inputchan| SoundIn.ar(inputchan)!2 }) .mold(2, 'audio') .set(\inputchan, ~inputchannel);
// Clouds synth (which is copied to a second clouds) Ndef(\clouds1, { MiClouds.ar( \input.ar([0.0, 0.0]), \pitch.kr(0, spec:[(-48),48,\lin,1]), \pos.kr(0.1, spec:[0.0,1.0]), \size.kr(0.5, spec:[0.0,1.0]), \density.kr(0.3, spec:[0.0,1.0]), \texture.kr(0.5, spec:[0.0,1.0]), \drywet.kr(1.0, spec:[0.0,1.0]), \gain.kr(1, spec: [0.125,8.0]), \spread.kr(0.5, spec:[0.0,1.0]), \rvb.kr(0, spec: [0.0,1.0]), \fb.kr(0, spec: [0.0,1.0]), \freeze.kr(0, spec: [0,1, \lin, 1]), \mode.kr(0, spec: [0,3,\lin,1]), \lofi.kr(0, spec: [0,1,\lin,1]), \trig.kr(0, spec: [0,1,\lin,1]) ) });
Ndef(\clouds1).copy(\clouds2);
// Patch the synths together or in parallel Ndef(\clouds1) .mold( Ndef(\clouds_input).numChannels, 'audio' ); Ndef(\clouds2) .mold( Ndef(\clouds_input).numChannels, 'audio' );
if(~patchInSeries, { Ndef(\clouds1).map(\input, Ndef(\clouds_input)); Ndef(\clouds2).map(\input, Ndef(\clouds1)); Ndef(\clouds1).stop; Ndef(\clouds2).play; }, { Ndef(\clouds1) .map(\input, Ndef(\clouds_input)) .play;
Ndef(\clouds2)
.map(\input, Ndef(\clouds_input))
.play;
}); ) ( //////////////////////////////////////////////////////////// // Helper function: Mapping values according to specs ////////////////////////////////////////////////////////////
~mapValSpecced = {|nodeproxy, setKey=\pitch, setVal=0.5| var paramnames = nodeproxy.controlKeys; var paramexists = paramnames.indexOf(setKey).isNil;
// Get specs from nodeproxy (if it has any)
var localspecs = nodeproxy.specs;
// If no specs are foudn neither locally nor globally, use the default 0.0-1.0
var fallbackspec = ControlSpec.new(minval: 0.0, maxval: 1.0);
// Order of specs:
// Use local specs if present, if not then global, if not global then 0.0-1.0 fallback spec
var spec = localspecs.at(setKey) ?? Spec.specs.at(setKey) ?? fallbackspec;
// Check if param exists in object at all
if(paramexists, {
"Param % does not exist in synth".format(setKey).error
}, {
nodeproxy.set(setKey, spec.map(setVal))
});
};
/////////////////////////////// // Controller setup ///////////////////////////////
k = MKtl('nan', "korg-nanokontrol2");
// Map sliders and knobs // Sliders and knobs produce values in the range 0 to 1, // Which is what our helper function expects, so this is easy: [ // [element, elementNum], ndefname, parameter [[\sl, \1 ], \clouds1, \pitch], [[\sl, \2 ], \clouds1, \size], [[\sl, \3 ], \clouds1, \density], [[\sl, \4 ], \clouds1, \texture], [[\sl, \5 ], \clouds1, \pos], [[\sl, \6 ], \clouds1, \rvb], [[\sl, \7 ], \clouds1, \fb], [[\sl, \8 ], \clouds1, \drywet],
[[\kn, \1 ], \clouds2, \pitch],
[[\kn, \2 ], \clouds2, \size],
[[\kn, \3 ], \clouds2, \density],
[[\kn, \4 ], \clouds2, \texture],
[[\kn, \5 ], \clouds2, \pos],
[[\kn, \6 ], \clouds2, \rvb],
[[\kn, \7 ], \clouds2, \fb],
[[\kn, \8 ], \clouds2, \drywet],
].do{|arr| var el = arr[0], ndef=arr[1], param=arr[2];
k.elAt(*el).action = {|el|
~mapValSpecced.value(Ndef(ndef), param, el.value)
}
};
// Map buttons // [element], callbackfunction [ [['tr', 'mleft'],{ "Clouds1: Lofi mode off".postln; Ndef(\clouds1).set(\lofi, 0) }], [['tr', 'mright'],{ "Clouds1: Lofi mode on".postln; Ndef(\clouds1).set(\lofi, 1) }],
[['tr', 'tright'],{ "Clouds2: Lofi mode off".postln; Ndef(\clouds2).set(\lofi, 0) }],
[['tr', 'tright'],{ "Clouds2: Lofi mode on".postln; Ndef(\clouds2).set(\lofi, 1) }],
[['tr', 'cycle'],{ "Scrambling modes!!!".postln; [\clouds1, \clouds2].do{|c| Ndef(c).set(\mode, rrand(0,3))}}],
[['tr', 'mset'],{ "Back to normal mode".postln; [\clouds1, \clouds2].do{|c| Ndef(c).set(\mode, 0)}}],
[['tr', 'play'],{ [\clouds1, \clouds2].do{|c| Ndef(c).play}}],
[['tr', 'stop'],{ [\clouds1, \clouds2].do{|c| Ndef(c).stop}}],
[['tr', 'rec'], { s.record }],
].do{|buttonarray| var el = buttonarray[0], callback = buttonarray[1];
// this if statement turns the callback into a trigger func
k.elAt(*el).action = {|el| if(el.value == 1, { callback.value() }) }
};
// Momentary k.elAt('tr', 'rew').action = {|el| Ndef(\clouds1).set(\freeze, el.value) };
k.elAt('tr', 'fwd').action = {|el| Ndef(\clouds2).set(\freeze, el.value) };
k.hasDevice.not.if({ k.gui });
)