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 +16 -0
- package/midi-script/AbletonJS.py +11 -8
- package/midi-script/Application.py +0 -1
- package/midi-script/Interface.py +3 -3
- package/midi-script/Logging.py +3 -0
- package/midi-script/Midi.py +7 -5
- package/midi-script/Socket.py +35 -34
- package/midi-script/Song.py +6 -8
- package/midi-script/TrackView.py +24 -0
- package/midi-script/version.py +1 -1
- package/ns/clip.d.ts +6 -0
- package/ns/clip.js +13 -0
- package/ns/song.d.ts +1 -7
- package/ns/song.js +4 -10
- package/ns/track-view.d.ts +30 -0
- package/ns/track-view.js +29 -0
- package/ns/track.d.ts +2 -0
- package/ns/track.js +3 -0
- package/package.json +2 -2
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)
|
package/midi-script/AbletonJS.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/midi-script/Interface.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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"):
|
package/midi-script/Midi.py
CHANGED
|
@@ -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(
|
|
26
|
+
self.outputs.add((midi_type, output.get(
|
|
27
|
+
"channel"), output.get("target")))
|
|
26
28
|
except ValueError as e:
|
|
27
|
-
|
|
29
|
+
logger.error(e)
|
|
28
30
|
except:
|
|
29
|
-
|
|
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
|
-
|
|
43
|
+
logger.warning("MIDI listener already exists")
|
|
42
44
|
return self.event_id
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
logger.info("Attaching MIDI listener")
|
|
45
47
|
|
|
46
48
|
self.tracked_midi.clear()
|
|
47
49
|
self.tracked_midi.update(self.outputs)
|
package/midi-script/Socket.py
CHANGED
|
@@ -4,7 +4,8 @@ import struct
|
|
|
4
4
|
import zlib
|
|
5
5
|
import os
|
|
6
6
|
import tempfile
|
|
7
|
-
|
|
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
|
|
43
|
+
def log_error_once(self, msg):
|
|
47
44
|
if self._last_error != msg:
|
|
48
45
|
self._last_error = msg
|
|
49
|
-
|
|
46
|
+
logger.error(msg)
|
|
50
47
|
|
|
51
48
|
def set_client_port(self, port):
|
|
52
|
-
|
|
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
|
-
|
|
58
|
+
logger.info("Stored server port: " + str(port))
|
|
62
59
|
return port
|
|
63
60
|
except Exception as e:
|
|
64
|
-
|
|
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.
|
|
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
|
-
|
|
85
|
-
|
|
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.
|
|
89
|
+
self.log_error_once(
|
|
90
|
+
"Couldn't read remote port file: " + str(e.args))
|
|
92
91
|
|
|
93
92
|
def shutdown(self):
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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.
|
|
148
|
-
|
|
147
|
+
self.log_error_once(msg + "(Client address: " +
|
|
148
|
+
str(self._client_addr) + ")")
|
|
149
149
|
self.show_message(msg)
|
|
150
|
-
t = Timer(
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
212
|
+
logger.info("Error while processing: " + str(e.args))
|
package/midi-script/Song.py
CHANGED
|
@@ -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
|
|
70
|
-
self.
|
|
71
|
-
|
|
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
|
package/midi-script/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "3.3.
|
|
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 =
|
|
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
|
+
}
|
package/ns/track-view.js
ADDED
|
@@ -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.
|
|
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
|
|
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",
|