ableton-js 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,8 +4,28 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### [v2.3.1](https://github.com/leolabs/ableton.js/compare/v2.3.0...v2.3.1)
8
+
9
+ - :bug: Fix invalid import of the Timer object in Live 10 [`e003530`](https://github.com/leolabs/ableton.js/commit/e003530857191a0dc9aaacef954f7929813797a6)
10
+
11
+ #### [v2.3.0](https://github.com/leolabs/ableton.js/compare/v2.2.1-0...v2.3.0)
12
+
13
+ > 27 March 2022
14
+
15
+ - :sparkles: Add the clip's name as an observable property [`78c9969`](https://github.com/leolabs/ableton.js/commit/78c99694e8e230ad4ecb022316ebed731994fc6f)
16
+
17
+ #### [v2.2.1-0](https://github.com/leolabs/ableton.js/compare/v2.2.0...v2.2.1-0)
18
+
19
+ > 26 March 2022
20
+
21
+ - :sparkles: Implement basic Application interface to fetch live version and dialog information [`5aba86f`](https://github.com/leolabs/ableton.js/commit/5aba86f811ff36d43a99a657a2789e3d9ac59cce)
22
+ - :white_check_mark: Add tests for the application namespace [`9954cdf`](https://github.com/leolabs/ableton.js/commit/9954cdf724a41d8705a62fa50ac5cb06b162e1f1)
23
+ - :sparkles: Add support for listing clips in arrangement view [`e7df690`](https://github.com/leolabs/ableton.js/commit/e7df69060ccc7e098b1b4f5d418a79a60d57c4ce)
24
+
7
25
  #### [v2.2.0](https://github.com/leolabs/ableton.js/compare/v2.1.8...v2.2.0)
8
26
 
27
+ > 18 March 2022
28
+
9
29
  - :memo: Fix details in readme [`#36`](https://github.com/leolabs/ableton.js/pull/36)
10
30
  - :bug: Fix errors on quick disconnect/connect [`#42`](https://github.com/leolabs/ableton.js/pull/42)
11
31
  - :bug: Fix connected status on load of Ableton class [`#40`](https://github.com/leolabs/ableton.js/pull/40)
package/index.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { EventEmitter } from "events";
3
3
  import { Song } from "./ns/song";
4
4
  import { Internal } from "./ns/internal";
5
+ import { Application } from "./ns/application";
5
6
  interface Command {
6
7
  uuid: string;
7
8
  ns: string;
@@ -42,6 +43,7 @@ export declare class Ableton extends EventEmitter implements ConnectionEventEmit
42
43
  private buffer;
43
44
  private latency;
44
45
  song: Song;
46
+ application: Application;
45
47
  internal: Internal;
46
48
  constructor(host?: string, sendPort?: number, listenPort?: number, heartbeatInterval?: number);
47
49
  close(): void;
package/index.js CHANGED
@@ -67,6 +67,7 @@ var semver_1 = __importDefault(require("semver"));
67
67
  var zlib_1 = require("zlib");
68
68
  var song_1 = require("./ns/song");
69
69
  var internal_1 = require("./ns/internal");
70
+ var application_1 = require("./ns/application");
70
71
  var package_version_1 = require("./util/package-version");
71
72
  var TimeoutError = /** @class */ (function (_super) {
72
73
  __extends(TimeoutError, _super);
@@ -97,6 +98,7 @@ var Ableton = /** @class */ (function (_super) {
97
98
  _this.buffer = [];
98
99
  _this.latency = 0;
99
100
  _this.song = new song_1.Song(_this);
101
+ _this.application = new application_1.Application(_this);
100
102
  _this.internal = new internal_1.Internal(_this);
101
103
  _this.client = dgram_1.default.createSocket({ type: "udp4", reuseAddr: true });
102
104
  _this.client.bind(_this.listenPort, host);
@@ -3,6 +3,7 @@ import sys
3
3
 
4
4
  from .Socket import Socket
5
5
  from .Interface import Interface
6
+ from .Application import Application
6
7
  from .CuePoint import CuePoint
7
8
  from .Device import Device
8
9
  from .DeviceParameter import DeviceParameter
@@ -14,6 +15,7 @@ from .Internal import Internal
14
15
  from .ClipSlot import ClipSlot
15
16
  from .Clip import Clip
16
17
  from _Framework.ControlSurface import ControlSurface
18
+ import Live
17
19
 
18
20
 
19
21
  class AbletonJS(ControlSurface):
@@ -25,6 +27,7 @@ class AbletonJS(ControlSurface):
25
27
  self.socket = Socket(self.command_handler)
26
28
 
27
29
  self.handlers = {
30
+ "application": Application(c_instance, self.socket, self.application()),
28
31
  "internal": Internal(c_instance, self.socket),
29
32
  "cue-point": CuePoint(c_instance, self.socket),
30
33
  "device": Device(c_instance, self.socket),
@@ -37,21 +40,21 @@ class AbletonJS(ControlSurface):
37
40
  "clip": Clip(c_instance, self.socket)
38
41
  }
39
42
 
40
- self.parse()
43
+ self.recv_loop = Live.Base.Timer(
44
+ callback=self.socket.process, interval=10, repeat=True)
45
+
46
+ self.recv_loop.start()
41
47
 
42
48
  self.socket.send("connect")
43
49
 
44
50
  def disconnect(self):
45
51
  self.log_message("Disconnecting")
52
+ self.recv_loop.stop()
46
53
  self.socket.send("disconnect")
47
54
  self.socket.shutdown()
48
55
  Interface.listeners.clear()
49
56
  super(AbletonJS, self).disconnect()
50
57
 
51
- def parse(self):
52
- self.socket.process()
53
- self.schedule_message(1, self.parse)
54
-
55
58
  def command_handler(self, payload):
56
59
  self.log_message("Received command: " + str(payload))
57
60
  namespace = payload["ns"]
@@ -0,0 +1,26 @@
1
+ from __future__ import absolute_import
2
+ from .Interface import Interface
3
+
4
+ import Live
5
+
6
+
7
+ class Application(Interface):
8
+ def __init__(self, c_instance, socket, application):
9
+ super(Application, self).__init__(c_instance, socket)
10
+ self.application = application
11
+ self.log_message("Version: " + self.get_version(self.get_ns()))
12
+
13
+ def get_ns(self, nsid=None):
14
+ return self.application
15
+
16
+ def get_major_version(self, ns):
17
+ return ns.get_major_version()
18
+
19
+ def get_minor_version(self, ns):
20
+ return ns.get_minor_version()
21
+
22
+ def get_bugfix_version(self, ns):
23
+ return ns.get_bugfix_version()
24
+
25
+ def get_version(self, ns):
26
+ return str(ns.get_major_version()) + "." + str(ns.get_minor_version()) + "." + str(ns.get_bugfix_version())
@@ -11,6 +11,7 @@ class Clip(Interface):
11
11
  clip_id = Interface.save_obj(clip)
12
12
  return {
13
13
  "id": clip_id,
14
+ "name": clip.name,
14
15
  "color": clip.color,
15
16
  "is_audio_clip": clip.is_audio_clip,
16
17
  "is_midi_clip": clip.is_midi_clip,
@@ -10,4 +10,4 @@ class Internal(Interface):
10
10
  return self
11
11
 
12
12
  def get_version(self, ns):
13
- return "2.2.0"
13
+ return "2.3.1"
@@ -82,19 +82,23 @@ class Socket(object):
82
82
  def process(self):
83
83
  try:
84
84
  buffer = bytes()
85
+ num_messages = 0
85
86
  while 1:
86
- data = self._socket.recv(65536)
87
+ data = self._socket.recv(8192)
87
88
  if len(data) and self.input_handler:
88
89
  buffer += data[1:]
90
+ num_messages += 1
89
91
 
90
92
  # \xFF for Live 10 (Python2) and 255 for Live 11 (Python3)
91
93
  if(data[0] == b'\xFF' or data[0] == 255):
92
94
  unzipped = zlib.decompress(buffer)
93
95
  payload = json.loads(unzipped)
94
96
 
95
- self.log_message("Receiving: " + str(payload))
97
+ self.log_message(
98
+ "Receiving from " + str(num_messages) + " messages, " + str(len(buffer)) + " bytes: " + str(payload))
96
99
  self.input_handler(payload)
97
100
  buffer = bytes()
101
+ num_messages = 0
98
102
  except socket.error as e:
99
103
  return
100
104
  except Exception as e:
@@ -1,6 +1,7 @@
1
1
  from __future__ import absolute_import
2
2
  from .Interface import Interface
3
3
  from .Device import Device
4
+ from .Clip import Clip
4
5
  from .ClipSlot import ClipSlot
5
6
 
6
7
 
@@ -16,6 +17,9 @@ class Track(Interface):
16
17
  def __init__(self, c_instance, socket):
17
18
  super(Track, self).__init__(c_instance, socket)
18
19
 
20
+ def get_arrangement_clips(self, ns):
21
+ return list(map(Clip.serialize_clip, ns.arrangement_clips))
22
+
19
23
  def get_devices(self, ns):
20
24
  return list(map(Device.serialize_device, ns.devices))
21
25
 
@@ -0,0 +1,22 @@
1
+ import { Ableton } from "..";
2
+ import { Namespace } from ".";
3
+ export interface GettableProperties {
4
+ bugfix_version: number;
5
+ major_version: number;
6
+ minor_version: number;
7
+ version: string;
8
+ current_dialog_button_count: number;
9
+ current_dialog_message: string;
10
+ open_dialog_count: number;
11
+ }
12
+ export interface TransformedProperties {
13
+ }
14
+ export interface SettableProperties {
15
+ }
16
+ export interface ObservableProperties {
17
+ open_dialog_count: number;
18
+ }
19
+ export declare class Application extends Namespace<GettableProperties, TransformedProperties, SettableProperties, ObservableProperties> {
20
+ constructor(ableton: Ableton);
21
+ pressCurrentDialogButton(index: number): Promise<any>;
22
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ var __generator = (this && this.__generator) || function (thisArg, body) {
27
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
+ function verb(n) { return function (v) { return step([n, v]); }; }
30
+ function step(op) {
31
+ if (f) throw new TypeError("Generator is already executing.");
32
+ while (_) try {
33
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
+ if (y = 0, t) op = [op[0] & 2, t.value];
35
+ switch (op[0]) {
36
+ case 0: case 1: t = op; break;
37
+ case 4: _.label++; return { value: op[1], done: false };
38
+ case 5: _.label++; y = op[1]; op = [0]; continue;
39
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
+ default:
41
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
+ if (t[2]) _.ops.pop();
46
+ _.trys.pop(); continue;
47
+ }
48
+ op = body.call(thisArg, _);
49
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
+ }
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.Application = void 0;
55
+ var _1 = require(".");
56
+ var Application = /** @class */ (function (_super) {
57
+ __extends(Application, _super);
58
+ function Application(ableton) {
59
+ return _super.call(this, ableton, "application") || this;
60
+ }
61
+ Application.prototype.pressCurrentDialogButton = function (index) {
62
+ return __awaiter(this, void 0, void 0, function () {
63
+ return __generator(this, function (_a) {
64
+ return [2 /*return*/, this.sendCommand("press_current_dialog_button", [index])];
65
+ });
66
+ });
67
+ };
68
+ return Application;
69
+ }(_1.Namespace));
70
+ exports.Application = Application;
@@ -0,0 +1 @@
1
+ import "jest-extended";
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (_) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var tests_1 = require("../util/tests");
40
+ require("jest-extended");
41
+ var gettableProps = [
42
+ "major_version",
43
+ "minor_version",
44
+ "bugfix_version",
45
+ "version",
46
+ "open_dialog_count",
47
+ "current_dialog_message",
48
+ "current_dialog_button_count",
49
+ ];
50
+ describe("Application", function () {
51
+ it("should be able to read all properties without erroring", function () { return __awaiter(void 0, void 0, void 0, function () {
52
+ return __generator(this, function (_a) {
53
+ switch (_a.label) {
54
+ case 0: return [4 /*yield*/, tests_1.withAbleton(function (ab) { return __awaiter(void 0, void 0, void 0, function () {
55
+ return __generator(this, function (_a) {
56
+ switch (_a.label) {
57
+ case 0: return [4 /*yield*/, Promise.all(gettableProps.map(function (p) { return ab.application.get(p); }))];
58
+ case 1:
59
+ _a.sent();
60
+ return [2 /*return*/];
61
+ }
62
+ });
63
+ }); })];
64
+ case 1:
65
+ _a.sent();
66
+ return [2 /*return*/];
67
+ }
68
+ });
69
+ }); });
70
+ });
package/ns/clip.d.ts CHANGED
@@ -115,6 +115,7 @@ export interface ObservableProperties {
115
115
  is_recording: boolean;
116
116
  loop_end: number;
117
117
  loop_start: number;
118
+ name: string;
118
119
  pitch_coarse: number;
119
120
  pitch_fine: number;
120
121
  playing_position: number;
@@ -127,6 +128,7 @@ export interface ObservableProperties {
127
128
  }
128
129
  export interface RawClip {
129
130
  id: number;
131
+ name: string;
130
132
  color: number;
131
133
  is_audio_clip: boolean;
132
134
  is_midi_clip: boolean;
package/ns/song.spec.js CHANGED
@@ -143,7 +143,7 @@ describe("Song", function () {
143
143
  switch (_a.label) {
144
144
  case 0:
145
145
  largeArray = [];
146
- for (i = 0; i < 10000; i++) {
146
+ for (i = 0; i < 100000; i++) {
147
147
  largeArray.push(i);
148
148
  }
149
149
  return [4 /*yield*/, ab.song.setData("abletonjs_test", largeArray)];
package/ns/track.d.ts CHANGED
@@ -2,9 +2,11 @@ import { Ableton } from "..";
2
2
  import { Namespace } from ".";
3
3
  import { Device, RawDevice } from "./device";
4
4
  import { ClipSlot, RawClipSlot } from "./clip-slot";
5
+ import { Clip, RawClip } from "./clip";
5
6
  import { Color } from "../util/color";
6
7
  export interface GettableProperties {
7
8
  arm: boolean;
9
+ arrangement_clips: RawClip[];
8
10
  can_be_armed: boolean;
9
11
  can_be_frozen: boolean;
10
12
  can_show_chains: boolean;
@@ -48,6 +50,7 @@ export interface TransformedProperties {
48
50
  color: Color;
49
51
  devices: Device[];
50
52
  clip_slots: ClipSlot[];
53
+ arrangement_clips: Clip[];
51
54
  }
52
55
  export interface SettableProperties {
53
56
  arm: boolean;
@@ -77,6 +80,7 @@ export interface SettableProperties {
77
80
  }
78
81
  export interface ObservableProperties {
79
82
  arm: number;
83
+ arrangement_clips: RawClip[];
80
84
  clip_slots: RawClipSlot[];
81
85
  color_index: number;
82
86
  color: number;
package/ns/track.js CHANGED
@@ -19,6 +19,7 @@ exports.Track = void 0;
19
19
  var _1 = require(".");
20
20
  var device_1 = require("./device");
21
21
  var clip_slot_1 = require("./clip-slot");
22
+ var clip_1 = require("./clip");
22
23
  var color_1 = require("../util/color");
23
24
  var Track = /** @class */ (function (_super) {
24
25
  __extends(Track, _super);
@@ -26,10 +27,13 @@ var Track = /** @class */ (function (_super) {
26
27
  var _this = _super.call(this, ableton, "track", raw.id) || this;
27
28
  _this.raw = raw;
28
29
  _this.transformers = {
30
+ arrangement_clips: function (clips) {
31
+ return clips.map(function (clip) { return new clip_1.Clip(ableton, clip); });
32
+ },
29
33
  color: function (c) { return new color_1.Color(c); },
30
- devices: function (devices) { return devices.map(function (d) { return new device_1.Device(_this.ableton, d); }); },
34
+ devices: function (devices) { return devices.map(function (d) { return new device_1.Device(ableton, d); }); },
31
35
  clip_slots: function (clip_slots) {
32
- return clip_slots.map(function (c) { return new clip_slot_1.ClipSlot(_this.ableton, c); });
36
+ return clip_slots.map(function (c) { return new clip_slot_1.ClipSlot(ableton, c); });
33
37
  },
34
38
  };
35
39
  return _this;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ableton-js",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Control Ableton Live from Node",
5
5
  "main": "index.js",
6
6
  "author": "Leo Bernard <admin@leolabs.org>",
@@ -20,7 +20,7 @@
20
20
  "ableton10:launch": "set -- /Applications/Ableton*10* && open \"$1\"",
21
21
  "ableton11:copy-script": "set -- /Applications/Ableton*11*/Contents/App-Resources/MIDI\\ Remote\\ Scripts && rm -rf \"$1/AbletonJS\" && cp -r \"$(pwd)/midi-script\" \"$1/AbletonJS\" && rm -rf \"$1/AbletonJS/_Framework\"",
22
22
  "ableton11:launch": "set -- /Applications/Ableton*11* && open \"$1\"",
23
- "ableton:logs": "tail -f ~/Library/Preferences/Ableton/*/Log.txt | grep -i -e RemoteScriptError -e RemoteScriptMessage | grep -i -e AbletonJS",
23
+ "ableton:logs": "tail -f ~/Library/Preferences/Ableton/*/Log.txt | grep -i -e RemoteScriptError -e RemoteScriptMessage",
24
24
  "ableton:kill": "pkill -KILL -f \"Ableton Live\"",
25
25
  "ableton10:start": "yarn ableton:kill; yarn ableton:clean && yarn ableton10:copy-script && yarn ableton10:launch && yarn ableton:logs",
26
26
  "ableton11:start": "yarn ableton:kill; yarn ableton:clean && yarn ableton11:copy-script && yarn ableton11:launch && yarn ableton:logs",