NanoKtl a class for handling mappings of the Korg nanoKontrol


Inherits from: MIDIKtl


The Korg nanoKontrol produces only MIDI controller events. It has 4 switchable scenes, 

in which all controllers (sliders, knobs, upper and lower buttons) have different cc numbers. 

The 6 transport controls remain the same in all scenes.

NanoKtl keeps a dictionary of actions for all mapped controllers.


See also: UsingMIDI, CCResponder, MIDIIn


First example


// make a ProxySpace and a mixer for it, map the NanoKtl to it

p = ProxySpace.push(s.boot);

m = ProxyMixer(p, 16);

// put some proxies in it 

(

12.do { |i| p[("test_bla" ++ i).asSymbol] = { |ring = 0.02|

Pan2.ar(Ringz.ar(Impulse.ar(exprand(0.5, 4)), exprand(300, 3000), ring), 1.0.rand2, 0.2) }; 

};

Spec.add(\ring, [0.0001, 1, \exp]);

)

// make a nanoKtl, and map it to the mixer:

// the sliders control the first 8 volumes, 

// the lower buttons play the proxy,

// the upper buttons send to the editor, 

// the knobs control the editor parameters, 

// and the last slider controls server volume!

(

g = NanoKtl.new;

g.mapToMixer(m, 1);

)



Creation / Class Methods


*new (srcID, dict) create a NanoKtl object.

srcID - every MIDI port (e.g. from a USB plug) gets a unique ID. 

This can be used to distinguish multiple controller which may have 

overlapping MIDI channel/controller numbers. 

dict - a dictionary of the actions that are mapped to controllers.

// simple example

g = NanoKtl.new; // listen to all midi in ports

// g = NanoKtl(1643926019); // only listen to the left USB input on my laptop

g.ccDict; // a dict where the maps will live

g.ccresp; // a midi CCResponder listing to that MIDI port

// map transport buttons 'play' and 'rec' to simple actions: 

g.mapCCS(0, 'play', { |ch, cc, val| " 'play': %.\n".postf([ch, cc, val]) });

g.mapCCS(0, 'rec', { |ch, cc, val| " 'rec': %.\n".postf([ch, cc, val]) });

// the actions are now in the dict:

g.ccDict.postcs;

// clean up

// ( g.free; )


Class Variables

verbose

can be set to true to post info for debugging.


NanoKtl.verbose = true;

NanoKtl.verbose = false;


Instance Variables


ctlSceneNames

a dictionary of names such as \sl1 to controller keys as used for lookup.

the keys are symbols like  '0_42' meaning midichannel 0, ctl num 42. 

// the transport controls remain the same in all scenes:

g.ctlNames[0];

// the names in scene 1 

g.ctlNames[1]


// add some more mapped actions

g.mapCCS(1, 'sl1', { |ch, cc, val| " scene 1, slider1: %.\n".postf([ch, cc, val]) });

g.mapCCS(1, 'kn2', { |ch, cc, val| " scene 1, knob2: %.\n".postf([ch, cc, val]) });

g.mapCCS(1, 'bu3', { |ch, cc, val| " scene 1, button up 3: %.\n".postf([ch, cc, val]) });

g.mapCCS(1, 'bd3', { |ch, cc, val| " scene 1, button down 3: %.\n".postf([ch, cc, val]) });


g.ccDict;


Instance Methods

mapCCS (editor, scene)


map one of the controls in a scene to a general function.


g = NanoKtl.new; // listen to all midi in ports


g.mapCCS(1, 'sl1', { |ch, cc, val| " scene 1, slider1: %.\n".postf([ch, cc, val]) });

g.mapCCS(1, 'kn2', { |ch, cc, val| " scene 1, knob2: %.\n".postf([ch, cc, val]) });

g.mapCCS(1, 'bu3', { |ch, cc, val| " scene 1, button up 3: %.\n".postf([ch, cc, val]) });

g.mapCCS(1, 'bd3', { |ch, cc, val| " scene 1, button down 3: %.\n".postf([ch, cc, val]) });

g.ccDict;



Mapping to JITGuis


mapToEnvirGui(gui, scene, indices) - map to an EnvirGui.

gui the gui

scene in which scene to map map the gui

indices which controls to map to. default is [1, 2 .. 8], which is kn01 .. kn08.


(

Spec.add(\harm, [1, 100]);

e = EnvirGui.new( (freq: 120, amp: 0.2, harm: 12, otto: 10), 4);

g.mapToEnvirGui(e, 1, (1..4));

)



mapToNdefGui(gui, scene)


map the knobs in a scene to a NodeproxyEditor or Ndef.

The first 8 knobs go to the parameters of the proxy, the 9th control the proxy's volume.

(

p = ProxySpace.push;

~test = { |freq=250, intv=19, timescale=1, curve=0, loopnode=0, formfact=2, widfact=0.5, spread=1, amp=0.1| 

var env = EnvGen.kr(

Env(

{ 1.0.rand2 }!11, 

{1.0.rand}!10 * timescale, 

curve,

releaseNode: 9, 

loopNode: loopnode)

);

var pitch = (env * [1, 0.33, -1] * intv).midiratio * freq;

Splay.ar(

Formant.ar(pitch, pitch.scramble * formfact, pitch.scramble * widfact), spread) * amp;

};

// add specs for all the parameter ranges - required!

Spec.add(\intv, [0, 36, \lin]);

Spec.add(\timescale, [0.001, 10, \exp]);

Spec.add(\curve, [-10, 10]);

Spec.add(\loopnode, [0, 7, \lin, 1]);

Spec.add(\formfact, [0.1, 10, \exp]);

Spec.add(\widfact, [0.1, 10, \exp]);

Spec.add(\spread, \unipolar);

);


n = NdefGui(~test, 8);

g = NanoKtl.new; // make a new NanoKtl

g.mapToNdefGui(n, 1); // and map its scene 1 to the editor n


// shift by hand which params are mapped

g.paramShift(0); // show which params are mapped

g.paramShift(1); // shift by one; when hitting the end, jumps back.


// test using a second proxy, NanoKtl scene 2:

~test2 = ~test.source;

o = NodeProxyEditor(~test2);

g.mapToPxEdit(o, 2); // scene 2!



mapToMixer (mixer, scene)


(

m = ProxyMixer(p, 16);


12.do { |i| p[("test_bla" ++ i).asSymbol] = { |ring = 0.02|

Pan2.ar(Ringz.ar(Impulse.ar(exprand(0.5, 4)), exprand(300, 3000), ring), 1.0.rand2, 0.2) }; 

};

Spec.add(\ring, [0.0001, 1, \exp]);

)

// make a nanoKtl, and map it to the ProxyMixer:

// the sliders control the first 8 volumes, 

// the lower buttons play the proxy,

// the upper buttons send to the editor, 

// the knobs control the editor parameters, 

// and the last slider controls server volume!

(

g = NanoKtl.new;

g.mapToMixer(m, 1);

)







mapToPxEdit (editor, scene)


map the knobs in a scene to a NodeproxyEditor or Ndef.

The first 8 knobs go to the parameters of the proxy, the 9th control the proxy's volume.

(

p = ProxySpace.push;

~test = { |freq=250, intv=19, timescale=1, curve=0, loopnode=0, formfact=2, widfact=0.5, spread=1, amp=0.1| 

var env = EnvGen.kr(

Env(

{ 1.0.rand2 }!11, 

{1.0.rand}!10 * timescale, 

curve,

releaseNode: 9, 

loopNode: loopnode)

);

var pitch = (env * [1, 0.33, -1] * intv).midiratio * freq;

Splay.ar(

Formant.ar(pitch, pitch.scramble * formfact, pitch.scramble * widfact), spread) * amp;

};

// add specs for all the parameter ranges - required!

Spec.add(\intv, [0, 36, \lin]);

Spec.add(\timescale, [0.001, 10, \exp]);

Spec.add(\curve, [-10, 10]);

Spec.add(\loopnode, [0, 7, \lin, 1]);

Spec.add(\formfact, [0.1, 10, \exp]);

Spec.add(\widfact, [0.1, 10, \exp]);

Spec.add(\spread, \unipolar);

n = NodeProxyEditor(~test);

);


g = NanoKtl.new; // make a new NanoKtl

g.mapToPxEdit(n, 1); // and map its scene 1 to the editor n


// shift by hand which params are mapped

g.paramShift(0); // show which params are mapped

g.paramShift(1); // shift by one; when hitting the end, jumps back.


// test using a second proxy, NanoKtl scene 2:

~test2 = ~test.source;

o = NodeProxyEditor(~test2);

g.mapToPxEdit(o, 2); // scene

mapToPxMix (mixer, scene)


Maps one scene of the NanoKtl to a proxymixer. 

The first 8 sliders control individual proxy volumes; these can be shifted.

The rightmost slider controls server volume.

The knobs in the scene are mapped to the proxymixer's editor: 

The first 8 knobs go to the parameters of the proxy, the 9th control the proxy's volume.

The upper buttons send the corresponding proxy to the editor, 

the lower buttons toggle play/stop of the respective proxy.

upper button 9 - shifts the mapped proxy volumes,

lower button 9 - shifts the mapped editor's params.

slider9 controls server volume.



make a proxymixer and some more proxies to test


p = ProxySpace.push(s.boot);

(

m = ProxyMixerOld(p, 16);


20.do { |i| p[("test_bla" ++ i).asSymbol] = { |ring = 0.02|

Pan2.ar(Ringz.ar(Impulse.ar(exprand(0.5, 4)), exprand(300, 3000), ring), 1.0.rand2, 0.2) }; 

};

Spec.add(\ring, [0.0001, 1, \exp]);

)

g = NanoKtl.new;

g.mapToPxMix(m, 1);


~test.filter(5, { |in, dt = 0.1, decay = 10| CombL.ar(in, 0.2, dt, decay) });


q = ProxySpace.new;

(

n = ProxyMixer(q, 16);


10.do { |i| q[("test" ++ i).asSymbol] = { |ring = 0.02, freqx=0.5|

Pan2.ar(Ringz.ar(Impulse.ar(exprand(0.5, 4)), exprand(300, 3000) * freqx, ring), 1.0.rand2, 0.2) }; 

};

Spec.add(\ring, [0.0001, 1, \exp]);

)


g.mapToPxMix(n, 2);