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