From 78adea5d88a37d462d5b0dc7d4578e51f91ddedf Mon Sep 17 00:00:00 2001 From: Eugene Petkevich Date: Sun, 5 Jun 2022 19:11:13 +0300 Subject: [PATCH] Add class for push handling --- extensions/classes/midihandler.sc | 4 +- extensions/classes/midimix.sc | 2 +- extensions/classes/midipedals.sc | 2 +- extensions/classes/midipush.sc | 271 ++++++++++++++ extensions/classes/midiremotesl.sc | 6 +- .../push-control.scd" | 4 +- .../sl25remote-control.scd" | 331 ------------------ 7 files changed, 280 insertions(+), 340 deletions(-) create mode 100644 extensions/classes/midipush.sc delete mode 100644 "\320\245\321\215\320\267\320\270\321\204\321\203/sl25remote-control.scd" diff --git a/extensions/classes/midihandler.sc b/extensions/classes/midihandler.sc index 6d7ac7c..518306f 100644 --- a/extensions/classes/midihandler.sc +++ b/extensions/classes/midihandler.sc @@ -12,13 +12,13 @@ MidiHandler { 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; } } diff --git a/extensions/classes/midimix.sc b/extensions/classes/midimix.sc index ce8d9d4..1d2d01b 100644 --- a/extensions/classes/midimix.sc +++ b/extensions/classes/midimix.sc @@ -98,7 +98,7 @@ MidiMix101 : MidiHandler { ]; } - *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; } diff --git a/extensions/classes/midipedals.sc b/extensions/classes/midipedals.sc index b999387..2b30ac3 100644 --- a/extensions/classes/midipedals.sc +++ b/extensions/classes/midipedals.sc @@ -2,7 +2,7 @@ MidiPedals : MidiHandler { 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; } diff --git a/extensions/classes/midipush.sc b/extensions/classes/midipush.sc new file mode 100644 index 0000000..218640a --- /dev/null +++ b/extensions/classes/midipush.sc @@ -0,0 +1,271 @@ +// 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 \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 diff --git a/extensions/classes/midiremotesl.sc b/extensions/classes/midiremotesl.sc index 58fdf88..196f1c7 100644 --- a/extensions/classes/midiremotesl.sc +++ b/extensions/classes/midiremotesl.sc @@ -28,10 +28,10 @@ MidiRemote25SL : MidiHandler { 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; } @@ -145,7 +145,7 @@ MidiRemote25SL : MidiHandler { \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| diff --git "a/\320\245\321\215\320\267\320\270\321\204\321\203/push-control.scd" "b/\320\245\321\215\320\267\320\270\321\204\321\203/push-control.scd" index d499ba3..278c0ab 100644 --- "a/\320\245\321\215\320\267\320\270\321\204\321\203/push-control.scd" +++ "b/\320\245\321\215\320\267\320\270\321\204\321\203/push-control.scd" @@ -241,7 +241,7 @@ 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; @@ -302,4 +302,4 @@ }); }); ) -*/ +*/ \ No newline at end of file diff --git "a/\320\245\321\215\320\267\320\270\321\204\321\203/sl25remote-control.scd" "b/\320\245\321\215\320\267\320\270\321\204\321\203/sl25remote-control.scd" deleted file mode 100644 index 000712e..0000000 --- "a/\320\245\321\215\320\267\320\270\321\204\321\203/sl25remote-control.scd" +++ /dev/null @@ -1,331 +0,0 @@ -// 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; -}); -}); -) -*/ -- 2.17.1