ableton-js 3.3.2 → 3.3.3

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.3.3](https://github.com/leolabs/ableton.js/compare/v3.3.2...v3.3.3)
8
+
9
+ - :mute: Fix every message being logged twice [`3a2571f`](https://github.com/leolabs/ableton.js/commit/3a2571f0a4ad61fdf751a0dfbb5da00b6def8508)
10
+ - :wastebasket: Deprecate `removeNotes` and add `removeNotesExtended` as a replacement [`ac02271`](https://github.com/leolabs/ableton.js/commit/ac022718f0b16317d8fdf70ff1c8d77eaaa0f384)
11
+ - :bug: Address a hang in newer versions of Live when the UDP port is already being used [`5caeaeb`](https://github.com/leolabs/ableton.js/commit/5caeaebc172fc819693c988643071c565e4dbcd3)
12
+
7
13
  #### [v3.3.2](https://github.com/leolabs/ableton.js/compare/v3.3.1...v3.3.2)
8
14
 
15
+ > 27 July 2023
16
+
9
17
  - :package: Add lodash as a dependency, fixes #104 [`#104`](https://github.com/leolabs/ableton.js/issues/104)
10
18
 
11
19
  #### [v3.3.1](https://github.com/leolabs/ableton.js/compare/v3.3.0...v3.3.1)
@@ -3,6 +3,7 @@ import time
3
3
 
4
4
  from .version import version
5
5
  from .Config import DEBUG, FAST_POLLING
6
+ from .Logging import logger
6
7
  from .Socket import Socket
7
8
  from .Interface import Interface
8
9
  from .Application import Application
@@ -29,11 +30,10 @@ import Live
29
30
  class AbletonJS(ControlSurface):
30
31
  def __init__(self, c_instance):
31
32
  super(AbletonJS, self).__init__(c_instance)
32
- self.log_message("Starting AbletonJS " + version + "...")
33
+ logger.info("Starting AbletonJS " + version + "...")
33
34
 
34
35
  self.tracked_midi = set()
35
36
 
36
- Socket.set_log(self.log_message)
37
37
  Socket.set_message(self.show_message)
38
38
  self.socket = Socket(self.command_handler)
39
39
 
@@ -69,8 +69,8 @@ class AbletonJS(ControlSurface):
69
69
  tick_time = time.time() * 1000
70
70
 
71
71
  if tick_time - self._last_tick > 200:
72
- self.log_message("UDP tick is lagging, delta: " +
73
- str(round(tick_time - self._last_tick)) + "ms")
72
+ logger.warn("UDP tick is lagging, delta: " +
73
+ str(round(tick_time - self._last_tick)) + "ms")
74
74
 
75
75
  self._last_tick = tick_time
76
76
  self.socket.process()
@@ -78,8 +78,8 @@ class AbletonJS(ControlSurface):
78
78
  process_time = time.time() * 1000
79
79
 
80
80
  if process_time - tick_time > 100:
81
- self.log_message("UDP processing is taking long, delta: " +
82
- str(round(tick_time - process_time)) + "ms")
81
+ logger.warn("UDP processing is taking long, delta: " +
82
+ str(round(tick_time - process_time)) + "ms")
83
83
 
84
84
  self.schedule_message(1, self.tick)
85
85
 
@@ -97,7 +97,7 @@ class AbletonJS(ControlSurface):
97
97
  self.handlers["midi"].send_midi(midi_bytes)
98
98
 
99
99
  def disconnect(self):
100
- self.log_message("Disconnecting")
100
+ logger.info("Disconnecting")
101
101
  if FAST_POLLING:
102
102
  self.recv_loop.stop()
103
103
  self.socket.send("disconnect")
@@ -110,7 +110,7 @@ class AbletonJS(ControlSurface):
110
110
 
111
111
  # Don't clutter the logs
112
112
  if not (namespace == "internal" and payload["name"] == "get_prop" and payload["args"]["prop"] == "ping") and DEBUG:
113
- self.log_message("Received command: " + str(payload))
113
+ logger.debug("Received command: " + str(payload))
114
114
 
115
115
  if namespace in self.handlers:
116
116
  handler = self.handlers[namespace]
@@ -6,7 +6,6 @@ class Application(Interface):
6
6
  def __init__(self, c_instance, socket, application):
7
7
  super(Application, self).__init__(c_instance, socket)
8
8
  self.application = application
9
- self.log_message("Version: " + self.get_version(self.get_ns()))
10
9
 
11
10
  def get_ns(self, nsid=None):
12
11
  return self.application
@@ -2,6 +2,7 @@ import hashlib
2
2
  import json
3
3
 
4
4
  from .Config import DEBUG
5
+ from .Logging import logger
5
6
 
6
7
 
7
8
  class Interface(object):
@@ -25,11 +26,10 @@ class Interface(object):
25
26
  def __init__(self, c_instance, socket):
26
27
  self.ableton = c_instance
27
28
  self.socket = socket
28
- self.log_message = c_instance.log_message
29
29
 
30
30
  def log_debug(self, message):
31
31
  if DEBUG:
32
- self.log_message(message)
32
+ logger.debug(message)
33
33
 
34
34
  def get_ns(self, nsid):
35
35
  return Interface.obj_ids[nsid]
@@ -78,7 +78,7 @@ class Interface(object):
78
78
  self.socket.send("error", "Function call failed: " + payload["name"] +
79
79
  " doesn't exist or isn't callable", uuid)
80
80
  except Exception as e:
81
- self.log_message("Handler Error: " + str(e.args))
81
+ logger.error("Handler Error: " + str(e.args))
82
82
  self.socket.send("error", str(e.args[0]), uuid)
83
83
 
84
84
  def add_listener(self, ns, prop, eventId, nsid="Default"):
@@ -0,0 +1,3 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger("AbletonJS")
@@ -1,6 +1,7 @@
1
1
  from __future__ import absolute_import
2
2
 
3
3
  from .Interface import Interface
4
+ from .Logging import logger
4
5
 
5
6
 
6
7
  class Midi(Interface):
@@ -22,11 +23,12 @@ class Midi(Interface):
22
23
  midi_type = output.get("type")
23
24
  if midi_type != "cc" and midi_type != "note":
24
25
  raise ValueError("invalid midi type " + str(midi_type))
25
- self.outputs.add((midi_type, output.get("channel"), output.get("target")))
26
+ self.outputs.add((midi_type, output.get(
27
+ "channel"), output.get("target")))
26
28
  except ValueError as e:
27
- self.log_message(e)
29
+ logger.error(e)
28
30
  except:
29
- self.log_message("invalid midi output requested: " + str(output))
31
+ logger.error("invalid midi output requested: " + str(output))
30
32
 
31
33
  def remove_midi_listener(self, fn):
32
34
  self.event_id = None
@@ -38,10 +40,10 @@ class Midi(Interface):
38
40
  raise Exception("Listener " + str(prop) + " does not exist.")
39
41
 
40
42
  if self.event_id is not None:
41
- self.log_message("midi listener already exists")
43
+ logger.warn("midi listener already exists")
42
44
  return self.event_id
43
45
 
44
- self.log_message("Attaching midi listener")
46
+ logger.info("Attaching midi listener")
45
47
 
46
48
  self.tracked_midi.clear()
47
49
  self.tracked_midi.update(self.outputs)
@@ -4,7 +4,8 @@ import struct
4
4
  import zlib
5
5
  import os
6
6
  import tempfile
7
- from threading import Timer
7
+
8
+ from .Logging import logger
8
9
 
9
10
  import Live
10
11
 
@@ -24,11 +25,6 @@ client_port_path = os.path.join(tempfile.gettempdir(), client_port_file)
24
25
 
25
26
 
26
27
  class Socket(object):
27
-
28
- @staticmethod
29
- def set_log(func):
30
- Socket.log_message = func
31
-
32
28
  @staticmethod
33
29
  def set_message(func):
34
30
  Socket.show_message = func
@@ -39,17 +35,18 @@ class Socket(object):
39
35
  self._client_addr = ("127.0.0.1", 39031)
40
36
  self._last_error = ""
41
37
  self._socket = None
38
+ self._chunk_limit = None
42
39
 
43
40
  self.read_remote_port()
44
41
  self.init_socket(True)
45
42
 
46
- def log_once(self, msg):
43
+ def log_error_once(self, msg):
47
44
  if self._last_error != msg:
48
45
  self._last_error = msg
49
- self.log_message(msg)
46
+ logger.error(msg)
50
47
 
51
48
  def set_client_port(self, port):
52
- self.log_message("Setting client port: ", str(port))
49
+ logger.info("Setting client port: ", str(port))
53
50
  self.show_message("Client connected on port " + str(port))
54
51
  self._client_addr = ("127.0.0.1", int(port))
55
52
 
@@ -58,10 +55,10 @@ class Socket(object):
58
55
  with open(server_port_path) as file:
59
56
  port = int(file.read())
60
57
 
61
- self.log_message("Stored server port: " + str(port))
58
+ logger.info("Stored server port: " + str(port))
62
59
  return port
63
60
  except Exception as e:
64
- self.log_message(
61
+ logger.info(
65
62
  "Couldn't read stored server port: " + str(e.args))
66
63
  return None
67
64
 
@@ -71,7 +68,8 @@ class Socket(object):
71
68
  try:
72
69
  os.stat(client_port_path)
73
70
  except Exception as e:
74
- self.log_once("Couldn't stat remote port file: " + str(e.args))
71
+ self.log_error_once(
72
+ "Couldn't stat remote port file: " + str(e.args))
75
73
  return
76
74
 
77
75
  try:
@@ -81,22 +79,23 @@ class Socket(object):
81
79
  port = int(file.read())
82
80
 
83
81
  if port != old_port:
84
- self.log_message("[" + str(id(self)) + "] Client port changed from " +
85
- str(old_port) + " to " + str(port))
82
+ logger.info("[" + str(id(self)) + "] Client port changed from " +
83
+ str(old_port) + " to " + str(port))
86
84
  self._client_addr = ("127.0.0.1", port)
87
85
 
88
86
  if self._socket:
89
87
  self.send("connect", {"port": self._server_addr[1]})
90
88
  except Exception as e:
91
- self.log_once("Couldn't read remote port file: " + str(e.args))
89
+ self.log_error_once(
90
+ "Couldn't read remote port file: " + str(e.args))
92
91
 
93
92
  def shutdown(self):
94
- self.log_message("Shutting down...")
93
+ logger.info("Shutting down...")
95
94
  self._socket.close()
96
95
  self._socket = None
97
96
 
98
97
  def init_socket(self, try_stored=False):
99
- self.log_message(
98
+ logger.info(
100
99
  "Initializing socket, from stored: " + str(try_stored))
101
100
 
102
101
  try:
@@ -118,7 +117,7 @@ class Socket(object):
118
117
  self._chunk_limit = self._socket.getsockopt(
119
118
  socket.SOL_SOCKET, socket.SO_SNDBUF) - 1
120
119
 
121
- self.log_message("Chunk limit: " + str(self._chunk_limit))
120
+ logger.info("Chunk limit: " + str(self._chunk_limit))
122
121
 
123
122
  # Write the chosen port to a file
124
123
  try:
@@ -126,35 +125,37 @@ class Socket(object):
126
125
  with open(server_port_path, "w") as file:
127
126
  file.write(str(port))
128
127
  except Exception as e:
129
- self.log_once("Couldn't save port in file: " + str(e.args))
128
+ self.log_error_once(
129
+ "Couldn't save port in file: " + str(e.args))
130
130
  raise e
131
131
 
132
132
  try:
133
133
  self.send("connect", {"port": self._server_addr[1]})
134
134
  except Exception as e:
135
- self.log_message("Couldn't send connect to " +
136
- str(self._client_addr) + ": " + str(e.args))
135
+ logger.info("Couldn't send connect to " +
136
+ str(self._client_addr) + ": " + str(e.args))
137
137
 
138
138
  self.show_message("Started server on port " + str(port))
139
139
 
140
- self.log_message('Started server on: ' + str(self._socket.getsockname()) +
141
- ', client addr: ' + str(self._client_addr))
140
+ logger.info('Started server on: ' + str(self._socket.getsockname()) +
141
+ ', client addr: ' + str(self._client_addr))
142
142
  except Exception as e:
143
143
  msg = 'ERROR: Cannot bind to ' + \
144
144
  str(self._server_addr) + ': ' + \
145
145
  str(e.args) + ', trying again. ' + \
146
146
  'If this keeps happening, try restarting your computer.'
147
- self.log_once(msg + "(Client address: " +
148
- str(self._client_addr) + ")")
147
+ self.log_error_once(msg + "(Client address: " +
148
+ str(self._client_addr) + ")")
149
149
  self.show_message(msg)
150
- t = Timer(5, self.init_socket)
150
+ t = Live.Base.Timer(
151
+ callback=self.init_socket, interval=5000, repeat=False)
151
152
  t.start()
152
153
 
153
154
  def _sendto(self, msg):
154
155
  '''Send a raw message to the client, compressed and chunked, if necessary'''
155
156
  compressed = zlib.compress(msg.encode("utf8")) + b'\n'
156
157
 
157
- if self._socket == None:
158
+ if self._socket == None or self._chunk_limit == None:
158
159
  return
159
160
 
160
161
  if len(compressed) < self._chunk_limit:
@@ -179,12 +180,12 @@ class Socket(object):
179
180
  {"event": name, "data": obj, "uuid": uuid}, default=jsonReplace, ensure_ascii=False)
180
181
  self._sendto(data)
181
182
  except socket.error as e:
182
- self.log_message("Socket error: " + str(e.args) + ", server: " + str(self._server_addr) +
183
- ", client: " + str(self._client_addr) + ", socket: " + str(self._socket))
184
- self.log_message("Data:" + data)
183
+ logger.info("Socket error: " + str(e.args) + ", server: " + str(self._server_addr) +
184
+ ", client: " + str(self._client_addr) + ", socket: " + str(self._socket))
185
+ logger.info("Data:" + data)
185
186
  except Exception as e:
186
187
  error = str(type(e).__name__) + ': ' + str(e.args)
187
- self.log_message("Error " + name + "(" + str(uuid) + "): " + error)
188
+ logger.info("Error " + name + "(" + str(uuid) + "): " + error)
188
189
 
189
190
  def process(self):
190
191
  try:
@@ -204,8 +205,8 @@ class Socket(object):
204
205
  buffer = bytes()
205
206
  num_messages = 0
206
207
  except socket.error as e:
207
- if (e.errno != 35):
208
- self.log_message("Socket error: " + str(e.args))
208
+ if (e.errno != 35 and e.errno != 10035 and e.errno != 10054):
209
+ logger.info("Socket error: " + str(e.args))
209
210
  return
210
211
  except Exception as e:
211
- self.log_message("Error while processing: " + str(e.args))
212
+ logger.info("Error while processing: " + str(e.args))
@@ -1 +1 @@
1
- version = "3.3.2"
1
+ version = "3.3.3"
package/ns/clip.d.ts CHANGED
@@ -218,8 +218,14 @@ export declare class Clip extends Namespace<GettableProperties, TransformedPrope
218
218
  quantizePitch(pitch: number, grid: number, amount: number): Promise<void>;
219
219
  /**
220
220
  * Deletes all notes that start in the given area.
221
+ *
222
+ * @deprecated starting with Live 11, use `removeNotesExtended` instead
221
223
  */
222
224
  removeNotes(fromTime: number, fromPitch: number, timeSpan: number, pitchSpan: number): Promise<any>;
225
+ /**
226
+ * Deletes all notes that start in the given area.
227
+ */
228
+ removeNotesExtended(fromTime: number, fromPitch: number, timeSpan: number, pitchSpan: number): Promise<any>;
223
229
  /**
224
230
  * Replaces selected notes with an array of new notes.
225
231
  */
package/ns/clip.js CHANGED
@@ -155,6 +155,8 @@ class Clip extends _1.Namespace {
155
155
  }
156
156
  /**
157
157
  * Deletes all notes that start in the given area.
158
+ *
159
+ * @deprecated starting with Live 11, use `removeNotesExtended` instead
158
160
  */
159
161
  removeNotes(fromTime, fromPitch, timeSpan, pitchSpan) {
160
162
  return this.sendCommand("remove_notes", [
@@ -164,6 +166,17 @@ class Clip extends _1.Namespace {
164
166
  pitchSpan,
165
167
  ]);
166
168
  }
169
+ /**
170
+ * Deletes all notes that start in the given area.
171
+ */
172
+ removeNotesExtended(fromTime, fromPitch, timeSpan, pitchSpan) {
173
+ return this.sendCommand("remove_notes_extended", [
174
+ fromTime,
175
+ fromPitch,
176
+ timeSpan,
177
+ pitchSpan,
178
+ ]);
179
+ }
167
180
  /**
168
181
  * Replaces selected notes with an array of new notes.
169
182
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ableton-js",
3
- "version": "3.3.2",
3
+ "version": "3.3.3",
4
4
  "description": "Control Ableton Live from Node",
5
5
  "main": "index.js",
6
6
  "author": "Leo Bernard <admin@leolabs.org>",
@@ -19,7 +19,7 @@
19
19
  "ableton:copy-script": "set -- ~/Music/Ableton/User\\ Library/Remote\\ Scripts && mkdir -p \"$1\" && rm -rf \"$1/AbletonJS\" && cp -r \"$(pwd)/midi-script\" \"$1/AbletonJS\" && rm -rf \"$1/AbletonJS/_Framework\"",
20
20
  "ableton10:launch": "set -- /Applications/Ableton*10* && open \"$1\"",
21
21
  "ableton11:launch": "set -- /Applications/Ableton*11* && open \"$1\"",
22
- "ableton:logs": "tail -n 50 -f ~/Library/Preferences/Ableton/*/Log.txt | grep --line-buffered -i -e RemoteScriptError -e RemoteScriptMessage | sed 's/info: RemoteScriptMessage: (AbletonJS) //'",
22
+ "ableton:logs": "tail -n 50 -f ~/Library/Preferences/Ableton/*/Log.txt | grep --line-buffered -i -e AbletonJS",
23
23
  "ableton:kill": "pkill -KILL -f \"Ableton Live\"",
24
24
  "ableton10:start": "yarn ableton:kill; yarn ableton:clean && yarn ableton:copy-script && yarn ableton10:launch && yarn ableton:logs",
25
25
  "ableton11:start": "yarn ableton:kill; yarn ableton:clean && yarn ableton:copy-script && yarn ableton11:launch && yarn ableton:logs",