midiInit {
var dest;
dest = MIDIClient.destinations.detect({ |item|
- item.device == midiNames[0];
+ (item.device == midiNames[0]).and(midiNames[1].includesEqual(item.name));
});
if (dest == nil) {
Error("Cannot connect to "++name).throw;
};
midiout = MIDIOut.newByName(dest.device, dest.name).latency_(0);
- idin = MIDIClient.sources.detect{|i| i.device == midiNames[0]}.uid;
+ idin = MIDIClient.sources.detect{|i| (i.device == midiNames[0]).and(midiNames[1].includesEqual(i.name))}.uid;
}
}
];
}
- *new {|name='DJ-Tech MIX-101', midiNames=#["Mix-101", "Mix-101 MIDI 1", "Mix-101 MIDI 1"], reveal=true|
+ *new {|name='DJ-Tech MIX-101', midiNames=#["Mix-101", ["Mix-101 MIDI 1", "Mix-101 MIDI 1"]], reveal=true|
^super.newCopyArgs(name, midiNames, reveal).midiInit.controlInit;
}
var <>sustain;
var <>express;
- *new {|name='Pedals', midiNames=#["ReMOTE SL", "ReMOTE SL MIDI 1", "ReMOTE SL MIDI 1"], reveal=true|
+ *new {|name='Pedals', midiNames=#["ReMOTE SL", ["ReMOTE SL MIDI 1", "ReMOTE SL MIDI 1"]], reveal=true|
^super.newCopyArgs(name, midiNames, reveal).midiInit.controlInit;
}
--- /dev/null
+// Ableton push handler
+// by Eugene Zuelum
+//
+// see https://github.com/Ableton/push-interface for info about ABI:
+// https://github.com/Ableton/push-interface/blob/master/doc/AbletonPush2MIDIDisplayInterface.asc
+
+MidiPush : MidiHandler {
+ classvar buttonControls;
+ var <>but, <>press, <>pad, <>bend, <>rotLeft, <>rotRight, <>rot, <>butup, <>butlo;
+ var <>touchBend, <>touchRotLeft, <>touchRotRight, <>touchRot;
+ var <tempo=1, <syncTempo=false, prTickPlayer;
+
+ *new {|name='Ableton Push 1', midiNames=#["Ableton Push", ["Ableton Push MIDI 2", "User Port"]], reveal=true|
+ ^super.newCopyArgs(name, midiNames, reveal).midiInit.controlInit;
+ }
+
+ *initClass {
+ buttonControls = IdentityDictionary[
+ 85 -> \play,
+ 86 -> \rec,
+ 87 -> \new,
+ 88 -> \duplicate,
+ 89 -> \automation,
+ 90 -> \fixedLength,
+ 116 -> \quantize,
+ 117 -> \double,
+ 118 -> \delete,
+ 119 -> \undo,
+ 9 -> \metronome,
+ 3 -> \tapTempo,
+ 36 -> \d14,
+ 37 -> \d14t,
+ 38 -> \d18,
+ 39 -> \d18t,
+ 40 -> \d116,
+ 41 -> \d116t,
+ 42 -> \d132,
+ 43 -> \d132t,
+ 29 -> \stop,
+ 28 -> \master,
+ 47 -> \down,
+ 46 -> \up,
+ 44 -> \left,
+ 45 -> \right,
+ 48 -> \select,
+ 49 -> \shift,
+ 50 -> \note,
+ 51 -> \session,
+ 52 -> \addEffect,
+ 53 -> \addTrack,
+ 54 -> \octaveUp,
+ 55 -> \octaveDown,
+ 56 -> \repeat,
+ 57 -> \accent,
+ 58 -> \scales,
+ 59 -> \user,
+ 60 -> \mute,
+ 61 -> \solo,
+ 62 -> \forward,
+ 63 -> \back,
+ 110 -> \device,
+ 111 -> \browse,
+ 112 -> \track,
+ 113 -> \clip,
+ 114 -> \volume,
+ 115 -> \panSend,
+ ];
+ }
+
+ controlInit {
+ but = IdentityDictionary(know: true);
+ buttonControls.do{|controlName, n|
+ but[controlName] = {|...args|
+ this.noCallback("but."++controlName, *args);
+ };
+ };
+
+ // bend
+ MIDIdef.bend((\bend++name).asSymbol, {|...args|
+ bend.value(args[0].bilin(8192, 0, 16383, 0, -1, 1), *args);
+ }, srcID: idin);
+
+ // pressure
+ MIDIdef.polytouch((\press++name).asSymbol, {|...args|
+ var note, row, col;
+ note = args[1] - 36;
+ col = note % 8;
+ row = (note / 8).asInteger;
+ press.value(args[0].linlin(0, 127, 0, 1), col, row, *args);
+ }, (36..99), srcID: idin);
+
+ // rotators
+ MIDIdef.cc((\rotLeft++name).asSymbol, {|...args|
+ rotLeft.value(args[0].wrap(-63, 64), *args);
+ }, 14, srcID: idin);
+ MIDIdef.cc((\rotRight++name).asSymbol, {|...args|
+ rotRight.value(args[0].wrap(-63, 64), *args);
+ }, 15, srcID: idin);
+ MIDIdef.cc((\rot++name).asSymbol, {|...args|
+ rot.value(args[0].wrap(-63, 64), args[1]-71, *args);
+ }, (71..79), srcID: idin);
+
+ // buttons
+ MIDIdef.cc((\namedButtons++name).asSymbol, {|...args|
+ var handler, controlName;
+ controlName = buttonControls[args[1]];
+ handler = but[controlName];
+ handler.value((args[0]==127), *args);
+ }, buttonControls.keys.asArray, srcID: idin);
+ MIDIdef.cc((\butup++name).asSymbol, {|...args|
+ butup.value((args[0]==127), args[1]-20, *args);
+ }, (20..27), srcID: idin);
+ MIDIdef.cc((\butlo++name).asSymbol, {|...args|
+ butlo.value((args[0]==127), args[1]-102, *args);
+ }, (102..109), srcID: idin);
+
+ // touch controls
+ MIDIdef.noteOn((\touchOn++name).asSymbol, {|...args|
+ case
+ { args[1] <= 8 } {
+ touchRot.value(true, args[1], *args);
+ }
+ { args[1] == 10 } {
+ touchRotLeft.value(true, *args);
+ }
+ { args[1] == 9 } {
+ touchRotRight.value(true, *args);
+ }
+ { args[1] == 12 } {
+ touchBend.value(true, *args);
+ };
+ }, (0..10)++[12], srcID: idin);
+ MIDIdef.noteOff((\touchOff++name).asSymbol, {|...args|
+ case
+ { args[1] <= 8 } {
+ touchRot.value(false, args[1], *args);
+ }
+ { args[1] == 10 } {
+ touchRotLeft.value(false, *args);
+ }
+ { args[1] == 9 } {
+ touchRotRight.value(false, *args);
+ }
+ { args[1] == 12 } {
+ touchBend.value(false, *args);
+ };
+ }, (0..10)++[12], srcID: idin);
+
+ // pads
+ MIDIdef.noteOn((\padOn++name).asSymbol, {|...args|
+ var note, row, col;
+ note = args[1] - 36;
+ col = note % 8;
+ row = (note / 8).asInteger;
+ pad.value(true, args[0].linlin(0, 127, 0, 1), col, row, *args);
+ }, (36..99), srcID: idin);
+ MIDIdef.noteOff((\padOff++name).asSymbol, {|...args|
+ var note, row, col;
+ note = args[1] - 36;
+ col = note % 8;
+ row = (note / 8).asInteger;
+ pad.value(false, args[0].linlin(0, 127, 0, 1), col, row, *args);
+ }, (36..99), srcID: idin);
+
+ // initialize handlers with revealing code
+ [
+ \bend, \press, \pad, \rotLeft, \rotRight,
+ \rot, \butup, \butlo,
+ \touchBend, \touchRotLeft, \touchRotRight, \touchRot
+ ].do{|handlerName|
+ this.perform((handlerName++\_).asSymbol, {|...args|
+ this.noCallback(handlerName, *args);
+ });
+ };
+
+ // Starter message and animation
+ {
+ (0..127).do({|i|
+ midiout.noteOn(1, i, 1);
+ midiout.control(1, i, 1);
+ });
+ 0.4.wait;
+ (0..127).do({|i|
+ midiout.noteOn(1, i, 0);
+ midiout.control(1, i, 0);
+ });
+ 0.2.wait;
+ ((0..19)++(28..101)++(110..127)).do({|i|
+ midiout.control(1, i, 1);
+ });
+ }.fork();
+
+ "Џяпюӈи готов к использованию 😊".postln;
+ }
+
+ tempo_ { |v=1|
+ {
+ tempo = v;
+ midiout.start;
+ 1.do({
+ 24.do({
+ midiout.midiClock;
+ (1.0/tempo/24).wait;
+ });
+ });
+ midiout.stop;
+ }.fork();
+ }
+
+ syncTempo_{|sync=true|
+ if (sync) {
+ prTickPlayer = {
+ midiout.start;
+ true.while{
+ midiout.midiClock;
+ (1.0/24).wait;
+ }
+ }.fork(TempoClock.default);
+ } {
+ prTickPlayer.stop;
+ };
+ }
+
+ free {
+ [
+ \namedButtons, \bend, \press, \padOn, \padOff,
+ \rotLeft, \rotRight, \rot, \butup, \butlo,
+ \touchOn, \touchOff
+ ].do{|prefix|
+ var defName = (prefix ++ name).asSymbol;
+ MIDIdef(defName).free;
+ };
+ }
+
+}
+
+// test
+/*
+
+~push = MidiPush();
+
+*/
+
+// Test all LED colors via rotaning left rotary control
+/*
+(
+~col = 0;
+
+~push.rotLeft_{ |delta|
+ ~col = (~col + delta).clip(0, 127);
+ ~col.postln;
+ (0..127).do({|i|
+ ~push.midiout.noteOn(0, i, ~col);
+ ~push.midiout.control(0, i, ~col);
+ });
+};
+)
+*/
+
+// Show first 64 colors
+/*
+(
+~color = 1;
+8.do({|i|
+ 8.do({|j|
+ ~push.midiout.noteOn(0, 36+(i*8)+j, ~color);
+ ~color = ~color + 1;
+ });
+});
+)
+*/
\ No newline at end of file
var <>touchx, <>touchy;
var <>uprot, <>lowrot, <>fader;
var <>butul, <>butll, <>butur, <>butlr;
- var <>rewind, <>forward, <>stop, <>play, <>loop, <>record;
+ var <>rewind, <>forward, <>stop, <>bplay, <>loop, <>brec;
var <>pad;
- *new {|name='Novation SL25 ReMOTE MKII', midiNames=#["ReMOTE SL", "ReMOTE SL MIDI 1", "ReMOTE SL MIDI 1"], reveal=true|
+ *new {|name='Novation SL25 ReMOTE MKII', midiNames=#["ReMOTE SL", ["ReMOTE SL MIDI 1", "ReMOTE SL MIDI 1"]], reveal=true|
^super.newCopyArgs(name, midiNames, reveal).midiInit.controlInit;
}
\key, \bend, \mod, \press, \tempo, \bank, \program,
\touchx, \touchy, \uprot, \lowrot, \fader,
\butul, \butll, \butur, \butlr,
- \rewind, \forward, \stop, \play, \loop, \record,
+ \rewind, \forward, \stop, \bplay, \loop, \brec,
\pad
].do{|handlerName|
this.perform((handlerName++\_).asSymbol, {|...args|
zi.pushOut.free;
};
- // todo — always send clocks, and separate start/stop from setting tempod
+ // todo — always send clocks, and separate start/stop from setting tempo
push.setTempo = { |zi, tempo|
{
zi.pushOut.start;
});
});
)
-*/
+*/
\ No newline at end of file
+++ /dev/null
-// Novation SL25 Remote handler
-// by Eugene Zuelum
-//
-// see https://github.com/Ableton/push-interface for info about ABI:
-// https://github.com/Ableton/push-interface/blob/master/doc/AbletonPush2MIDIDisplayInterface.asc
-
-(
-~pushFactory = {
- var dest;
- var push = Environment[
- \version -> "33.7.2.5",
-
- \tempo -> 2,
-
- \padColor -> IdentityDictionary[
- \white -> 0,
- \red -> 1,
- \amber -> 2,
- \yellow -> 3,
- \lime -> 4,
- \green -> 5,
- \spring -> 6,
- \turquoise -> 7,
- \cyan -> 8,
- \sky -> 9,
- \ocean -> 10,
- \blue -> 11,
- \orchid -> 12,
- \magenta -> 13,
- \pink -> 14
- ],
-
- // preprocess relative controls
- \ppcr -> { |zi ...args|
- if (args[0] > 64, {
- args[0] = args[0] - 128;
- });
- args;
- },
-
- // preprocess absolute controls and notes
- \ppca -> { |zi ...args|
- args[0] = args[0].linlin(0, 127, 0, 1);
- args;
- },
-
- // preprocess bend
- \ppcb -> { |zi ...args|
- if (args[0] >= 8192, {
- args[0] = args[0].linlin(8192, 16383, 0, 1);
- }, {
- args[0] = args[0].linlin(0, 8192, -1, 0);
- });
- args;
- },
-
- // preprocess push color
- \pppc -> {|zi, c=0,l=3,w=false|
- var color;
- c = zi.padColor[c] ? c;
- if (c == 0, {
- color = l;
- },{
- if (l == 0, {
- color = 0;
- }, {
- if (w, {
- color = c*4;
- }, {
- color = c*4 + 4 - l;
- });
- });
- });
- color;
- },
-
- \hcPushTurnN -> Array.newClear(9),
- \hcPushTouchN -> Array.newClear(9)
- ];
-
- push.know = true;
-
- push.controllerFuncs = [
- // Rotators
- MIDIFunc.cc({ |...args|
- push.hcPushTurnS(*push.ppcr(*args));
- }, 14, 0),
- MIDIFunc.cc({ |...args|
- push.hcPushTurnL(*push.ppcr(*args));
- }, 15, 0),
- MIDIFunc.cc({ |...args|
- var col = args[1] - 71;
- push.hcPushTurnN[col].value(*push.ppcr(*args));
- },
- (71..79), 0),
- MIDIFunc.noteOn({ |...args|
- push.hcPushTouchS(true, *args);
-
- }, 10, 0),
- MIDIFunc.noteOn({ |...args|
- push.hcPushTouchL(true, *args);
-
- }, 9, 0),
- MIDIFunc.noteOn({ |...args|
- //args[1] = args[1] - 71;
- push.hcPushTouchN[args[1]].value(true, *args);
- },
- (0..8), 0),
- MIDIFunc.noteOff({ |...args|
- push.hcPushTouchS(false, *args);
-
- }, 10, 0),
- MIDIFunc.noteOff({ |...args|
- push.hcPushTouchL(false, *args);
-
- }, 9, 0),
- MIDIFunc.noteOff({ |...args|
- args[1] = args[1] - 71;
- push.hcPushTouchN[args[1]].value(false, *args);
- },
- (0..8), 0),
-
- // Left buttons
- MIDIFunc.cc({ |...args|
- if (args[0] == 127, {
- push.playDown(*args);
- }, {
- push.playUp(*args);
- });
- }, 85, 0),
- MIDIFunc.cc({ |...args|
- if (args[0] == 127, {
- push.recDown(*args);
- }, {
- push.recUp(*args);
- });
- }, 86, 0),
-
- // Pads
- MIDIFunc.noteOn({ |...args|
- var note, row, col;
- note = args[1] - 36;
- col = note % 8;
- row = (note/ 8).asInteger;
- push.padDown(row, col, *push.ppca(*args));
- },
- (36..99), 0),
-
- MIDIFunc.noteOff({ |...args|
- var note, row, col;
- note = args[1] - 36;
- col = note % 8;
- row = (note/ 8).asInteger;
- push.padUp(row, col, *push.ppca(*args));
- }, (36..99), 0),
-
- MIDIFunc.polytouch({ |...args|
- var note, row, col;
- note = args[1] - 36;
- col = note % 8;
- row = (note/ 8).asInteger;
- push.padPressure(row, col, *push.ppca(*args));
- }, (36..99), 0),
- ];
-
- //============================== Binding hc to synth parameters
-
- push.bindMul = { |zi, synth, param, divisor=1|
- { |d|
- synth.get(param, { |v|
- var newVal = v*((d)/divisor).midiratio;
- "%: % = %\n".postf(synth, param, newVal);
- synth.set(param, newVal);
- });
- }
- };
-
- push.bindAdd = { |zi, synth, param, divisor=1|
- { |d|
- synth.get(param, { |v|
- var newVal = v+(d/divisor);
- "%: % = %\n".postf(synth, param, newVal);
- synth.set(param, newVal);
- });
- }
- };
-
- push.bindRange = { |zi, synth, param, start, end|
- { |v|
- var newVal = v.linlin(0, 1, start, end);
- "%: % = %\n".postf(synth, param, newVal);
- synth.set(param, newVal);
- }
- };
-
- //============================== LED control
- dest = MIDIClient.destinations.detect({ |item|
- var res = false;
- if (item.device == "Ableton Push", {
- if (item.name == "Ableton Push MIDI 2", {
- res = true;
- });
- if (item.name == "User Port", {
- res = true;
- });
- });
- res;
- });
- push.pushOut = MIDIOut.newByName(dest.device, dest.name);
- // The following is working by itself,
- // but factory function assignment to global var
- // returns nil
- /*try {
- // linux
- push.pushOut = MIDIOut.newByName("Ableton Push", "Ableton Push MIDI 2");
- }{
- // mac os
- push.pushOut = MIDIOut.newByName("Ableton Push", "User Port");
- };*/
-
- push.pushOut.latency_(0);
- if (push.pushOut==nil, {"Cannot control push LEDs!"});
-
- push.padLight = {|zi, row, col, light=3, color=0, whiten=false, mode=0|
- if ((col < 0) || (col > 7) || (row < 0) || (row > 7), {}, {
- zi.pushOut.noteOn(mode, 36+(row*8)+col, zi.pppc(color, light, whiten));
- });
- };
-
- push.hcPushPadLedClear = { |zi|
- for (0, 7, {|i|
- for (0, 7, {|j|
- zi.hcPushPadLed.value(i, j, 0);
- });
- });
- };
-
- push.freeHC = { |zi|
- zi.controllerFuncs.do({ |item| item.free; });
- zi.pushOut.free;
- };
-
- // todo — always send clocks, and separate start/stop from setting tempod
- push.setTempo = { |zi, tempo|
- {
- zi.pushOut.start;
- 1.do({
- 24.do({
- zi.pushOut.midiClock;
- (1.0/zi.tempo/24).wait;
- });
- });
- zi.pushOut.stop;
- }.fork();
- };
-
- // Starter message and animation
-
- {
- (0..127).do({|i|
- ~push.pushOut.noteOn(0, i, 1);
- ~push.pushOut.control(0, i, 1);
- });
- 1.wait;
- (0..127).do({|i|
- ~push.pushOut.noteOn(0, i, 0);
- ~push.pushOut.control(0, i, 0);
- });
- }.fork();
-
- "Џяпюӈи готов к использованию 😊".postln;
-
- push;
-};
-)
-
-//============================================================================= Testing
-
-/* Test incoming MIDI messages from push
-(
-MIDIdef.cc(\testcc, {|...args|
-(["cc"] ++ args).postln;
-});
-MIDIdef.noteOn(\testnoteon, {|...args|
-(["note on"] ++ args).postln;
-});
-MIDIdef.noteOff(\testnoteoff, {|...args|
-(["note off"] ++ args).postln;
-});
-MIDIdef.touch(\testtouch, {|...args|
-(["touch"] ++ args).postln;
-});
-MIDIdef.polytouch(\testpolytouch, {|...args|
-(["polytouch"] ++ args).postln;
-});
-MIDIdef.bend(\testbend, {|...args|
-(["bend"] ++ args).postln;
-});
-MIDIdef.sysex(\testsysex, {|...args|
-(["sysex"] ++ args).postln;
-});
-)
-*/
-
-/* Test all LED colors via rotaning left rotary control
-~push = ~pushFactory.value;
-~col = 0;
-
-(
-~push.hcPushTurnS = { |zi, delta|
-~col = (~col + delta).clip(0, 127);
-~col.postln;
-(0..127).do({|i|
-~push.pushOut.noteOn(0, i, ~col);
-~push.pushOut.control(0, i, ~col);
-});
-};
-)
-*/
-
-/* Show first 64 colors
-(
-~color = 1;
-8.do({|i|
-8.do({|j|
-~push.pushOut.noteOn(0, 36+(i*8)+j, ~color);
-~color = ~color + 1;
-});
-});
-)
-*/