ableton-js 3.2.10 → 3.3.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,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.0](https://github.com/leolabs/ableton.js/compare/v3.2.11...v3.3.0)
8
+
9
+ - Browsing and loading functionalities [`bc48c7c`](https://github.com/leolabs/ableton.js/commit/bc48c7c46c55c54baca42c58003865596370afe3)
10
+ - :art: Improve formatting [`6e2e22b`](https://github.com/leolabs/ableton.js/commit/6e2e22b102cf560d39c07e60ae1afd7e2ecc851b)
11
+ - :sparkles: Turn the browser into a singleton [`6d0152b`](https://github.com/leolabs/ableton.js/commit/6d0152b4610683460256951801ada08f8598abeb)
12
+
13
+ #### [v3.2.11](https://github.com/leolabs/ableton.js/compare/v3.2.10...v3.2.11)
14
+
15
+ > 15 July 2023
16
+
17
+ - :loud_sound: Warn when the UDP tick is lagging or when processing UDP takes too long to finish [`c419afc`](https://github.com/leolabs/ableton.js/commit/c419afc96de5b5834ae2807baf8fc4b3fb9c21ba)
18
+ - :sparkles: Use larger buffer sizes if possible for sending responses from Live [`ae480cb`](https://github.com/leolabs/ableton.js/commit/ae480cb6e86fcbbb4e0417b0869a5a78d43511c6)
19
+ - :memo: Update docs to reflect that chunks now have a dynamic size [`83afbb6`](https://github.com/leolabs/ableton.js/commit/83afbb6431a28883fdc459e31081b94bd9c6b54a)
20
+
7
21
  #### [v3.2.10](https://github.com/leolabs/ableton.js/compare/v3.2.9...v3.2.10)
8
22
 
23
+ > 15 July 2023
24
+
9
25
  - :sparkles: Remove the socket after closing it to avoid dangling event listeners trying to send messages after the plugin has been shut down [`54278f2`](https://github.com/leolabs/ableton.js/commit/54278f2b4ccb6efe34592fc108cd10c35e404e58)
10
26
 
11
27
  #### [v3.2.9](https://github.com/leolabs/ableton.js/compare/v3.2.8...v3.2.9)
package/README.md CHANGED
@@ -103,11 +103,11 @@ port in a local file so the other side knows which port to send messages to.
103
103
  ### Compression and Chunking
104
104
 
105
105
  To allow sending large JSON payloads, requests to and responses from the MIDI
106
- Script are compressed using gzip and chunked every 7500 bytes. The first byte of
107
- every message contains the chunk index (0x00-0xFF) followed by the gzipped
108
- chunk. The last chunk always has the index 0xFF. This indicates to the JS
109
- library that the previous received messages should be stiched together,
110
- unzipped, and processed.
106
+ Script are compressed using gzip and chunked to fit into the maximum allowed
107
+ package size. The first byte of every message chunk contains the chunk index
108
+ (0x00-0xFF) followed by the gzipped chunk. The last chunk always has the index
109
+ 0xFF. This indicates to the JS library that the previous received messages
110
+ should be stiched together, unzipped, and processed.
111
111
 
112
112
  ### Caching
113
113
 
package/index.js CHANGED
@@ -434,9 +434,7 @@ class Ableton extends events_1.EventEmitter {
434
434
  throw new Error("The client hasn't been started yet. Please call start() first.");
435
435
  }
436
436
  const buffer = (0, zlib_1.deflateSync)(Buffer.from(msg));
437
- // Based on this thread, 7500 bytes seems like a safe value
438
- // https://stackoverflow.com/questions/22819214/udp-message-too-long
439
- const byteLimit = 7500;
437
+ const byteLimit = this.client.getSendBufferSize() - 1;
440
438
  const chunks = Math.ceil(buffer.byteLength / byteLimit);
441
439
  // Split the message into chunks if it becomes too large
442
440
  for (let i = 0; i < chunks; i++) {
@@ -1,4 +1,5 @@
1
1
  from __future__ import absolute_import
2
+ import time
2
3
 
3
4
  from .version import version
4
5
  from .Config import DEBUG, FAST_POLLING
@@ -6,6 +7,8 @@ from .Socket import Socket
6
7
  from .Interface import Interface
7
8
  from .Application import Application
8
9
  from .ApplicationView import ApplicationView
10
+ from .Browser import Browser
11
+ from .BrowserItem import BrowserItem
9
12
  from .CuePoint import CuePoint
10
13
  from .Device import Device
11
14
  from .DeviceParameter import DeviceParameter
@@ -37,6 +40,8 @@ class AbletonJS(ControlSurface):
37
40
  self.handlers = {
38
41
  "application": Application(c_instance, self.socket, self.application()),
39
42
  "application-view": ApplicationView(c_instance, self.socket, self.application()),
43
+ "browser": Browser(c_instance, self.socket, self.application()),
44
+ "browser-item": BrowserItem(c_instance, self.socket),
40
45
  "cue-point": CuePoint(c_instance, self.socket),
41
46
  "device": Device(c_instance, self.socket),
42
47
  "device-parameter": DeviceParameter(c_instance, self.socket),
@@ -51,6 +56,7 @@ class AbletonJS(ControlSurface):
51
56
  "clip": Clip(c_instance, self.socket),
52
57
  }
53
58
 
59
+ self._last_tick = time.time() * 1000
54
60
  self.tick()
55
61
 
56
62
  if FAST_POLLING:
@@ -60,7 +66,21 @@ class AbletonJS(ControlSurface):
60
66
  self.recv_loop.start()
61
67
 
62
68
  def tick(self):
69
+ tick_time = time.time() * 1000
70
+
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")
74
+
75
+ self._last_tick = tick_time
63
76
  self.socket.process()
77
+
78
+ process_time = time.time() * 1000
79
+
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")
83
+
64
84
  self.schedule_message(1, self.tick)
65
85
 
66
86
  def build_midi_map(self, midi_map_handle):
@@ -0,0 +1,66 @@
1
+ from __future__ import absolute_import
2
+ from .Interface import Interface
3
+ from .BrowserItem import BrowserItem
4
+
5
+
6
+ class Browser(Interface):
7
+ def __init__(self, c_instance, socket, application):
8
+ super(Browser, self).__init__(c_instance, socket)
9
+ self.application = application
10
+
11
+ def get_ns(self, nsid=None):
12
+ return self.application.browser
13
+
14
+ def get_audio_effects(self, ns):
15
+ return map(BrowserItem.serialize_browser_item, ns.audio_effects.children)
16
+
17
+ def get_clips(self, ns):
18
+ return map(BrowserItem.serialize_browser_item, ns.clips.children)
19
+
20
+ def get_colors(self, ns):
21
+ return map(BrowserItem.serialize_browser_item, ns.colors)
22
+
23
+ def get_current_project(self, ns):
24
+ return map(BrowserItem.serialize_browser_item, ns.current_project.children)
25
+
26
+ def get_drums(self, ns):
27
+ return map(BrowserItem.serialize_browser_item, ns.drums.children)
28
+
29
+ def get_instruments(self, ns):
30
+ return map(BrowserItem.serialize_browser_item, ns.instruments.children)
31
+
32
+ def get_max_for_live(self, ns):
33
+ return map(BrowserItem.serialize_browser_item, ns.max_for_live.children)
34
+
35
+ def get_midi_effects(self, ns):
36
+ return map(BrowserItem.serialize_browser_item, ns.midi_effects.children)
37
+
38
+ def get_packs(self, ns):
39
+ return map(BrowserItem.serialize_browser_item, ns.packs.children)
40
+
41
+ def get_plugins(self, ns):
42
+ return map(BrowserItem.serialize_browser_item, ns.plugins.children)
43
+
44
+ def get_samples(self, ns):
45
+ return map(BrowserItem.serialize_browser_item, ns.samples.children)
46
+
47
+ def get_sounds(self, ns):
48
+ return map(BrowserItem.serialize_browser_item, ns.sounds.children)
49
+
50
+ def get_user_folders(self, ns):
51
+ return map(BrowserItem.serialize_browser_item, ns.user_folders)
52
+
53
+ def get_user_library(self, ns):
54
+ return map(BrowserItem.serialize_browser_item, ns.user_library.children)
55
+
56
+ def get_hotswap_target(self, ns):
57
+ return BrowserItem.serialize_browser_item(ns.hotswap_target)
58
+
59
+ def load_item(self, ns, id):
60
+ return ns.load_item(self.get_obj(id))
61
+
62
+ def preview_item(self, ns, id):
63
+ return ns.preview_item(self.get_obj(id))
64
+
65
+ def stop_preview(self, ns):
66
+ return ns.stop_preview()
@@ -0,0 +1,26 @@
1
+ from __future__ import absolute_import
2
+ from .Interface import Interface
3
+
4
+
5
+ class BrowserItem(Interface):
6
+ @staticmethod
7
+ def serialize_browser_item(browser_item):
8
+ if browser_item is None:
9
+ return None
10
+ browser_item_id = Interface.save_obj(browser_item)
11
+ return {
12
+ "id": browser_item_id,
13
+ "name": browser_item.name,
14
+ "is_loadable": browser_item.is_loadable,
15
+ "is_selected": browser_item.is_selected,
16
+ "is_device": browser_item.is_device,
17
+ "is_folder": browser_item.is_folder,
18
+ "source": browser_item.source,
19
+ "uri": browser_item.uri,
20
+ }
21
+
22
+ def __init__(self, c_instance, socket):
23
+ super(BrowserItem, self).__init__(c_instance, socket)
24
+
25
+ def get_children(self, ns):
26
+ return map(BrowserItem.serialize_browser_item, ns.children)
@@ -114,6 +114,12 @@ class Socket(object):
114
114
  self._socket.bind(self._server_addr)
115
115
  port = self._socket.getsockname()[1]
116
116
 
117
+ # Get the chunk limit of the socket, minus 1 for the ordering byte
118
+ self._chunk_limit = self._socket.getsockopt(
119
+ socket.SOL_SOCKET, socket.SO_SNDBUF) - 1
120
+
121
+ self.log_message("Chunk limit: " + str(self._chunk_limit))
122
+
117
123
  # Write the chosen port to a file
118
124
  try:
119
125
  if stored_port != port:
@@ -147,17 +153,14 @@ class Socket(object):
147
153
  def _sendto(self, msg):
148
154
  '''Send a raw message to the client, compressed and chunked, if necessary'''
149
155
  compressed = zlib.compress(msg.encode("utf8")) + b'\n'
150
- # Based on this thread, 7500 bytes seems like a safe value
151
- # https://stackoverflow.com/questions/22819214/udp-message-too-long
152
- limit = 7500
153
156
 
154
157
  if self._socket == None:
155
158
  return
156
159
 
157
- if len(compressed) < limit:
160
+ if len(compressed) < self._chunk_limit:
158
161
  self._socket.sendto(b'\xFF' + compressed, self._client_addr)
159
162
  else:
160
- chunks = list(split_by_n(compressed, limit))
163
+ chunks = list(split_by_n(compressed, self._chunk_limit))
161
164
  count = len(chunks)
162
165
  for i, chunk in enumerate(chunks):
163
166
  count_byte = struct.pack("B", i if i + 1 < count else 255)
@@ -188,7 +191,7 @@ class Socket(object):
188
191
  buffer = bytes()
189
192
  num_messages = 0
190
193
  while 1:
191
- data = self._socket.recv(8192)
194
+ data = self._socket.recv(65536)
192
195
  if len(data) and self.input_handler:
193
196
  buffer += data[1:]
194
197
  num_messages += 1
@@ -4,12 +4,17 @@ 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
7
8
 
9
+ INSERT_MODES = {'default':Live.Track.DeviceInsertMode.default,
10
+ 'left':Live.Track.DeviceInsertMode.selected_left,
11
+ 'right':Live.Track.DeviceInsertMode.selected_right}
8
12
 
9
13
  class Song(Interface):
10
14
  def __init__(self, c_instance, socket):
11
15
  super(Song, self).__init__(c_instance, socket)
12
16
  self.song = self.ableton.song()
17
+ self._insert_mode = INSERT_MODES['default']
13
18
 
14
19
  def get_ns(self, nsid):
15
20
  return self.song
@@ -60,3 +65,7 @@ class Song(Interface):
60
65
 
61
66
  def set_appointed_device(self, ns, device_id):
62
67
  ns.appointed_device = Interface.get_obj(device_id)
68
+
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
@@ -1 +1 @@
1
- version = "3.2.10"
1
+ version = "3.3.0"
@@ -1,6 +1,7 @@
1
1
  import { Ableton } from "..";
2
2
  import { Namespace } from ".";
3
3
  import { ApplicationView } from "./application-view";
4
+ import { Browser, RawBrowser } from "./browser";
4
5
  export interface GettableProperties {
5
6
  bugfix_version: number;
6
7
  major_version: number;
@@ -9,8 +10,10 @@ export interface GettableProperties {
9
10
  current_dialog_button_count: number;
10
11
  current_dialog_message: string;
11
12
  open_dialog_count: number;
13
+ browser: RawBrowser;
12
14
  }
13
15
  export interface TransformedProperties {
16
+ browser: Browser;
14
17
  }
15
18
  export interface SettableProperties {
16
19
  }
@@ -19,6 +22,7 @@ export interface ObservableProperties {
19
22
  }
20
23
  export declare class Application extends Namespace<GettableProperties, TransformedProperties, SettableProperties, ObservableProperties> {
21
24
  constructor(ableton: Ableton);
25
+ browser: Browser;
22
26
  view: ApplicationView;
23
27
  pressCurrentDialogButton(index: number): Promise<any>;
24
28
  }
package/ns/application.js CHANGED
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Application = void 0;
4
4
  const _1 = require(".");
5
5
  const application_view_1 = require("./application-view");
6
+ const browser_1 = require("./browser");
6
7
  class Application extends _1.Namespace {
7
8
  constructor(ableton) {
8
9
  super(ableton, "application");
9
10
  }
11
+ browser = new browser_1.Browser(this.ableton);
10
12
  view = new application_view_1.ApplicationView(this.ableton);
11
13
  async pressCurrentDialogButton(index) {
12
14
  return this.sendCommand("press_current_dialog_button", [index]);
@@ -0,0 +1,34 @@
1
+ import { Ableton } from "..";
2
+ import { Namespace } from ".";
3
+ export interface RawBrowserItem {
4
+ id: string;
5
+ children: RawBrowserItem[];
6
+ name: string;
7
+ is_loadable: boolean;
8
+ is_selected: boolean;
9
+ is_device: boolean;
10
+ is_folder: boolean;
11
+ source: string;
12
+ uri: string;
13
+ }
14
+ export interface GettableProperties {
15
+ children: RawBrowserItem[];
16
+ is_device: boolean;
17
+ is_folder: boolean;
18
+ is_loadable: boolean;
19
+ is_selected: boolean;
20
+ name: string;
21
+ source: string;
22
+ uri: string;
23
+ }
24
+ export interface TransformedProperties {
25
+ children: BrowserItem[];
26
+ }
27
+ export interface SettableProperties {
28
+ }
29
+ export interface ObservableProperties {
30
+ }
31
+ export declare class BrowserItem extends Namespace<GettableProperties, TransformedProperties, SettableProperties, ObservableProperties> {
32
+ raw: RawBrowserItem;
33
+ constructor(ableton: Ableton, raw: RawBrowserItem);
34
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserItem = void 0;
4
+ const _1 = require(".");
5
+ class BrowserItem extends _1.Namespace {
6
+ raw;
7
+ constructor(ableton, raw) {
8
+ super(ableton, "browser-item", raw.id);
9
+ this.raw = raw;
10
+ this.transformers = {
11
+ children: (children) => children.map((c) => new BrowserItem(ableton, c)),
12
+ };
13
+ this.cachedProps = {
14
+ children: true,
15
+ is_device: true,
16
+ is_folder: true,
17
+ is_loadable: false,
18
+ is_selected: false,
19
+ name: true,
20
+ source: true,
21
+ uri: true,
22
+ };
23
+ }
24
+ }
25
+ exports.BrowserItem = BrowserItem;
@@ -0,0 +1,55 @@
1
+ import { Ableton } from "..";
2
+ import { Namespace } from ".";
3
+ import { BrowserItem, RawBrowserItem } from "./browser-item";
4
+ export interface GettableProperties {
5
+ audio_effects: RawBrowserItem[];
6
+ clips: RawBrowserItem[];
7
+ colors: RawBrowserItem[];
8
+ current_project: RawBrowserItem[];
9
+ drums: RawBrowserItem[];
10
+ instruments: RawBrowserItem[];
11
+ max_for_live: RawBrowserItem[];
12
+ midi_effects: RawBrowserItem[];
13
+ packs: RawBrowserItem[];
14
+ plugins: RawBrowserItem[];
15
+ samples: RawBrowserItem[];
16
+ sounds: RawBrowserItem[];
17
+ user_library: RawBrowserItem[];
18
+ user_folders: RawBrowserItem[];
19
+ hotswap_target: RawBrowserItem;
20
+ }
21
+ export interface TransformedProperties {
22
+ audio_effects: BrowserItem[];
23
+ clips: BrowserItem[];
24
+ colors: BrowserItem[];
25
+ current_project: BrowserItem[];
26
+ drums: BrowserItem[];
27
+ instruments: BrowserItem[];
28
+ max_for_live: BrowserItem[];
29
+ midi_effects: BrowserItem[];
30
+ packs: BrowserItem[];
31
+ plugins: BrowserItem[];
32
+ samples: BrowserItem[];
33
+ sounds: BrowserItem[];
34
+ user_library: BrowserItem[];
35
+ user_folders: BrowserItem[];
36
+ hotswap_target: BrowserItem;
37
+ }
38
+ export interface SettableProperties {
39
+ }
40
+ export interface ObservableProperties {
41
+ filter_type: never;
42
+ hotswap_target: BrowserItem;
43
+ }
44
+ export interface RawBrowser {
45
+ id: string;
46
+ }
47
+ export declare class Browser extends Namespace<GettableProperties, TransformedProperties, SettableProperties, ObservableProperties> {
48
+ constructor(ableton: Ableton);
49
+ /** Loads the provided browser item. */
50
+ loadItem(item: BrowserItem): Promise<any>;
51
+ /** Previews the provided browser item. */
52
+ previewItem(item: BrowserItem): Promise<any>;
53
+ /** Stops the current preview. */
54
+ stopPreview(): Promise<any>;
55
+ }
package/ns/browser.js ADDED
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Browser = void 0;
4
+ const _1 = require(".");
5
+ const browser_item_1 = require("./browser-item");
6
+ class Browser extends _1.Namespace {
7
+ constructor(ableton) {
8
+ super(ableton, "browser");
9
+ const makeBrowserItems = (items) => items.map((item) => new browser_item_1.BrowserItem(ableton, item));
10
+ this.transformers = {
11
+ audio_effects: makeBrowserItems,
12
+ clips: makeBrowserItems,
13
+ colors: makeBrowserItems,
14
+ current_project: makeBrowserItems,
15
+ drums: makeBrowserItems,
16
+ instruments: makeBrowserItems,
17
+ max_for_live: makeBrowserItems,
18
+ midi_effects: makeBrowserItems,
19
+ packs: makeBrowserItems,
20
+ plugins: makeBrowserItems,
21
+ samples: makeBrowserItems,
22
+ sounds: makeBrowserItems,
23
+ user_library: makeBrowserItems,
24
+ user_folders: makeBrowserItems,
25
+ hotswap_target: (t) => new browser_item_1.BrowserItem(ableton, t),
26
+ };
27
+ this.cachedProps = {
28
+ audio_effects: true,
29
+ clips: true,
30
+ colors: true,
31
+ current_project: true,
32
+ drums: true,
33
+ instruments: true,
34
+ max_for_live: true,
35
+ midi_effects: true,
36
+ packs: true,
37
+ plugins: true,
38
+ samples: true,
39
+ sounds: true,
40
+ user_library: true,
41
+ user_folders: true,
42
+ hotswap_target: true,
43
+ };
44
+ }
45
+ /** Loads the provided browser item. */
46
+ async loadItem(item) {
47
+ return this.sendCommand("load_item", { id: item.raw.id });
48
+ }
49
+ /** Previews the provided browser item. */
50
+ async previewItem(item) {
51
+ return this.sendCommand("preview_item", { id: item.raw.id });
52
+ }
53
+ /** Stops the current preview. */
54
+ async stopPreview() {
55
+ return this.sendCommand("stop_preview");
56
+ }
57
+ }
58
+ exports.Browser = Browser;
package/ns/song.d.ts CHANGED
@@ -61,6 +61,11 @@ 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
+ }
64
69
  export interface SettableProperties {
65
70
  appointed_device: string;
66
71
  arrangement_overdub: boolean;
@@ -71,6 +76,7 @@ export interface SettableProperties {
71
76
  exclusive_arm: number;
72
77
  exclusive_solo: number;
73
78
  groove_amount: number;
79
+ insert_mode: DeviceInsertMode;
74
80
  is_counting_in: boolean;
75
81
  is_playing: boolean;
76
82
  last_event_time: number;
@@ -213,4 +219,5 @@ export declare class Song extends Namespace<GettableProperties, TransformedPrope
213
219
  stopPlaying(): Promise<any>;
214
220
  tapTempo(): Promise<any>;
215
221
  undo(): Promise<any>;
222
+ set_insert_mode(args: DeviceInsertMode): Promise<any>;
216
223
  }
package/ns/song.js CHANGED
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Song = exports.RecordingQuantization = exports.Quantization = exports.TimeFormat = void 0;
3
+ exports.Song = exports.RecordingQuantization = exports.Quantization = exports.TimeFormat = exports.DeviceInsertMode = 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 = {}));
9
15
  var TimeFormat;
10
16
  (function (TimeFormat) {
11
17
  TimeFormat[TimeFormat["MsTime"] = 0] = "MsTime";
@@ -149,5 +155,8 @@ class Song extends _1.Namespace {
149
155
  async undo() {
150
156
  return this.sendCommand("undo");
151
157
  }
158
+ async set_insert_mode(args) {
159
+ return this.sendCommand("set_insert_mode", { args });
160
+ }
152
161
  }
153
162
  exports.Song = Song;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ableton-js",
3
- "version": "3.2.10",
3
+ "version": "3.3.0",
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 -i -e RemoteScriptError -e RemoteScriptMessage",
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) //'",
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",