[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [sc-users] NanoKontrol.sc
On Wednesday 29 July 2009 20:51:40 nescivi wrote:
> On Wednesday 29 July 2009 14:49:43 Alberto de Campo wrote:
> > hi,
> >
> > I've made a simple NanoKtl class, mainly for connecting to NodeProxies,
> > but also general. I did not think of conforming to JInt, but could be
> > worth converting and adding.
>
> Oooh, these are really nice!
and a small bugfix for the map to proxy mixer...
the start/stop buttons were not shifted along with the shift function.
sincerely,
Marije
MIDIController {
var <srcID, <ccDict, <noteOnDict, <noteOffDict, <ccresp;
*new { |srcID, ccDict|
^super.newCopyArgs(srcID, ccDict).init;
}
free {
ccresp.remove;
ccDict.clear;
noteOnDict.clear;
noteOffDict.clear;
// redraw pxmix and pxedit with clear colors...
}
init {
ccDict = ccDict ?? ();
noteOnDict = noteOnDict ?? ();
noteOffDict = noteOffDict ?? ();
ccresp.remove;
ccresp = CCResponder({ |src, chan, ccn, val|
var lookie = this.makeCCKey(chan, ccn);
if (this.class.verbose, { ['cc', src, chan, ccn, val].postcs });
ccDict[lookie].value(chan, ccn, val);
}, srcID);
}
makeCCKey { |chan, cc| ^(chan.asString ++ "_" ++ cc).asSymbol }
ccKeyToChanCtl { |ccKey| ^ccKey.asString.split($_).asInteger }
makeNoteKey { |chan, note|
var key = chan.asString;
if (note.notNil) { key = key ++ "_" ++ note };
^key.asSymbol
}
}
/* // scene 1: all on chan 0
# 1 2 3 4 5 6 7 8 9
\kn (14 .. 22)
\sl 2, 3, 4, 5, 6, 8, 9, 12, 13
\bu (23 .. 31)
\bd (33 .. 41)
*/
NanoKtl : MIDIController {
classvar <>verbose = false, <defaultSceneNames;
var <pxmixers, <pxEditors, <pxOffset = 0, <parOffset = 0;
*initClass {
this.makeDefaults;
}
init {
super.init;
pxmixers = ();
pxEditors = ();
^this
}
mapCC { |scene=2, ctl= \sl1, action|
var ccDictKey = defaultSceneNames[scene][ctl]; // '0_42'
ccDict.put(ccDictKey, action);
}
mapToPxEdit { |editor, scene=1|
pxEditors.put(scene, editor);
// map 8 knobs to params - can be shifted
[\kn1, \kn2, \kn3, \kn4, \kn5, \kn6, \kn7, \kn8].do { |key, i|
this.mapCC(scene, key,
{ |ch, cc, val|
var parKey = pxEditors[scene].editKeys[i + parOffset];
if (parKey.notNil) { pxEditors[scene].proxy.softSet(parKey, val / 127, 0.05) };
}
)
};
// and use 9th knob for proxy volume
this.mapCC(scene, \kn9, { |ch, cc, val|
pxEditors[scene].proxy.softVol_(\amp.asSpec.map(val / 127), 0.05)
} );
}
mapToPxPars { |scene = 2, proxy ... pairs|
pairs.do { |pair|
var ctlName, paramName;
#ctlName, paramName = pair;
this.mapCC(scene, ctlName,
{ |ch, cc, midival|
proxy.set(paramName, paramName.asSpec.map(midival / 127))
}
);
};
}
// convenience method to map to a proxymixer
// could and should be refactored for more general use!
mapToPxMix { |mixer, scene = 1|
var server, mastaFunc;
pxmixers.put(scene, mixer);
server = mixer.proxyspace.server;
// add master volume to all 4 scenes, on slider 9:
mastaFunc = { |chan, cc, val| server.volume.volume_(\mastaVol.asSpec.map(val/127)) };
Spec.add(\mastaVol, [server.volume.min, server.volume.max, \db]);
(1..4).do { |scene| this.mapCC(scene, \sl9, mastaFunc) };
// scene 1:
// map first 8 volumes to sliders
[\sl1, \sl2, \sl3, \sl4, \sl5, \sl6, \sl7, \sl8].do { |key, i|
this.mapCC(scene, key,
{ |ch, cc, val|
try { pxmixers[scene].pxMons[i + pxOffset].proxy
.softVol_( \amp.asSpec.map(val / 127), 0.05)
}
};
)
};
// upper buttons: send to editor
[\bu1, \bu2, \bu3, \bu4, \bu5, \bu6, \bu7, \bu8].do { |key, i|
this.mapCC(scene, key,
{ |ch, cc, val| defer { pxmixers[scene].editBtnsAr[i + pxOffset].doAction }; })
};
// lower buttons: toggle play/stop
[\bd1, \bd2, \bd3, \bd4, \bd5, \bd6, \bd7, \bd8].do { |key, i|
this.mapCC(scene, key,
{ |ch, cc, val| defer {
var px = pxmixers[scene].pxMons[i + pxOffset].proxy;
if (val == 127) {
try { if (px.monitor.isPlaying) { px.stop } { px.play } };
};
}; })
};
this.mapCC(scene, \bu9, { |src, chan, val| if (val > 0) { this.pxShift(1, scene) } });
this.mapCC(scene, \bd9, { |src, chan, val| if (val > 0) { this.paramShift(1, scene) } });
this.pxShift(0, scene);
this.mapToPxEdit(mixer.editor, scene);
this.paramShift(0, scene);
}
// proxymixer shifting support:
pxShift { |step = 1, scene=1|
{
var onCol = Color(1, 0.5, 0.5);
var offCol = Color.clear;
var numActive = pxmixers[scene].pxMons.count { |mon| mon.zone.visible == true };
var maxOff = (numActive - 8).max(0);
pxOffset = pxOffset + step;
pxOffset = pxOffset.wrap(0, maxOff);
[ \pxOffset, pxOffset].postcs;
pxmixers[scene].pxMons.do { |mong, i|
var col = if (i >= pxOffset and: (i < (pxOffset + 8).max(0)), onCol, offCol);
mong.nameView.background_(col);
// write indices there as well
}
}.defer;
}
paramShift { |step = 1, scene=1|
{
var onCol = Color(1, 0.5, 0.5);
var offCol = Color.clear;
var numActive = pxEditors[scene].edits.count { |edi| edi.visible == true };
var maxOff = (numActive - 8).max(0);
parOffset = parOffset + step;
parOffset = parOffset.wrap(0, maxOff);
[ \parOffset, parOffset].postcs;
pxEditors[scene].edits.do { |edi, i|
var col = if (i >= parOffset and: (i < (parOffset + 8).max(0)), onCol, offCol);
edi.labelView.background_(col);
} }.defer;
}
*makeDefaults {
// lookup for all scenes and ctlNames, \sl1, \kn1, \bu1, \bd1,
defaultSceneNames = (
// general controls that do not change with scenes:
0: (
rew: '0_47',
fwd: '0_48',
play: '0_45',
loop: '0_49',
stop: '0_46',
rec: '0_44'
),
1: (
sl1: '0_2',
sl2: '0_3',
sl3: '0_4',
sl4: '0_5',
sl5: '0_6',
sl6: '0_8',
sl7: '0_9',
sl8: '0_12',
sl9: '0_13',
kn1: '0_14',
kn2: '0_15',
kn3: '0_16',
kn4: '0_17',
kn5: '0_18',
kn6: '0_19',
kn7: '0_20',
kn8: '0_21',
kn9: '0_22',
bu1: '0_23',
bu2: '0_24',
bu3: '0_25',
bu4: '0_26',
bu5: '0_27',
bu6: '0_28',
bu7: '0_29',
bu8: '0_30',
bu9: '0_31',
bd1: '0_33',
bd2: '0_34',
bd3: '0_35',
bd4: '0_36',
bd5: '0_37',
bd6: '0_38',
bd7: '0_39',
bd8: '0_40',
bd9: '0_41'
),
2: (
sl1: '0_42',
sl2: '0_43',
sl3: '0_50',
sl4: '0_51',
sl5: '0_52',
sl6: '0_53',
sl7: '0_54',
sl8: '0_55',
sl9: '0_56',
kn1: '0_57',
kn2: '0_58',
kn3: '0_59',
kn4: '0_60',
kn5: '0_61',
kn6: '0_62',
kn7: '0_63',
kn8: '0_65',
kn9: '0_66',
bu1: '0_67',
bu2: '0_68',
bu3: '0_69',
bu4: '0_70',
bu5: '0_71',
bu6: '0_72',
bu7: '0_73',
bu8: '0_74',
bu9: '0_75',
bd1: '0_76',
bd2: '0_77',
bd3: '0_78',
bd4: '0_79',
bd5: '0_80',
bd6: '0_81',
bd7: '0_82',
bd8: '0_83',
bd9: '0_84'
),
3: (
sl1: '0_85',
sl2: '0_86',
sl3: '0_87',
sl4: '0_88',
sl5: '0_89',
sl6: '0_90',
sl7: '0_91',
sl8: '0_92',
sl9: '0_93',
kn1: '0_94',
kn2: '0_95',
kn3: '0_96',
kn4: '0_97',
kn5: '0_102',
kn6: '0_103',
kn7: '0_104',
kn8: '0_105',
kn9: '0_106',
bu1: '0_107',
bu2: '0_108',
bu3: '0_109',
bu4: '0_110',
bu5: '0_111',
bu6: '0_112',
bu7: '0_113',
bu8: '0_114',
bu9: '0_115',
bd1: '0_116',
bd2: '0_117',
bd3: '0_118',
bd4: '0_119',
bd5: '0_120',
bd6: '0_121',
bd7: '0_122',
bd8: '0_123',
bd9: '0_124'
),
4: (
sl1: '0_7',
sl2: '1_7',
sl3: '2_7',
sl4: '3_7',
sl5: '4_7',
sl6: '5_7',
sl7: '6_7',
sl8: '7_7',
sl9: '8_7',
kn1: '0_10',
kn2: '1_10',
kn3: '2_10',
kn4: '3_10',
kn5: '4_10',
kn6: '5_10',
kn7: '6_10',
kn8: '7_10',
kn9: '8_10',
// buttons toggle!
bu1: '0_16',
bu2: '1_16',
bu3: '2_16',
bu4: '3_16',
bu5: '4_16',
bu6: '5_16',
bu7: '6_16',
bu8: '7_16',
bu9: '8_16',
bd1: '0_17',
bd2: '1_17',
bd3: '2_17',
bd4: '3_17',
bd5: '4_17',
bd6: '5_17',
bd7: '6_17',
bd8: '7_17',
bd9: '8_17'
)
);
}
}
BCRKtl : MIDIController {
classvar <>verbose = false, <defaultCtlNames;
classvar <>midiOut;
*makeMIDIOut { |index = 0|
midiOut = MIDIOut(index, MIDIClient.destinations[index].uid)
}
*initClass {
this.makeDefaults;
}
init {
super.init;
^this
}
mapCC { |ctl= \sl1, action|
var ccDictKey = defaultCtlNames[ctl]; // '0_42'
ccDict.put(ccDictKey, action);
}
// mapToPxEdit { |editor, scene=1|
// pxEditors.put(scene, editor);
//
// // map 8 knobs to params - can be shifted
// [\kn1, \kn2, \kn3, \kn4, \kn5, \kn6, \kn7, \kn8].do { |key, i|
// this.mapCC(scene, key,
// { |ch, cc, val|
// var parKey = pxEditors[scene].editKeys[i + parOffset];
// if (parKey.notNil) { pxEditors[scene].proxy.softSet(parKey, val / 127, 0.05) };
// }
// )
// };
// // and use 9th knob for proxy volume
// this.mapCC(scene, \kn9, { |ch, cc, val|
// pxEditors[scene].proxy.softVol_(\amp.asSpec.map(val / 127), 0.05)
// } );
// }
mapToPxPars { |proxy ... pairs|
if (midiOut.notNil) {
this.sendFromProxy(proxy, pairs);
};
pairs.do { |pair|
var ctlName, paramName;
#ctlName, paramName = pair;
this.mapCC(ctlName,
{ |ch, cc, midival|
proxy.set(paramName, paramName.asSpec.map(midival / 127))
}
);
};
}
sendFromProxy { |proxy, pairs|
var ctlNames, paramNames, currVals, midivals;
#ctlNames, paramNames = pairs.flop;
currVals = proxy.getKeysValues(paramNames).flop[1];
midivals = currVals.collect { |currval, i|
(paramNames[i].asSpec.unmap(currval) * 127).round.asInteger;
};
midivals.postln;
[ctlNames, midivals].flop.do { |pair|
this.sendCtlValue(*pair);
}
}
sendCtlValue { |ctlName, midival|
var chanCtl = this.ccKeyToChanCtl(defaultCtlNames[ctlName]);
midiOut.control(chanCtl[0], chanCtl[1], midival);
}
// // convenience method to map to a proxymixer
// // could and should be refactored for more general use!
// mapToPxMix { |mixer, scene = 1|
//
// var server, mastaFunc;
// pxmixers.put(scene, mixer);
// server = mixer.proxyspace.server;
//
// // add master volume to all 4 scenes, on slider 9:
// mastaFunc = { |chan, cc, val| server.volume.volume_(\mastaVol.asSpec.map(val/127)) };
// Spec.add(\mastaVol, [server.volume.min, server.volume.max, \db]);
// (1..4).do { |scene| this.mapCC(scene, \sl9, mastaFunc) };
//
// // scene 1:
//
// // map first 8 volumes to sliders
// [\sl1, \sl2, \sl3, \sl4, \sl5, \sl6, \sl7, \sl8].do { |key, i|
// this.mapCC(scene, key,
// { |ch, cc, val|
// try { pxmixers[scene].pxMons[i + pxOffset].proxy
// .softVol_( \amp.asSpec.map(val / 127), 0.05)
// }
// };
// )
// };
// // upper buttons: send to editor
// [\bu1, \bu2, \bu3, \bu4, \bu5, \bu6, \bu7, \bu8].do { |key, i|
// this.mapCC(scene, key,
// { |ch, cc, val| defer { pxmixers[scene].editBtnsAr[i + pxOffset].doAction }; })
// };
//
// // lower buttons: toggle play/stop
// [\bd1, \bd2, \bd3, \bd4, \bd5, \bd6, \bd7, \bd8].do { |key, i|
// this.mapCC(scene, key,
// { |ch, cc, val| defer {
// var px = pxmixers[scene].pxMons[i].proxy;
// if (val == 127) {
// try { if (px.monitor.isPlaying) { px.stop } { px.play } };
// };
// }; })
// };
//
// this.mapCC(scene, \bu9, { |src, chan, val| if (val > 0) { this.pxShift(1, scene) } });
// this.mapCC(scene, \bd9, { |src, chan, val| if (val > 0) { this.paramShift(1, scene) } });
//
// this.pxShift(0, scene);
// this.mapToPxEdit(mixer.editor, scene);
// this.paramShift(0, scene);
// }
//
// // proxymixer shifting support:
//
// pxShift { |step = 1, scene=1|
// {
// var onCol = Color(1, 0.5, 0.5);
// var offCol = Color.clear;
// var numActive = pxmixers[scene].pxMons.count { |mon| mon.zone.visible == true };
// var maxOff = (numActive - 8).max(0);
// pxOffset = pxOffset + step;
// pxOffset = pxOffset.wrap(0, maxOff);
//
// [ \pxOffset, pxOffset].postcs;
//
// pxmixers[scene].pxMons.do { |mong, i|
// var col = if (i >= pxOffset and: (i < (pxOffset + 8).max(0)), onCol, offCol);
// mong.nameView.background_(col);
// // write indices there as well
// }
// }.defer;
// }
//
// paramShift { |step = 1, scene=1|
// {
// var onCol = Color(1, 0.5, 0.5);
// var offCol = Color.clear;
// var numActive = pxEditors[scene].edits.count { |edi| edi.visible == true };
// var maxOff = (numActive - 8).max(0);
// parOffset = parOffset + step;
// parOffset = parOffset.wrap(0, maxOff);
//
// [ \parOffset, parOffset].postcs;
//
// pxEditors[scene].edits.do { |edi, i|
// var col = if (i >= parOffset and: (i < (parOffset + 8).max(0)), onCol, offCol);
// edi.labelView.background_(col);
// } }.defer;
// }
*makeDefaults {
// lookup for all scenes and ctlNames, \sl1, \kn1, \bu1, \bd1,
defaultCtlNames = (
knA1: '0_1',
knA2: '0_2',
knA3: '0_3',
knA4: '0_4',
knA5: '0_5',
knA6: '0_6',
knA7: '0_7',
knA8: '0_8',
knB1: '0_33',
knB2: '0_34',
knB3: '0_35',
knB4: '0_36',
knB5: '0_37',
knB6: '0_38',
knB7: '0_39',
knB8: '0_40',
// knC : 41-49
// knD : 50-57
btA1: '0_89',
btA2: '0_90',
btA3: '0_91',
btB1: '0_97',
btB2: '0_98',
btB3: '0_99'
);
}
}