ableton-js 3.3.2 → 3.3.4

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,24 @@ 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.4](https://github.com/leolabs/ableton.js/compare/v3.3.3...v3.3.4)
8
+
9
+ - :sparkles: Add support for the Track View class [`3ff5980`](https://github.com/leolabs/ableton.js/commit/3ff5980e5e5f407bc6b767a5329b54774ca10534)
10
+ - :sparkles: Add a `safeStopPlaying` function that only stops playback when Live is currently playing to prevent accidental jumps to the beginning of the timeline [`a7b84e2`](https://github.com/leolabs/ableton.js/commit/a7b84e2146a69ab00cc75f83a4eedd5d91495eda)
11
+ - :art: Fix formatting [`f06d3d0`](https://github.com/leolabs/ableton.js/commit/f06d3d0cde4632612b1db8ea04e85f1276eb8bb6)
12
+
13
+ #### [v3.3.3](https://github.com/leolabs/ableton.js/compare/v3.3.2...v3.3.3)
14
+
15
+ > 10 September 2023
16
+
17
+ - :mute: Fix every message being logged twice [`3a2571f`](https://github.com/leolabs/ableton.js/commit/3a2571f0a4ad61fdf751a0dfbb5da00b6def8508)
18
+ - :wastebasket: Deprecate `removeNotes` and add `removeNotesExtended` as a replacement [`ac02271`](https://github.com/leolabs/ableton.js/commit/ac022718f0b16317d8fdf70ff1c8d77eaaa0f384)
19
+ - :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)
20
+
7
21
  #### [v3.3.2](https://github.com/leolabs/ableton.js/compare/v3.3.1...v3.3.2)
8
22
 
23
+ > 27 July 2023
24
+
9
25
  - :package: Add lodash as a dependency, fixes #104 [`#104`](https://github.com/leolabs/ableton.js/issues/104)
10
26
 
11
27
  #### [v3.3.1](https://github.com/leolabs/ableton.js/compare/v3.3.0...v3.3.1)
@@ -1,8 +1,10 @@
1
1
  from __future__ import absolute_import
2
2
  import time
3
3
 
4
+
4
5
  from .version import version
5
6
  from .Config import DEBUG, FAST_POLLING
7
+ from .Logging import logger
6
8
  from .Socket import Socket
7
9
  from .Interface import Interface
8
10
  from .Application import Application
@@ -17,6 +19,7 @@ from .Scene import Scene
17
19
  from .Song import Song
18
20
  from .SongView import SongView
19
21
  from .Track import Track
22
+ from .TrackView import TrackView
20
23
  from .Internal import Internal
21
24
  from .ClipSlot import ClipSlot
22
25
  from .Clip import Clip
@@ -29,11 +32,10 @@ import Live
29
32
  class AbletonJS(ControlSurface):
30
33
  def __init__(self, c_instance):
31
34
  super(AbletonJS, self).__init__(c_instance)
32
- self.log_message("Starting AbletonJS " + version + "...")
35
+ logger.info("Starting AbletonJS " + version + "...")
33
36
 
34
37
  self.tracked_midi = set()
35
38
 
36
- Socket.set_log(self.log_message)
37
39
  Socket.set_message(self.show_message)
38
40
  self.socket = Socket(self.command_handler)
39
41
 
@@ -52,6 +54,7 @@ class AbletonJS(ControlSurface):
52
54
  "song": Song(c_instance, self.socket),
53
55
  "song-view": SongView(c_instance, self.socket),
54
56
  "track": Track(c_instance, self.socket),
57
+ "track-view": TrackView(c_instance, self.socket),
55
58
  "clip_slot": ClipSlot(c_instance, self.socket),
56
59
  "clip": Clip(c_instance, self.socket),
57
60
  }
@@ -69,8 +72,8 @@ class AbletonJS(ControlSurface):
69
72
  tick_time = time.time() * 1000
70
73
 
71
74
  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")
75
+ logger.warning("UDP tick is lagging, delta: " +
76
+ str(round(tick_time - self._last_tick)) + "ms")
74
77
 
75
78
  self._last_tick = tick_time
76
79
  self.socket.process()
@@ -78,8 +81,8 @@ class AbletonJS(ControlSurface):
78
81
  process_time = time.time() * 1000
79
82
 
80
83
  if process_time - tick_time > 100:
81
- self.log_message("UDP processing is taking long, delta: " +
82
- str(round(tick_time - process_time)) + "ms")
84
+ logger.warning("UDP processing is taking long, delta: " +
85
+ str(round(tick_time - process_time)) + "ms")
83
86
 
84
87
  self.schedule_message(1, self.tick)
85
88
 
@@ -97,7 +100,7 @@ class AbletonJS(ControlSurface):
97
100
  self.handlers["midi"].send_midi(midi_bytes)
98
101
 
99
102
  def disconnect(self):
100
- self.log_message("Disconnecting")
103
+ logger.info("Disconnecting")
101
104
  if FAST_POLLING:
102
105
  self.recv_loop.stop()
103
106
  self.socket.send("disconnect")
@@ -110,7 +113,7 @@ class AbletonJS(ControlSurface):
110
113
 
111
114
  # Don't clutter the logs
112
115
  if not (namespace == "internal" and payload["name"] == "get_prop" and payload["args"]["prop"] == "ping") and DEBUG:
113
- self.log_message("Received command: " + str(payload))
116
+ logger.debug("Received command: " + str(payload))
114
117
 
115
118
  if namespace in self.handlers:
116
119
  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.warning("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))
@@ -4,17 +4,12 @@ from .CuePoint import CuePoint
4
4
  from .Device import Device
5
5
  from .Scene import Scene
6
6
  from .Track import Track
7
- import Live
8
7
 
9
- INSERT_MODES = {'default':Live.Track.DeviceInsertMode.default,
10
- 'left':Live.Track.DeviceInsertMode.selected_left,
11
- 'right':Live.Track.DeviceInsertMode.selected_right}
12
8
 
13
9
  class Song(Interface):
14
10
  def __init__(self, c_instance, socket):
15
11
  super(Song, self).__init__(c_instance, socket)
16
12
  self.song = self.ableton.song()
17
- self._insert_mode = INSERT_MODES['default']
18
13
 
19
14
  def get_ns(self, nsid):
20
15
  return self.song
@@ -66,6 +61,9 @@ class Song(Interface):
66
61
  def set_appointed_device(self, ns, device_id):
67
62
  ns.appointed_device = Interface.get_obj(device_id)
68
63
 
69
- def set_insert_mode(self,ns, args):
70
- self._insert_mode = INSERT_MODES.get(str(args), INSERT_MODES['default'])
71
- self.song.view.selected_track.view.device_insert_mode = self._insert_mode
64
+ def safe_stop_playing(self, ns):
65
+ if self.song.is_playing:
66
+ self.song.stop_playing()
67
+ return True
68
+
69
+ return False
@@ -0,0 +1,24 @@
1
+ from __future__ import absolute_import
2
+ from .Interface import Interface
3
+ from .Device import Device
4
+
5
+ import Live
6
+
7
+ INSERT_MODES = {'default': Live.Track.DeviceInsertMode.default,
8
+ 'left': Live.Track.DeviceInsertMode.selected_left,
9
+ 'right': Live.Track.DeviceInsertMode.selected_right}
10
+
11
+
12
+ class TrackView(Interface):
13
+ def __init__(self, c_instance, socket):
14
+ super(TrackView, self).__init__(c_instance, socket)
15
+
16
+ def get_ns(self, nsid):
17
+ return Interface.obj_ids[nsid].view
18
+
19
+ def get_selected_device(self, ns):
20
+ return Device.serialize_device(ns.selected_device)
21
+
22
+ def set_device_insert_mode(self, ns, name):
23
+ mode = INSERT_MODES.get(str(name), INSERT_MODES['default'])
24
+ ns.device_insert_mode = mode
@@ -1 +1 @@
1
- version = "3.3.2"
1
+ version = "3.3.4"
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/ns/song.d.ts CHANGED
@@ -61,11 +61,6 @@ export interface TransformedProperties {
61
61
  visible_tracks: Track[];
62
62
  scenes: Scene[];
63
63
  }
64
- export declare enum DeviceInsertMode {
65
- default = "default",
66
- left = "left",
67
- right = "right"
68
- }
69
64
  export interface SettableProperties {
70
65
  appointed_device: string;
71
66
  arrangement_overdub: boolean;
@@ -76,7 +71,6 @@ export interface SettableProperties {
76
71
  exclusive_arm: number;
77
72
  exclusive_solo: number;
78
73
  groove_amount: number;
79
- insert_mode: DeviceInsertMode;
80
74
  is_counting_in: boolean;
81
75
  is_playing: boolean;
82
76
  last_event_time: number;
@@ -217,7 +211,7 @@ export declare class Song extends Namespace<GettableProperties, TransformedPrope
217
211
  startPlaying(): Promise<any>;
218
212
  stopAllClips(): Promise<any>;
219
213
  stopPlaying(): Promise<any>;
214
+ safeStopPlaying(): Promise<any>;
220
215
  tapTempo(): Promise<any>;
221
216
  undo(): Promise<any>;
222
- set_insert_mode(args: DeviceInsertMode): Promise<any>;
223
217
  }
package/ns/song.js CHANGED
@@ -1,17 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Song = exports.RecordingQuantization = exports.Quantization = exports.TimeFormat = exports.DeviceInsertMode = void 0;
3
+ exports.Song = exports.RecordingQuantization = exports.Quantization = exports.TimeFormat = void 0;
4
4
  const _1 = require(".");
5
5
  const track_1 = require("./track");
6
6
  const cue_point_1 = require("./cue-point");
7
7
  const song_view_1 = require("./song-view");
8
8
  const scene_1 = require("./scene");
9
- var DeviceInsertMode;
10
- (function (DeviceInsertMode) {
11
- DeviceInsertMode["default"] = "default";
12
- DeviceInsertMode["left"] = "left";
13
- DeviceInsertMode["right"] = "right";
14
- })(DeviceInsertMode || (exports.DeviceInsertMode = DeviceInsertMode = {}));
15
9
  var TimeFormat;
16
10
  (function (TimeFormat) {
17
11
  TimeFormat[TimeFormat["MsTime"] = 0] = "MsTime";
@@ -149,14 +143,14 @@ class Song extends _1.Namespace {
149
143
  async stopPlaying() {
150
144
  return this.sendCommand("stop_playing");
151
145
  }
146
+ async safeStopPlaying() {
147
+ return this.sendCommand("safe_stop_playing");
148
+ }
152
149
  async tapTempo() {
153
150
  return this.sendCommand("tap_tempo");
154
151
  }
155
152
  async undo() {
156
153
  return this.sendCommand("undo");
157
154
  }
158
- async set_insert_mode(args) {
159
- return this.sendCommand("set_insert_mode", { args });
160
- }
161
155
  }
162
156
  exports.Song = Song;
@@ -0,0 +1,30 @@
1
+ import { Ableton } from "..";
2
+ import { Namespace } from ".";
3
+ import { Device, RawDevice } from "./device";
4
+ export declare enum DeviceInsertMode {
5
+ Default = "default",
6
+ Left = "left",
7
+ Right = "right"
8
+ }
9
+ export interface GettableProperties {
10
+ is_collapsed: boolean;
11
+ selected_device: RawDevice;
12
+ }
13
+ export interface TransformedProperties {
14
+ selected_device: Device;
15
+ }
16
+ export interface SettableProperties {
17
+ device_insert_mode: DeviceInsertMode;
18
+ is_collapsed: boolean;
19
+ }
20
+ export interface ObservableProperties {
21
+ is_collapsed: boolean;
22
+ selected_device: RawDevice;
23
+ }
24
+ export declare class TrackView extends Namespace<GettableProperties, TransformedProperties, SettableProperties, ObservableProperties> {
25
+ constructor(ableton: Ableton, nsid: string);
26
+ /**
27
+ * Selects the track's instrument if it has one.
28
+ */
29
+ selectInstrument(): Promise<any>;
30
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrackView = exports.DeviceInsertMode = void 0;
4
+ const _1 = require(".");
5
+ const device_1 = require("./device");
6
+ var DeviceInsertMode;
7
+ (function (DeviceInsertMode) {
8
+ DeviceInsertMode["Default"] = "default";
9
+ DeviceInsertMode["Left"] = "left";
10
+ DeviceInsertMode["Right"] = "right";
11
+ })(DeviceInsertMode || (exports.DeviceInsertMode = DeviceInsertMode = {}));
12
+ class TrackView extends _1.Namespace {
13
+ constructor(ableton, nsid) {
14
+ super(ableton, "track-view", nsid);
15
+ this.transformers = {
16
+ selected_device: (device) => new device_1.Device(ableton, device),
17
+ };
18
+ this.cachedProps = {
19
+ selected_device: true,
20
+ };
21
+ }
22
+ /**
23
+ * Selects the track's instrument if it has one.
24
+ */
25
+ async selectInstrument() {
26
+ return this.sendCommand("select_instrument");
27
+ }
28
+ }
29
+ exports.TrackView = TrackView;
package/ns/track.d.ts CHANGED
@@ -5,6 +5,7 @@ import { ClipSlot, RawClipSlot } from "./clip-slot";
5
5
  import { MixerDevice, RawMixerDevice } from "./mixer-device";
6
6
  import { Clip, RawClip } from "./clip";
7
7
  import { Color } from "../util/color";
8
+ import { TrackView } from "./track-view";
8
9
  export declare enum RoutingLayout {
9
10
  Mono = 1,
10
11
  Stereo = 2
@@ -149,6 +150,7 @@ export interface RawTrack {
149
150
  }
150
151
  export declare class Track extends Namespace<GettableProperties, TransformedProperties, SettableProperties, ObservableProperties> {
151
152
  raw: RawTrack;
153
+ view: TrackView;
152
154
  constructor(ableton: Ableton, raw: RawTrack);
153
155
  /**
154
156
  * Duplicates the given clip into the arrangement of this track at the provided destination time and returns it.
package/ns/track.js CHANGED
@@ -7,6 +7,7 @@ const clip_slot_1 = require("./clip-slot");
7
7
  const mixer_device_1 = require("./mixer-device");
8
8
  const clip_1 = require("./clip");
9
9
  const color_1 = require("../util/color");
10
+ const track_view_1 = require("./track-view");
10
11
  var RoutingLayout;
11
12
  (function (RoutingLayout) {
12
13
  RoutingLayout[RoutingLayout["Mono"] = 1] = "Mono";
@@ -25,9 +26,11 @@ var RoutingCategory;
25
26
  })(RoutingCategory || (exports.RoutingCategory = RoutingCategory = {}));
26
27
  class Track extends _1.Namespace {
27
28
  raw;
29
+ view;
28
30
  constructor(ableton, raw) {
29
31
  super(ableton, "track", raw.id);
30
32
  this.raw = raw;
33
+ this.view = new track_view_1.TrackView(this.ableton, raw.id);
31
34
  this.transformers = {
32
35
  arrangement_clips: (clips) => clips.map((clip) => new clip_1.Clip(ableton, clip)),
33
36
  color: (c) => new color_1.Color(c),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ableton-js",
3
- "version": "3.3.2",
3
+ "version": "3.3.4",
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",