ableton-js 2.9.1 → 3.0.0

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,16 @@ 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
+ #### [v3.0.0](https://github.com/leolabs/ableton.js/compare/v2.9.1...v3.0.0)
8
+
9
+ - :sparkles: Don't rely on fixed ports, make server and client bind to a random free one [`54f7737`](https://github.com/leolabs/ableton.js/commit/54f773758ec359ab9b5bc3f5fd3e7c96cae4e8b8)
10
+ - :sparkles: Add a timeout param to the start method [`600e752`](https://github.com/leolabs/ableton.js/commit/600e752c5f7c0e349b0cb69aaf4b8f6238f6a1d4)
11
+ - :loud_sound: Add a logger interface for the library to log information [`e31e9cc`](https://github.com/leolabs/ableton.js/commit/e31e9cc1a33b90e0c482e87eae645976a8668ad2)
12
+
7
13
  #### [v2.9.1](https://github.com/leolabs/ableton.js/compare/v2.9.0...v2.9.1)
8
14
 
15
+ > 21 February 2023
16
+
9
17
  - :sparkles: Add support for available_(input/output)_routing_(channels/types) [`0c09881`](https://github.com/leolabs/ableton.js/commit/0c098819369cccf257284ad4510178833853d1e5)
10
18
  - Use enum-style values for NavDirection [`713f864`](https://github.com/leolabs/ableton.js/commit/713f86475036debabf4e401c87ddb8b08510d262)
11
19
 
package/README.md CHANGED
@@ -51,6 +51,9 @@ import { Ableton } from "ableton-js";
51
51
  const ableton = new Ableton();
52
52
 
53
53
  const test = async () => {
54
+ // Establishes a connection with Live
55
+ await ableton.start();
56
+
54
57
  // Observe the current playback state and tempo
55
58
  ableton.song.addListener("is_playing", (p) => console.log("Playing:", p));
56
59
  ableton.song.addListener("tempo", (t) => console.log("Tempo:", t));
@@ -94,6 +97,11 @@ Ableton.js uses UDP to communicate with the MIDI Script. Each message is a JSON
94
97
  object containing required data and a UUID so request and response can be
95
98
  associated with each other.
96
99
 
100
+ ### Used Ports
101
+
102
+ Both the client and the server bind to a random available port and store that
103
+ port in a local file so the other side knows which port to send messages to.
104
+
97
105
  ### Compression and Chunking
98
106
 
99
107
  To allow sending large JSON payloads, requests to and responses from the MIDI
package/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import { Internal } from "./ns/internal";
6
6
  import { Application } from "./ns/application";
7
7
  import { Midi } from "./ns/midi";
8
8
  import { Cache } from "./util/cache";
9
+ import { Logger } from "./util/logger";
9
10
  interface Command {
10
11
  uuid: string;
11
12
  ns: string;
@@ -36,13 +37,14 @@ export declare class TimeoutError extends Error {
36
37
  constructor(message: string, payload: Command);
37
38
  }
38
39
  export interface AbletonOptions {
39
- host?: string;
40
- sendPort?: number;
41
- listenPort?: number;
40
+ serverPortFile?: string;
41
+ clientPortFile?: string;
42
42
  heartbeatInterval?: number;
43
43
  cacheOptions?: LruCache.Options<string, any>;
44
+ logger?: Logger;
44
45
  }
45
46
  export declare class Ableton extends EventEmitter implements ConnectionEventEmitter {
47
+ private options?;
46
48
  private client;
47
49
  private msgMap;
48
50
  private eventListeners;
@@ -51,16 +53,26 @@ export declare class Ableton extends EventEmitter implements ConnectionEventEmit
51
53
  private cancelConnectionEvent;
52
54
  private buffer;
53
55
  private latency;
54
- private host;
55
- private sendPort;
56
- private listenPort;
56
+ private serverPort;
57
57
  cache: Cache;
58
58
  song: Song;
59
59
  application: Application;
60
60
  internal: Internal;
61
61
  midi: Midi;
62
- constructor(options?: AbletonOptions);
63
- close(): void;
62
+ private clientPortFile;
63
+ private serverPortFile;
64
+ private logger;
65
+ constructor(options?: AbletonOptions | undefined);
66
+ /**
67
+ * Starts the server and waits for a connection with Live to be established.
68
+ *
69
+ * @param timeoutMs
70
+ * If set, the function will throw an error if it can't establish a connection
71
+ * in the given time. Should be higher than 2000ms to avoid false positives.
72
+ */
73
+ start(timeoutMs?: number): Promise<void>;
74
+ /** Closes the client */
75
+ close(): Promise<unknown>;
64
76
  /**
65
77
  * Returns the latency between the last command and its response.
66
78
  * This is a rough measurement, so don't rely too much on it.
package/index.js CHANGED
@@ -87,18 +87,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
87
87
  };
88
88
  Object.defineProperty(exports, "__esModule", { value: true });
89
89
  exports.getPackageVersion = exports.Ableton = exports.TimeoutError = void 0;
90
+ var os_1 = __importDefault(require("os"));
91
+ var path_1 = __importDefault(require("path"));
90
92
  var dgram_1 = __importDefault(require("dgram"));
91
93
  var events_1 = require("events");
92
94
  var uuid_1 = require("uuid");
93
95
  var semver_1 = __importDefault(require("semver"));
94
96
  var zlib_1 = require("zlib");
95
97
  var lru_cache_1 = __importDefault(require("lru-cache"));
98
+ var fs_1 = require("fs");
99
+ var promises_1 = require("fs/promises");
96
100
  var song_1 = require("./ns/song");
97
101
  var internal_1 = require("./ns/internal");
98
102
  var application_1 = require("./ns/application");
99
103
  var midi_1 = require("./ns/midi");
100
104
  var package_version_1 = require("./util/package-version");
101
105
  var cache_1 = require("./util/cache");
106
+ var SERVER_PORT_FILE = "ableton-js-server.port";
107
+ var CLIENT_PORT_FILE = "ableton-js-client.port";
102
108
  var TimeoutError = /** @class */ (function (_super) {
103
109
  __extends(TimeoutError, _super);
104
110
  function TimeoutError(message, payload) {
@@ -115,6 +121,7 @@ var Ableton = /** @class */ (function (_super) {
115
121
  function Ableton(options) {
116
122
  var _a, _b, _c, _d;
117
123
  var _this = _super.call(this) || this;
124
+ _this.options = options;
118
125
  _this.msgMap = new Map();
119
126
  _this.eventListeners = new Map();
120
127
  _this._isConnected = false;
@@ -125,61 +132,178 @@ var Ableton = /** @class */ (function (_super) {
125
132
  _this.application = new application_1.Application(_this);
126
133
  _this.internal = new internal_1.Internal(_this);
127
134
  _this.midi = new midi_1.Midi(_this);
128
- _this.host = (_a = options === null || options === void 0 ? void 0 : options.host) !== null && _a !== void 0 ? _a : "127.0.0.1";
129
- _this.sendPort = (_b = options === null || options === void 0 ? void 0 : options.sendPort) !== null && _b !== void 0 ? _b : 39041;
130
- _this.listenPort = (_c = options === null || options === void 0 ? void 0 : options.listenPort) !== null && _c !== void 0 ? _c : 39031;
131
- _this.client = dgram_1.default.createSocket({ type: "udp4" });
132
- _this.client.bind(_this.listenPort, _this.host);
133
- _this.client.addListener("message", _this.handleIncoming.bind(_this));
135
+ _this.logger = options === null || options === void 0 ? void 0 : options.logger;
134
136
  _this.cache = new lru_cache_1.default(__assign({ max: 500, ttl: 1000 * 60 * 10 }, options === null || options === void 0 ? void 0 : options.cacheOptions));
135
- var heartbeat = function () { return __awaiter(_this, void 0, void 0, function () {
136
- var e_1;
137
- return __generator(this, function (_a) {
138
- switch (_a.label) {
137
+ _this.clientPortFile = path_1.default.join(os_1.default.tmpdir(), (_b = (_a = _this.options) === null || _a === void 0 ? void 0 : _a.clientPortFile) !== null && _b !== void 0 ? _b : CLIENT_PORT_FILE);
138
+ _this.serverPortFile = path_1.default.join(os_1.default.tmpdir(), (_d = (_c = _this.options) === null || _c === void 0 ? void 0 : _c.serverPortFile) !== null && _d !== void 0 ? _d : SERVER_PORT_FILE);
139
+ return _this;
140
+ }
141
+ /**
142
+ * Starts the server and waits for a connection with Live to be established.
143
+ *
144
+ * @param timeoutMs
145
+ * If set, the function will throw an error if it can't establish a connection
146
+ * in the given time. Should be higher than 2000ms to avoid false positives.
147
+ */
148
+ Ableton.prototype.start = function (timeoutMs) {
149
+ var _a, _b, _c, _d;
150
+ return __awaiter(this, void 0, void 0, function () {
151
+ var connection, timeout, heartbeat;
152
+ var _this = this;
153
+ return __generator(this, function (_e) {
154
+ switch (_e.label) {
139
155
  case 0:
140
- this.cancelConnectionEvent = false;
141
- _a.label = 1;
156
+ this.client = dgram_1.default.createSocket({ type: "udp4" });
157
+ this.client.addListener("message", this.handleIncoming.bind(this));
158
+ this.client.addListener("listening", function () { return __awaiter(_this, void 0, void 0, function () {
159
+ var clientPort;
160
+ var _a, _b;
161
+ return __generator(this, function (_c) {
162
+ switch (_c.label) {
163
+ case 0:
164
+ clientPort = (_a = this.client) === null || _a === void 0 ? void 0 : _a.address().port;
165
+ (_b = this.logger) === null || _b === void 0 ? void 0 : _b.info("Bound client to port:", { clientPort: clientPort });
166
+ // Write used port to a file to Live can read from it
167
+ return [4 /*yield*/, promises_1.writeFile(this.clientPortFile, String(clientPort))];
168
+ case 1:
169
+ // Write used port to a file to Live can read from it
170
+ _c.sent();
171
+ return [2 /*return*/];
172
+ }
173
+ });
174
+ }); });
175
+ this.client.bind(undefined, "127.0.0.1");
176
+ // Wait for the server port file to exist
177
+ return [4 /*yield*/, new Promise(function (res) { return __awaiter(_this, void 0, void 0, function () {
178
+ var serverPort, e_1;
179
+ var _this = this;
180
+ var _a;
181
+ return __generator(this, function (_b) {
182
+ switch (_b.label) {
183
+ case 0:
184
+ _b.trys.push([0, 2, , 3]);
185
+ return [4 /*yield*/, promises_1.readFile(this.serverPortFile)];
186
+ case 1:
187
+ serverPort = _b.sent();
188
+ this.serverPort = Number(serverPort.toString());
189
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Server port:", { port: this.serverPort });
190
+ res();
191
+ return [3 /*break*/, 3];
192
+ case 2:
193
+ e_1 = _b.sent();
194
+ return [3 /*break*/, 3];
195
+ case 3:
196
+ // Set up a watcher in case the server port changes
197
+ fs_1.watchFile(this.serverPortFile, function (curr) { return __awaiter(_this, void 0, void 0, function () {
198
+ var serverPort, newPort;
199
+ var _a;
200
+ return __generator(this, function (_b) {
201
+ switch (_b.label) {
202
+ case 0:
203
+ if (!curr.isFile()) return [3 /*break*/, 2];
204
+ return [4 /*yield*/, promises_1.readFile(this.serverPortFile)];
205
+ case 1:
206
+ serverPort = _b.sent();
207
+ newPort = Number(serverPort.toString());
208
+ if (!isNaN(newPort) && newPort !== this.serverPort) {
209
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Server port changed:", { port: newPort });
210
+ this.serverPort = Number(serverPort.toString());
211
+ }
212
+ res();
213
+ _b.label = 2;
214
+ case 2: return [2 /*return*/];
215
+ }
216
+ });
217
+ }); });
218
+ return [2 /*return*/];
219
+ }
220
+ });
221
+ }); })];
142
222
  case 1:
143
- _a.trys.push([1, 3, , 4]);
144
- return [4 /*yield*/, this.song.get("current_song_time")];
223
+ // Wait for the server port file to exist
224
+ _e.sent();
225
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Checking connection...");
226
+ connection = new Promise(function (res) { return _this.once("connect", res); });
227
+ if (!timeoutMs) return [3 /*break*/, 3];
228
+ timeout = new Promise(function (_, rej) {
229
+ return setTimeout(function () { return rej("Connection timed out."); }, timeoutMs);
230
+ });
231
+ return [4 /*yield*/, Promise.race([connection, timeout])];
145
232
  case 2:
146
- _a.sent();
147
- if (!this._isConnected && !this.cancelConnectionEvent) {
148
- this._isConnected = true;
149
- this.emit("connect", "heartbeat");
150
- }
151
- return [3 /*break*/, 4];
152
- case 3:
153
- e_1 = _a.sent();
154
- if (this._isConnected && !this.cancelConnectionEvent) {
155
- this._isConnected = false;
156
- this.eventListeners.clear();
157
- this.msgMap.forEach(function (msg) { return msg.clearTimeout(); });
158
- this.msgMap.clear();
159
- this.emit("disconnect", "heartbeat");
160
- }
161
- return [3 /*break*/, 4];
162
- case 4: return [2 /*return*/];
233
+ _e.sent();
234
+ return [3 /*break*/, 5];
235
+ case 3: return [4 /*yield*/, connection];
236
+ case 4:
237
+ _e.sent();
238
+ _e.label = 5;
239
+ case 5:
240
+ (_b = this.logger) === null || _b === void 0 ? void 0 : _b.info("Got connection!");
241
+ heartbeat = function () { return __awaiter(_this, void 0, void 0, function () {
242
+ var e_2;
243
+ return __generator(this, function (_a) {
244
+ switch (_a.label) {
245
+ case 0:
246
+ this.cancelConnectionEvent = false;
247
+ _a.label = 1;
248
+ case 1:
249
+ _a.trys.push([1, 3, , 4]);
250
+ return [4 /*yield*/, this.internal.get("ping")];
251
+ case 2:
252
+ _a.sent();
253
+ if (!this._isConnected && !this.cancelConnectionEvent) {
254
+ this._isConnected = true;
255
+ this.emit("connect", "heartbeat");
256
+ }
257
+ return [3 /*break*/, 4];
258
+ case 3:
259
+ e_2 = _a.sent();
260
+ if (this._isConnected && !this.cancelConnectionEvent) {
261
+ this._isConnected = false;
262
+ this.eventListeners.clear();
263
+ this.msgMap.forEach(function (msg) { return msg.clearTimeout(); });
264
+ this.msgMap.clear();
265
+ this.emit("disconnect", "heartbeat");
266
+ }
267
+ return [3 /*break*/, 4];
268
+ case 4: return [2 /*return*/];
269
+ }
270
+ });
271
+ }); };
272
+ this.heartbeatInterval = setInterval(heartbeat, (_d = (_c = this.options) === null || _c === void 0 ? void 0 : _c.heartbeatInterval) !== null && _d !== void 0 ? _d : 2000);
273
+ heartbeat();
274
+ this.internal
275
+ .get("version")
276
+ .then(function (v) {
277
+ var jsVersion = package_version_1.getPackageVersion();
278
+ if (semver_1.default.lt(v, jsVersion)) {
279
+ console.warn("The installed version of your AbletonJS plugin (" + v + ") is lower than the JS library (" + jsVersion + ").", "Please update your AbletonJS plugin to the latest version: https://git.io/JvaOu");
280
+ }
281
+ })
282
+ .catch(function () { });
283
+ return [2 /*return*/];
163
284
  }
164
285
  });
165
- }); };
166
- _this.heartbeatInterval = setInterval(heartbeat, (_d = options === null || options === void 0 ? void 0 : options.heartbeatInterval) !== null && _d !== void 0 ? _d : 2000);
167
- heartbeat();
168
- _this.internal
169
- .get("version")
170
- .then(function (v) {
171
- var jsVersion = package_version_1.getPackageVersion();
172
- if (semver_1.default.lt(v, jsVersion)) {
173
- console.warn("The installed version of your AbletonJS plugin (" + v + ") is lower than the JS library (" + jsVersion + ").", "Please update your AbletonJS plugin to the latest version: https://git.io/JvaOu");
174
- }
175
- })
176
- .catch(function () { });
177
- return _this;
178
- }
286
+ });
287
+ };
288
+ /** Closes the client */
179
289
  Ableton.prototype.close = function () {
180
- this.cancelConnectionEvent = true;
181
- clearInterval(this.heartbeatInterval);
182
- this.client.close();
290
+ return __awaiter(this, void 0, void 0, function () {
291
+ var closePromise;
292
+ var _this = this;
293
+ return __generator(this, function (_a) {
294
+ this.cancelConnectionEvent = true;
295
+ fs_1.unwatchFile(this.serverPortFile);
296
+ if (this.heartbeatInterval) {
297
+ clearInterval(this.heartbeatInterval);
298
+ }
299
+ if (this.client) {
300
+ closePromise = new Promise(function (res) { var _a; return (_a = _this.client) === null || _a === void 0 ? void 0 : _a.once("close", res); });
301
+ this.client.close();
302
+ return [2 /*return*/, closePromise];
303
+ }
304
+ return [2 /*return*/];
305
+ });
306
+ });
183
307
  };
184
308
  /**
185
309
  * Returns the latency between the last command and its response.
@@ -209,6 +333,7 @@ var Ableton = /** @class */ (function (_super) {
209
333
  }
210
334
  };
211
335
  Ableton.prototype.handleUncompressedMessage = function (msg) {
336
+ var _a;
212
337
  this.emit("raw_message", msg);
213
338
  var data = JSON.parse(msg);
214
339
  var functionCallback = this.msgMap.get(data.uuid);
@@ -233,6 +358,12 @@ var Ableton = /** @class */ (function (_super) {
233
358
  return;
234
359
  }
235
360
  if (data.event === "connect") {
361
+ if (data.data.port && data.data.port !== this.serverPort) {
362
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Got new server port via connect:", {
363
+ port: data.data.port,
364
+ });
365
+ this.serverPort = data.data.port;
366
+ }
236
367
  if (this._isConnected === false) {
237
368
  this._isConnected = true;
238
369
  this.cancelConnectionEvent = true;
@@ -269,8 +400,7 @@ var Ableton = /** @class */ (function (_super) {
269
400
  rej(new TimeoutError([
270
401
  "The command " + cls + "." + command.name + "(" + arg + ") timed out after " + timeout + " ms.",
271
402
  "Please make sure that Ableton is running and that you have the latest",
272
- "version of AbletonJS' midi script installed and renamed to \"AbletonJS\", listening on port",
273
- _this.sendPort + " and sending on port " + _this.listenPort + ".",
403
+ "version of AbletonJS' MIDI script installed and renamed to \"AbletonJS\".",
274
404
  ].join(" "), payload));
275
405
  }, timeout);
276
406
  var currentTimestamp = Date.now();
@@ -417,6 +547,9 @@ var Ableton = /** @class */ (function (_super) {
417
547
  this.eventListeners.clear();
418
548
  };
419
549
  Ableton.prototype.sendRaw = function (msg) {
550
+ if (!this.client || !this.serverPort) {
551
+ throw new Error("The client hasn't been started yet. Please call start() first.");
552
+ }
420
553
  var buffer = zlib_1.deflateSync(Buffer.from(msg));
421
554
  // Based on this thread, 7500 bytes seems like a safe value
422
555
  // https://stackoverflow.com/questions/22819214/udp-message-too-long
@@ -429,7 +562,7 @@ var Ableton = /** @class */ (function (_super) {
429
562
  Buffer.alloc(1, i + 1 === chunks ? 255 : i),
430
563
  buffer.slice(i * byteLimit, i * byteLimit + byteLimit),
431
564
  ]);
432
- this.client.send(chunk, 0, chunk.length, this.sendPort, this.host);
565
+ this.client.send(chunk, 0, chunk.length, this.serverPort, "127.0.0.1");
433
566
  }
434
567
  };
435
568
  Ableton.prototype.isConnected = function () {
@@ -52,8 +52,6 @@ class AbletonJS(ControlSurface):
52
52
 
53
53
  self.recv_loop.start()
54
54
 
55
- self.socket.send("connect")
56
-
57
55
  def build_midi_map(self, midi_map_handle):
58
56
  script_handle = self._c_instance.handle()
59
57
  for midi in self.tracked_midi:
@@ -76,9 +74,12 @@ class AbletonJS(ControlSurface):
76
74
  super(AbletonJS, self).disconnect()
77
75
 
78
76
  def command_handler(self, payload):
79
- self.log_message("Received command: " + str(payload))
80
77
  namespace = payload["ns"]
81
78
 
79
+ # Don't clutter the logs
80
+ if not (namespace == "internal" and payload["name"] == "get_prop" and payload["args"]["prop"] == "ping"):
81
+ self.log_message("Received command: " + str(payload))
82
+
82
83
  if namespace in self.handlers:
83
84
  handler = self.handlers[namespace]
84
85
  handler.handle(payload)
@@ -9,5 +9,8 @@ class Internal(Interface):
9
9
  def get_ns(self, nsid):
10
10
  return self
11
11
 
12
+ def get_ping(self, nsid):
13
+ return True
14
+
12
15
  def get_version(self, ns):
13
- return "2.9.1"
16
+ return "3.0.0"
@@ -2,7 +2,8 @@ import socket
2
2
  import json
3
3
  import struct
4
4
  import zlib
5
- import hashlib
5
+ import os
6
+ import tempfile
6
7
  from threading import Timer
7
8
 
8
9
  import Live
@@ -15,6 +16,13 @@ def split_by_n(seq, n):
15
16
  seq = seq[n:]
16
17
 
17
18
 
19
+ server_port_file = "ableton-js-server.port"
20
+ client_port_file = "ableton-js-client.port"
21
+
22
+ client_port_path = os.path.join(tempfile.gettempdir(), client_port_file)
23
+ server_port_path = os.path.join(tempfile.gettempdir(), server_port_file)
24
+
25
+
18
26
  class Socket(object):
19
27
 
20
28
  @staticmethod
@@ -25,12 +33,30 @@ class Socket(object):
25
33
  def set_message(func):
26
34
  Socket.show_message = func
27
35
 
28
- def __init__(self, handler, remotehost='127.0.0.1', remoteport=39031, localhost='127.0.0.1', localport=39041):
36
+ def __init__(self, handler):
29
37
  self.input_handler = handler
30
- self._local_addr = (localhost, localport)
31
- self._remote_addr = (remotehost, remoteport)
38
+ self._server_addr = ('127.0.0.1', 0)
39
+ self._client_addr = ('127.0.0.1', 39031)
40
+ self.read_remote_port()
32
41
  self.init_socket()
33
42
 
43
+ def read_remote_port(self):
44
+ '''Reads the port our client is listening on'''
45
+ try:
46
+ with open(client_port_path) as file:
47
+ port = int(file.read())
48
+ if port != self._client_addr[1]:
49
+ self.log_message("Client port changed: " + str(port))
50
+ self._client_addr = ('127.0.0.1', port)
51
+
52
+ if hasattr(self, "_socket"):
53
+ self.send("connect", {"port": self._server_addr[1]})
54
+ except Exception as e:
55
+ self.log_message("Couldn't read remote port:", str(e.args))
56
+
57
+ t = Timer(1, self.read_remote_port)
58
+ t.start()
59
+
34
60
  def init_socket(self):
35
61
  self._socket = socket.socket(
36
62
  socket.AF_INET, socket.SOCK_DGRAM)
@@ -43,12 +69,22 @@ class Socket(object):
43
69
 
44
70
  def bind(self):
45
71
  try:
46
- self._socket.bind(self._local_addr)
47
- self.log_message('Starting on: ' + str(self._local_addr) +
48
- ', remote addr: ' + str(self._remote_addr))
49
- except:
72
+ self._socket.bind(self._server_addr)
73
+ port = self._socket.getsockname()[1]
74
+
75
+ # Write the chosen port to a file
76
+ with open(server_port_path, "w") as file:
77
+ file.write(str(port))
78
+
79
+ self.send("connect", {"port": port})
80
+
81
+ self.log_message('Starting on: ' + str(self._socket.getsockname()) +
82
+ ', remote addr: ' + str(self._client_addr))
83
+ except Exception as e:
50
84
  msg = 'ERROR: Cannot bind to ' + \
51
- str(self._local_addr) + ', port in use. Trying again...'
85
+ str(self._server_addr) + ': ' + \
86
+ str(e.args) + ', trying again. ' + \
87
+ 'If this keeps happening, try restarting your computer.'
52
88
  self.show_message(msg)
53
89
  self.log_message(msg)
54
90
  t = Timer(5, self.bind)
@@ -62,13 +98,13 @@ class Socket(object):
62
98
  limit = 7500
63
99
 
64
100
  if len(compressed) < limit:
65
- self._socket.sendto(b'\xFF' + compressed, self._remote_addr)
101
+ self._socket.sendto(b'\xFF' + compressed, self._client_addr)
66
102
  else:
67
103
  chunks = list(split_by_n(compressed, limit))
68
104
  count = len(chunks)
69
105
  for i, chunk in enumerate(chunks):
70
106
  count_byte = struct.pack("B", i if i + 1 < count else 255)
71
- self._socket.sendto(count_byte + chunk, self._remote_addr)
107
+ self._socket.sendto(count_byte + chunk, self._client_addr)
72
108
 
73
109
  def send(self, name, obj=None, uuid=None):
74
110
  def jsonReplace(o):
package/ns/internal.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Ableton } from "..";
2
2
  import { Namespace } from ".";
3
3
  export interface GettableProperties {
4
4
  version: string;
5
+ ping: boolean;
5
6
  }
6
7
  export interface TransformedProperties {
7
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ableton-js",
3
- "version": "2.9.1",
3
+ "version": "3.0.0",
4
4
  "description": "Control Ableton Live from Node",
5
5
  "main": "index.js",
6
6
  "author": "Leo Bernard <admin@leolabs.org>",
@@ -0,0 +1,6 @@
1
+ export interface Logger {
2
+ debug: (msg: string, ...args: any[]) => unknown;
3
+ info: (msg: string, ...args: any[]) => unknown;
4
+ warn: (msg: string, ...args: any[]) => unknown;
5
+ error: (msg: string, ...args: any[]) => unknown;
6
+ }
package/util/logger.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/util/tests.js CHANGED
@@ -45,17 +45,20 @@ var withAbleton = function (callback) { return __awaiter(void 0, void 0, void 0,
45
45
  case 0:
46
46
  ab = new __1.Ableton();
47
47
  ab.on("error", console.error);
48
- _a.label = 1;
48
+ return [4 /*yield*/, ab.start(2000)];
49
49
  case 1:
50
- _a.trys.push([1, , 3, 4]);
51
- return [4 /*yield*/, callback(ab)];
52
- case 2:
53
50
  _a.sent();
54
- return [3 /*break*/, 4];
51
+ _a.label = 2;
52
+ case 2:
53
+ _a.trys.push([2, , 4, 5]);
54
+ return [4 /*yield*/, callback(ab)];
55
55
  case 3:
56
+ _a.sent();
57
+ return [3 /*break*/, 5];
58
+ case 4:
56
59
  ab.close();
57
60
  return [7 /*endfinally*/];
58
- case 4: return [2 /*return*/];
61
+ case 5: return [2 /*return*/];
59
62
  }
60
63
  });
61
64
  }); };