Last active
December 16, 2024 19:44
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ( | |
| /////////////////////////////// | |
| // 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