Skip to content

Instantly share code, notes, and snippets.

@madskjeldgaard
Last active December 16, 2024 19:44
Show Gist options
  • Select an option

  • Save madskjeldgaard/058732cce5ae6e3ceb11f3f5b7ad1237 to your computer and use it in GitHub Desktop.

Select an option

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:
- 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 });
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment