Add class for push handling
authorEugene Petkevich <nasedil.genio.code@gmail.com>
Sun, 5 Jun 2022 16:11:13 +0000 (19:11 +0300)
committerEugene Petkevich <nasedil.genio.code@gmail.com>
Sun, 5 Jun 2022 16:11:13 +0000 (19:11 +0300)
extensions/classes/midihandler.sc
extensions/classes/midimix.sc
extensions/classes/midipedals.sc
extensions/classes/midipush.sc [new file with mode: 0644]
extensions/classes/midiremotesl.sc
Хэзифу/push-control.scd
Хэзифу/sl25remote-control.scd [deleted file]

index 6d7ac7c..518306f 100644 (file)
@@ -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;
   }
 }
 
index ce8d9d4..1d2d01b 100644 (file)
@@ -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;
   }
 
index b999387..2b30ac3 100644 (file)
@@ -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 (file)
index 0000000..218640a
--- /dev/null
@@ -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 <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
index 58fdf88..196f1c7 100644 (file)
@@ -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|
index d499ba3..278c0ab 100644 (file)
     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
diff --git a/Хэзифу/sl25remote-control.scd b/Хэзифу/sl25remote-control.scd
deleted file mode 100644 (file)
index 000712e..0000000
+++ /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;
-});
-});
-)
-*/