@m4l-jweb/wrapper 0.9.0 → 0.9.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/liveapi.ts +40 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m4l-jweb/wrapper",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "m4l-jweb: the Max for Live glue layer connecting a device to LiveAPI.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/liveapi.ts CHANGED
@@ -139,9 +139,26 @@ interface LiveNote {
139
139
  mute?: number;
140
140
  }
141
141
 
142
- /** The LiveAPI for the track this device sits on. */
142
+ /**
143
+ * The LiveAPI for the TRACK this device sits on - clip I/O belongs to the track.
144
+ *
145
+ * `this_device canonical_parent` is the track ONLY when the device sits directly on
146
+ * it. Inside a Rack it is the CHAIN the device is in, and a Chain has no `clip_slots` -
147
+ * so `getcount("clip_slots")` on it throws "invalid property name" once a second (the
148
+ * clip-availability poll), and clip read/write silently target the wrong object. So
149
+ * climb the `canonical_parent` chain until a Track is reached, which handles a device
150
+ * on a bare track (no climb), in a rack (one hop), and in a nested rack (several).
151
+ */
143
152
  function ownTrack(): LiveAPI {
144
- return new LiveAPI("this_device canonical_parent");
153
+ var api = new LiveAPI("this_device canonical_parent");
154
+ var guard = 0;
155
+ // id 0 is an unresolved path; stop rather than build "... canonical_parent" onto
156
+ // nothing. The guard is a backstop against a parent cycle the LOM should never have.
157
+ while (api && api.id && api.type !== "Track" && guard < 12) {
158
+ api = new LiveAPI(api.unquotedpath + " canonical_parent");
159
+ guard++;
160
+ }
161
+ return api;
145
162
  }
146
163
 
147
164
  /**
@@ -154,9 +171,20 @@ function write_clip(): void {
154
171
  var lengthBeats = a[0];
155
172
  var n = Number(a[1]);
156
173
 
174
+ // No reachable Track is a STRUCTURAL failure (clip I/O is impossible here), distinct
175
+ // from a track whose slots are all full. The UI disables clip export on the former
176
+ // and only reports the latter, so `write_error` says which.
177
+ var track = ownTrack();
178
+ if (!track || !track.id || track.type !== "Track") {
179
+ post("m4l-jweb: write_clip - no reachable track (device not on a track?)\n");
180
+ outlet(0, "write_error", "no_track");
181
+ return;
182
+ }
183
+
157
184
  var slot = firstEmptySlot();
158
185
  if (!slot) {
159
186
  post("m4l-jweb: no empty clip slot on this track\n");
187
+ outlet(0, "write_error", "no_slot");
160
188
  return;
161
189
  }
162
190
  slot.call("create_clip", lengthBeats);
@@ -177,6 +205,7 @@ function write_clip(): void {
177
205
  clip.call("add_new_notes", { notes: notes });
178
206
  } catch (e) {
179
207
  post("m4l-jweb: add_new_notes failed - " + (e as Error).message + "\n");
208
+ outlet(0, "write_error", "add_failed");
180
209
  return;
181
210
  }
182
211
  post("m4l-jweb: wrote " + n + " notes over " + lengthBeats + " beats\n");
@@ -207,6 +236,15 @@ function firstEmptySlot(): LiveAPI | null {
207
236
  * happens to be. For the selection-driven case use read_selected_clip below.
208
237
  */
209
238
  function read_notes(): void {
239
+ // Structural failure (no reachable track) is reported distinctly from "a track with
240
+ // no clip", so the UI can disable clip import where it is impossible and merely say
241
+ // "no clip" where it is not.
242
+ var track = ownTrack();
243
+ if (!track || !track.id || track.type !== "Track") {
244
+ post("m4l-jweb: read_notes - no reachable track (device not on a track?)\n");
245
+ outlet(0, "read_error", "no_track");
246
+ return;
247
+ }
210
248
  var clip = pickClip();
211
249
  if (!clip) {
212
250
  post("m4l-jweb: no clip found on this track\n");