Skip to content

Instantly share code, notes, and snippets.

@madskjeldgaard
Last active January 17, 2023 21:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save madskjeldgaard/058732cce5ae6e3ceb11f3f5b7ad1237 to your computer and use it in GitHub Desktop.
Save madskjeldgaard/058732cce5ae6e3ceb11f3f5b7ad1237 to your computer and use it in GitHub Desktop.
Using the mi ugens and Modality toolkit to create a double Clouds granulator synth with a korg Nanokontrol2 as the interface

( /////////////////////////////// // Korg nanokontrol2 Clouds interface /////////////////////////////// /*

By Mads Kjeldgaard, 08-11-2020

Dependencies:

*/

// 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 });

)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment