@yam8d/m8-sdk 0.5.1 → 0.5.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/dist/index.js CHANGED
@@ -705,7 +705,7 @@ var D = (e) => {
705
705
  down: I.Down,
706
706
  left: I.Left,
707
707
  right: I.Right
708
- }, re = I.Up | I.Down | I.Left | I.Right, ie = 24, ae = 150, L = .1, oe = (e) => e === "up" || e === "down" ? "vertical" : "horizontal", se = (e) => {
708
+ }, re = I.Up | I.Down | I.Left | I.Right | I.Edit | I.Opt | I.Shift, ie = 24, ae = 150, L = .1, oe = (e) => e === "up" || e === "down" ? "vertical" : "horizontal", se = (e) => {
709
709
  switch (e) {
710
710
  case "up": return "down";
711
711
  case "down": return "up";
@@ -835,22 +835,22 @@ var D = (e) => {
835
835
  y(), e.protocol.eventBus.off("key", k), A();
836
836
  }
837
837
  };
838
- }, z = (e) => e?.trim().toLowerCase() ?? "", B = /\.m[a-z0-9]{2}$/i, me = (e) => [
838
+ }, z = (e) => e?.trim().toLowerCase() ?? "", me = /\.m[a-z0-9]{2}$/i, he = (e) => [
839
839
  e.viewName ?? "",
840
840
  e.viewTitle ?? "",
841
841
  e.cursorPos?.x ?? "",
842
842
  e.cursorPos?.y ?? "",
843
843
  e.textUnderCursor ?? "",
844
844
  e.currentLine ?? ""
845
- ].join("|"), he = (e) => e.textUnderCursor?.trim() || e.currentLine?.trim() || null, ge = (e) => {
845
+ ].join("|"), ge = (e) => e.textUnderCursor?.trim() || e.currentLine?.trim() || null, _e = (e) => {
846
846
  let t = z(e.viewName), n = z(e.viewTitle);
847
847
  return t.includes("load") || t.includes("save") || n.includes("load") || n.includes("save");
848
- }, _e = (e) => z(e) === "/..", ve = (e) => !!e?.trim().startsWith("/"), ye = (e) => !!e && !ve(e) && B.test(e.trim()), be = (e) => {
848
+ }, ve = (e) => z(e) === "/..", ye = (e) => !!e?.trim().startsWith("/"), be = (e) => !!e && !ye(e) && me.test(e.trim()), xe = (e) => {
849
849
  let t = e?.trim() ?? "";
850
850
  if (!t) return [];
851
- let n = z(t), r = z(t.replace(B, ""));
851
+ let n = z(t), r = z(t.replace(me, ""));
852
852
  return Array.from(new Set([n, r].filter(Boolean)));
853
- }, xe = (e) => _e(e) ? "parent" : ve(e) ? /^\/[^/]+$/.test(e.trim()) ? "dir" : "other" : ye(e) ? "file" : "other", Se = (e, t) => {
853
+ }, Se = (e) => ve(e) ? "parent" : ye(e) ? /^\/[^/]+$/.test(e.trim()) ? "dir" : "other" : be(e) ? "file" : "other", Ce = (e, t) => {
854
854
  let n = (t) => e[t]?.trim() ?? "", r = t.cursorPos?.y ?? null, i = z(t.viewTitle), a = 0, o = e.length - 1;
855
855
  if (r !== null && n(r)) {
856
856
  for (a = r; a > 0 && n(a - 1);) --a;
@@ -863,7 +863,7 @@ var D = (e) => {
863
863
  row: e,
864
864
  text: t,
865
865
  key: z(t),
866
- kind: xe(t)
866
+ kind: Se(t)
867
867
  });
868
868
  }
869
869
  let c = t.textUnderCursor?.trim();
@@ -874,7 +874,7 @@ var D = (e) => {
874
874
  hasParent: s.some((e) => e.kind === "parent"),
875
875
  consistent: !c || r === null || n(r).includes(c)
876
876
  };
877
- }, Ce = (e, t) => {
877
+ }, we = (e, t) => {
878
878
  if (t.length === 0) return {
879
879
  order: e,
880
880
  consistent: !0
@@ -908,26 +908,26 @@ var D = (e) => {
908
908
  order: i,
909
909
  consistent: !0
910
910
  };
911
- }, we = (e) => e.split(/[^a-z0-9]+/).filter((e) => e.length >= 3), Te = /* @__PURE__ */ new Set([
911
+ }, Te = (e) => e.split(/[^a-z0-9]+/).filter((e) => e.length >= 3), Ee = /* @__PURE__ */ new Set([
912
912
  "load",
913
913
  "save",
914
914
  "select",
915
915
  "create",
916
916
  "directory",
917
917
  "new"
918
- ]), Ee = (e, t) => ({
919
- target: we(z(e).replace(B, "")),
920
- title: we(z(t)).filter((e) => !Te.has(e))
921
- }), De = (e, t) => {
918
+ ]), De = (e, t) => ({
919
+ target: Te(z(e).replace(me, "")),
920
+ title: Te(z(t)).filter((e) => !Ee.has(e))
921
+ }), Oe = (e, t) => {
922
922
  let n = e.replace(/^\//, "");
923
923
  if (n.length < 3) return 0;
924
924
  let r = (e) => e.includes(n) || n.includes(e);
925
925
  return t.target.some(r) ? 2 : +!!t.title.some(r);
926
- }, V = (e) => new Promise((t) => setTimeout(t, e)), Oe = ({ getKeySink: e, store: t, debugLog: n, getScreenLines: r }) => {
926
+ }, B = (e) => new Promise((t) => setTimeout(t, e)), ke = ({ getKeySink: e, store: t, debugLog: n, getScreenLines: r }) => {
927
927
  let i = () => !!e(), a = (t) => {
928
928
  e()?.sendKeys(t);
929
929
  }, o = async (e, t = 50) => {
930
- a(e), await V(t), a(0), await V(t);
930
+ a(e), await B(t), a(0), await B(t);
931
931
  }, s = (e, n = 500) => new Promise((r) => {
932
932
  let i = !1, a = null, o = null, s = (e) => {
933
933
  i || (i = !0, o !== null && clearTimeout(o), a?.(), r(e));
@@ -965,21 +965,21 @@ var D = (e) => {
965
965
  }),
966
966
  getFileBrowserSnapshot: c,
967
967
  waitForFileBrowserSnapshotChange: (e, n = 500) => new Promise((r) => {
968
- let i = !1, a = null, o = null, s = null, l = null, u = null, d = null, f = me(e), p = (e) => {
968
+ let i = !1, a = null, o = null, s = null, l = null, u = null, d = null, f = he(e), p = (e) => {
969
969
  i || (i = !0, d !== null && clearTimeout(d), a?.(), o?.(), s?.(), l?.(), u?.(), r(e));
970
970
  }, m = () => {
971
971
  let e = c();
972
- me(e) !== f && p(e);
972
+ he(e) !== f && p(e);
973
973
  }, h = c();
974
- me(h) === f ? (a = t.sub("textUnderCursor", m), o = t.sub("currentLine", m), s = t.sub("cursorPos", m), l = t.sub("viewName", m), u = t.sub("viewTitle", m), d = setTimeout(() => {
974
+ he(h) === f ? (a = t.sub("textUnderCursor", m), o = t.sub("currentLine", m), s = t.sub("cursorPos", m), l = t.sub("viewName", m), u = t.sub("viewTitle", m), d = setTimeout(() => {
975
975
  p(c());
976
976
  }, n)) : p(h);
977
977
  }),
978
978
  getScreenLines: r
979
979
  };
980
- }, H = (e, t) => {
980
+ }, V = (e, t) => {
981
981
  t === void 0 ? console.log("[M8SDK browseFile]", e) : console.log("[M8SDK browseFile]", e, t);
982
- }, ke = 8, Ae = 8, je = 64, Me = 512, Ne = () => ({
982
+ }, Ae = 8, je = 8, Me = 64, Ne = 512, Pe = () => ({
983
983
  order: [],
984
984
  kinds: /* @__PURE__ */ new Map(),
985
985
  names: /* @__PURE__ */ new Map(),
@@ -989,40 +989,40 @@ var D = (e) => {
989
989
  sorted: !1,
990
990
  visited: /* @__PURE__ */ new Set(),
991
991
  rejected: /* @__PURE__ */ new Set()
992
- }), Pe = (e) => e === "up" ? "down" : "up", Fe = (e) => {
992
+ }), Fe = (e) => e === "up" ? "down" : "up", Ie = (e) => {
993
993
  let { hasBus: t, pressAndRelease: n, getFileBrowserSnapshot: r, waitForFileBrowserSnapshotChange: i, getScreenLines: a } = e, o = /* @__PURE__ */ new Map(), s = null, c = null;
994
994
  return async (e, l = !0) => {
995
995
  if (!t()) return !1;
996
996
  let u = z(e);
997
997
  if (!u) return !1;
998
- let d = B.test(u), f = Ee(e, r().viewTitle);
999
- H("start", {
998
+ let d = me.test(u), f = De(e, r().viewTitle);
999
+ V("start", {
1000
1000
  targetText: e,
1001
1001
  normalizedTarget: u,
1002
1002
  exact: l
1003
1003
  });
1004
- let p = () => ge(r()), m = async (e, t = 350) => {
1004
+ let p = () => _e(r()), m = async (e, t = 350) => {
1005
1005
  let a = r();
1006
1006
  await n(e, 35);
1007
1007
  let o = await i(a, t);
1008
1008
  return {
1009
- changed: me(o) !== me(a),
1009
+ changed: he(o) !== he(a),
1010
1010
  snapshot: o
1011
1011
  };
1012
1012
  }, h = (e) => e === "up" ? I.Up : I.Down, g = (e) => I.Opt | h(e), _ = async () => {
1013
1013
  let e = r();
1014
- H("apply alphabetical sort"), await n(I.Shift | I.Opt, 35), await V(180), await i(e, 300);
1015
- }, v = (e) => ye(e) ? be(e).some((e) => l ? e === u : e.includes(u)) : !1, y = (e) => {
1014
+ V("apply alphabetical sort"), await n(I.Shift | I.Opt, 35), await B(180), await i(e, 300);
1015
+ }, v = (e) => be(e) ? xe(e).some((e) => l ? e === u : e.includes(u)) : !1, y = (e) => {
1016
1016
  let t = e.trim();
1017
- return d ? t : t.replace(B, "");
1017
+ return d ? t : t.replace(me, "");
1018
1018
  }, b = (e) => z(y(e)), x = y(e), S = async () => {
1019
1019
  for (let e = 0;; e += 1) {
1020
- let t = Se(a(), r());
1020
+ let t = Ce(a(), r());
1021
1021
  if (t.consistent || e >= 2) return t;
1022
- await V(40);
1022
+ await B(40);
1023
1023
  }
1024
1024
  }, C = (e, t) => {
1025
- let n = t.entries.map((e) => e.key), i = Ce(e.order, n);
1025
+ let n = t.entries.map((e) => e.key), i = we(e.order, n);
1026
1026
  e.order = i.order, i.consistent || (e.top = !1, e.bottom = !1);
1027
1027
  for (let n of t.entries) e.kinds.set(n.key, n.kind), e.names.set(n.key, n.text);
1028
1028
  let a = z(r().viewName);
@@ -1031,10 +1031,10 @@ var D = (e) => {
1031
1031
  let e = o.get(a);
1032
1032
  (e === void 0 || n.length < e) && o.set(a, n.length);
1033
1033
  }
1034
- n.length < Math.max(Ae, o.get(a) ?? 0) && (e.top = !0, e.bottom = !0);
1034
+ n.length < Math.max(je, o.get(a) ?? 0) && (e.top = !0, e.bottom = !0);
1035
1035
  }, w = async (e, t, n) => {
1036
1036
  let r = (await m(g(n), 280)).changed, i = r, a = !1;
1037
- if (!r && s !== !0) for (let e = 0; e < ke; e += 1) {
1037
+ if (!r && s !== !0) for (let e = 0; e < Ae; e += 1) {
1038
1038
  if (!(await m(h(n), 250)).changed) {
1039
1039
  a = !0;
1040
1040
  break;
@@ -1042,11 +1042,11 @@ var D = (e) => {
1042
1042
  i = !0, s = !1;
1043
1043
  }
1044
1044
  else r || (a = !0);
1045
- if (a && (n === "up" ? e.top = !0 : e.bottom = !0, H("end of list", { direction: n })), !i) return null;
1045
+ if (a && (n === "up" ? e.top = !0 : e.bottom = !0, V("end of list", { direction: n })), !i) return null;
1046
1046
  let o = await S();
1047
1047
  C(e, o);
1048
1048
  let c = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1, l = o.cursorKey ? e.order.indexOf(o.cursorKey) : -1, u = Math.abs(l - c);
1049
- return r && c >= 0 && l >= 0 && u > 0 && u < ke && (s = !0), o;
1049
+ return r && c >= 0 && l >= 0 && u > 0 && u < Ae && (s = !0), o;
1050
1050
  }, T = (e, t, n) => {
1051
1051
  if (n === "/..") return "up";
1052
1052
  let r = e.order.indexOf(n), i = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1;
@@ -1060,7 +1060,7 @@ var D = (e) => {
1060
1060
  let t = n.row - r.cursorRow;
1061
1061
  if (t === 0) return r;
1062
1062
  let i = t < 0 ? "up" : "down";
1063
- if (!(await m(Math.abs(t) >= ke ? g(i) : h(i), 250)).changed) return null;
1063
+ if (!(await m(Math.abs(t) >= Ae ? g(i) : h(i), 250)).changed) return null;
1064
1064
  r = await S(), C(e, r);
1065
1065
  continue;
1066
1066
  }
@@ -1068,17 +1068,17 @@ var D = (e) => {
1068
1068
  let o = await w(e, r, i);
1069
1069
  if (o) r = o;
1070
1070
  else if (a) return null;
1071
- else a = !0, i = Pe(i);
1071
+ else a = !0, i = Fe(i);
1072
1072
  }
1073
1073
  return null;
1074
1074
  }, D = async (e, t) => {
1075
1075
  let n = t.cursorRow ?? 0, i = t.entries.filter((t) => t.kind === "file" && !e.rejected.has(t.key) && v(t.text)).sort((e, t) => Math.abs(e.row - n) - Math.abs(t.row - n))[0];
1076
1076
  if (!i) return "none";
1077
- let a = await E(e, i.key, t), o = he(r());
1078
- return a && v(o) ? (H("match found", {
1077
+ let a = await E(e, i.key, t), o = ge(r());
1078
+ return a && v(o) ? (V("match found", {
1079
1079
  entry: o,
1080
1080
  y: a.cursorRow
1081
- }), await m(I.Edit, 500), "found") : (H("match not reachable", { candidate: i.text }), e.rejected.add(i.key), "failed");
1081
+ }), await m(I.Edit, 500), "found") : (V("match not reachable", { candidate: i.text }), e.rejected.add(i.key), "failed");
1082
1082
  }, O = (e) => e.order.filter((t) => e.kinds.get(t) === "file").map((t) => {
1083
1083
  let n = e.names.get(t) ?? "";
1084
1084
  return {
@@ -1089,7 +1089,7 @@ var D = (e) => {
1089
1089
  if (!e.sorted) return;
1090
1090
  let t = O(e);
1091
1091
  if (t.some((e, n) => n > 0 && (t[n - 1].ci > e.ci || t[n - 1].raw > e.raw))) {
1092
- e.sorted = !1, H("listing is not alphabetical, order hints disabled");
1092
+ e.sorted = !1, V("listing is not alphabetical, order hints disabled");
1093
1093
  return;
1094
1094
  }
1095
1095
  let n = e.order.map((t) => {
@@ -1109,20 +1109,20 @@ var D = (e) => {
1109
1109
  let n = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1, r = null;
1110
1110
  for (let [i, a] of e.kinds) {
1111
1111
  if (a !== "dir" || e.visited.has(i)) continue;
1112
- let o = t.entries.find((e) => e.key === i), s = e.order.indexOf(i), c = o && t.cursorRow !== null ? Math.abs(o.row - t.cursorRow) : 100 + (n >= 0 && s >= 0 ? Math.abs(s - n) : 500), l = De(i, f) * 1e4 - c;
1112
+ let o = t.entries.find((e) => e.key === i), s = e.order.indexOf(i), c = o && t.cursorRow !== null ? Math.abs(o.row - t.cursorRow) : 100 + (n >= 0 && s >= 0 ? Math.abs(s - n) : 500), l = Oe(i, f) * 1e4 - c;
1113
1113
  (!r || l > r.rank) && (r = {
1114
1114
  key: i,
1115
1115
  rank: l
1116
1116
  });
1117
1117
  }
1118
1118
  return r?.key ?? null;
1119
- }, N = async (e, t) => await E(e, "/..", t) ? (H("navigate to parent directory"), (await m(I.Edit, 500)).changed && p()) : (H("parent directory entry unreachable"), !1), P = async (e) => {
1120
- if (e > je || !p()) return H("search aborted", {
1119
+ }, N = async (e, t) => await E(e, "/..", t) ? (V("navigate to parent directory"), (await m(I.Edit, 500)).changed && p()) : (V("parent directory entry unreachable"), !1), P = async (e) => {
1120
+ if (e > Me || !p()) return V("search aborted", {
1121
1121
  depth: e,
1122
1122
  active: p()
1123
1123
  }), "aborted";
1124
- let t = Ne(), n = await S();
1125
- C(t, n), H("search directory", {
1124
+ let t = Pe(), n = await S();
1125
+ C(t, n), V("search directory", {
1126
1126
  depth: e,
1127
1127
  entries: n.entries.length,
1128
1128
  complete: t.top && t.bottom
@@ -1130,7 +1130,7 @@ var D = (e) => {
1130
1130
  for (let r = 0;; r += 1) {
1131
1131
  let i = await D(t, n);
1132
1132
  if (i === "found") return "found";
1133
- if (i === "failed" && (n = await S(), C(t, n)), k(t), A(t) && (H("rest of listing is past the target", { depth: e }), t.bottom = !0), t.top && t.bottom || r >= Me) break;
1133
+ if (i === "failed" && (n = await S(), C(t, n)), k(t), A(t) && (V("rest of listing is past the target", { depth: e }), t.bottom = !0), t.top && t.bottom || r >= Ne) break;
1134
1134
  let a = await w(t, n, j(t, n));
1135
1135
  a && (n = a);
1136
1136
  }
@@ -1138,10 +1138,10 @@ var D = (e) => {
1138
1138
  let r = M(t, n);
1139
1139
  if (!r) break;
1140
1140
  if (t.visited.add(r), !await E(t, r, n)) {
1141
- H("directory unreachable", { directory: r }), n = await S(), C(t, n);
1141
+ V("directory unreachable", { directory: r }), n = await S(), C(t, n);
1142
1142
  continue;
1143
1143
  }
1144
- H("enter directory", {
1144
+ V("enter directory", {
1145
1145
  directory: r,
1146
1146
  depth: e
1147
1147
  });
@@ -1160,12 +1160,12 @@ var D = (e) => {
1160
1160
  if (!p()) return console.warn("[M8SDK] browseFile requires a file browser view (load/save)"), !1;
1161
1161
  if (a().length === 0) return console.warn("[M8SDK] browseFile needs the screen reader, which is not available"), !1;
1162
1162
  let F = await P(0);
1163
- return F !== "found" && H("search failed", {
1163
+ return F !== "found" && V("search failed", {
1164
1164
  targetText: e,
1165
1165
  outcome: F
1166
1166
  }), F === "found";
1167
1167
  };
1168
- }, Ie = (e) => {
1168
+ }, Le = (e) => {
1169
1169
  if (!e) return null;
1170
1170
  let t = e.trim();
1171
1171
  if (t === "--") return 0;
@@ -1173,7 +1173,7 @@ var D = (e) => {
1173
1173
  if (!n) return null;
1174
1174
  let r = parseInt(n[0], 16);
1175
1175
  return Number.isNaN(r) ? null : r;
1176
- }, Le = (e) => {
1176
+ }, Re = (e) => {
1177
1177
  if (!e) return null;
1178
1178
  let t = e.trim();
1179
1179
  if (t === "--") return 0;
@@ -1181,11 +1181,11 @@ var D = (e) => {
1181
1181
  if (!n) return null;
1182
1182
  let r = Number.parseInt(n[0], 10);
1183
1183
  return Number.isNaN(r) ? null : r;
1184
- }, Re = /[+-]?\d+\.\d+/g, ze = (e) => {
1184
+ }, ze = /[+-]?\d+\.\d+/g, Be = (e) => {
1185
1185
  if (!e) return [];
1186
1186
  let t = [];
1187
- Re.lastIndex = 0;
1188
- let n = Re.exec(e);
1187
+ ze.lastIndex = 0;
1188
+ let n = ze.exec(e);
1189
1189
  for (; n !== null;) {
1190
1190
  let r = n[0], i = r.startsWith("-"), a = i || r.startsWith("+") ? r.slice(1) : r, o = a.indexOf("."), s = a.slice(0, o), c = a.slice(o + 1), l = Number.parseInt(s || "0", 10), u = Number.parseInt(c || "0", 10), d = 10 ** c.length, f = d > 0 ? 100 / d : 1, p = l * 100 + u * 100 / d;
1191
1191
  !Number.isNaN(l) && !Number.isNaN(u) && t.push({
@@ -1196,16 +1196,16 @@ var D = (e) => {
1196
1196
  decScale: d,
1197
1197
  tickHundredths: f,
1198
1198
  ticks: (i ? -1 : 1) * Math.round(p / f)
1199
- }), n = Re.exec(e);
1199
+ }), n = ze.exec(e);
1200
1200
  }
1201
1201
  return t;
1202
- }, Be = (e, t) => {
1202
+ }, Ve = (e, t) => {
1203
1203
  if (e.length <= 1) return 0;
1204
1204
  let n = t?.trim() ?? "";
1205
1205
  if (!n) return 0;
1206
1206
  let r = e.findIndex((e) => e.raw === n || e.raw.replace(/^[+-]/, "") === n || `${e.negative ? "-" : ""}${e.intDigits}` === n || e.intDigits === n || e.decDigits === n);
1207
1207
  return r >= 0 ? r : 0;
1208
- }, Ve = (e, t) => {
1208
+ }, He = (e, t) => {
1209
1209
  if (!e) return null;
1210
1210
  let n = t?.trim() ?? "";
1211
1211
  if (!n) return null;
@@ -1219,13 +1219,13 @@ var D = (e) => {
1219
1219
  if (r && !t) return "dec";
1220
1220
  }
1221
1221
  return null;
1222
- }, He = (e, t) => t.length === 1 ? e.raw === t || e.raw.replace(/^[+-]/, "") === t : e.raw === t || e.raw.replace(/^[+-]/, "") === t || `${e.negative ? "-" : ""}${e.intDigits}` === t || e.intDigits === t || e.decDigits === t, Ue = (e, t, n) => {
1222
+ }, Ue = (e, t) => t.length === 1 ? e.raw === t || e.raw.replace(/^[+-]/, "") === t : e.raw === t || e.raw.replace(/^[+-]/, "") === t || `${e.negative ? "-" : ""}${e.intDigits}` === t || e.intDigits === t || e.decDigits === t, We = (e, t, n) => {
1223
1223
  let r = n?.trim() ?? "";
1224
- return !r || !e[t] || He(e[t], r) ? !1 : e.some((e, n) => n !== t && He(e, r));
1225
- }, U = (e, t) => {
1224
+ return !r || !e[t] || Ue(e[t], r) ? !1 : e.some((e, n) => n !== t && Ue(e, r));
1225
+ }, H = (e, t) => {
1226
1226
  let n = Math.round(e * t), r = n < 0 ? "-" : "", i = Math.abs(n), a = Math.floor(i / 100);
1227
1227
  return `${r}${a}.${(i - a * 100).toString().padStart(2, "0")}`;
1228
- }, We = [
1228
+ }, Ge = [
1229
1229
  "C-",
1230
1230
  "C#",
1231
1231
  "D-",
@@ -1238,35 +1238,35 @@ var D = (e) => {
1238
1238
  "A-",
1239
1239
  "A#",
1240
1240
  "B-"
1241
- ], W = (e) => {
1241
+ ], U = (e) => {
1242
1242
  if (!e) return null;
1243
1243
  let t = e.trim().toUpperCase().match(/([A-G][#-][0-9A-F])/);
1244
1244
  if (!t) return null;
1245
- let n = t[1], r = n.slice(0, 2), i = Number.parseInt(n.slice(2, 3), 16), a = We.indexOf(r);
1245
+ let n = t[1], r = n.slice(0, 2), i = Number.parseInt(n.slice(2, 3), 16), a = Ge.indexOf(r);
1246
1246
  return a < 0 || Number.isNaN(i) ? null : {
1247
1247
  label: n,
1248
1248
  noteName: r,
1249
1249
  octave: i,
1250
1250
  semitoneIndex: i * 12 + a
1251
1251
  };
1252
- }, Ge = (e) => {
1252
+ }, Ke = (e) => {
1253
1253
  let { store: t, debugLog: n, hasBus: r, sendKeys: i, pressAndRelease: a, waitForTextUnderCursorChange: o, waitForCurrentLineChange: s } = e;
1254
1254
  return async (e) => {
1255
1255
  if (!r() || !Number.isFinite(e)) return !1;
1256
1256
  let c = 0, l = 1, u = [], d = () => {
1257
- let e = ze(t.get("currentLine"));
1257
+ let e = Be(t.get("currentLine"));
1258
1258
  if (u = e, !e.length) return null;
1259
1259
  c = Math.min(c, e.length - 1);
1260
1260
  let n = e[c];
1261
1261
  return n.tickHundredths > 0 && (l = n.tickHundredths), n;
1262
- }, f = ze(t.get("currentLine"));
1262
+ }, f = Be(t.get("currentLine"));
1263
1263
  if (!f.length) return console.warn("[M8SDK] Could not find a float value like \"440.00\" or \"-10.69\" on the current line:", t.get("currentLine")), !1;
1264
- c = Be(f, t.get("textUnderCursor")), f.length > 1 && n("[M8SDK] Multiple float values on line, editing:", f[c]?.raw);
1264
+ c = Ve(f, t.get("textUnderCursor")), f.length > 1 && n("[M8SDK] Multiple float values on line, editing:", f[c]?.raw);
1265
1265
  let p = f[c];
1266
1266
  l = p.tickHundredths;
1267
1267
  let m = Math.round(Math.round(e * 100) / l);
1268
- if (p.ticks === m) return n("[M8SDK] Already at target float value:", U(m, l)), !0;
1269
- n("[M8SDK] Setting float value from", U(p.ticks, l), "to", U(m, l)), await a(I.Edit), await V(80);
1268
+ if (p.ticks === m) return n("[M8SDK] Already at target float value:", H(m, l)), !0;
1269
+ n("[M8SDK] Setting float value from", H(p.ticks, l), "to", H(m, l)), await a(I.Edit), await B(80);
1270
1270
  let h = () => ({
1271
1271
  upDown: {
1272
1272
  mag: null,
@@ -1280,17 +1280,17 @@ var D = (e) => {
1280
1280
  full: h(),
1281
1281
  int: h(),
1282
1282
  dec: h()
1283
- }, _ = p.decScale, v = Date.now(), y = Ve(p, t.get("textUnderCursor")) ?? "int";
1283
+ }, _ = p.decScale, v = Date.now(), y = He(p, t.get("textUnderCursor")) ?? "int";
1284
1284
  n("[M8SDK] Float cursor focus:", y);
1285
1285
  let b = !1, x = !1, S = p.ticks, C = null, w = p.ticks, T = Math.abs(p.ticks - m), E = 0, D = !1, O = !1, k = (e, t) => {
1286
1286
  let n = e === "upDown" ? I.Up : I.Right, r = e === "upDown" ? I.Down : I.Left;
1287
1287
  return I.Edit | (t >= 0 ? n : r);
1288
1288
  }, A = async (e, n, r) => {
1289
1289
  let a = g[y][e], o = k(e, n * a.bias), c = t.get("currentLine");
1290
- for (let e = 0; e < r; e++) i(o), await V(22);
1290
+ for (let e = 0; e < r; e++) i(o), await B(22);
1291
1291
  await s(c, 350);
1292
1292
  }, ee = async (e) => {
1293
- let n = () => Ve(d(), t.get("textUnderCursor"));
1293
+ let n = () => He(d(), t.get("textUnderCursor"));
1294
1294
  if (n() === e) return y = e, !0;
1295
1295
  let r = t.get("textUnderCursor"), i = e === "dec" ? I.Right : I.Left, s = e === "dec" ? I.Left : I.Right;
1296
1296
  if (await a(i, 35), await o(r, 250), n() === e) return y = e, !0;
@@ -1304,7 +1304,7 @@ var D = (e) => {
1304
1304
  console.warn("[M8SDK] Float value no longer parseable while editing:", t.get("currentLine")), O = !0;
1305
1305
  break;
1306
1306
  }
1307
- if (Ue(u, c, t.get("textUnderCursor"))) {
1307
+ if (We(u, c, t.get("textUnderCursor"))) {
1308
1308
  console.warn("[M8SDK] Float cursor drifted to another field, aborting to avoid editing the wrong value"), x = !0, O = !0;
1309
1309
  break;
1310
1310
  }
@@ -1323,7 +1323,7 @@ var D = (e) => {
1323
1323
  if (C = S, S = e.ticks, y !== "full" && !b) {
1324
1324
  let e = i >= _ ? "int" : "dec";
1325
1325
  if (e !== y && !await ee(e)) {
1326
- if (Ue(u, c, t.get("textUnderCursor"))) {
1326
+ if (We(u, c, t.get("textUnderCursor"))) {
1327
1327
  console.warn("[M8SDK] Float cursor drifted to another field, aborting to avoid editing the wrong value"), x = !0, O = !0;
1328
1328
  break;
1329
1329
  }
@@ -1341,7 +1341,7 @@ var D = (e) => {
1341
1341
  }
1342
1342
  let P = N.ticks - k;
1343
1343
  if (P === 0) {
1344
- E += 1, E >= 4 && (console.warn(`[M8SDK] Float value stopped reacting at ${U(N.ticks, l)} (target ${U(m, l)} may be out of range)`), O = !0);
1344
+ E += 1, E >= 4 && (console.warn(`[M8SDK] Float value stopped reacting at ${H(N.ticks, l)} (target ${H(m, l)} may be out of range)`), O = !0);
1345
1345
  continue;
1346
1346
  }
1347
1347
  E = 0;
@@ -1365,9 +1365,9 @@ var D = (e) => {
1365
1365
  }
1366
1366
  x || await a(I.Edit);
1367
1367
  let j = d(), M = !!j && j.ticks === m;
1368
- return n(`[M8SDK] Float setting ${M ? "succeeded" : "failed"}: "${j?.raw ?? "N/A"}" (${j ? U(j.ticks, l) : "N/A"})`), M;
1368
+ return n(`[M8SDK] Float setting ${M ? "succeeded" : "failed"}: "${j?.raw ?? "N/A"}" (${j ? H(j.ticks, l) : "N/A"})`), M;
1369
1369
  };
1370
- }, Ke = {
1370
+ }, qe = {
1371
1371
  song: [
1372
1372
  {
1373
1373
  to: "project",
@@ -1386,15 +1386,68 @@ var D = (e) => {
1386
1386
  keys: [20, 0]
1387
1387
  }
1388
1388
  ],
1389
- project: [{
1390
- to: "song",
1391
- dir: "down",
1392
- keys: [48, 0]
1393
- }, {
1394
- to: "chain",
1395
- dir: "down",
1396
- keys: [48, 0]
1397
- }],
1389
+ project: [
1390
+ {
1391
+ to: "song",
1392
+ dir: "down",
1393
+ keys: [48, 0],
1394
+ back: !0
1395
+ },
1396
+ {
1397
+ to: "chain",
1398
+ dir: "down",
1399
+ keys: [48, 0],
1400
+ back: !0
1401
+ },
1402
+ {
1403
+ to: "midisettings",
1404
+ via: "button",
1405
+ cursor: [14, 11],
1406
+ label: "SETTINGS",
1407
+ keys: [1, 0],
1408
+ weight: 2
1409
+ },
1410
+ {
1411
+ to: "midimapping",
1412
+ via: "button",
1413
+ cursor: [23, 11],
1414
+ label: "MAPPINGS",
1415
+ keys: [1, 0],
1416
+ weight: 2
1417
+ },
1418
+ {
1419
+ to: "renderaudio",
1420
+ via: "button",
1421
+ cursor: [14, 15],
1422
+ label: "RENDER",
1423
+ keys: [1, 0],
1424
+ weight: 2
1425
+ },
1426
+ {
1427
+ to: "instrumentpool",
1428
+ via: "button",
1429
+ cursor: [14, 17],
1430
+ label: "VIEW INST. POOL",
1431
+ keys: [1, 0],
1432
+ weight: 2
1433
+ },
1434
+ {
1435
+ to: "timestats",
1436
+ via: "button",
1437
+ cursor: [14, 19],
1438
+ label: "VIEW TIME STATS",
1439
+ keys: [1, 0],
1440
+ weight: 2
1441
+ },
1442
+ {
1443
+ to: "systemsettings",
1444
+ via: "button",
1445
+ cursor: [14, 20],
1446
+ label: "SETTINGS",
1447
+ keys: [1, 0],
1448
+ weight: 2
1449
+ }
1450
+ ],
1398
1451
  mixer: [
1399
1452
  {
1400
1453
  to: "effectsettings",
@@ -1405,38 +1458,95 @@ var D = (e) => {
1405
1458
  to: "song",
1406
1459
  dir: "up",
1407
1460
  keys: [80, 0],
1408
- weight: 10
1461
+ weight: 10,
1462
+ back: !0
1409
1463
  },
1410
1464
  {
1411
1465
  to: "chain",
1412
1466
  dir: "up",
1413
1467
  keys: [80, 0],
1414
- weight: 10
1468
+ weight: 10,
1469
+ back: !0
1415
1470
  },
1416
1471
  {
1417
1472
  to: "phrase",
1418
1473
  dir: "up",
1419
1474
  keys: [80, 0],
1420
- weight: 10
1475
+ weight: 10,
1476
+ back: !0
1421
1477
  },
1422
1478
  {
1423
1479
  to: "inst",
1424
1480
  dir: "up",
1425
1481
  keys: [80, 0],
1426
- weight: 10
1482
+ weight: 10,
1483
+ back: !0
1427
1484
  },
1428
1485
  {
1429
1486
  to: "table",
1430
1487
  dir: "up",
1431
1488
  keys: [80, 0],
1432
- weight: 10
1489
+ weight: 10,
1490
+ back: !0
1491
+ },
1492
+ {
1493
+ to: "mixeq",
1494
+ via: "button",
1495
+ cursor: [28, 17],
1496
+ keys: [
1497
+ 64,
1498
+ 0,
1499
+ 20,
1500
+ 0
1501
+ ],
1502
+ weight: 2
1503
+ },
1504
+ {
1505
+ to: "mixscope",
1506
+ via: "button",
1507
+ cursor: [28, 17],
1508
+ keys: [20, 0],
1509
+ weight: 2
1510
+ },
1511
+ {
1512
+ to: "limiterscope",
1513
+ via: "button",
1514
+ cursor: [28, 18],
1515
+ keys: [20, 0],
1516
+ weight: 2
1517
+ }
1518
+ ],
1519
+ effectsettings: [
1520
+ {
1521
+ to: "mixer",
1522
+ dir: "up",
1523
+ keys: [80, 0]
1524
+ },
1525
+ {
1526
+ to: "modfxeq",
1527
+ via: "button",
1528
+ cursor: [22, 6],
1529
+ label: "EQ",
1530
+ keys: [1, 0],
1531
+ weight: 2
1532
+ },
1533
+ {
1534
+ to: "delayeq",
1535
+ via: "button",
1536
+ cursor: [22, 11],
1537
+ label: "EQ",
1538
+ keys: [1, 0],
1539
+ weight: 2
1540
+ },
1541
+ {
1542
+ to: "reverbeq",
1543
+ via: "button",
1544
+ cursor: [22, 17],
1545
+ label: "EQ",
1546
+ keys: [1, 0],
1547
+ weight: 2
1433
1548
  }
1434
1549
  ],
1435
- effectsettings: [{
1436
- to: "mixer",
1437
- dir: "up",
1438
- keys: [80, 0]
1439
- }],
1440
1550
  inst: [
1441
1551
  {
1442
1552
  to: "table",
@@ -1458,6 +1568,20 @@ var D = (e) => {
1458
1568
  dir: "down",
1459
1569
  keys: [48, 0],
1460
1570
  weight: 10
1571
+ },
1572
+ {
1573
+ to: "loadinstrument",
1574
+ via: "button",
1575
+ cursor: [23, 5],
1576
+ label: "LOAD",
1577
+ keys: [1, 0],
1578
+ weight: 2
1579
+ },
1580
+ {
1581
+ to: "instrumentpool",
1582
+ dir: "left",
1583
+ keys: [144, 0],
1584
+ back: !0
1461
1585
  }
1462
1586
  ],
1463
1587
  table: [
@@ -1547,71 +1671,199 @@ var D = (e) => {
1547
1671
  {
1548
1672
  to: "inst",
1549
1673
  dir: "down",
1550
- keys: [48, 0]
1674
+ keys: [48, 0],
1675
+ back: !0
1551
1676
  },
1552
1677
  {
1553
1678
  to: "table",
1554
1679
  dir: "down",
1680
+ keys: [48, 0],
1681
+ back: !0
1682
+ }
1683
+ ],
1684
+ instrumentpool: [
1685
+ {
1686
+ to: "instmods",
1687
+ dir: "down",
1555
1688
  keys: [48, 0]
1689
+ },
1690
+ {
1691
+ to: "inst",
1692
+ dir: "right",
1693
+ keys: [20, 0]
1694
+ },
1695
+ {
1696
+ to: "phrase",
1697
+ dir: "left",
1698
+ keys: [144, 0],
1699
+ weight: 4
1556
1700
  }
1557
1701
  ],
1558
- instrumentpool: [{
1559
- to: "instmods",
1560
- dir: "down",
1561
- keys: [48, 0]
1702
+ systemsettings: [
1703
+ {
1704
+ to: "themesettings",
1705
+ via: "button",
1706
+ cursor: [16, 7],
1707
+ label: "EDIT THEME",
1708
+ keys: [1, 0],
1709
+ weight: 2
1710
+ },
1711
+ {
1712
+ to: "project",
1713
+ via: "exit",
1714
+ keys: [2, 0]
1715
+ },
1716
+ {
1717
+ to: "project",
1718
+ dir: "up",
1719
+ keys: [80, 0],
1720
+ weight: 2
1721
+ }
1722
+ ],
1723
+ midisettings: [{
1724
+ to: "project",
1725
+ via: "exit",
1726
+ keys: [2, 0]
1727
+ }, {
1728
+ to: "project",
1729
+ dir: "up",
1730
+ keys: [80, 0],
1731
+ weight: 3
1732
+ }],
1733
+ midimapping: [{
1734
+ to: "project",
1735
+ via: "exit",
1736
+ keys: [2, 0]
1737
+ }, {
1738
+ to: "project",
1739
+ dir: "up",
1740
+ keys: [80, 0],
1741
+ weight: 3
1742
+ }],
1743
+ renderaudio: [{
1744
+ to: "project",
1745
+ via: "exit",
1746
+ keys: [2, 0]
1747
+ }, {
1748
+ to: "project",
1749
+ dir: "up",
1750
+ keys: [80, 0],
1751
+ weight: 3
1752
+ }],
1753
+ timestats: [{
1754
+ to: "project",
1755
+ via: "exit",
1756
+ keys: [2, 0]
1757
+ }, {
1758
+ to: "project",
1759
+ dir: "up",
1760
+ keys: [80, 0],
1761
+ weight: 3
1762
+ }],
1763
+ loadproject: [{
1764
+ to: "project",
1765
+ via: "exit",
1766
+ keys: [2, 0]
1767
+ }],
1768
+ themesettings: [{
1769
+ to: "systemsettings",
1770
+ via: "exit",
1771
+ keys: [2, 0]
1772
+ }],
1773
+ mixeq: [{
1774
+ to: "mixer",
1775
+ via: "exit",
1776
+ keys: [2, 0]
1777
+ }, {
1778
+ to: "mixer",
1779
+ dir: "left",
1780
+ keys: [144, 0],
1781
+ weight: 3
1782
+ }],
1783
+ mixscope: [{
1784
+ to: "mixer",
1785
+ via: "exit",
1786
+ keys: [2, 0]
1787
+ }, {
1788
+ to: "mixer",
1789
+ dir: "left",
1790
+ keys: [144, 0],
1791
+ weight: 3
1792
+ }],
1793
+ limiterscope: [{
1794
+ to: "mixer",
1795
+ via: "exit",
1796
+ keys: [2, 0]
1797
+ }, {
1798
+ to: "mixer",
1799
+ dir: "left",
1800
+ keys: [144, 0],
1801
+ weight: 3
1802
+ }],
1803
+ modfxeq: [{
1804
+ to: "effectsettings",
1805
+ via: "exit",
1806
+ keys: [2, 0]
1807
+ }],
1808
+ delayeq: [{
1809
+ to: "effectsettings",
1810
+ via: "exit",
1811
+ keys: [2, 0]
1812
+ }],
1813
+ reverbeq: [{
1814
+ to: "effectsettings",
1815
+ via: "exit",
1816
+ keys: [2, 0]
1817
+ }],
1818
+ loadinstrument: [{
1819
+ to: "inst",
1820
+ via: "exit",
1821
+ keys: [2, 0]
1562
1822
  }]
1563
- }, qe = { views: [
1564
- "chain",
1565
- "effectsettings",
1566
- "groove",
1567
- "instmods",
1568
- "inst",
1569
- "instrumentpool",
1570
- "mixer",
1571
- "phrase",
1572
- "project",
1573
- "scale",
1574
- "song",
1575
- "table",
1576
- "mixeq",
1577
- "limiterscope",
1578
- "mixscope",
1579
- "modfxeq",
1580
- "delayeq",
1581
- "reverbeq",
1582
- "loadinstrument",
1583
- "selectsavedirectory",
1584
- "createdirectory",
1585
- "systemsettings"
1586
- ] }, Je = (e) => {
1823
+ }, Je = { views: /* @__PURE__ */ "chain.effectsettings.groove.instmods.inst.instrumentpool.mixer.phrase.project.scale.song.table.mixeq.limiterscope.mixscope.modfxeq.delayeq.reverbeq.loadinstrument.selectsavedirectory.createdirectory.systemsettings.midisettings.midimapping.renderaudio.timestats.themesettings.loadproject".split(".") }, Ye = (e) => {
1587
1824
  let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
1588
1825
  for (let [r, i] of Object.entries(e)) {
1589
1826
  let e = r.toLowerCase(), a = [], o = /* @__PURE__ */ new Map();
1590
- for (let e of i) e.dir && a.push({
1591
- to: e.to,
1592
- dir: e.dir,
1593
- weight: e.weight
1594
- }), e.keys?.length && o.set(e.to, e.keys.slice());
1827
+ for (let t of i) {
1828
+ let n = t.via ?? "shift";
1829
+ if (n === "shift" && !t.dir || n === "button" && !t.cursor) continue;
1830
+ let r = t.keys?.slice() ?? [];
1831
+ a.push({
1832
+ from: e,
1833
+ to: t.to.toLowerCase(),
1834
+ dir: t.dir,
1835
+ weight: t.weight,
1836
+ kind: n,
1837
+ keys: r,
1838
+ cursor: t.cursor ? {
1839
+ x: t.cursor[0],
1840
+ y: t.cursor[1]
1841
+ } : void 0,
1842
+ label: t.label,
1843
+ back: t.back
1844
+ }), n === "shift" && r.length && o.set(t.to, r);
1845
+ }
1595
1846
  t.set(e, a), o.size && n.set(e, o);
1596
1847
  }
1848
+ for (let e of [...t.values()]) for (let n of e) t.has(n.to) || t.set(n.to, []);
1597
1849
  return {
1598
1850
  graph: t,
1599
1851
  edgeKeys: n
1600
1852
  };
1601
- }, Ye = null, Xe = null, Ze = () => (Ye ??= Je(Ke), Ye), Qe = () => {
1602
- if (!Xe) {
1603
- let e = Array.isArray(qe.views) ? qe.views : [];
1604
- Xe = new Set(e.map((e) => String(e).trim().toLowerCase()).filter((e) => !!e));
1853
+ }, Xe = null, Ze = null, Qe = () => (Xe ??= Ye(qe), Xe), $e = () => {
1854
+ if (!Ze) {
1855
+ let e = Array.isArray(Je.views) ? Je.views : [];
1856
+ Ze = new Set(e.map((e) => String(e).trim().toLowerCase()).filter((e) => !!e));
1605
1857
  }
1606
- return Xe;
1607
- }, $e = 50, et = {
1858
+ return Ze;
1859
+ }, et = 50, tt = {
1608
1860
  x: 0,
1609
1861
  y: 0,
1610
1862
  w: 1,
1611
1863
  h: 1
1612
- }, tt = class {
1864
+ }, nt = class {
1613
1865
  constructor() {
1614
- this.assemblies = [], this.selectionRectSamples = [], this.lastCursor = et, this.lastSelectionCursor = null, this.lastCursorColor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0, this.ASSEMBLY_TIMEOUT = 100, this.SELECTION_SAMPLE_WINDOW = 50, this.SELECTION_HOLD_DURATION = 150;
1866
+ this.assemblies = [], this.selectionRectSamples = [], this.lastCursor = tt, this.lastSelectionCursor = null, this.lastCursorColor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0, this.ASSEMBLY_TIMEOUT = 100, this.SELECTION_SAMPLE_WINDOW = 50, this.SELECTION_HOLD_DURATION = 150;
1615
1867
  }
1616
1868
  processRect(e) {
1617
1869
  if (!e) return {
@@ -1803,7 +2055,7 @@ var D = (e) => {
1803
2055
  ].filter((e) => e.foundVert || e.foundTip).length >= 2;
1804
2056
  }
1805
2057
  buildCursorFromAssembly(e) {
1806
- if (!e.tr || !e.bl || !e.br) return et;
2058
+ if (!e.tr || !e.bl || !e.br) return tt;
1807
2059
  let t = e.tr.x - e.tl.x - 1, n = e.bl.y - e.tl.y - 1;
1808
2060
  return {
1809
2061
  x: e.tl.x + 1,
@@ -1820,19 +2072,19 @@ var D = (e) => {
1820
2072
  h: 1
1821
2073
  }, this.lastSelectionCursor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0;
1822
2074
  }
1823
- }, nt = (e) => e ? `#${[
2075
+ }, rt = (e) => e ? `#${[
1824
2076
  e.r,
1825
2077
  e.g,
1826
2078
  e.b
1827
2079
  ].map((e) => e.toString(16).padStart(2, "0")).join("")}` : null;
1828
- function rt(e, t) {
1829
- let n = new tt();
2080
+ function it(e, t) {
2081
+ let n = new nt();
1830
2082
  class r {
1831
2083
  constructor() {
1832
2084
  this.rowChars = /* @__PURE__ */ new Map(), this.timer = null, this.knownViews = [], this.earlyAccepted = !1;
1833
2085
  }
1834
2086
  init() {
1835
- this.knownViews = Array.from(Qe());
2087
+ this.knownViews = Array.from($e());
1836
2088
  }
1837
2089
  normForCompare(e) {
1838
2090
  return e = e.includes("live") ? "song" : e, e.toLowerCase().trim().replace(/[{}]/g, "0");
@@ -1876,7 +2128,7 @@ function rt(e, t) {
1876
2128
  bg: e.background
1877
2129
  }), this.tryEarlyAccept(o), this.timer && clearTimeout(this.timer), this.timer = setTimeout(() => {
1878
2130
  this.emitStable();
1879
- }, $e)));
2131
+ }, et)));
1880
2132
  }
1881
2133
  reset() {
1882
2134
  this.rowChars.clear(), this.timer &&= (clearTimeout(this.timer), null), this.earlyAccepted = !1;
@@ -1955,7 +2207,7 @@ function rt(e, t) {
1955
2207
  let e = this.grid.get(i), a = [], o = [];
1956
2208
  for (let n = 0; n < t; n += 1) {
1957
2209
  let t = e?.get(n);
1958
- a.push(nt(t?.fg)), o.push(nt(t?.bg));
2210
+ a.push(rt(t?.fg)), o.push(rt(t?.bg));
1959
2211
  }
1960
2212
  n.push(a), r.push(o);
1961
2213
  }
@@ -1982,7 +2234,7 @@ function rt(e, t) {
1982
2234
  if (!e) return;
1983
2235
  Date.now() < s && console.log(`[rect] type=${e.type} pos=(${e.pos.x},${e.pos.y}) size=${e.size.width}x${e.size.height}`);
1984
2236
  let { cellW: r, cellH: i, offX: a, offY: o } = t.get("cellMetrics"), { cursor: c, cursorColor: l, selectionMode: u } = n.processRect(e);
1985
- l && nt(l) !== nt(t.get("cursorColor")) && t.set("cursorColor", l);
2237
+ l && rt(l) !== rt(t.get("cursorColor")) && t.set("cursorColor", l);
1986
2238
  let { x: d, y: f, w: p, h: m } = c, h = Math.round((d - a) / r), g = Math.round((f - o) / i), _ = t.get("cursorPos"), v = t.get("selectionMode");
1987
2239
  if (u) {
1988
2240
  let e = t.get("cursorRect");
@@ -2038,7 +2290,7 @@ function rt(e, t) {
2038
2290
  }
2039
2291
  //#endregion
2040
2292
  //#region src/engine/host/hostState.ts
2041
- var it = (e) => {
2293
+ var at = (e) => {
2042
2294
  let t = e.get("macroStatus");
2043
2295
  return {
2044
2296
  viewName: e.get("viewName"),
@@ -2060,13 +2312,13 @@ var it = (e) => {
2060
2312
  macroCurrentStep: t.currentStep,
2061
2313
  macroSequenceLength: t.sequenceLength
2062
2314
  };
2063
- }, at = (e, t, n) => {
2315
+ }, ot = (e, t, n) => {
2064
2316
  let r = e.get("cellMetrics");
2065
2317
  return {
2066
2318
  x: t * r.cellW + r.offX + r.cellW / 2,
2067
2319
  y: n * r.cellH + r.offY + r.cellH / 2
2068
2320
  };
2069
- }, ot = (e) => te({
2321
+ }, st = (e) => te({
2070
2322
  left: e.includes("left"),
2071
2323
  right: e.includes("right"),
2072
2324
  up: e.includes("up"),
@@ -2075,12 +2327,12 @@ var it = (e) => {
2075
2327
  play: e.includes("play"),
2076
2328
  opt: e.includes("opt"),
2077
2329
  edit: e.includes("edit")
2078
- }), st = ({ io: e, editors: t, setValueFloat: n, browseFile: r, navigateToView: i, navigateTo: a, enableMidiTransport: o, getScreenCellColors: s }) => {
2330
+ }), ct = ({ io: e, editors: t, setValueFloat: n, browseFile: r, navigateToView: i, navigateTo: a, enableMidiTransport: o, getScreenCellColors: s }) => {
2079
2331
  let { debugLog: c, hasBus: l, sendKeys: u, pressAndRelease: d, store: f, getScreenLines: p } = e, m = (e, t, n) => async (...r) => l() ? n(...r) : (console.warn(`[M8SDK] ${e}: no bus connection`), t);
2080
2332
  return {
2081
- navigateToView: m("navigateToView", !1, async (e) => (c("[M8SDK] Executing navigateToView:", e), await i(e), !0)),
2333
+ navigateToView: m("navigateToView", !1, async (e) => (c("[M8SDK] Executing navigateToView:", e), i(e))),
2082
2334
  navigateTo: m("navigateTo", void 0, async (e, t) => {
2083
- await a(at(f, e, t));
2335
+ await a(ot(f, e, t));
2084
2336
  }),
2085
2337
  setValueToHex: m("setValueToHex", !1, async (e) => (c("[M8SDK] Executing setValueToHex:", e), t.setValueToHex(e))),
2086
2338
  setValueToInt: m("setValueToInt", !1, async (e) => (c("[M8SDK] Executing setValueToInt:", e), t.setValueToInt(e))),
@@ -2096,15 +2348,15 @@ var it = (e) => {
2096
2348
  exact: t
2097
2349
  }), r(e, t))),
2098
2350
  sendKeyDown: m("sendKeyDown", void 0, async (e) => {
2099
- c("[M8SDK] Executing sendKeyDown:", e), u(ot(e));
2351
+ c("[M8SDK] Executing sendKeyDown:", e), u(st(e));
2100
2352
  }),
2101
2353
  sendKeyUp: m("sendKeyUp", void 0, async () => {
2102
2354
  c("[M8SDK] Executing sendKeyUp"), u(0);
2103
2355
  }),
2104
2356
  sendKeyPress: m("sendKeyPress", void 0, async (e) => {
2105
- c("[M8SDK] Executing sendKeyPress:", e), await d(ot(e));
2357
+ c("[M8SDK] Executing sendKeyPress:", e), await d(st(e));
2106
2358
  }),
2107
- getState: async () => it(f),
2359
+ getState: async () => at(f),
2108
2360
  getScreen: async () => ({
2109
2361
  width: 40,
2110
2362
  height: 24,
@@ -2118,7 +2370,7 @@ var it = (e) => {
2118
2370
  }),
2119
2371
  enableMidiTransport: async () => o()
2120
2372
  };
2121
- }, ct = 6, lt = (e) => {
2373
+ }, lt = 6, ut = (e) => {
2122
2374
  let t = null, n = 0, r = null, i = null, a = (e) => performance.timeOrigin + e, o = (t) => {
2123
2375
  let o = t.data;
2124
2376
  if (!o || o.length === 0) return;
@@ -2129,7 +2381,7 @@ var it = (e) => {
2129
2381
  let e = s - r;
2130
2382
  e > 0 && e < 200 && (i = i == null ? e : i * .9 + e * .1);
2131
2383
  }
2132
- r = s, n += 1, n % ct === 0 && e({
2384
+ r = s, n += 1, n % lt === 0 && e({
2133
2385
  kind: "clock",
2134
2386
  ticks: n,
2135
2387
  at: s,
@@ -2197,7 +2449,7 @@ var it = (e) => {
2197
2449
  t?.removeEventListener("midimessage", o), t = null;
2198
2450
  }
2199
2451
  };
2200
- }, ut = (e, t) => {
2452
+ }, dt = (e, t) => {
2201
2453
  let n = [], r = e, i = t;
2202
2454
  for (; r !== i;) {
2203
2455
  let e = i - r, t = Math.abs(e), a = e > 0 ? 1 : -1;
@@ -2210,7 +2462,7 @@ var it = (e) => {
2210
2462
  } else break;
2211
2463
  }
2212
2464
  return n;
2213
- }, dt = (e, t) => {
2465
+ }, ft = (e, t) => {
2214
2466
  let n = [], r = e;
2215
2467
  for (; r !== t;) {
2216
2468
  let e = t - r, i = e > 0 ? 1 : -1;
@@ -2224,13 +2476,13 @@ var it = (e) => {
2224
2476
  if (n.length > 2048) break;
2225
2477
  }
2226
2478
  return n;
2227
- }, ft = (e) => {
2479
+ }, pt = (e) => {
2228
2480
  let { store: t, debugLog: n, hasBus: r, sendKeys: i, pressAndRelease: a, waitForTextUnderCursorChange: o } = e;
2229
2481
  return {
2230
2482
  setValueToHex: async (e) => {
2231
2483
  if (!r()) return !1;
2232
2484
  e = Math.max(0, Math.min(255, e));
2233
- let s = t.get("textUnderCursor"), c = Ie(s), l = s?.trim() === "--";
2485
+ let s = t.get("textUnderCursor"), c = Le(s), l = s?.trim() === "--";
2234
2486
  if (c === null) return console.warn("[M8SDK] Could not parse current value under cursor:", s), !1;
2235
2487
  if (c === e && !l) return n("[M8SDK] Already at target value:", e.toString(16).padStart(2, "0").toUpperCase()), !0;
2236
2488
  n(`[M8SDK] Setting value from "${s?.trim() ?? "N/A"}" (${c.toString(16).padStart(2, "0").toUpperCase()}) to ${e.toString(16).padStart(2, "0").toUpperCase()}`);
@@ -2239,18 +2491,18 @@ var it = (e) => {
2239
2491
  n("[M8SDK] Initial value is \"--\", pressing edit to recall last value");
2240
2492
  let e = s;
2241
2493
  await a(I.Edit), await o(e, 250);
2242
- let r = t.get("textUnderCursor"), i = Ie(r);
2494
+ let r = t.get("textUnderCursor"), i = Le(r);
2243
2495
  if (n(`[M8SDK] After edit press, value is now: "${r?.trim() ?? "N/A"}" (${i?.toString(16).padStart(2, "0").toUpperCase() ?? "N/A"})`), i === null) return console.warn("[M8SDK] Failed to read value after edit press on \"--\""), await a(I.Edit), !1;
2244
2496
  u = i;
2245
2497
  } else {
2246
2498
  let e = s;
2247
2499
  await a(I.Edit), await o(e, 250);
2248
2500
  }
2249
- let d = ut(u, e);
2501
+ let d = dt(u, e);
2250
2502
  n(`[M8SDK] Precalculated ${d.length / 2} key presses from ${u.toString(16).padStart(2, "0").toUpperCase()} to ${e.toString(16).padStart(2, "0").toUpperCase()}`);
2251
- for (let e of d) i(e), await V(30);
2503
+ for (let e of d) i(e), await B(30);
2252
2504
  await o(s, 250);
2253
- let f = t.get("textUnderCursor"), p = Ie(f);
2505
+ let f = t.get("textUnderCursor"), p = Le(f);
2254
2506
  if (p !== e) {
2255
2507
  n("[M8SDK] Precomputation missed target, falling back to iterative mode");
2256
2508
  let r = p ?? u, i = Date.now();
@@ -2264,10 +2516,10 @@ var it = (e) => {
2264
2516
  await a(t);
2265
2517
  }
2266
2518
  await o(l, 250);
2267
- let u = Ie(t.get("textUnderCursor"));
2519
+ let u = Le(t.get("textUnderCursor"));
2268
2520
  u !== null && (r = u, n(`[M8SDK] Iterative: current value: ${r.toString(16).padStart(2, "0").toUpperCase()}`));
2269
2521
  }
2270
- p = Ie(t.get("textUnderCursor"));
2522
+ p = Le(t.get("textUnderCursor"));
2271
2523
  }
2272
2524
  await a(I.Edit);
2273
2525
  let m = p === e;
@@ -2276,7 +2528,7 @@ var it = (e) => {
2276
2528
  setValueToInt: async (e) => {
2277
2529
  if (!r()) return !1;
2278
2530
  e = Math.floor(e);
2279
- let n = t.get("textUnderCursor"), i = Le(n);
2531
+ let n = t.get("textUnderCursor"), i = Re(n);
2280
2532
  if (i === null) return console.warn("[M8SDK] Could not parse current integer value under cursor:", n), !1;
2281
2533
  if (i === e) return !0;
2282
2534
  await a(I.Edit), await o(n, 250);
@@ -2291,7 +2543,7 @@ var it = (e) => {
2291
2543
  await a(I.Edit | e, 35);
2292
2544
  }
2293
2545
  await o(d, 250);
2294
- let p = Le(t.get("textUnderCursor"));
2546
+ let p = Re(t.get("textUnderCursor"));
2295
2547
  if (p === null || p === s) break;
2296
2548
  let m = Math.abs(p - e);
2297
2549
  m < l && (l = m, c = p);
@@ -2311,12 +2563,12 @@ var it = (e) => {
2311
2563
  await a(I.Edit | e, 35);
2312
2564
  }
2313
2565
  await o(i, 250);
2314
- let l = Le(t.get("textUnderCursor"));
2566
+ let l = Re(t.get("textUnderCursor"));
2315
2567
  if (l === null || l === s) break;
2316
2568
  s = l;
2317
2569
  }
2318
2570
  }
2319
- return await a(I.Edit), Le(t.get("textUnderCursor")) === e;
2571
+ return await a(I.Edit), Re(t.get("textUnderCursor")) === e;
2320
2572
  },
2321
2573
  setNote: async (e) => {
2322
2574
  if (!r()) return !1;
@@ -2331,19 +2583,19 @@ var it = (e) => {
2331
2583
  let i = t.get("textUnderCursor")?.trim().toUpperCase();
2332
2584
  return n("[M8SDK] setNote special value result:", i), i === s;
2333
2585
  }
2334
- let c = W(e);
2586
+ let c = U(e);
2335
2587
  if (!c) return console.warn("[M8SDK] Invalid note format. Expected like C-1, C#1, D#A, ---, OFF:", e), !1;
2336
2588
  let l = t.get("textUnderCursor");
2337
2589
  (l?.trim() === "---" || l?.trim().toUpperCase() === "OFF") && (n("[M8SDK] Initial note is \"---\" or \"OFF\", priming with edit key"), await a(I.Edit), await o(l, 250), l = t.get("textUnderCursor"));
2338
- let u = W(l);
2590
+ let u = U(l);
2339
2591
  if (!u) return console.warn("[M8SDK] Could not parse current note under cursor:", l), !1;
2340
2592
  if (u.semitoneIndex === c.semitoneIndex) return !0;
2341
2593
  await a(I.Edit), await o(l, 250);
2342
- let d = dt(u.semitoneIndex, c.semitoneIndex);
2594
+ let d = ft(u.semitoneIndex, c.semitoneIndex);
2343
2595
  if (d.length > 0) {
2344
- for (let e of d) i(e), await V(25);
2596
+ for (let e of d) i(e), await B(25);
2345
2597
  await o(l, 280);
2346
- let e = W(t.get("textUnderCursor"));
2598
+ let e = U(t.get("textUnderCursor"));
2347
2599
  if (e?.semitoneIndex === c.semitoneIndex) return await a(I.Edit), !0;
2348
2600
  u = e ?? u;
2349
2601
  }
@@ -2355,19 +2607,19 @@ var it = (e) => {
2355
2607
  if (e === 0) break;
2356
2608
  let n = Math.abs(e) >= 12, r = e > 0, i = n ? r ? I.Up : I.Down : r ? I.Right : I.Left, s = n ? r ? I.Down : I.Up : r ? I.Left : I.Right, l = u, d = Math.abs(l.semitoneIndex - c.semitoneIndex), p = t.get("textUnderCursor");
2357
2609
  await a(I.Edit | i, 35), await o(p, 250);
2358
- let m = W(t.get("textUnderCursor"));
2610
+ let m = U(t.get("textUnderCursor"));
2359
2611
  if (!m || m.label === l.label) break;
2360
2612
  if (u = m, Math.abs(u.semitoneIndex - c.semitoneIndex) > d) {
2361
2613
  let e = t.get("textUnderCursor");
2362
2614
  await a(I.Edit | s, 35), await o(e, 250);
2363
- let n = W(t.get("textUnderCursor"));
2615
+ let n = U(t.get("textUnderCursor"));
2364
2616
  n && (u = n);
2365
2617
  break;
2366
2618
  }
2367
2619
  if (f.has(u.label)) break;
2368
2620
  f.add(u.label);
2369
2621
  }
2370
- return await a(I.Edit), W(t.get("textUnderCursor"))?.semitoneIndex === c.semitoneIndex;
2622
+ return await a(I.Edit), U(t.get("textUnderCursor"))?.semitoneIndex === c.semitoneIndex;
2371
2623
  },
2372
2624
  setValueToString: async (e, i = !0, s = !1) => {
2373
2625
  if (!r()) return !1;
@@ -2416,7 +2668,7 @@ var it = (e) => {
2416
2668
  return await a(I.Edit), !1;
2417
2669
  }
2418
2670
  };
2419
- }, pt = ({ bus: e, store: t, cancelOnKeydown: n = !1 }) => {
2671
+ }, mt = ({ bus: e, store: t, cancelOnKeydown: n = !1 }) => {
2420
2672
  let r = [], i = null, a = !1, o = () => {
2421
2673
  a && u("preempted by keyboard");
2422
2674
  }, s = () => {
@@ -2442,8 +2694,8 @@ var it = (e) => {
2442
2694
  });
2443
2695
  } else c(), i = null, t.set("macroStatus", { running: !1 });
2444
2696
  }
2445
- function u(e) {
2446
- (a || r.length !== 0 || t.get("macroStatus").running) && (r = [], i = null, c(), t.set("macroStatus", { running: !1 }));
2697
+ function u(n) {
2698
+ (a || r.length !== 0 || t.get("macroStatus").running) && (i && e.commands.sendKeys(0), r = [], i = null, c(), t.set("macroStatus", { running: !1 }));
2447
2699
  }
2448
2700
  return {
2449
2701
  start: (n) => {
@@ -2464,7 +2716,7 @@ var it = (e) => {
2464
2716
  r = [], i = null, c();
2465
2717
  }
2466
2718
  };
2467
- }, mt = () => ({
2719
+ }, ht = () => ({
2468
2720
  viewName: null,
2469
2721
  viewTitle: null,
2470
2722
  minimapKey: null,
@@ -2487,9 +2739,9 @@ var it = (e) => {
2487
2739
  },
2488
2740
  systemInfo: null,
2489
2741
  macroStatus: { running: !1 }
2490
- }), ht = (e = {}) => {
2742
+ }), gt = (e = {}) => {
2491
2743
  let t = {
2492
- ...mt(),
2744
+ ...ht(),
2493
2745
  ...e
2494
2746
  }, n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set();
2495
2747
  return {
@@ -2512,8 +2764,8 @@ var it = (e) => {
2512
2764
  }),
2513
2765
  snapshot: () => ({ ...t })
2514
2766
  };
2515
- }, gt = () => 0, _t = () => 1;
2516
- function vt(e, t, n, r = gt, i = _t) {
2767
+ }, _t = () => 0, vt = () => 1;
2768
+ function yt(e, t, n, r = _t, i = vt) {
2517
2769
  let a = t.trim(), o = n.trim();
2518
2770
  if (!e.has(a) || !e.has(o)) return null;
2519
2771
  let s = /* @__PURE__ */ new Set([a]), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
@@ -2535,12 +2787,13 @@ function vt(e, t, n, r = gt, i = _t) {
2535
2787
  for (; n !== a;) {
2536
2788
  let r = c.get(n);
2537
2789
  if (!r) break;
2538
- t.unshift(r.viaDir), e.unshift(r.prev), n = r.prev;
2790
+ t.unshift(r.via), e.unshift(r.prev), n = r.prev;
2539
2791
  }
2540
2792
  return {
2541
2793
  pages: e,
2542
- directions: t,
2543
- cost: t.length
2794
+ directions: t.map((e) => e.dir),
2795
+ edges: t,
2796
+ cost: l.get(o) ?? t.length
2544
2797
  };
2545
2798
  }
2546
2799
  s.delete(t);
@@ -2549,7 +2802,7 @@ function vt(e, t, n, r = gt, i = _t) {
2549
2802
  let n = e.weight ?? i(t, e.to, e.dir), a = (l.get(t) ?? Infinity) + n;
2550
2803
  a < (l.get(e.to) ?? Infinity) && (c.set(e.to, {
2551
2804
  prev: t,
2552
- viaDir: e.dir
2805
+ via: e
2553
2806
  }), l.set(e.to, a), u.set(e.to, a + r(e.to, o)), s.has(e.to) || s.add(e.to));
2554
2807
  }
2555
2808
  }
@@ -2557,7 +2810,7 @@ function vt(e, t, n, r = gt, i = _t) {
2557
2810
  }
2558
2811
  //#endregion
2559
2812
  //#region src/engine/viewNavigation.ts
2560
- var yt = 20, bt = (e, t) => {
2813
+ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2561
2814
  let n = !!t?.shift, r = I.Shift, i = {
2562
2815
  up: (n ? r : 0) | I.Up,
2563
2816
  down: (n ? r : 0) | I.Down,
@@ -2567,96 +2820,203 @@ var yt = 20, bt = (e, t) => {
2567
2820
  n && a.push(r);
2568
2821
  for (let t of e) a.push(i[t], n ? r : 0);
2569
2822
  return a.push(0), a;
2570
- }, xt = ({ store: e, runner: t }) => {
2571
- let n = Qe(), r = null, i = 0, a = null, o = (e) => {
2572
- let t = (e || "").toLowerCase();
2573
- if (n.has(t)) return t;
2574
- for (let e of n) if (e.toLowerCase() === t) return e;
2575
- return console.warn("Target not in viewlist", { goal: t }), t;
2576
- }, s = (e) => {
2577
- let t = (e || "").toLowerCase(), r = "";
2578
- for (let e of n) t.startsWith(e) && e.length > r.length && (r = e);
2579
- return r;
2580
- }, c = (t) => {
2581
- let { graph: n } = Ze(), r = e.get("viewName") || "", i = [], a = [];
2582
- for (let e of n.keys()) {
2583
- let n = s(e);
2584
- n && (n === r && i.push(e), n === t && a.push(e));
2823
+ }, Et = (e) => [{
2824
+ from: e,
2825
+ to: "",
2826
+ kind: "exit",
2827
+ keys: [I.Opt, 0]
2828
+ }, {
2829
+ from: e,
2830
+ to: "",
2831
+ kind: "shift",
2832
+ dir: "up",
2833
+ keys: [I.Shift | I.Up, 0]
2834
+ }], Dt = (e) => `${e.kind}:${e.dir ?? ""}:${e.keys.join(",")}:${e.cursor ? `${e.cursor.x},${e.cursor.y}` : ""}`, Ot = (e) => e[e.length - 1] === 0 ? e.slice() : [...e, 0], kt = "#goal", At = (e, t) => `${e}<${t}`, jt = (e) => new Promise((t) => setTimeout(t, e)), Mt = ({ store: e, runner: t, getScreenLines: n, moveCursor: r }) => {
2835
+ let i = $e(), a = null, o = 0, s = null, c = 0, l = /* @__PURE__ */ new Map(), u = "", d = e.get("viewName") || "", f = e.sub("viewName", () => {
2836
+ let t = e.get("viewName") || "";
2837
+ t && t !== d && (d && (u = d), d = t);
2838
+ }), p = null, m = null, h = () => {
2839
+ m && clearTimeout(m), m = null;
2840
+ }, g = (e) => {
2841
+ let t = (e || "").toLowerCase().trim();
2842
+ return i.has(t) || console.warn("Target not in viewlist", { goal: t }), t;
2843
+ }, _ = (e, t) => `${e.from}<${t}|${Dt(e)}`, v = (e, t) => {
2844
+ let n = /* @__PURE__ */ new Map();
2845
+ for (let t of Qe().graph.get(e) ?? []) {
2846
+ if (t.kind === "button" && !r) continue;
2847
+ let e = Dt(t);
2848
+ n.set(e, [...n.get(e) ?? [], t]);
2585
2849
  }
2586
- if (!a.length) return null;
2587
- if (!i.length) return {
2588
- pages: ["unknown", "fallback"],
2589
- directions: ["up"],
2590
- cost: 1
2591
- };
2592
- let o = null;
2593
- for (let e of i) for (let t of a) {
2594
- let r = vt(n, e, t);
2595
- r && (!o || r.cost < o.cost) && (o = r);
2850
+ let i = [];
2851
+ for (let r of n.values()) {
2852
+ if (l.get(_(r[0], "*")) === e) continue;
2853
+ let n = l.get(_(r[0], t));
2854
+ if (n !== void 0) {
2855
+ n !== e && i.push({
2856
+ ...r[0],
2857
+ to: n
2858
+ });
2859
+ continue;
2860
+ }
2861
+ let a = r.find((e) => e.back && e.to === t);
2862
+ if (a) {
2863
+ i.push(a);
2864
+ continue;
2865
+ }
2866
+ let o = r.filter((e) => !e.back);
2867
+ i.push(...o.length ? o : r);
2596
2868
  }
2597
- return o;
2598
- }, l = (e, t, n) => {
2599
- let r = Ze().edgeKeys.get(e.toLowerCase())?.get(t);
2600
- return r?.length ? r[r.length - 1] === 0 ? r.slice() : [...r, 0] : bt([n], { shift: !0 });
2601
- }, u = () => {
2602
- if (r = null, i = 0, a) {
2603
- let e = a;
2604
- a = null, e();
2869
+ return i;
2870
+ }, y = (e, t, n) => {
2871
+ let r = /* @__PURE__ */ new Map([[kt, []]]), i = [[e, t]];
2872
+ for (r.set(At(e, t), []); i.length;) {
2873
+ let [e, t] = i.shift(), a = r.get(At(e, t));
2874
+ if (e === n) a.push({
2875
+ to: kt,
2876
+ weight: 0,
2877
+ nav: {
2878
+ from: e,
2879
+ to: e,
2880
+ kind: "shift",
2881
+ keys: []
2882
+ }
2883
+ });
2884
+ else for (let n of v(e, t)) {
2885
+ let t = At(n.to, e);
2886
+ a.push({
2887
+ to: t,
2888
+ weight: n.weight,
2889
+ nav: n
2890
+ }), r.has(t) || (r.set(t, []), i.push([n.to, e]));
2891
+ }
2892
+ }
2893
+ return r;
2894
+ }, b = (e, t) => {
2895
+ let n = yt(y(e, u, t), At(e, u), kt);
2896
+ if (n?.edges.length) return n.edges[0].nav;
2897
+ let r = Qe().graph.get(e) ?? [];
2898
+ return r.length && !r.some((e) => e.kind === "exit") ? null : Et(e).find((e) => !l.has(_(e, u)) && !l.has(_(e, "*"))) ?? null;
2899
+ }, x = (e) => {
2900
+ let t = e.cursor;
2901
+ if (!e.label || !n) return t;
2902
+ let r = null, i = n();
2903
+ for (let n = 4; n < i.length; n++) {
2904
+ let a = (i[n] ?? "").slice(0, 33);
2905
+ for (let i = a.indexOf(e.label); i >= 0; i = a.indexOf(e.label, i + 1)) {
2906
+ let e = Math.hypot(i - t.x, (n - t.y) * 1.5);
2907
+ (!r || e < r.d) && (r = {
2908
+ x: i,
2909
+ y: n,
2910
+ d: e
2911
+ });
2912
+ }
2605
2913
  }
2606
- }, d = () => {
2607
- let n = r, a = e.get("viewName") || "";
2608
- if (!n || !a) {
2609
- u();
2914
+ return r ? {
2915
+ x: r.x,
2916
+ y: r.y
2917
+ } : t;
2918
+ }, S = (t) => {
2919
+ let n = e.get("cursorPos"), r = e.get("cursorRect");
2920
+ if (!n || n.y !== t.y) return !1;
2921
+ let i = r ? Math.max(1, Math.round(r.w / e.get("cellMetrics").cellW)) : 1;
2922
+ return t.x >= n.x && t.x < n.x + i;
2923
+ }, C = (e) => {
2924
+ if (a = null, o = 0, p = null, h(), s) {
2925
+ let t = s;
2926
+ s = null, t(e);
2927
+ }
2928
+ }, w = (e) => {
2929
+ if (!p) return;
2930
+ let { edge: t, from: n, prev: r } = p, i = !!e && e !== n;
2931
+ l.set(_(t, i ? r : "*"), i ? e : n), p = null, h(), e && e !== n && t.to && e !== t.to ? console.log("Navigation step landed elsewhere", {
2932
+ from: n,
2933
+ expected: t.to,
2934
+ reached: e
2935
+ }) : (!e || e === n) && console.log("Navigation step blocked", {
2936
+ from: n,
2937
+ to: t.to || "?",
2938
+ kind: t.kind,
2939
+ dir: t.dir
2940
+ }), E();
2941
+ }, T = async (n, i) => {
2942
+ let a = x(n.edge);
2943
+ S(a) || (await Promise.race([r(a.x, a.y), jt(Ct)]), await jt(wt)), i === c && p === n && (e.get("viewName") !== n.from || !S(a) ? (console.warn("Navigation: button not reached", {
2944
+ from: n.from,
2945
+ to: n.edge.to,
2946
+ cell: a,
2947
+ cursor: e.get("cursorPos")
2948
+ }), w(e.get("viewName") === n.from ? n.from : e.get("viewName") || "")) : (n.phase = "keys", t.cancel("navigation button"), t.start(Ot(n.edge.keys))));
2949
+ }, E = () => {
2950
+ let n = a, r = e.get("viewName") || "";
2951
+ if (!n) return;
2952
+ if (!r) {
2953
+ console.warn("Navigation aborted: no view name (a dialog?)", { goal: n }), C(!1);
2610
2954
  return;
2611
2955
  }
2612
- if (a === n) {
2956
+ if (r === n) {
2613
2957
  console.log("Navigation complete:", {
2614
2958
  goal: n,
2615
- steps: i
2616
- }), u();
2959
+ steps: o
2960
+ }), C(!0);
2617
2961
  return;
2618
2962
  }
2619
- if (i >= yt) {
2963
+ if (o >= bt) {
2620
2964
  console.warn("Navigation aborted: max steps reached", {
2621
- startTitle: a,
2965
+ startTitle: r,
2622
2966
  goalTitle: n,
2623
- steps: i
2624
- }), u();
2967
+ steps: o
2968
+ }), C(!1);
2625
2969
  return;
2626
2970
  }
2627
- let o = c(n);
2628
- if (!o) console.warn("No path found (iterative)", {
2629
- startTitle: a,
2630
- goalTitle: n,
2631
- step: i
2632
- }), u();
2633
- else if (o.pages.length > 1) {
2634
- let e = o.pages[0], r = o.pages[1], c = o.directions[0], u = l(e, r, c);
2635
- i += 1, console.log("Navigation step", {
2636
- step: i,
2637
- from: a,
2638
- to: s(r),
2639
- dir: c,
2640
- goal: n,
2641
- frames: u
2642
- }), t.cancel("iterative navigation step"), t.start(u);
2971
+ let i = b(r, n);
2972
+ if (!i) {
2973
+ console.warn("No path found", {
2974
+ startTitle: r,
2975
+ goalTitle: n,
2976
+ step: o
2977
+ }), C(!1);
2978
+ return;
2979
+ }
2980
+ o += 1, console.log("Navigation step", {
2981
+ step: o,
2982
+ from: r,
2983
+ to: i.to || "?",
2984
+ kind: i.kind,
2985
+ dir: i.dir,
2986
+ goal: n
2987
+ });
2988
+ let s = {
2989
+ from: r,
2990
+ prev: u,
2991
+ edge: i,
2992
+ phase: i.kind === "button" ? "cursor" : "keys"
2993
+ };
2994
+ if (p = s, i.kind === "button") {
2995
+ T(s, c);
2996
+ return;
2643
2997
  }
2644
- }, f = () => {
2645
- r && e.get("viewName") && !e.get("macroStatus").running && d();
2646
- }, p = e.sub("viewName", f), m = e.sub("macroStatus", f);
2998
+ let l = i.keys.length ? Ot(i.keys) : Tt([i.dir], { shift: !0 });
2999
+ t.cancel("iterative navigation step"), t.start(l);
3000
+ }, D = () => {
3001
+ if (p?.phase !== "keys") return;
3002
+ let n = e.get("macroStatus").running, r = e.get("viewName") || "", i = !!r && r !== p.from;
3003
+ (!n || i) && (h(), m = n ? setTimeout(() => {
3004
+ t.cancel("navigation: no release echo"), t.start([0]), w(e.get("viewName") || "");
3005
+ }, xt) : setTimeout(() => w(e.get("viewName") || ""), i ? St : xt));
3006
+ }, O = e.sub("viewName", D), k = e.sub("macroStatus", D);
2647
3007
  return {
2648
3008
  navigateToView: (t) => new Promise((n) => {
2649
- let s = o(t);
2650
- s ? (a?.(), a = n, r = s, i = 0, console.log("Starting iterative navigation", {
2651
- goal: s,
3009
+ let r = g(t);
3010
+ r ? (C(!1), c += 1, l = /* @__PURE__ */ new Map(), s = n, a = r, o = 0, console.log("Starting iterative navigation", {
3011
+ goal: r,
2652
3012
  from: e.get("viewName") || "unknown"
2653
- }), d()) : n();
3013
+ }), E()) : n(!1);
2654
3014
  }),
2655
3015
  dispose: () => {
2656
- p(), m(), u();
3016
+ f(), O(), k(), c += 1, C(!1);
2657
3017
  }
2658
3018
  };
2659
- }, St = /* @__PURE__ */ new Set([
3019
+ }, Nt = /* @__PURE__ */ new Set([
2660
3020
  "macroStatus",
2661
3021
  "deviceModel",
2662
3022
  "fontMode",
@@ -2664,31 +3024,33 @@ var yt = 20, bt = (e, t) => {
2664
3024
  "titleColor",
2665
3025
  "backgroundColor",
2666
3026
  "minimapKey"
2667
- ]), Ct = (e, t = {}) => {
2668
- let n = t.store ?? ht(), r = (...e) => {
3027
+ ]), Pt = (e, t = {}) => {
3028
+ let n = t.store ?? gt(), r = (...e) => {
2669
3029
  t.debug && console.log(...e);
2670
3030
  }, i = /* @__PURE__ */ new Map(), a = (e, t) => {
2671
3031
  for (let n of [...i.get(e) ?? []]) n(t);
2672
- }, o = rt(e.protocol.eventBus, n), s = pt({
3032
+ }, o = it(e.protocol.eventBus, n), s = mt({
2673
3033
  bus: e,
2674
3034
  store: n,
2675
3035
  cancelOnKeydown: t.cancelMacroOnKeydown
2676
3036
  }), c = pe({
2677
3037
  bus: e,
2678
3038
  store: n
2679
- }), l = xt({
3039
+ }), l = Mt({
2680
3040
  store: n,
2681
- runner: s
2682
- }), u = lt((e) => a("transport", e)), d = Oe({
3041
+ runner: s,
3042
+ getScreenLines: o.getScreenLines,
3043
+ moveCursor: (e, t) => c.navigateTo(ot(n, e, t))
3044
+ }), u = ut((e) => a("transport", e)), d = ke({
2683
3045
  getKeySink: () => e.commands,
2684
3046
  store: n,
2685
3047
  debugLog: r,
2686
3048
  getScreenLines: o.getScreenLines
2687
- }), f = st({
3049
+ }), f = ct({
2688
3050
  io: d,
2689
- editors: ft(d),
2690
- setValueFloat: Ge(d),
2691
- browseFile: Fe(d),
3051
+ editors: pt(d),
3052
+ setValueFloat: Ke(d),
3053
+ browseFile: Ie(d),
2692
3054
  navigateToView: (e) => l.navigateToView(e),
2693
3055
  navigateTo: (e) => c.navigateTo(e),
2694
3056
  enableMidiTransport: () => u.enable(),
@@ -2718,7 +3080,7 @@ var yt = 20, bt = (e, t) => {
2718
3080
  };
2719
3081
  (!_ || _.textUnderCursor !== e.textUnderCursor || _.currentLine !== e.currentLine) && (_ = e, r("[M8SDK] Text updated:", e.textUnderCursor, e.currentLine), a("textUpdated", e));
2720
3082
  }
2721
- [...e].some((e) => St.has(e)) && a("stateChanged", it(n));
3083
+ [...e].some((e) => Nt.has(e)) && a("stateChanged", at(n));
2722
3084
  }, y = n.subAll((e) => {
2723
3085
  p.add(e), m || (m = !0, queueMicrotask(v));
2724
3086
  }), b = (e) => {
@@ -2731,7 +3093,7 @@ var yt = 20, bt = (e, t) => {
2731
3093
  macro: s,
2732
3094
  navigateTo: (e) => c.navigateTo(e),
2733
3095
  navigateToView: (e) => l.navigateToView(e),
2734
- getState: () => it(n),
3096
+ getState: () => at(n),
2735
3097
  getScreenLines: o.getScreenLines,
2736
3098
  getScreenCellColors: o.getScreenCellColors,
2737
3099
  on: (e, t) => {
@@ -2744,7 +3106,7 @@ var yt = 20, bt = (e, t) => {
2744
3106
  e.protocol.eventBus.off("key", b), y(), l.dispose(), c.dispose(), s.dispose(), u.disable(), o.dispose(), i.clear();
2745
3107
  }
2746
3108
  };
2747
- }, G = {
3109
+ }, W = {
2748
3110
  version: "1.0",
2749
3111
  description: "Semantic mapping of M8 tracker views: defines the structure of each grid view so cursor position + line text can be interpreted as typed field values.",
2750
3112
  notes: {
@@ -2911,17 +3273,17 @@ var yt = 20, bt = (e, t) => {
2911
3273
  },
2912
3274
  relativeCommands: { description: "Relative commands add to the instrument parameter's current value (01-7F add, 80-FF subtract) and keep accumulating until a note is triggered with a RET command or with an instrument number in the I column; that resets every parameter to the instrument's own value." }
2913
3275
  }
2914
- }, wt = {
3276
+ }, Ft = {
2915
3277
  version: "1.0",
2916
3278
  description: "Layouts of the non-grid ('parameter') views: which text at which column is an instrument type, name, parameter, option or action. Column positions were measured on the manual's screenshots (font mode 0, 40 columns) and cross-checked with the phrase view grid; descriptions come from the manual.",
2917
3279
  notes: {
2918
3280
  rows: "A row is recognised by 'match' conditions on the line text (x = column, text = exact text, regex = pattern on the value.width chars). The first matching row wins; its cells give the field columns.",
2919
3281
  zones: "Views whose labels change (instrument parameters depend on the instrument type, modulation slots on the modulation type) use two label/value zones instead: the label text is read at labelX and the value at x. The label is then looked up in the vocabulary.",
2920
- formats: "hex = 2 hex digits; hexOption = 2 hex digits followed by the option name ('00CHORUS'); hexPair = 'XX:YY'; decimal / decimalPair = base-10 numbers ('120', '150:45'); float = decimal with a fraction ('-05.00'); text = free text or option name; auto = detect from the text.",
3282
+ formats: "hex = 2 hex digits; hexOption = 2 hex digits followed by the option name ('00CHORUS'); hexPair = 'XX:YY' (or 'XX/YY'); decimal / decimalPair = base-10 numbers ('120', '150:45'); float = decimal with a fraction ('-05.00'); text = free text or option name; auto = detect from the text.",
2921
3283
  unverified: "Positions are read off the manual's screenshots, not from a device capture. Instrument-type specific layouts other than the Wavsynth header/parameter grid (Sampler SAMPLE row, FM operators, Hypersynth chord, MIDI CC rows) are only reached through the generic label/value zones."
2922
3284
  },
2923
- views: /* @__PURE__ */ JSON.parse("{\"project\":{\"rows\":[{\"id\":\"tempo\",\"match\":[{\"x\":0,\"text\":\"TEMPO\"}],\"cells\":[{\"x\":13,\"width\":6,\"key\":\"tempo\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" BPM\",\"label\":\"Tempo (TEMPO)\",\"description\":\"Song speed in beats per minute. [EDIT] pressed rhythmically sets the tempo by tapping.\"}]},{\"id\":\"transpose\",\"match\":[{\"x\":0,\"text\":\"TRANSPOSE\"}],\"cells\":[{\"x\":13,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"format\":\"hex\",\"label\":\"Project Transpose (TRANSPOSE)\",\"description\":\"Transposes the whole song in semitones (same as the TSP command). Only affects instruments with TRANSP. enabled; applied when [EDIT] is released.\"}]},{\"id\":\"groove\",\"match\":[{\"x\":0,\"text\":\"GROOVE\"}],\"cells\":[{\"x\":13,\"width\":14,\"key\":\"groove\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Default Groove (GROOVE)\",\"description\":\"Groove used when the song plays (number and name); applied when [EDIT] is released. See Groove View.\"}]},{\"id\":\"scale\",\"match\":[{\"x\":0,\"text\":\"SCALE\"}],\"cells\":[{\"x\":13,\"width\":16,\"key\":\"scale\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Default Scale (SCALE)\",\"description\":\"Scale (number, key and name) used when the song plays; applied when [EDIT] is released. See Scale View.\"}]},{\"id\":\"liveQuantize\",\"match\":[{\"x\":0,\"text\":\"LIVE QUANTIZ\"}],\"cells\":[{\"x\":13,\"width\":14,\"key\":\"liveQuantize\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Live Quantize (LIVE QUANTIZE)\",\"description\":\"How long a cued chain waits before starting when playing in live mode.\"}]},{\"id\":\"midi\",\"match\":[{\"x\":0,\"text\":\"MIDI\"},{\"x\":13,\"text\":\"SETTINGS\"}],\"cells\":[{\"x\":13,\"width\":8,\"key\":\"midiSettings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"MIDI Settings\",\"description\":\"Open the MIDI Settings View.\"},{\"x\":22,\"width\":8,\"key\":\"midiMappings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"MIDI Mappings\",\"description\":\"Open the MIDI Mappings View.\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":13,\"width\":12,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Song Name (NAME)\",\"description\":\"Name of the current song, entered before saving.\"}]},{\"id\":\"file\",\"match\":[{\"x\":0,\"text\":\"PROJECT\"},{\"x\":13,\"text\":\"LOAD\"}],\"cells\":[{\"x\":13,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Song\",\"description\":\"Load a song from the SD card.\"},{\"x\":18,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Song\",\"description\":\"Save the current song.\"},{\"x\":23,\"width\":3,\"key\":\"new\",\"type\":\"action\",\"format\":\"text\",\"label\":\"New Song\",\"description\":\"Create a new song.\"}]},{\"id\":\"export\",\"match\":[{\"x\":0,\"text\":\"EXPORT/SHARE\"}],\"cells\":[{\"x\":13,\"width\":6,\"key\":\"render\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Render\",\"description\":\"Open the Render View to render audio.\"},{\"x\":20,\"width\":6,\"key\":\"bundle\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Bundle\",\"description\":\"Bundle the song with the instruments and samples it uses.\"}]},{\"id\":\"clearUnused\",\"match\":[{\"x\":0,\"text\":\"CLEAR UNUSED\"}],\"cells\":[{\"x\":13,\"width\":7,\"key\":\"clearPhrases\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Clear Unused Phrases\",\"description\":\"Frees chains and phrases that are not used in the song.\"},{\"x\":21,\"width\":8,\"key\":\"clearInstTbl\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Clear Unused Instruments/Tables\",\"description\":\"Frees instruments, EQs and tables that are not used in the song.\"}]},{\"id\":\"instPool\",\"match\":[{\"x\":0,\"text\":\"INST. POOL\"}],\"cells\":[{\"x\":13,\"width\":15,\"key\":\"viewInstPool\",\"type\":\"action\",\"format\":\"text\",\"label\":\"View Instrument Pool\",\"description\":\"Go to the Instrument Pool View.\"}]},{\"id\":\"timeStats\",\"match\":[{\"x\":0,\"text\":\"TIME STATS\"}],\"cells\":[{\"x\":13,\"width\":15,\"key\":\"viewTimeStats\",\"type\":\"action\",\"format\":\"text\",\"label\":\"View Time Stats\",\"description\":\"Open the Time Stats View (time spent on the song).\"}]},{\"id\":\"system\",\"match\":[{\"x\":0,\"text\":\"SYSTEM\"},{\"x\":13,\"text\":\"SETTINGS\"}],\"cells\":[{\"x\":13,\"width\":8,\"key\":\"systemSettings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"System Settings\",\"description\":\"Go to the System Settings View.\"},{\"x\":22,\"width\":9,\"key\":\"usbDrive\",\"type\":\"action\",\"format\":\"text\",\"label\":\"USB Drive\",\"description\":\"Puts the M8 in USB drive mode so SD card files can be transferred to a computer.\"}]}]},\"systemsettings\":{\"rows\":[{\"id\":\"backlight\",\"match\":[{\"x\":0,\"text\":\"BACKLIGHT\"}],\"cells\":[{\"x\":15,\"width\":2,\"key\":\"backlight\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Backlight (BACKLIGHT)\",\"description\":\"Backlight LED brightness of the display.\"}]},{\"id\":\"fontOptions\",\"match\":[{\"x\":0,\"text\":\"FONT OPTIONS\"}],\"cells\":[{\"x\":15,\"width\":15,\"key\":\"fontOptions\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Font Options (FONT OPTIONS)\",\"description\":\"Font settings, including a large font mode for readability.\"}]},{\"id\":\"theme\",\"match\":[{\"x\":0,\"text\":\"THEME\"}],\"cells\":[{\"x\":15,\"width\":10,\"key\":\"editTheme\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Theme (EDIT THEME)\",\"description\":\"Open the Theme View.\"}]},{\"id\":\"notePreview\",\"match\":[{\"x\":0,\"text\":\"NOTE PREVIEW\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"notePreview\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Note Preview (NOTE PREVIEW)\",\"description\":\"Plays the note when editing notes in the phrase view.\"}]},{\"id\":\"recCountIn\",\"match\":[{\"x\":0,\"regex\":\"^REC\\\\.\\\\s?(COUNT IN|METRONOME)\\\\s*$\",\"width\":13}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"recCountIn\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Recording Count-in (REC. COUNT IN)\",\"description\":\"Number of phrases/measures of metronome count-in before recording audio in the Sample Editor or Selection to Sample.\"}]},{\"id\":\"metronomeVol\",\"match\":[{\"x\":0,\"text\":\"METRONOME VOL\"}],\"cells\":[{\"x\":15,\"width\":2,\"key\":\"metronomeVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Metronome Volume (METRONOME VOL)\",\"description\":\"Volume of the metronome sound (edit /System/Metronome.m8i to change the sound).\"}]},{\"id\":\"songTemplate\",\"match\":[{\"x\":0,\"text\":\"SONG TEMPLATE\"}],\"cells\":[{\"x\":15,\"width\":4,\"key\":\"songTemplateSave\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Song Template (SAVE)\",\"description\":\"Use the currently opened song as the template for new songs.\"},{\"x\":20,\"width\":5,\"key\":\"songTemplateClear\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Song Template (CLEAR)\",\"description\":\"Clear the song template.\"}]},{\"id\":\"usbAudioMode\",\"match\":[{\"x\":0,\"text\":\"USB AUDIO MODE\"}],\"cells\":[{\"x\":15,\"width\":14,\"key\":\"usbAudioMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"USB Audio Mode (USB AUDIO MODE)\",\"description\":\"USB output as stereo or multi-channel (multi-channel sends all 8 tracks and the effects individually, 12 stereo channels).\",\"options\":{\"STEREO OUT\":\"Stereo mix over USB.\"}}]},{\"id\":\"usbMainOut\",\"match\":[{\"x\":0,\"text\":\"USB MAIN OUT\"}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"usbMainOut\",\"type\":\"option\",\"format\":\"text\",\"label\":\"USB Main Out (USB MAIN OUT)\",\"description\":\"Routing behaviour of USB audio (see the USB audio routing options).\"}]},{\"id\":\"lineInGate\",\"match\":[{\"x\":0,\"text\":\"LINE-IN GATE\"}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"lineInGate\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Line-in Gate (LINE-IN GATE)\",\"description\":\"Noise gate threshold for the line input, adjustable from -70 dB to -10 dB (OFF disables it).\"}]},{\"id\":\"splashScreen\",\"match\":[{\"x\":0,\"text\":\"SPLASH SCREEN\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"splashScreen\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Splash Screen (SPLASH SCREEN)\",\"description\":\"Show the M8 logo and play the start-up sound when turning on the device.\"}]},{\"id\":\"hpProtection\",\"match\":[{\"x\":0,\"text\":\"HP PROTECTION\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"hpProtection\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Headphone Protection (HP PROTECTION)\",\"description\":\"Limits the headphone output volume to F0 when a pack is unplugged and re-inserted.\"}]},{\"id\":\"keyDelayRep\",\"match\":[{\"x\":0,\"text\":\"KEY DELAY:REP\"}],\"cells\":[{\"x\":15,\"width\":7,\"key\":\"keyDelayRep\",\"type\":\"parameter\",\"format\":\"decimalPair\",\"unit\":\" ms\",\"parts\":[\"delay\",\"repeat\"],\"label\":\"Key Delay:Repeat (KEY DELAY:REP)\",\"description\":\"Key repeat timing in milliseconds: delay before a held key starts repeating, then the time between repeats.\"}]},{\"id\":\"battCharging\",\"match\":[{\"x\":0,\"regex\":\"^BATT\\\\.\\\\s?(CHARGING|STATUS)\\\\s*$\",\"width\":14}],\"cells\":[{\"x\":15,\"width\":14,\"key\":\"battCharging\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Battery Charging (BATT. CHARGING / STATUS)\",\"description\":\"Whether the battery charges while the M8 is on and USB is connected (turn off to reduce power drawn from the host); also shows the current charging status.\"}]}]},\"effectsettings\":{\"sections\":[\"MODFX\",\"DELAY\",\"REVERB\"],\"rows\":[{\"id\":\"modfxType\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"MOD TYPE\"}],\"cells\":[{\"x\":21,\"width\":10,\"key\":\"modfxType\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"ModFX Type (MOD TYPE)\",\"description\":\"ModFX effect: Chorus, Phaser or Flanger (also sets the phase position).\",\"options\":{\"CHORUS\":\"Stereo chorus.\",\"PHASER\":\"Stereo phaser.\",\"FLANGER\":\"Stereo flanger.\"}}]},{\"id\":\"modfxInputEq\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"modfxDepthFreq\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"MOD DEPTH:FRQ\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"modfxDepthFreq\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"mod depth\",\"mod frequency\"],\"label\":\"ModFX Mod Depth:Frequency (MOD DEPTH:FRQ)\",\"description\":\"Amount of modulation in the stereo signal (first value) and speed of the modulation (second value).\"}]},{\"id\":\"modfxWidth\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]},{\"id\":\"modfxReverbSend\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"REVERB SEND\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxReverbSend\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REVERB SEND)\",\"description\":\"Amount of this effect sent to the reverb.\"}]},{\"id\":\"delayInputEq\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"delayTime\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"TIME L:R\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"delayTime\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"left time\",\"right time\"],\"label\":\"Delay Time L:R (TIME L:R)\",\"description\":\"Delay time for the left and right channels: first digit in beats, second digit in fractions of a beat (40 is 4 beats).\"}]},{\"id\":\"delayFeedback\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"FEEDBACK\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayFeedback\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback (FEEDBACK)\",\"description\":\"Amount of the delayed signal fed back into the delay.\"}]},{\"id\":\"delayWidth\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]},{\"id\":\"delayReverbSend\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"REVERB SEND\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayReverbSend\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REVERB SEND)\",\"description\":\"Amount of this effect sent to the reverb.\"}]},{\"id\":\"reverbInputEq\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"reverbRoomSize\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"ROOM SIZE\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbRoomSize\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Room Size (ROOM SIZE)\",\"description\":\"Size of the reverb room.\"}]},{\"id\":\"reverbDecayShimmer\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"DECAY:SHIMMER\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"reverbDecayShimmer\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"decay\",\"shimmer\"],\"label\":\"Reverb Decay:Shimmer (DECAY:SHIMMER)\",\"description\":\"Reverb decay time (first value) and shimmer amount (second value).\"}]},{\"id\":\"reverbDepthFreq\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"MOD DEPTH:FRQ\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"reverbDepthFreq\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"mod depth\",\"mod frequency\"],\"label\":\"Reverb Mod Depth:Frequency (MOD DEPTH:FRQ)\",\"description\":\"Reverb modulation depth (first value) and frequency (second value).\"}]},{\"id\":\"reverbWidth\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]}]},\"mixer\":{\"rows\":[{\"id\":\"speaker\",\"match\":[{\"x\":0,\"text\":\"SPEAKER\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"speakerVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Speaker Volume (SPEAKER VOL)\",\"description\":\"Volume of the built-in speaker.\"}]},{\"id\":\"outputVol\",\"match\":[{\"x\":0,\"text\":\"OUTPUT VOL\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"outputVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Output Volume (OUTPUT VOL)\",\"description\":\"Volume of the audio output (headphones / line out).\"}]},{\"id\":\"tracks\",\"match\":[{\"x\":0,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":21,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2}],\"cells\":[{\"x\":0,\"width\":2,\"key\":\"track1\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 1 Volume\",\"meta\":{\"trackIndex\":1},\"description\":\"Volume of track 1. Instrument volumes are applied on top; the track's send levels are set in the instruments.\"},{\"x\":3,\"width\":2,\"key\":\"track2\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 2 Volume\",\"meta\":{\"trackIndex\":2},\"description\":\"Volume of track 2.\"},{\"x\":6,\"width\":2,\"key\":\"track3\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 3 Volume\",\"meta\":{\"trackIndex\":3},\"description\":\"Volume of track 3.\"},{\"x\":9,\"width\":2,\"key\":\"track4\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 4 Volume\",\"meta\":{\"trackIndex\":4},\"description\":\"Volume of track 4.\"},{\"x\":12,\"width\":2,\"key\":\"track5\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 5 Volume\",\"meta\":{\"trackIndex\":5},\"description\":\"Volume of track 5.\"},{\"x\":15,\"width\":2,\"key\":\"track6\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 6 Volume\",\"meta\":{\"trackIndex\":6},\"description\":\"Volume of track 6.\"},{\"x\":18,\"width\":2,\"key\":\"track7\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 7 Volume\",\"meta\":{\"trackIndex\":7},\"description\":\"Volume of track 7.\"},{\"x\":21,\"width\":2,\"key\":\"track8\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 8 Volume\",\"meta\":{\"trackIndex\":8},\"description\":\"Volume of track 8.\"}]},{\"id\":\"eq\",\"match\":[{\"x\":27,\"text\":\"EQ\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mixEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Main Mix EQ (EQ)\",\"description\":\"Open the main mix EQ editor ([EDIT] or [SHIFT]+[RIGHT]).\"}]},{\"id\":\"levels\",\"match\":[{\"x\":0,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":3,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":6,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":12,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2}],\"cells\":[{\"x\":0,\"width\":2,\"key\":\"modfxVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"ModFX Volume (MX)\",\"description\":\"Output level of the ModFX send effect.\"},{\"x\":3,\"width\":2,\"key\":\"delayVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Delay Volume (DE)\",\"description\":\"Output level of the delay send effect.\"},{\"x\":6,\"width\":2,\"key\":\"reverbVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Reverb Volume (RE)\",\"description\":\"Output level of the reverb send effect.\"},{\"x\":12,\"width\":2,\"key\":\"inputVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Volume (INPUT)\",\"description\":\"Volume of the analog input for monitoring. In dual-mono mode this is the left channel.\"},{\"x\":15,\"width\":2,\"key\":\"inputVol2\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Volume, 2nd channel\",\"description\":\"Enables dual-mono input mode when set (right channel volume); clear with [EDIT]+[OPTION] to disable.\"},{\"x\":18,\"width\":2,\"key\":\"usbVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Volume (USB)\",\"description\":\"Volume of anything the USB host computer sends to the M8.\"},{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main song volume, applied after the limiter, DJ filter and OTT. [EDIT] or [SHIFT]+[RIGHT] opens the Mix Scope.\"}]},{\"id\":\"limiter\",\"match\":[{\"x\":0,\"text\":\"MX\"},{\"x\":3,\"text\":\"DE\"},{\"x\":6,\"text\":\"RE\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Main mix limiter amount; the limiter is engaged when the value is above 00. [EDIT] or [SHIFT]+[RIGHT] opens the Limiter Scope.\"}]},{\"id\":\"sendModfx\",\"match\":[{\"x\":9,\"text\":\"MX\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputModfx\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input ModFX Send (INPUT MX)\",\"description\":\"How much of the analog input goes to the ModFX effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbModfx\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input ModFX Send (USB MX)\",\"description\":\"How much of the USB input goes to the ModFX effect.\"},{\"x\":27,\"width\":2,\"key\":\"djFilter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter (DJF)\",\"description\":\"DJ filter cutoff: 80 is off, below 80 low-pass (or band-stop), above 80 high-pass. Mode and options are in the Scope View.\"}]},{\"id\":\"sendDelay\",\"match\":[{\"x\":9,\"text\":\"DE\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputDelay\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Delay Send (INPUT DE)\",\"description\":\"How much of the analog input goes to the delay effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbDelay\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Delay Send (USB DE)\",\"description\":\"How much of the USB input goes to the delay effect.\"},{\"x\":27,\"width\":2,\"key\":\"ott\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT (OTT)\",\"description\":\"Over The Top multi-band compression mixed in parallel: 00 to 80 mixes it in; from 80 and above the main mix is faded out.\"}]},{\"id\":\"sendReverb\",\"match\":[{\"x\":9,\"text\":\"RE\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputReverb\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Reverb Send (INPUT RE)\",\"description\":\"How much of the analog input goes to the reverb effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbReverb\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Reverb Send (USB RE)\",\"description\":\"How much of the USB input goes to the reverb effect.\"}]}]},\"mixeq\":{\"rows\":[{\"id\":\"gain\",\"match\":[{\"x\":0,\"text\":\"GAIN\"}],\"cells\":[{\"x\":5,\"width\":6,\"key\":\"lowGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"LOW Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the LOW band, in decibels.\"},{\"x\":16,\"width\":6,\"key\":\"midGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"MID Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the MID band, in decibels.\"},{\"x\":27,\"width\":6,\"key\":\"highGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"HIGH Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the HIGH band, in decibels.\"}]},{\"id\":\"freq\",\"match\":[{\"x\":0,\"text\":\"FREQ\"}],\"cells\":[{\"x\":6,\"width\":5,\"key\":\"lowFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"LOW Frequency\",\"description\":\"Centre (or corner) frequency of the LOW band.\"},{\"x\":17,\"width\":5,\"key\":\"midFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"MID Frequency\",\"description\":\"Centre (or corner) frequency of the MID band.\"},{\"x\":28,\"width\":5,\"key\":\"highFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"HIGH Frequency\",\"description\":\"Centre (or corner) frequency of the HIGH band.\"}]},{\"id\":\"q\",\"match\":[{\"x\":0,\"text\":\"Q\"},{\"x\":1,\"regex\":\"^\\\\s?$\",\"width\":1}],\"cells\":[{\"x\":6,\"width\":3,\"key\":\"lowQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"LOW Q\",\"description\":\"Quality factor (bandwidth) of the LOW band: higher values narrow it.\"},{\"x\":17,\"width\":3,\"key\":\"midQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MID Q\",\"description\":\"Quality factor (bandwidth) of the MID band: higher values narrow it.\"},{\"x\":28,\"width\":3,\"key\":\"highQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"HIGH Q\",\"description\":\"Quality factor (bandwidth) of the HIGH band: higher values narrow it.\"}]},{\"id\":\"type\",\"match\":[{\"x\":0,\"text\":\"TYPE\"}],\"cells\":[{\"x\":6,\"width\":9,\"key\":\"lowType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"LOW Type\",\"description\":\"Filter shape of the LOW band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}},{\"x\":17,\"width\":9,\"key\":\"midType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"MID Type\",\"description\":\"Filter shape of the MID band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}},{\"x\":28,\"width\":9,\"key\":\"highType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"HIGH Type\",\"description\":\"Filter shape of the HIGH band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}}]},{\"id\":\"mode\",\"match\":[{\"x\":0,\"text\":\"MODE\"}],\"cells\":[{\"x\":6,\"width\":6,\"key\":\"lowMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"LOW Mode\",\"description\":\"How the LOW band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}},{\"x\":17,\"width\":6,\"key\":\"midMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"MID Mode\",\"description\":\"How the MID band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}},{\"x\":28,\"width\":6,\"key\":\"highMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"HIGH Mode\",\"description\":\"How the HIGH band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}}]}]},\"modfxeq\":{\"extends\":\"mixeq\"},\"delayeq\":{\"extends\":\"mixeq\"},\"reverbeq\":{\"extends\":\"mixeq\"},\"mixscope\":{\"rows\":[{\"id\":\"limiterAtkRel\",\"match\":[{\"x\":9,\"text\":\"ATK\"},{\"x\":16,\"text\":\"REL\"},{\"x\":23,\"text\":\"LIM\"}],\"cells\":[{\"x\":13,\"width\":2,\"key\":\"attack\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Attack (ATK)\",\"description\":\"Limiter attack time from 0 to 100 ms (see the on-screen help text for the conversion to milliseconds).\"},{\"x\":20,\"width\":2,\"key\":\"release\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Release (REL)\",\"description\":\"Limiter release time from 4 to 1000 ms; 00 activates AUTO mode (100-900 ms depending on the compression amount).\"},{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Limiter amount.\"}]},{\"id\":\"zoomPeak\",\"match\":[{\"x\":0,\"text\":\"ZOOM\"}],\"cells\":[{\"x\":5,\"width\":5,\"key\":\"zoom\",\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Zoom (ZOOM)\",\"description\":\"Lower limit of the scope histogram, in decibels.\"},{\"x\":20,\"width\":6,\"key\":\"peak\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"Peak (PEAK)\",\"description\":\"Current peak level of the main mix in dB. Resets when MIX or LIM change; clear it with [OPT]+[EDIT].\"}]},{\"id\":\"eq\",\"match\":[{\"x\":27,\"text\":\"EQ\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mixEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Main Mix EQ (EQ)\",\"description\":\"Open the main mix EQ editor.\"}]},{\"id\":\"softClip\",\"match\":[{\"x\":9,\"text\":\"SOFT CLIP\"}],\"cells\":[{\"x\":19,\"width\":3,\"key\":\"softClip\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Soft Clip (SOFT CLIP)\",\"description\":\"Gentle saturation after the limiter to prevent harsh clipping; raise MIX to exaggerate it.\"},{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main mix / song volume, applied after the limiter stage.\"}]},{\"id\":\"mix\",\"match\":[{\"x\":23,\"text\":\"MIX\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main mix / song volume, applied after the limiter stage.\"}]},{\"id\":\"lim\",\"match\":[{\"x\":23,\"text\":\"LIM\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Limiter amount.\"}]},{\"id\":\"djf\",\"match\":[{\"x\":23,\"text\":\"DJF\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"djFilter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter (DJF)\",\"description\":\"DJ filter cutoff: 80 is off, 00-80 low-pass, 80-FF high-pass (mode set with TYPE).\"}]},{\"id\":\"ott\",\"match\":[{\"x\":23,\"text\":\"OTT\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"ott\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT (OTT)\",\"description\":\"OTT mix amount; from 80 and above the main mix volume is faded out.\"}]}],\"zones\":[{\"id\":\"right\",\"labelX\":23,\"labelWidth\":4,\"x\":27,\"width\":6}],\"vocabulary\":{\"ATK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Attack (ATK)\",\"description\":\"Limiter attack time from 0 to 100 ms (see the on-screen help text for the conversion).\"},\"REL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Release (REL)\",\"description\":\"Limiter release time from 4 to 1000 ms; 00 activates AUTO mode (100-900 ms depending on the compression amount).\"},\"TYPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"DJ Filter Type (TYPE)\",\"description\":\"DJ filter type: low-pass/high-pass, low-pass/band-stop or band-stop/high-pass.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter Resonance (RES)\",\"description\":\"Resonance of the DJ filter.\"},\"TIME\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT Time (TIME)\",\"description\":\"OTT compressor envelope time as a percentage from 10% to 1000%.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT Color (COLOR)\",\"description\":\"OTT compressor bands and low/high gains: above 80 shifts toward treble, below 80 toward bass.\"}}},\"limiterscope\":{\"extends\":\"mixscope\"},\"inst\":{\"rows\":[{\"id\":\"type\",\"match\":[{\"x\":0,\"text\":\"TYPE\"}],\"cells\":[{\"x\":8,\"width\":13,\"key\":\"instrumentType\",\"type\":\"instrumentType\",\"format\":\"text\",\"label\":\"Instrument Type (TYPE)\",\"description\":\"Kind of instrument in this slot: Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out (or NONE for an unused slot). Changing it changes the parameters below.\"},{\"x\":22,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Preset (LOAD)\",\"description\":\"Load an instrument preset from the SD card.\"},{\"x\":27,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Preset (SAVE)\",\"description\":\"Save this instrument as a preset on the SD card.\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":8,\"width\":12,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Instrument Name (NAME)\",\"description\":\"Name of the instrument: helps keep the song organised when browsing, saving or bundling instruments.\"}]},{\"id\":\"transp\",\"match\":[{\"x\":0,\"text\":\"TRANSP.\"}],\"cells\":[{\"x\":8,\"width\":3,\"key\":\"transpose\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Transpose Enabled (TRANSP.)\",\"description\":\"Enable or disable note transposition from scales, chain transpose columns and the project transpose for this instrument (turn off for percussive or effect instruments).\"},{\"x\":21,\"width\":2,\"key\":\"tableTic\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Table Tick (TBL. TIC)\",\"description\":\"Default speed at which this instrument's table advances: 01 is one tick per step, 02 two ticks per step. Can be overridden by the TIC command.\"},{\"x\":29,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"format\":\"hex\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ bank assigned to this instrument (-- for none). [EDIT] or [SHIFT]+[RIGHT] opens the EQ editor.\"}]}],\"zones\":[{\"id\":\"left\",\"labelX\":0,\"labelWidth\":8,\"x\":8,\"width\":20},{\"id\":\"right\",\"labelX\":18,\"labelWidth\":4,\"x\":22,\"width\":11}],\"vocabularyFrom\":\"instrumentTypes\"},\"instmods\":{\"slotLabel\":\"MOD\\\\d\",\"zones\":[{\"id\":\"left\",\"labelX\":0,\"labelWidth\":4,\"x\":5,\"width\":10},{\"id\":\"right\",\"labelX\":16,\"labelWidth\":4,\"x\":21,\"width\":10}],\"vocabulary\":{\"MOD#\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulator Type\",\"description\":\"Type of this modulation slot: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope or TRACKING. Slots can modulate their neighbour (Mod 1 affects Mod 2, 2→3, 3→4, 4→1).\",\"options\":{\"AHD ENV\":\"Attack Hold Decay envelope based on tempo.\",\"ADSR ENV\":\"Traditional Attack Decay Sustain Release envelope.\",\"DRUM ENV\":\"Sharp peak and swelling hold envelope for percussive elements.\",\"LFO\":\"Tempo-synced low frequency oscillator.\",\"TRIG ENV\":\"AHD envelope triggered by another playing track or instrument.\",\"TRACKING\":\"Modulates an instrument parameter from notes or velocities.\",\"OFF\":\"Slot unused.\"}},\"DEST\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Destination (DEST)\",\"description\":\"Instrument parameter this modulator controls. MOD AMT, MOD RATE or MOD BOTH modulate the neighbouring modulator's amount, rate or both.\"},\"SRC\":{\"type\":\"parameter\",\"format\":\"hexOption\",\"label\":\"Source (SRC)\",\"description\":\"Modulation source. Trig envelope: an instrument number (00-7F) or a whole track (80-87 = tracks 1-8). Tracking: NOTE, VELOCITY or VELOCITY TAKEOVER (velocity no longer affects instrument volume).\"},\"AMT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amount (AMT)\",\"description\":\"How strongly the modulator is applied to the destination.\"},\"ATK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Attack (ATK)\",\"description\":\"Time to reach the amount (in ticks, relative to tempo).\"},\"HOLD\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Hold (HOLD)\",\"description\":\"Time to wait after the attack before decaying (in ticks).\"},\"DEC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Decay (DEC)\",\"description\":\"Time to fall back (AHD/Drum: to zero; ADSR: to the sustain level), in ticks.\"},\"SUS\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sustain (SUS)\",\"description\":\"Level held while the note is playing (ADSR).\"},\"REL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Release (REL)\",\"description\":\"Time to fall back to zero after the note is released (ADSR); needs an OFF note or FX command.\"},\"PEAK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Peak (PEAK)\",\"description\":\"Shape of the initial transient (drum envelope); modifies the duck amount and peak time.\"},\"BODY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Body (BODY)\",\"description\":\"Time to hold the modulation before decaying (drum envelope).\"},\"OSC\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"LFO Shape (OSC)\",\"description\":\"LFO shape: triangle, sine, ramp down/up, exponential down/up, square down/up, random, drunk. The 'T' variants run in ticks (much faster).\",\"options\":{\"TRI\":\"Triangle.\",\"SIN\":\"Sine.\",\"RAMP DN\":\"Ramp down.\",\"RAMP UP\":\"Ramp up.\",\"EXP DN\":\"Exponential down.\",\"EXP UP\":\"Exponential up.\",\"SQU DN\":\"Square down.\",\"SQU UP\":\"Square up.\",\"RANDOM\":\"Random values.\",\"DRUNK\":\"Random walk.\"}},\"TRIG\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"LFO Trigger (TRIG)\",\"description\":\"How the LFO reacts when the instrument triggers and when the shape ends.\",\"options\":{\"FREE\":\"Loop the shape and don't reset on new notes.\",\"RETRIG\":\"Loop the shape and reset on each instrument trigger.\",\"HOLD\":\"Hold the last value of the shape.\",\"ONCE\":\"Play the shape once and reset to the start value.\"}},\"FREQ\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"LFO Frequency (FREQ)\",\"description\":\"Rate of the LFO in steps (16th notes), or in ticks for the 'T' shapes.\"},\"LVAL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Low Value (LVAL)\",\"description\":\"Lowest value of the source range scaled to the destination (tracking). Can be higher than HVAL to invert the range.\"},\"HVAL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"High Value (HVAL)\",\"description\":\"Highest value of the source range scaled to the destination (tracking).\"}}},\"scale\":{\"notes\":\"Only the rows below the 12 interval rows; the intervals themselves are grid columns in m8-view-context.json.\",\"rows\":[{\"id\":\"key\",\"match\":[{\"x\":0,\"text\":\"KEY\"}],\"cells\":[{\"x\":6,\"width\":2,\"key\":\"key\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Key (KEY)\",\"description\":\"Global key: the root note of the default scale (can be overridden per track with SCA / SCG).\"}]},{\"id\":\"tune\",\"match\":[{\"x\":0,\"text\":\"TUNE\"}],\"cells\":[{\"x\":6,\"width\":6,\"key\":\"tune\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" Hz\",\"label\":\"Tuning (TUNE)\",\"description\":\"Reference pitch of A4 in Hz (440.00 is standard).\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":6,\"width\":16,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Scale Name (NAME)\",\"description\":\"Name of this scale (up to 16 characters); 92 factory scales load from /Scales/Factory.\"}]},{\"id\":\"file\",\"match\":[{\"x\":6,\"text\":\"LOAD\"},{\"x\":11,\"text\":\"SAVE\"}],\"cells\":[{\"x\":6,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Scale (LOAD)\",\"description\":\"Load a scale from the SD card.\"},{\"x\":11,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Scale (SAVE)\",\"description\":\"Save this scale to the SD card.\"}]}]},\"loadinstrument\":{\"kind\":\"browser\",\"browserFor\":\"instrument preset\"},\"selectsavedirectory\":{\"kind\":\"browser\",\"browserFor\":\"save location\"},\"createdirectory\":{\"kind\":\"text\",\"fieldLabel\":\"Directory Name\",\"description\":\"Name of the directory to create on the SD card.\"}}"),
2924
- instrumentTypes: /* @__PURE__ */ JSON.parse("{\"common\":{\"name\":\"Common\",\"description\":\"Parameters shared by most instrument types.\",\"params\":{\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type: low-pass, high-pass, band-pass, band-stop, LP>HP (low-pass with RES controlling a 1-pole high-pass), ZDF low-pass, ZDF high-pass.\"},\"CUTOFF\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Cutoff (CUTOFF)\",\"description\":\"Filter cutoff frequency.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Resonance (RES)\",\"description\":\"Filter resonance amount.\"},\"AMP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amplification (AMP)\",\"description\":\"Amplifies the signal; values above the maximum level are handled according to LIM.\"},\"LIM\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Limit Mode (LIM)\",\"description\":\"Limit / clipping behaviour of the amplified signal.\",\"options\":{\"CLIP\":\"Values above the maximum level are clipped.\",\"SIN\":\"The waveform is fed through a sine function.\",\"FOLD\":\"Values above the maximum level are folded back into themselves.\",\"WRAP\":\"Values above the maximum level are wrapped vertically.\",\"POST\":\"Amplification applied with hard-clipping after the filter stage.\",\"POST:AD\":\"Post filter amplification with soft-clipping.\",\"POST:W1\":\"Post filter folding distortion.\",\"POST:W2\":\"Post filter folding distortion.\",\"POST:W3\":\"Post filter folding distortion.\"}},\"PAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pan (PAN)\",\"description\":\"Stereo position of the instrument (80 is centre).\"},\"DRY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Dry Volume (DRY)\",\"description\":\"Dry volume sent to the mixer channel.\"},\"MFX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"ModFX Send (MFX)\",\"description\":\"ModFX send effect volume.\"},\"DEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Delay Send (DEL)\",\"description\":\"Delay send effect volume.\"},\"REV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REV)\",\"description\":\"Reverb send effect volume.\"}}},\"wavsynth\":{\"name\":\"Wavsynth\",\"description\":\"Wave table synthesizer and noise generator: an 8-bit sample buffer generated in real time from one of 9 basic shapes (repeated, skewed, mirrored, filtered, overdriven) or one of 61 built-in wave tables of 64 waveforms each.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Base waveform: PULSE 12% to 75%, SAW, TRIANGLE, SINE, NOISE PITCHED, NOISE, or WT wave tables. For PWM set PULSE 50% and adjust MIRROR (smoothness set by SIZE).\"},\"SIZE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Size (SIZE)\",\"description\":\"Horizontal size of the waveform (number of samples).\"},\"MULT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Multiply (MULT)\",\"description\":\"Multiplies the waveform by adding repeats of the shape; can sound like hard sync.\"},\"WARP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Warp (WARP)\",\"description\":\"Pushes the shape to one side of the wave table.\"},\"SCAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Scan / Mirror (SCAN)\",\"description\":\"Shapes 0-8: mirrors the waveform at the given position (0-200%). Wave tables: scans through the 64 waveforms.\"},\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type, plus 4 modes that apply the filter into the waveform: WAV LP, WAV HP, WAV BP, WAV BS.\"}}},\"macrosynth\":{\"name\":\"Macrosynth\",\"description\":\"44 engines ('shapes') from basic analog to 2-op FM, physical modelling and percussion; TIMBRE and COLOR change meaning per shape.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"One of the 44 Macrosynth engines (see the Macrosynth Models appendix).\"},\"TIMBRE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Timbre (TIMBRE)\",\"description\":\"First parameter of the selected shape.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Color (COLOR)\",\"description\":\"Second parameter of the selected shape.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction effect.\"},\"REDUX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Redux (REDUX)\",\"description\":\"Bit rate reduction effect.\"}}},\"sampler\":{\"name\":\"Sampler\",\"description\":\"Sample player, editor and recorder for 1 or 2 channel PCM .wav files.\",\"params\":{\"SAMPLE\":{\"type\":\"name\",\"format\":\"text\",\"label\":\"Sample (SAMPLE)\",\"description\":\"The loaded sample; use LOAD to browse for one, REC. to record or EDIT to open the Sample Editor.\"},\"SLICE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Slice (SLICE)\",\"description\":\"Slices the sample into equal sections, mapped from the lowest octave (C-1); 01 FILE uses the markers stored in the file.\"},\"PLAY\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Play Mode (PLAY)\",\"description\":\"Play direction and loop mode.\",\"options\":{\"FWD\":\"Forward, no loop.\",\"REV\":\"Reverse, no loop.\",\"FWDLOOP\":\"Looping forward.\",\"REVLOOP\":\"Looping reverse.\",\"FWD PP\":\"Ping-pong looping, forward first.\",\"REV PP\":\"Ping-pong looping, reverse first.\",\"OSC\":\"Oscillator mode: ignores the start position and doesn't restart on retrigger.\",\"OSC REV\":\"Oscillator mode, reverse.\",\"OSC PP\":\"Oscillator mode, ping-pong.\",\"REPITCH\":\"Pitches the sample from the song tempo (adjust with STEPS/BPM).\"}},\"START\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Start (START)\",\"description\":\"Sample start position (ignored in OSC play modes).\"},\"LOOP ST.\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST.)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LEN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LEN)\",\"description\":\"Sample length.\"},\"DETUNE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Detune (DETUNE)\",\"description\":\"Detune with 80 as the centre frequency: first digit steps semitones, second digit 1/16 semitone.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction; any value above 00 disables sample interpolation.\"}}},\"fmsynth\":{\"name\":\"FM Synth\",\"description\":\"Four-operator, 12-algorithm FM synthesizer. Each operator has a base shape, ratio, level, feedback and two modulation slots.\",\"params\":{\"ALGO\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Algorithm (ALGO)\",\"description\":\"One of 12 algorithms (oscillator routing); the last one is additive mode.\"},\"A\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator A (A)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"B\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator B (B)\",\"description\":\"Operator letter with optional shape.\"},\"C\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator C (C)\",\"description\":\"Operator letter with optional shape.\"},\"D\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator D (D)\",\"description\":\"Operator letter with optional shape.\"},\"RATIO\":{\"type\":\"parameter\",\"format\":\"float\",\"label\":\"Ratio (RATIO)\",\"description\":\"Operator frequency ratio relative to the played note (2.00 is an octave up, 0.50 an octave down).\"},\"LEV/FB\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Level / Feedback (LEV/FB)\",\"description\":\"Operator volume and feedback.\"},\"MOD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulation Slot (MOD)\",\"description\":\"Assigns one of MOD1-MOD4 to control a destination on the operator.\"},\"LEV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Level Mod (LEV)\",\"description\":\"Controls the level from 00 to the currently set level.\"},\"RAT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Ratio Mod (RAT)\",\"description\":\"Adds 0.00 to +16.00 to the current ratio.\"},\"PIT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pitch Mod (PIT)\",\"description\":\"Adds to the note pitch, in semitones, for the operator.\"},\"FBK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback Mod (FBK)\",\"description\":\"Controls feedback from 00 to the currently set feedback.\"},\"MOD#\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Modulation Source\",\"description\":\"Modulation source value freely assigned to operators through the MOD slots.\"}}},\"hypersynth\":{\"name\":\"Hypersynth\",\"description\":\"Six-note saw wave chord synthesizer with 16 chord banks of up to 6 intervals, detune (swarm), stereo width and a square sub-oscillator.\",\"params\":{\"SCALE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Scale (SCALE)\",\"description\":\"One of the song's scales, or 00 to use the song/track scale settings.\"},\"CHORD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Chord (CHORD)\",\"description\":\"Chord bank and its intervals (copy with [SHIFT]+[OPTION], paste with [SHIFT]+[EDIT]).\"},\"SHIFT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Shift (SHIFT)\",\"description\":\"Cross-fades volume between the first 3 intervals and the second 3.\"},\"SWARM\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Swarm (SWARM)\",\"description\":\"Detunes the oscillators relative to their interval.\"},\"WIDTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (WIDTH)\",\"description\":\"Stereo width of the oscillators, from mono 00 to full stereo FF.\"},\"SUBOSC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sub Oscillator (SUBOSC)\",\"description\":\"Mixes in a sub-oscillator 2 octaves below the root (below 80) or 1 octave below (above 80).\"}}},\"external\":{\"name\":\"External Instrument\",\"description\":\"Hybrid MIDI output and audio input device: notes and MIDI FX are sent to the chosen port and channel, audio returns through its filter and amp stage.\",\"params\":{\"INPUT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Audio Input (INPUT)\",\"description\":\"Audio input: line-in (stereo, left, right), USB (stereo, left, right), or line-in and USB.\"},\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"CC number\",\"value\"],\"label\":\"Custom CC (CC:VAL)\",\"description\":\"A custom MIDI CC number with an optional default value, sent with the note-on after program change messages.\"}}},\"midiout\":{\"name\":\"MIDI Out\",\"description\":\"Controls external devices over USB MIDI or the TRS (type A) MIDI output.\",\"params\":{\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"CC number\",\"value\"],\"label\":\"Custom CC (CC:VAL)\",\"description\":\"One of up to 10 custom MIDI CC numbers per instrument with an optional default value, sent with the note-on after program change messages.\"}}}}"),
3285
+ views: /* @__PURE__ */ JSON.parse("{\"project\":{\"rows\":[{\"id\":\"tempo\",\"match\":[{\"x\":0,\"text\":\"TEMPO\"}],\"cells\":[{\"x\":13,\"width\":6,\"key\":\"tempo\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" BPM\",\"label\":\"Tempo (TEMPO)\",\"description\":\"Song speed in beats per minute. [EDIT] pressed rhythmically sets the tempo by tapping.\"}]},{\"id\":\"transpose\",\"match\":[{\"x\":0,\"text\":\"TRANSPOSE\"}],\"cells\":[{\"x\":13,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"format\":\"hex\",\"label\":\"Project Transpose (TRANSPOSE)\",\"description\":\"Transposes the whole song in semitones (same as the TSP command). Only affects instruments with TRANSP. enabled; applied when [EDIT] is released.\"}]},{\"id\":\"groove\",\"match\":[{\"x\":0,\"text\":\"GROOVE\"}],\"cells\":[{\"x\":13,\"width\":14,\"key\":\"groove\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Default Groove (GROOVE)\",\"description\":\"Groove used when the song plays (number and name); applied when [EDIT] is released. See Groove View.\"}]},{\"id\":\"scale\",\"match\":[{\"x\":0,\"text\":\"SCALE\"}],\"cells\":[{\"x\":13,\"width\":16,\"key\":\"scale\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Default Scale (SCALE)\",\"description\":\"Scale (number, key and name) used when the song plays; applied when [EDIT] is released. See Scale View.\"}]},{\"id\":\"liveQuantize\",\"match\":[{\"x\":0,\"text\":\"LIVE QUANTIZ\"}],\"cells\":[{\"x\":13,\"width\":14,\"key\":\"liveQuantize\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Live Quantize (LIVE QUANTIZE)\",\"description\":\"How long a cued chain waits before starting when playing in live mode.\"}]},{\"id\":\"midi\",\"match\":[{\"x\":0,\"text\":\"MIDI\"},{\"x\":13,\"text\":\"SETTINGS\"}],\"cells\":[{\"x\":13,\"width\":8,\"key\":\"midiSettings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"MIDI Settings\",\"description\":\"Open the MIDI Settings View.\"},{\"x\":22,\"width\":8,\"key\":\"midiMappings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"MIDI Mappings\",\"description\":\"Open the MIDI Mappings View.\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":13,\"width\":12,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Song Name (NAME)\",\"description\":\"Name of the current song, entered before saving.\"}]},{\"id\":\"file\",\"match\":[{\"x\":0,\"text\":\"PROJECT\"},{\"x\":13,\"text\":\"LOAD\"}],\"cells\":[{\"x\":13,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Song\",\"description\":\"Load a song from the SD card.\"},{\"x\":18,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Song\",\"description\":\"Save the current song.\"},{\"x\":23,\"width\":3,\"key\":\"new\",\"type\":\"action\",\"format\":\"text\",\"label\":\"New Song\",\"description\":\"Create a new song.\"}]},{\"id\":\"export\",\"match\":[{\"x\":0,\"text\":\"EXPORT/SHARE\"}],\"cells\":[{\"x\":13,\"width\":6,\"key\":\"render\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Render\",\"description\":\"Open the Render View to render audio.\"},{\"x\":20,\"width\":6,\"key\":\"bundle\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Bundle\",\"description\":\"Bundle the song with the instruments and samples it uses.\"}]},{\"id\":\"clearUnused\",\"match\":[{\"x\":0,\"text\":\"CLEAR UNUSED\"}],\"cells\":[{\"x\":13,\"width\":7,\"key\":\"clearPhrases\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Clear Unused Phrases\",\"description\":\"Frees chains and phrases that are not used in the song.\"},{\"x\":21,\"width\":8,\"key\":\"clearInstTbl\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Clear Unused Instruments/Tables\",\"description\":\"Frees instruments, EQs and tables that are not used in the song.\"}]},{\"id\":\"instPool\",\"match\":[{\"x\":0,\"text\":\"INST. POOL\"}],\"cells\":[{\"x\":13,\"width\":15,\"key\":\"viewInstPool\",\"type\":\"action\",\"format\":\"text\",\"label\":\"View Instrument Pool\",\"description\":\"Go to the Instrument Pool View.\"}]},{\"id\":\"timeStats\",\"match\":[{\"x\":0,\"text\":\"TIME STATS\"}],\"cells\":[{\"x\":13,\"width\":15,\"key\":\"viewTimeStats\",\"type\":\"action\",\"format\":\"text\",\"label\":\"View Time Stats\",\"description\":\"Open the Time Stats View (time spent on the song).\"}]},{\"id\":\"system\",\"match\":[{\"x\":0,\"text\":\"SYSTEM\"},{\"x\":13,\"text\":\"SETTINGS\"}],\"cells\":[{\"x\":13,\"width\":8,\"key\":\"systemSettings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"System Settings\",\"description\":\"Go to the System Settings View.\"},{\"x\":22,\"width\":9,\"key\":\"usbDrive\",\"type\":\"action\",\"format\":\"text\",\"label\":\"USB Drive\",\"description\":\"Puts the M8 in USB drive mode so SD card files can be transferred to a computer.\"}]}]},\"systemsettings\":{\"rows\":[{\"id\":\"backlight\",\"match\":[{\"x\":0,\"text\":\"BACKLIGHT\"}],\"cells\":[{\"x\":15,\"width\":2,\"key\":\"backlight\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Backlight (BACKLIGHT)\",\"description\":\"Backlight LED brightness of the display.\"}]},{\"id\":\"fontOptions\",\"match\":[{\"x\":0,\"text\":\"FONT OPTIONS\"}],\"cells\":[{\"x\":15,\"width\":15,\"key\":\"fontOptions\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Font Options (FONT OPTIONS)\",\"description\":\"Font settings, including a large font mode for readability.\"}]},{\"id\":\"theme\",\"match\":[{\"x\":0,\"text\":\"THEME\"}],\"cells\":[{\"x\":15,\"width\":10,\"key\":\"editTheme\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Theme (EDIT THEME)\",\"description\":\"Open the Theme View.\"}]},{\"id\":\"notePreview\",\"match\":[{\"x\":0,\"text\":\"NOTE PREVIEW\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"notePreview\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Note Preview (NOTE PREVIEW)\",\"description\":\"Plays the note when editing notes in the phrase view.\"}]},{\"id\":\"recCountIn\",\"match\":[{\"x\":0,\"regex\":\"^REC\\\\.\\\\s?(COUNT IN|METRONOME)\\\\s*$\",\"width\":13}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"recCountIn\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Recording Count-in (REC. COUNT IN)\",\"description\":\"Number of phrases/measures of metronome count-in before recording audio in the Sample Editor or Selection to Sample.\"}]},{\"id\":\"metronomeVol\",\"match\":[{\"x\":0,\"text\":\"METRONOME VOL\"}],\"cells\":[{\"x\":15,\"width\":2,\"key\":\"metronomeVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Metronome Volume (METRONOME VOL)\",\"description\":\"Volume of the metronome sound (edit /System/Metronome.m8i to change the sound).\"}]},{\"id\":\"songTemplate\",\"match\":[{\"x\":0,\"text\":\"SONG TEMPLATE\"}],\"cells\":[{\"x\":15,\"width\":4,\"key\":\"songTemplateSave\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Song Template (SAVE)\",\"description\":\"Use the currently opened song as the template for new songs.\"},{\"x\":20,\"width\":5,\"key\":\"songTemplateClear\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Song Template (CLEAR)\",\"description\":\"Clear the song template.\"}]},{\"id\":\"usbAudioMode\",\"match\":[{\"x\":0,\"text\":\"USB AUDIO MODE\"}],\"cells\":[{\"x\":15,\"width\":14,\"key\":\"usbAudioMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"USB Audio Mode (USB AUDIO MODE)\",\"description\":\"USB output as stereo or multi-channel (multi-channel sends all 8 tracks and the effects individually, 12 stereo channels).\",\"options\":{\"STEREO OUT\":\"Stereo mix over USB.\"}}]},{\"id\":\"usbMainOut\",\"match\":[{\"x\":0,\"text\":\"USB MAIN OUT\"}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"usbMainOut\",\"type\":\"option\",\"format\":\"text\",\"label\":\"USB Main Out (USB MAIN OUT)\",\"description\":\"Routing behaviour of USB audio (see the USB audio routing options).\"}]},{\"id\":\"lineInGate\",\"match\":[{\"x\":0,\"text\":\"LINE-IN GATE\"}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"lineInGate\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Line-in Gate (LINE-IN GATE)\",\"description\":\"Noise gate threshold for the line input, adjustable from -70 dB to -10 dB (OFF disables it).\"}]},{\"id\":\"splashScreen\",\"match\":[{\"x\":0,\"text\":\"SPLASH SCREEN\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"splashScreen\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Splash Screen (SPLASH SCREEN)\",\"description\":\"Show the M8 logo and play the start-up sound when turning on the device.\"}]},{\"id\":\"hpProtection\",\"match\":[{\"x\":0,\"text\":\"HP PROTECTION\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"hpProtection\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Headphone Protection (HP PROTECTION)\",\"description\":\"Limits the headphone output volume to F0 when a pack is unplugged and re-inserted.\"}]},{\"id\":\"keyDelayRep\",\"match\":[{\"x\":0,\"text\":\"KEY DELAY:REP\"}],\"cells\":[{\"x\":15,\"width\":7,\"key\":\"keyDelayRep\",\"type\":\"parameter\",\"format\":\"decimalPair\",\"unit\":\" ms\",\"parts\":[\"delay\",\"repeat\"],\"label\":\"Key Delay:Repeat (KEY DELAY:REP)\",\"description\":\"Key repeat timing in milliseconds: delay before a held key starts repeating, then the time between repeats.\"}]},{\"id\":\"battCharging\",\"match\":[{\"x\":0,\"regex\":\"^BATT\\\\.\\\\s?(CHARGING|STATUS)\\\\s*$\",\"width\":14}],\"cells\":[{\"x\":15,\"width\":14,\"key\":\"battCharging\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Battery Charging (BATT. CHARGING / STATUS)\",\"description\":\"Whether the battery charges while the M8 is on and USB is connected (turn off to reduce power drawn from the host); also shows the current charging status.\"}]}]},\"effectsettings\":{\"sections\":[\"MODFX\",\"DELAY\",\"REVERB\"],\"rows\":[{\"id\":\"modfxType\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"MOD TYPE\"}],\"cells\":[{\"x\":21,\"width\":10,\"key\":\"modfxType\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"ModFX Type (MOD TYPE)\",\"description\":\"ModFX effect: Chorus, Phaser or Flanger (also sets the phase position).\",\"options\":{\"CHORUS\":\"Stereo chorus.\",\"PHASER\":\"Stereo phaser.\",\"FLANGER\":\"Stereo flanger.\"}}]},{\"id\":\"modfxInputEq\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"modfxDepthFreq\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"MOD DEPTH:FRQ\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"modfxDepthFreq\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"mod depth\",\"mod frequency\"],\"label\":\"ModFX Mod Depth:Frequency (MOD DEPTH:FRQ)\",\"description\":\"Amount of modulation in the stereo signal (first value) and speed of the modulation (second value).\"}]},{\"id\":\"modfxWidth\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]},{\"id\":\"modfxReverbSend\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"REVERB SEND\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxReverbSend\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REVERB SEND)\",\"description\":\"Amount of this effect sent to the reverb.\"}]},{\"id\":\"delayInputEq\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"delayTime\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"TIME L:R\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"delayTime\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"left time\",\"right time\"],\"label\":\"Delay Time L:R (TIME L:R)\",\"description\":\"Delay time for the left and right channels: first digit in beats, second digit in fractions of a beat (40 is 4 beats).\"}]},{\"id\":\"delayFeedback\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"FEEDBACK\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayFeedback\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback (FEEDBACK)\",\"description\":\"Amount of the delayed signal fed back into the delay.\"}]},{\"id\":\"delayWidth\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]},{\"id\":\"delayReverbSend\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"REVERB SEND\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayReverbSend\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REVERB SEND)\",\"description\":\"Amount of this effect sent to the reverb.\"}]},{\"id\":\"reverbInputEq\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"reverbRoomSize\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"ROOM SIZE\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbRoomSize\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Room Size (ROOM SIZE)\",\"description\":\"Size of the reverb room.\"}]},{\"id\":\"reverbDecayShimmer\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"DECAY:SHIMMER\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"reverbDecayShimmer\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"decay\",\"shimmer\"],\"label\":\"Reverb Decay:Shimmer (DECAY:SHIMMER)\",\"description\":\"Reverb decay time (first value) and shimmer amount (second value).\"}]},{\"id\":\"reverbDepthFreq\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"MOD DEPTH:FRQ\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"reverbDepthFreq\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"mod depth\",\"mod frequency\"],\"label\":\"Reverb Mod Depth:Frequency (MOD DEPTH:FRQ)\",\"description\":\"Reverb modulation depth (first value) and frequency (second value).\"}]},{\"id\":\"reverbWidth\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]}]},\"mixer\":{\"rows\":[{\"id\":\"speaker\",\"match\":[{\"x\":0,\"text\":\"SPEAKER\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"speakerVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Speaker Volume (SPEAKER VOL)\",\"description\":\"Volume of the built-in speaker.\"}]},{\"id\":\"outputVol\",\"match\":[{\"x\":0,\"text\":\"OUTPUT VOL\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"outputVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Output Volume (OUTPUT VOL)\",\"description\":\"Volume of the audio output (headphones / line out).\"}]},{\"id\":\"tracks\",\"match\":[{\"x\":0,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":21,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2}],\"cells\":[{\"x\":0,\"width\":2,\"key\":\"track1\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 1 Volume\",\"meta\":{\"trackIndex\":1},\"description\":\"Volume of track 1. Instrument volumes are applied on top; the track's send levels are set in the instruments.\"},{\"x\":3,\"width\":2,\"key\":\"track2\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 2 Volume\",\"meta\":{\"trackIndex\":2},\"description\":\"Volume of track 2.\"},{\"x\":6,\"width\":2,\"key\":\"track3\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 3 Volume\",\"meta\":{\"trackIndex\":3},\"description\":\"Volume of track 3.\"},{\"x\":9,\"width\":2,\"key\":\"track4\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 4 Volume\",\"meta\":{\"trackIndex\":4},\"description\":\"Volume of track 4.\"},{\"x\":12,\"width\":2,\"key\":\"track5\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 5 Volume\",\"meta\":{\"trackIndex\":5},\"description\":\"Volume of track 5.\"},{\"x\":15,\"width\":2,\"key\":\"track6\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 6 Volume\",\"meta\":{\"trackIndex\":6},\"description\":\"Volume of track 6.\"},{\"x\":18,\"width\":2,\"key\":\"track7\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 7 Volume\",\"meta\":{\"trackIndex\":7},\"description\":\"Volume of track 7.\"},{\"x\":21,\"width\":2,\"key\":\"track8\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 8 Volume\",\"meta\":{\"trackIndex\":8},\"description\":\"Volume of track 8.\"}]},{\"id\":\"eq\",\"match\":[{\"x\":27,\"text\":\"EQ\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mixEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Main Mix EQ (EQ)\",\"description\":\"Open the main mix EQ editor ([EDIT] or [SHIFT]+[RIGHT]).\"}]},{\"id\":\"levels\",\"match\":[{\"x\":0,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":3,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":6,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":12,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2}],\"cells\":[{\"x\":0,\"width\":2,\"key\":\"modfxVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"ModFX Volume (MX)\",\"description\":\"Output level of the ModFX send effect.\"},{\"x\":3,\"width\":2,\"key\":\"delayVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Delay Volume (DE)\",\"description\":\"Output level of the delay send effect.\"},{\"x\":6,\"width\":2,\"key\":\"reverbVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Reverb Volume (RE)\",\"description\":\"Output level of the reverb send effect.\"},{\"x\":12,\"width\":2,\"key\":\"inputVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Volume (INPUT)\",\"description\":\"Volume of the analog input for monitoring. In dual-mono mode this is the left channel.\"},{\"x\":15,\"width\":2,\"key\":\"inputVol2\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Volume, 2nd channel\",\"description\":\"Enables dual-mono input mode when set (right channel volume); clear with [EDIT]+[OPTION] to disable.\"},{\"x\":18,\"width\":2,\"key\":\"usbVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Volume (USB)\",\"description\":\"Volume of anything the USB host computer sends to the M8.\"},{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main song volume, applied after the limiter, DJ filter and OTT. [EDIT] or [SHIFT]+[RIGHT] opens the Mix Scope.\"}]},{\"id\":\"limiter\",\"match\":[{\"x\":0,\"text\":\"MX\"},{\"x\":3,\"text\":\"DE\"},{\"x\":6,\"text\":\"RE\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Main mix limiter amount; the limiter is engaged when the value is above 00. [EDIT] or [SHIFT]+[RIGHT] opens the Limiter Scope.\"}]},{\"id\":\"sendModfx\",\"match\":[{\"x\":9,\"text\":\"MX\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputModfx\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input ModFX Send (INPUT MX)\",\"description\":\"How much of the analog input goes to the ModFX effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbModfx\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input ModFX Send (USB MX)\",\"description\":\"How much of the USB input goes to the ModFX effect.\"},{\"x\":27,\"width\":2,\"key\":\"djFilter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter (DJF)\",\"description\":\"DJ filter cutoff: 80 is off, below 80 low-pass (or band-stop), above 80 high-pass. Mode and options are in the Scope View.\"}]},{\"id\":\"sendDelay\",\"match\":[{\"x\":9,\"text\":\"DE\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputDelay\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Delay Send (INPUT DE)\",\"description\":\"How much of the analog input goes to the delay effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbDelay\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Delay Send (USB DE)\",\"description\":\"How much of the USB input goes to the delay effect.\"},{\"x\":27,\"width\":2,\"key\":\"ott\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT (OTT)\",\"description\":\"Over The Top multi-band compression mixed in parallel: 00 to 80 mixes it in; from 80 and above the main mix is faded out.\"}]},{\"id\":\"sendReverb\",\"match\":[{\"x\":9,\"text\":\"RE\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputReverb\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Reverb Send (INPUT RE)\",\"description\":\"How much of the analog input goes to the reverb effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbReverb\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Reverb Send (USB RE)\",\"description\":\"How much of the USB input goes to the reverb effect.\"}]}]},\"mixeq\":{\"rows\":[{\"id\":\"gain\",\"match\":[{\"x\":0,\"text\":\"GAIN\"}],\"cells\":[{\"x\":5,\"width\":6,\"key\":\"lowGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"LOW Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the LOW band, in decibels.\"},{\"x\":16,\"width\":6,\"key\":\"midGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"MID Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the MID band, in decibels.\"},{\"x\":27,\"width\":6,\"key\":\"highGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"HIGH Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the HIGH band, in decibels.\"}]},{\"id\":\"freq\",\"match\":[{\"x\":0,\"text\":\"FREQ\"}],\"cells\":[{\"x\":6,\"width\":5,\"key\":\"lowFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"LOW Frequency\",\"description\":\"Centre (or corner) frequency of the LOW band.\"},{\"x\":17,\"width\":5,\"key\":\"midFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"MID Frequency\",\"description\":\"Centre (or corner) frequency of the MID band.\"},{\"x\":28,\"width\":5,\"key\":\"highFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"HIGH Frequency\",\"description\":\"Centre (or corner) frequency of the HIGH band.\"}]},{\"id\":\"q\",\"match\":[{\"x\":0,\"text\":\"Q\"},{\"x\":1,\"regex\":\"^\\\\s?$\",\"width\":1}],\"cells\":[{\"x\":6,\"width\":3,\"key\":\"lowQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"LOW Q\",\"description\":\"Quality factor (bandwidth) of the LOW band: higher values narrow it.\"},{\"x\":17,\"width\":3,\"key\":\"midQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MID Q\",\"description\":\"Quality factor (bandwidth) of the MID band: higher values narrow it.\"},{\"x\":28,\"width\":3,\"key\":\"highQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"HIGH Q\",\"description\":\"Quality factor (bandwidth) of the HIGH band: higher values narrow it.\"}]},{\"id\":\"type\",\"match\":[{\"x\":0,\"text\":\"TYPE\"}],\"cells\":[{\"x\":6,\"width\":9,\"key\":\"lowType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"LOW Type\",\"description\":\"Filter shape of the LOW band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}},{\"x\":17,\"width\":9,\"key\":\"midType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"MID Type\",\"description\":\"Filter shape of the MID band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}},{\"x\":28,\"width\":9,\"key\":\"highType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"HIGH Type\",\"description\":\"Filter shape of the HIGH band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}}]},{\"id\":\"mode\",\"match\":[{\"x\":0,\"text\":\"MODE\"}],\"cells\":[{\"x\":6,\"width\":6,\"key\":\"lowMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"LOW Mode\",\"description\":\"How the LOW band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}},{\"x\":17,\"width\":6,\"key\":\"midMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"MID Mode\",\"description\":\"How the MID band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}},{\"x\":28,\"width\":6,\"key\":\"highMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"HIGH Mode\",\"description\":\"How the HIGH band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}}]}]},\"modfxeq\":{\"extends\":\"mixeq\"},\"delayeq\":{\"extends\":\"mixeq\"},\"reverbeq\":{\"extends\":\"mixeq\"},\"mixscope\":{\"rows\":[{\"id\":\"limiterAtkRel\",\"match\":[{\"x\":9,\"text\":\"ATK\"},{\"x\":16,\"text\":\"REL\"},{\"x\":23,\"text\":\"LIM\"}],\"cells\":[{\"x\":13,\"width\":2,\"key\":\"attack\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Attack (ATK)\",\"description\":\"Limiter attack time from 0 to 100 ms (see the on-screen help text for the conversion to milliseconds).\"},{\"x\":20,\"width\":2,\"key\":\"release\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Release (REL)\",\"description\":\"Limiter release time from 4 to 1000 ms; 00 activates AUTO mode (100-900 ms depending on the compression amount).\"},{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Limiter amount.\"}]},{\"id\":\"zoomPeak\",\"match\":[{\"x\":0,\"text\":\"ZOOM\"}],\"cells\":[{\"x\":5,\"width\":5,\"key\":\"zoom\",\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Zoom (ZOOM)\",\"description\":\"Lower limit of the scope histogram, in decibels.\"},{\"x\":20,\"width\":6,\"key\":\"peak\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"Peak (PEAK)\",\"description\":\"Current peak level of the main mix in dB. Resets when MIX or LIM change; clear it with [OPT]+[EDIT].\"}]},{\"id\":\"eq\",\"match\":[{\"x\":27,\"text\":\"EQ\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mixEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Main Mix EQ (EQ)\",\"description\":\"Open the main mix EQ editor.\"}]},{\"id\":\"softClip\",\"match\":[{\"x\":9,\"text\":\"SOFT CLIP\"}],\"cells\":[{\"x\":19,\"width\":3,\"key\":\"softClip\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Soft Clip (SOFT CLIP)\",\"description\":\"Gentle saturation after the limiter to prevent harsh clipping; raise MIX to exaggerate it.\"},{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main mix / song volume, applied after the limiter stage.\"}]},{\"id\":\"mix\",\"match\":[{\"x\":23,\"text\":\"MIX\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main mix / song volume, applied after the limiter stage.\"}]},{\"id\":\"lim\",\"match\":[{\"x\":23,\"text\":\"LIM\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Limiter amount.\"}]},{\"id\":\"djf\",\"match\":[{\"x\":23,\"text\":\"DJF\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"djFilter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter (DJF)\",\"description\":\"DJ filter cutoff: 80 is off, 00-80 low-pass, 80-FF high-pass (mode set with TYPE).\"}]},{\"id\":\"ott\",\"match\":[{\"x\":23,\"text\":\"OTT\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"ott\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT (OTT)\",\"description\":\"OTT mix amount; from 80 and above the main mix volume is faded out.\"}]}],\"zones\":[{\"id\":\"right\",\"labelX\":23,\"labelWidth\":4,\"x\":27,\"width\":6}],\"vocabulary\":{\"ATK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Attack (ATK)\",\"description\":\"Limiter attack time from 0 to 100 ms (see the on-screen help text for the conversion).\"},\"REL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Release (REL)\",\"description\":\"Limiter release time from 4 to 1000 ms; 00 activates AUTO mode (100-900 ms depending on the compression amount).\"},\"TYPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"DJ Filter Type (TYPE)\",\"description\":\"DJ filter type: low-pass/high-pass, low-pass/band-stop or band-stop/high-pass.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter Resonance (RES)\",\"description\":\"Resonance of the DJ filter.\"},\"TIME\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT Time (TIME)\",\"description\":\"OTT compressor envelope time as a percentage from 10% to 1000%.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT Color (COLOR)\",\"description\":\"OTT compressor bands and low/high gains: above 80 shifts toward treble, below 80 toward bass.\"}}},\"limiterscope\":{\"extends\":\"mixscope\"},\"inst\":{\"rows\":[{\"id\":\"type\",\"match\":[{\"x\":0,\"text\":\"TYPE\"}],\"cells\":[{\"x\":8,\"width\":13,\"key\":\"instrumentType\",\"type\":\"instrumentType\",\"format\":\"text\",\"label\":\"Instrument Type (TYPE)\",\"description\":\"Kind of instrument in this slot: Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out (or NONE for an unused slot). Changing it changes the parameters below.\"},{\"x\":22,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Preset (LOAD)\",\"description\":\"Load an instrument preset from the SD card.\"},{\"x\":27,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Preset (SAVE)\",\"description\":\"Save this instrument as a preset on the SD card.\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":8,\"width\":12,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Instrument Name (NAME)\",\"description\":\"Name of the instrument: helps keep the song organised when browsing, saving or bundling instruments.\"}]},{\"id\":\"transp\",\"match\":[{\"x\":0,\"text\":\"TRANSP.\"}],\"cells\":[{\"x\":8,\"width\":3,\"key\":\"transpose\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Transpose Enabled (TRANSP.)\",\"description\":\"Enable or disable note transposition from scales, chain transpose columns and the project transpose for this instrument (turn off for percussive or effect instruments).\"},{\"x\":21,\"width\":2,\"key\":\"tableTic\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Table Tick (TBL. TIC)\",\"description\":\"Default speed at which this instrument's table advances: 01 is one tick per step, 02 two ticks per step. Can be overridden by the TIC command.\"},{\"x\":29,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"format\":\"hex\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ bank assigned to this instrument (-- for none). [EDIT] or [SHIFT]+[RIGHT] opens the EQ editor.\"}]}],\"zones\":[{\"id\":\"left\",\"labelX\":0,\"labelWidth\":8,\"x\":8,\"width\":24},{\"id\":\"right\",\"labelX\":18,\"labelWidth\":4,\"x\":22,\"width\":11}],\"operators\":{\"instrument\":\"fmsynth\",\"x\":8,\"step\":6,\"width\":5,\"letters\":[\"A\",\"B\",\"C\",\"D\"]},\"vocabularyFrom\":\"instrumentTypes\"},\"instmods\":{\"slotLabel\":\"MOD\\\\d\",\"zones\":[{\"id\":\"left\",\"labelX\":0,\"labelWidth\":4,\"x\":5,\"width\":10},{\"id\":\"right\",\"labelX\":16,\"labelWidth\":4,\"x\":21,\"width\":10}],\"vocabulary\":{\"MOD#\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulator Type\",\"description\":\"Type of this modulation slot: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope or TRACKING. Slots can modulate their neighbour (Mod 1 affects Mod 2, 2→3, 3→4, 4→1).\",\"options\":{\"AHD ENV\":\"Attack Hold Decay envelope based on tempo.\",\"ADSR ENV\":\"Traditional Attack Decay Sustain Release envelope.\",\"DRUM ENV\":\"Sharp peak and swelling hold envelope for percussive elements.\",\"LFO\":\"Tempo-synced low frequency oscillator.\",\"TRIG ENV\":\"AHD envelope triggered by another playing track or instrument.\",\"TRACKING\":\"Modulates an instrument parameter from notes or velocities.\",\"OFF\":\"Slot unused.\"}},\"DEST\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Destination (DEST)\",\"description\":\"Instrument parameter this modulator controls. MOD AMT, MOD RATE or MOD BOTH modulate the neighbouring modulator's amount, rate or both.\"},\"SRC\":{\"type\":\"parameter\",\"format\":\"hexOption\",\"label\":\"Source (SRC)\",\"description\":\"Modulation source. Trig envelope: an instrument number (00-7F) or a whole track (80-87 = tracks 1-8). Tracking: NOTE, VELOCITY or VELOCITY TAKEOVER (velocity no longer affects instrument volume).\"},\"AMT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amount (AMT)\",\"description\":\"How strongly the modulator is applied to the destination.\"},\"ATK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Attack (ATK)\",\"description\":\"Time to reach the amount (in ticks, relative to tempo).\"},\"HOLD\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Hold (HOLD)\",\"description\":\"Time to wait after the attack before decaying (in ticks).\"},\"DEC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Decay (DEC)\",\"description\":\"Time to fall back (AHD/Drum: to zero; ADSR: to the sustain level), in ticks.\"},\"SUS\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sustain (SUS)\",\"description\":\"Level held while the note is playing (ADSR).\"},\"REL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Release (REL)\",\"description\":\"Time to fall back to zero after the note is released (ADSR); needs an OFF note or FX command.\"},\"PEAK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Peak (PEAK)\",\"description\":\"Shape of the initial transient (drum envelope); modifies the duck amount and peak time.\"},\"BODY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Body (BODY)\",\"description\":\"Time to hold the modulation before decaying (drum envelope).\"},\"OSC\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"LFO Shape (OSC)\",\"description\":\"LFO shape: triangle, sine, ramp down/up, exponential down/up, square down/up, random, drunk. The 'T' variants run in ticks (much faster).\",\"options\":{\"TRI\":\"Triangle.\",\"SIN\":\"Sine.\",\"RAMP DN\":\"Ramp down.\",\"RAMP UP\":\"Ramp up.\",\"EXP DN\":\"Exponential down.\",\"EXP UP\":\"Exponential up.\",\"SQU DN\":\"Square down.\",\"SQU UP\":\"Square up.\",\"RANDOM\":\"Random values.\",\"DRUNK\":\"Random walk.\"}},\"TRIG\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"LFO Trigger (TRIG)\",\"description\":\"How the LFO reacts when the instrument triggers and when the shape ends.\",\"options\":{\"FREE\":\"Loop the shape and don't reset on new notes.\",\"RETRIG\":\"Loop the shape and reset on each instrument trigger.\",\"HOLD\":\"Hold the last value of the shape.\",\"ONCE\":\"Play the shape once and reset to the start value.\"}},\"FREQ\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"LFO Frequency (FREQ)\",\"description\":\"Rate of the LFO in steps (16th notes), or in ticks for the 'T' shapes.\"},\"LVAL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Low Value (LVAL)\",\"description\":\"Lowest value of the source range scaled to the destination (tracking). Can be higher than HVAL to invert the range.\"},\"HVAL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"High Value (HVAL)\",\"description\":\"Highest value of the source range scaled to the destination (tracking).\"}}},\"scale\":{\"notes\":\"Only the rows below the 12 interval rows; the intervals themselves are grid columns in m8-view-context.json.\",\"rows\":[{\"id\":\"key\",\"match\":[{\"x\":0,\"text\":\"KEY\"}],\"cells\":[{\"x\":6,\"width\":2,\"key\":\"key\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Key (KEY)\",\"description\":\"Global key: the root note of the default scale (can be overridden per track with SCA / SCG).\"}]},{\"id\":\"tune\",\"match\":[{\"x\":0,\"text\":\"TUNE\"}],\"cells\":[{\"x\":6,\"width\":6,\"key\":\"tune\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" Hz\",\"label\":\"Tuning (TUNE)\",\"description\":\"Reference pitch of A4 in Hz (440.00 is standard).\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":6,\"width\":16,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Scale Name (NAME)\",\"description\":\"Name of this scale (up to 16 characters); 92 factory scales load from /Scales/Factory.\"}]},{\"id\":\"file\",\"match\":[{\"x\":6,\"text\":\"LOAD\"},{\"x\":11,\"text\":\"SAVE\"}],\"cells\":[{\"x\":6,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Scale (LOAD)\",\"description\":\"Load a scale from the SD card.\"},{\"x\":11,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Scale (SAVE)\",\"description\":\"Save this scale to the SD card.\"}]}]},\"loadinstrument\":{\"kind\":\"browser\",\"browserFor\":\"instrument preset\"},\"selectsavedirectory\":{\"kind\":\"browser\",\"browserFor\":\"save location\"},\"createdirectory\":{\"kind\":\"text\",\"fieldLabel\":\"Directory Name\",\"description\":\"Name of the directory to create on the SD card.\"}}"),
3286
+ instrumentTypes: /* @__PURE__ */ JSON.parse("{\"common\":{\"name\":\"Common\",\"description\":\"Parameters shared by most instrument types.\",\"params\":{\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type: low-pass, high-pass, band-pass, band-stop, LP>HP (low-pass with RES controlling a 1-pole high-pass), ZDF low-pass, ZDF high-pass.\"},\"CUTOFF\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Cutoff (CUTOFF)\",\"description\":\"Filter cutoff frequency.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Resonance (RES)\",\"description\":\"Filter resonance amount.\"},\"AMP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amplification (AMP)\",\"description\":\"Amplifies the signal; values above the maximum level are handled according to LIM.\"},\"LIM\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Limit Mode (LIM)\",\"description\":\"Limit / clipping behaviour of the amplified signal.\",\"options\":{\"CLIP\":\"Values above the maximum level are clipped.\",\"SIN\":\"The waveform is fed through a sine function.\",\"FOLD\":\"Values above the maximum level are folded back into themselves.\",\"WRAP\":\"Values above the maximum level are wrapped vertically.\",\"POST\":\"Amplification applied with hard-clipping after the filter stage.\",\"POST:AD\":\"Post filter amplification with soft-clipping.\",\"POST:W1\":\"Post filter folding distortion.\",\"POST:W2\":\"Post filter folding distortion.\",\"POST:W3\":\"Post filter folding distortion.\"}},\"PAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pan (PAN)\",\"description\":\"Stereo position of the instrument (80 is centre).\"},\"DRY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Dry Volume (DRY)\",\"description\":\"Dry volume sent to the mixer channel.\"},\"MFX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"ModFX Send (MFX)\",\"description\":\"ModFX send effect volume.\"},\"DEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Delay Send (DEL)\",\"description\":\"Delay send effect volume.\"},\"REV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REV)\",\"description\":\"Reverb send effect volume.\"}}},\"wavsynth\":{\"name\":\"Wavsynth\",\"description\":\"Wave table synthesizer and noise generator: an 8-bit sample buffer generated in real time from one of 9 basic shapes (repeated, skewed, mirrored, filtered, overdriven) or one of 61 built-in wave tables of 64 waveforms each.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Base waveform: PULSE 12% to 75%, SAW, TRIANGLE, SINE, NOISE PITCHED, NOISE, or WT wave tables. For PWM set PULSE 50% and adjust MIRROR (smoothness set by SIZE).\"},\"SIZE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Size (SIZE)\",\"description\":\"Horizontal size of the waveform (number of samples).\"},\"MULT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Multiply (MULT)\",\"description\":\"Multiplies the waveform by adding repeats of the shape; can sound like hard sync.\"},\"WARP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Warp (WARP)\",\"description\":\"Pushes the shape to one side of the wave table.\"},\"SCAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Scan / Mirror (SCAN)\",\"description\":\"Shapes 0-8: mirrors the waveform at the given position (0-200%). Wave tables: scans through the 64 waveforms.\"},\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type, plus 4 modes that apply the filter into the waveform: WAV LP, WAV HP, WAV BP, WAV BS.\"}}},\"macrosynth\":{\"name\":\"Macrosynth\",\"description\":\"44 engines ('shapes') from basic analog to 2-op FM, physical modelling and percussion; TIMBRE and COLOR change meaning per shape.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"One of the 44 Macrosynth engines (see the Macrosynth Models appendix).\"},\"TIMBRE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Timbre (TIMBRE)\",\"description\":\"First parameter of the selected shape.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Color (COLOR)\",\"description\":\"Second parameter of the selected shape.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction effect.\"},\"REDUX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Redux (REDUX)\",\"description\":\"Bit rate reduction effect.\"}}},\"sampler\":{\"name\":\"Sampler\",\"description\":\"Sample player, editor and recorder for 1 or 2 channel PCM .wav files.\",\"params\":{\"SAMPLE\":{\"type\":\"name\",\"format\":\"text\",\"label\":\"Sample (SAMPLE)\",\"description\":\"The loaded sample; use LOAD to browse for one, REC. to record or EDIT to open the Sample Editor.\"},\"SLICE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Slice (SLICE)\",\"description\":\"Slices the sample into equal sections, mapped from the lowest octave (C-1); 01 FILE uses the markers stored in the file.\"},\"PLAY\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Play Mode (PLAY)\",\"description\":\"Play direction and loop mode.\",\"options\":{\"FWD\":\"Forward, no loop.\",\"REV\":\"Reverse, no loop.\",\"FWDLOOP\":\"Looping forward.\",\"REVLOOP\":\"Looping reverse.\",\"FWD PP\":\"Ping-pong looping, forward first.\",\"REV PP\":\"Ping-pong looping, reverse first.\",\"OSC\":\"Oscillator mode: ignores the start position and doesn't restart on retrigger.\",\"OSC REV\":\"Oscillator mode, reverse.\",\"OSC PP\":\"Oscillator mode, ping-pong.\",\"REPITCH\":\"Pitches the sample from the song tempo (adjust with STEPS/BPM).\"}},\"START\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Start (START)\",\"description\":\"Sample start position (ignored in OSC play modes).\"},\"LOOP ST.\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST.)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LOOP ST\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LEN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LEN)\",\"description\":\"Sample length.\"},\"LENGTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LENGTH)\",\"description\":\"Sample length.\"},\"DETUNE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Detune (DETUNE)\",\"description\":\"Detune with 80 as the centre frequency: first digit steps semitones, second digit 1/16 semitone.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction; any value above 00 disables sample interpolation.\"}}},\"fmsynth\":{\"name\":\"FM Synth\",\"description\":\"Four-operator, 12-algorithm FM synthesizer. Each operator has a base shape, ratio, level, feedback and two modulation slots.\",\"operatorDestinations\":{\"LEV\":\"controls the level from 00 to the currently set level.\",\"RAT\":\"adds 0.00 to +16.00 to the current ratio.\",\"PIT\":\"adds to the note pitch, in semitones, for this operator.\",\"FBK\":\"controls the feedback from 00 to the currently set feedback.\"},\"params\":{\"ALGO\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Algorithm (ALGO)\",\"description\":\"One of 12 algorithms (oscillator routing); the last one is additive mode.\"},\"A\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator A (A)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"B\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator B (B)\",\"description\":\"Operator letter with optional shape.\"},\"C\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator C (C)\",\"description\":\"Operator letter with optional shape.\"},\"D\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator D (D)\",\"description\":\"Operator letter with optional shape.\"},\"RATIO\":{\"type\":\"parameter\",\"format\":\"float\",\"label\":\"Ratio (RATIO)\",\"description\":\"Operator frequency ratio relative to the played note (2.00 is an octave up, 0.50 an octave down).\"},\"LEV/FB\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"level\",\"feedback\"],\"label\":\"Level / Feedback (LEV/FB)\",\"description\":\"Operator volume and feedback.\"},\"MOD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulation Slot (MOD)\",\"description\":\"Assigns one of MOD1-MOD4 to control a destination on the operator.\"},\"LEV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Level Mod (LEV)\",\"description\":\"Controls the level from 00 to the currently set level.\"},\"RAT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Ratio Mod (RAT)\",\"description\":\"Adds 0.00 to +16.00 to the current ratio.\"},\"PIT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pitch Mod (PIT)\",\"description\":\"Adds to the note pitch, in semitones, for the operator.\"},\"FBK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback Mod (FBK)\",\"description\":\"Controls feedback from 00 to the currently set feedback.\"},\"MOD#\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Modulation Source\",\"description\":\"Modulation source value freely assigned to operators through the MOD slots.\"}}},\"hypersynth\":{\"name\":\"Hypersynth\",\"description\":\"Six-note saw wave chord synthesizer with 16 chord banks of up to 6 intervals, detune (swarm), stereo width and a square sub-oscillator.\",\"params\":{\"SCALE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Scale (SCALE)\",\"description\":\"One of the song's scales, or 00 to use the song/track scale settings.\"},\"CHORD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Chord (CHORD)\",\"description\":\"Chord bank and its intervals (copy with [SHIFT]+[OPTION], paste with [SHIFT]+[EDIT]).\"},\"SHIFT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Shift (SHIFT)\",\"description\":\"Cross-fades volume between the first 3 intervals and the second 3.\"},\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Waveform of the chord oscillators (SAW by default; not in the manual, seen on a Model:02, firmware 6.5).\"},\"SWARM\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Swarm (SWARM)\",\"description\":\"Detunes the oscillators relative to their interval.\"},\"WIDTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (WIDTH)\",\"description\":\"Stereo width of the oscillators, from mono 00 to full stereo FF.\"},\"SUBOSC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sub Oscillator (SUBOSC)\",\"description\":\"Mixes in a sub-oscillator 2 octaves below the root (below 80) or 1 octave below (above 80).\"}}},\"external\":{\"name\":\"External Instrument\",\"description\":\"Hybrid MIDI output and audio input device: notes and MIDI FX are sent to the chosen port and channel, audio returns through its filter and amp stage.\",\"params\":{\"INPUT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Audio Input (INPUT)\",\"description\":\"Audio input: line-in (stereo, left, right), USB (stereo, left, right), or line-in and USB.\"},\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Custom CC (CC:VAL)\",\"description\":\"A custom MIDI CC number, then an optional default value (-- for none), sent with the note-on after program change messages.\"}}},\"midiout\":{\"name\":\"MIDI Out\",\"description\":\"Controls external devices over USB MIDI or the TRS (type A) MIDI output.\",\"params\":{\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Custom CC (CC:VAL)\",\"description\":\"One of up to 10 custom MIDI CC numbers per instrument, then an optional default value (-- for none), sent with the note-on after program change messages.\"}}}}"),
2925
3287
  instrumentTypeNames: {
2926
3288
  WAVS: "wavsynth",
2927
3289
  MACRO: "macrosynth",
@@ -2931,23 +3293,23 @@ var yt = 20, bt = (e, t) => {
2931
3293
  EXTERN: "external",
2932
3294
  MIDI: "midiout"
2933
3295
  }
2934
- }, Tt = wt.views, Et = wt.instrumentTypes, Dt = wt.instrumentTypeNames, Ot = {};
2935
- for (let [e, t] of Object.entries(Tt)) {
3296
+ }, It = Ft.views, Lt = Ft.instrumentTypes, Rt = Ft.instrumentTypeNames, zt = {};
3297
+ for (let [e, t] of Object.entries(It)) {
2936
3298
  let { extends: n, ...r } = t;
2937
- Ot[e] = n ? {
2938
- ...Tt[n],
3299
+ zt[e] = n ? {
3300
+ ...It[n],
2939
3301
  ...r
2940
3302
  } : r;
2941
3303
  }
2942
- function kt(e) {
2943
- return Ot[e] ?? null;
3304
+ function Bt(e) {
3305
+ return zt[e] ?? null;
2944
3306
  }
2945
- var K = (e, t, n) => t >= e.length ? "" : e.slice(t, t + n);
2946
- function At(e, t) {
2947
- let n = e.width ?? e.text?.length ?? 1, r = K(t, e.x, n);
3307
+ var G = (e, t, n) => t >= e.length ? "" : e.slice(t, t + n);
3308
+ function Vt(e, t) {
3309
+ let n = e.width ?? e.text?.length ?? 1, r = G(t, e.x, n);
2948
3310
  return e.text == null ? e.regex != null && new RegExp(e.regex, "i").test(r) : r.toUpperCase() === e.text.toUpperCase();
2949
3311
  }
2950
- function jt(e, t) {
3312
+ function Ht(e, t) {
2951
3313
  let n = e.sections;
2952
3314
  if (!n) return null;
2953
3315
  for (let e of t) {
@@ -2956,52 +3318,111 @@ function jt(e, t) {
2956
3318
  }
2957
3319
  return null;
2958
3320
  }
2959
- function Mt(e, t, n) {
3321
+ function Ut(e, t, n) {
2960
3322
  let r = e.zones?.find((e) => e.id === t);
2961
3323
  if (!e.slotLabel || !r) return null;
2962
3324
  let i = RegExp(`^${e.slotLabel}$`, "i");
2963
3325
  for (let e of n) {
2964
- let t = K(e, r.labelX, r.labelWidth).trim();
3326
+ let t = G(e, r.labelX, r.labelWidth).trim();
2965
3327
  if (i.test(t)) return t.toUpperCase();
2966
3328
  }
2967
3329
  return null;
2968
3330
  }
2969
- function Nt(e) {
3331
+ function Wt(e) {
2970
3332
  let t = e.toUpperCase().replace(/[^A-Z]/g, "");
2971
3333
  if (!t) return null;
2972
- let n = Object.keys(Dt).find((e) => t.startsWith(e));
2973
- return n ? Dt[n] : null;
3334
+ let n = Object.keys(Rt).find((e) => t.startsWith(e));
3335
+ return n ? Rt[n] : null;
2974
3336
  }
2975
- function Pt(e) {
3337
+ function Gt(e) {
2976
3338
  for (let t of e) if (t.toUpperCase().startsWith("TYPE")) {
2977
- let e = K(t, 8, 13).trim();
3339
+ let e = G(t, 8, 13).trim();
2978
3340
  return e ? {
2979
3341
  text: e,
2980
- key: Nt(e)
3342
+ key: Wt(e)
2981
3343
  } : null;
2982
3344
  }
2983
3345
  return null;
2984
3346
  }
2985
- function Ft(e) {
2986
- let t = Et[e];
3347
+ function Kt(e) {
3348
+ let t = Lt[e];
2987
3349
  return t ? {
2988
3350
  name: t.name,
2989
3351
  description: t.description
2990
3352
  } : null;
2991
3353
  }
2992
- function It(e, t, n) {
3354
+ function qt(e, t, n) {
2993
3355
  let r = t.replace(/^MOD\d$/, "MOD#").replace(/^CC[A-J]$/, "CC#");
2994
3356
  if (e.vocabulary) return e.vocabulary[r] ?? null;
2995
3357
  if (e.vocabularyFrom !== "instrumentTypes") return null;
2996
- let i = n ? [n, "common"] : ["common", ...Object.keys(Et).filter((e) => e !== "common")];
3358
+ let i = n ? [n, "common"] : ["common", ...Object.keys(Lt).filter((e) => e !== "common")];
2997
3359
  for (let e of i) {
2998
- let t = Et[e]?.params[r];
3360
+ let t = Lt[e]?.params[r];
2999
3361
  if (t) return t;
3000
3362
  }
3001
3363
  return null;
3002
3364
  }
3003
- var Lt = (e) => e.toLowerCase().replace(/[^a-z0-9]+(.)?/g, (e, t) => t ? t.toUpperCase() : "").replace(/^[^a-z]+/, ""), Rt = /^[A-Z][A-Z0-9.:#/ -]*$/;
3004
- function zt(e, t, n) {
3365
+ var Jt = (e) => e.toLowerCase().replace(/[^a-z0-9]+(.)?/g, (e, t) => t ? t.toUpperCase() : "").replace(/^[^a-z]+/, ""), Yt = /^[A-Z][A-Z0-9.:#/ -]*$/, Xt = {
3366
+ RATIO: "ratio",
3367
+ "LEV/FB": "levFb",
3368
+ MOD: "mod"
3369
+ }, Zt = /^(?:-----|[1-4]>[A-Z]{3})$/i;
3370
+ function Qt(e, t, n) {
3371
+ let r = G(t, 0, e.x).trim().toUpperCase(), i = e.letters.map((t, n) => ({
3372
+ letter: t,
3373
+ x: e.x + n * e.step
3374
+ })), a = (e, t, n, i, a) => ({
3375
+ ...i,
3376
+ x: t,
3377
+ width: n,
3378
+ key: `${e}${a}`,
3379
+ label: `${i.label} · operator ${a}`,
3380
+ meta: {
3381
+ paramLabel: r || e,
3382
+ operator: a
3383
+ }
3384
+ }), o = Xt[r];
3385
+ if (o) {
3386
+ let t = n.params[r];
3387
+ return t ? i.map(({ letter: i, x: s }) => a(o, s, e.width, r === "MOD" ? $t(t, 1, n) : t, i)) : null;
3388
+ }
3389
+ if (r) return null;
3390
+ if (/^\s+[A-D] \S/i.test(t)) {
3391
+ let r = [];
3392
+ for (let i of t.matchAll(/([A-D]) (\S+)/gi)) {
3393
+ let t = i[1].toUpperCase(), a = n.params[t];
3394
+ a && e.letters.includes(t) && r.push({
3395
+ ...a,
3396
+ type: "option",
3397
+ format: "text",
3398
+ x: (i.index ?? 0) + 2,
3399
+ width: i[2].length,
3400
+ key: `shape${t}`,
3401
+ label: `Shape · operator ${t}`,
3402
+ meta: {
3403
+ paramLabel: "SHAPE",
3404
+ operator: t
3405
+ }
3406
+ });
3407
+ }
3408
+ return r.length ? r : null;
3409
+ }
3410
+ let s = i.map(({ x: n }) => G(t, n, e.width).trim()), c = n.params.MOD;
3411
+ return c && s.every((e) => Zt.test(e)) && !G(t, 0, e.x).trim() ? i.map(({ letter: t, x: r }) => a("mod2", r, e.width, $t(c, 2, n), t)) : null;
3412
+ }
3413
+ function $t(e, t, n) {
3414
+ let r = {};
3415
+ for (let [e, t] of Object.entries(n.operatorDestinations ?? {})) for (let n = 1; n <= 4; n += 1) r[`${n}>${e}`] = `MOD${n} ${e}: ${t}`;
3416
+ return {
3417
+ ...e,
3418
+ type: "option",
3419
+ format: "text",
3420
+ label: `Modulation Slot ${t} (MOD)`,
3421
+ options: r
3422
+ };
3423
+ }
3424
+ var en = /^(CC[A-J])(?: CC:VAL)? +(?=\S)/i, tn = 6;
3425
+ function nn(e, t, n) {
3005
3426
  if (e.kind === "browser") {
3006
3427
  let r = n.entryText?.trim() || t.trim();
3007
3428
  return r ? {
@@ -3039,28 +3460,35 @@ function zt(e, t, n) {
3039
3460
  }]
3040
3461
  };
3041
3462
  }
3042
- for (let r of e.rows ?? []) if (!(r.section && n.section && r.section !== n.section) && r.match.every((e) => At(e, t))) return {
3463
+ for (let r of e.rows ?? []) if (!(r.section && n.section && r.section !== n.section) && r.match.every((e) => Vt(e, t))) return {
3043
3464
  rowId: r.id,
3044
3465
  cells: r.cells
3045
3466
  };
3467
+ if (e.operators && n.instrumentKey === e.operators.instrument) {
3468
+ let n = Qt(e.operators, t, Lt[e.operators.instrument]);
3469
+ if (n) return {
3470
+ rowId: "operators",
3471
+ cells: n
3472
+ };
3473
+ }
3046
3474
  let r = e.zones ?? [], i = [];
3047
3475
  return r.forEach((a, o) => {
3048
- let s = K(t, a.labelX, a.labelWidth).trim().toUpperCase();
3049
- if (!Rt.test(s)) return;
3050
- let c = r[o + 1], l = c != null && K(t, c.labelX - 1, 1).trim() === "" && Rt.test(K(t, c.labelX, c.labelWidth).trim().toUpperCase()), u = c && l ? Math.max(1, c.labelX - 1 - a.x) : a.width;
3051
- if (!K(t, a.x, u).trim()) return;
3052
- let d = It(e, s, n.instrumentKey), f = Lt(s) || a.id, p = i.some((e) => e.key === f) ? `${f}${a.id[0].toUpperCase()}${a.id.slice(1)}` : f;
3476
+ let s = G(t, a.labelX, a.labelWidth).trim().toUpperCase(), c = a.x, l = null, u = o === 0 ? en.exec(t) : null;
3477
+ if (u && (s = u[1].toUpperCase(), c = u[0].length, l = tn), !Yt.test(s)) return;
3478
+ let d = r[o + 1], f = d != null && G(t, d.labelX - 1, 1).trim() === "" && Yt.test(G(t, d.labelX, d.labelWidth).trim().toUpperCase()), p = l ?? (d && f ? Math.max(1, d.labelX - 1 - c) : a.width);
3479
+ if (!G(t, c, p).trim()) return;
3480
+ let m = qt(e, s, n.instrumentKey), h = Jt(s) || a.id, g = i.some((e) => e.key === h) ? `${h}${a.id[0].toUpperCase()}${a.id.slice(1)}` : h;
3053
3481
  i.push({
3054
- x: a.x,
3055
- width: u,
3056
- key: p,
3057
- type: d?.type ?? "parameter",
3058
- format: d?.format ?? "auto",
3059
- label: d?.label ?? s,
3060
- description: d?.description,
3061
- parts: d?.parts,
3062
- options: d?.options,
3063
- unit: d?.unit,
3482
+ x: c,
3483
+ width: p,
3484
+ key: g,
3485
+ type: m?.type ?? "parameter",
3486
+ format: m?.format ?? "auto",
3487
+ label: m?.label ?? s,
3488
+ description: m?.description,
3489
+ parts: m?.parts,
3490
+ options: m?.options,
3491
+ unit: m?.unit,
3064
3492
  meta: {
3065
3493
  paramLabel: s,
3066
3494
  zone: a.id
@@ -3071,19 +3499,19 @@ function zt(e, t, n) {
3071
3499
  cells: i
3072
3500
  };
3073
3501
  }
3074
- var q = {
3502
+ var K = {
3075
3503
  hexValue: null,
3076
3504
  numericValue: null,
3077
3505
  values: null,
3078
3506
  text: null
3079
3507
  };
3080
- function Bt(e) {
3508
+ function rn(e) {
3081
3509
  return /^[0-9A-F]{2}$/i.test(e) ? "hex" : /^[0-9A-F]{2}:[0-9A-F]{2}$/i.test(e) ? "hexPair" : /^[+-]?\d+([.,]\d+)?$/.test(e) ? "float" : /^[0-9A-F]{2}\S/i.test(e) ? "hexOption" : "text";
3082
3510
  }
3083
- function Vt(e, t) {
3511
+ function an(e, t) {
3084
3512
  let n = e.trim();
3085
- if (!n) return q;
3086
- switch (t === "auto" ? Bt(n) : t) {
3513
+ if (!n) return K;
3514
+ switch (t === "auto" ? rn(n) : t) {
3087
3515
  case "hex": {
3088
3516
  let e = /^([0-9A-F]{2})/i.exec(n), t = e ? parseInt(e[1], 16) : null;
3089
3517
  return {
@@ -3096,7 +3524,7 @@ function Vt(e, t) {
3096
3524
  case "hexOption": {
3097
3525
  let e = /^([0-9A-F]{2})\s?(.*)$/i.exec(n);
3098
3526
  if (!e) return {
3099
- ...q,
3527
+ ...K,
3100
3528
  text: n
3101
3529
  };
3102
3530
  let t = parseInt(e[1], 16);
@@ -3108,9 +3536,9 @@ function Vt(e, t) {
3108
3536
  };
3109
3537
  }
3110
3538
  case "hexPair": {
3111
- let e = /^([0-9A-F]{2}):([0-9A-F]{2})/i.exec(n);
3539
+ let e = /^([0-9A-F]{2})[:/]([0-9A-F]{2})/i.exec(n);
3112
3540
  if (!e) return {
3113
- ...q,
3541
+ ...K,
3114
3542
  text: n
3115
3543
  };
3116
3544
  let t = [parseInt(e[1], 16), parseInt(e[2], 16)];
@@ -3124,7 +3552,7 @@ function Vt(e, t) {
3124
3552
  case "decimal": {
3125
3553
  let e = parseInt(n, 10);
3126
3554
  return Number.isNaN(e) ? {
3127
- ...q,
3555
+ ...K,
3128
3556
  text: n
3129
3557
  } : {
3130
3558
  hexValue: null,
@@ -3136,7 +3564,7 @@ function Vt(e, t) {
3136
3564
  case "decimalPair": {
3137
3565
  let e = /^(\d+):(\d+)/.exec(n);
3138
3566
  if (!e) return {
3139
- ...q,
3567
+ ...K,
3140
3568
  text: n
3141
3569
  };
3142
3570
  let t = [parseInt(e[1], 10), parseInt(e[2], 10)];
@@ -3150,7 +3578,7 @@ function Vt(e, t) {
3150
3578
  case "float": {
3151
3579
  let e = parseFloat(n.replace(",", "."));
3152
3580
  return Number.isNaN(e) ? {
3153
- ...q,
3581
+ ...K,
3154
3582
  text: n
3155
3583
  } : {
3156
3584
  hexValue: null,
@@ -3160,12 +3588,12 @@ function Vt(e, t) {
3160
3588
  };
3161
3589
  }
3162
3590
  default: return {
3163
- ...q,
3591
+ ...K,
3164
3592
  text: n
3165
3593
  };
3166
3594
  }
3167
3595
  }
3168
- function Ht(e, t) {
3596
+ function on(e, t) {
3169
3597
  if (!e || !t) return null;
3170
3598
  let n = t.toUpperCase().trim();
3171
3599
  if (e[n]) return e[n];
@@ -3174,28 +3602,28 @@ function Ht(e, t) {
3174
3602
  }
3175
3603
  //#endregion
3176
3604
  //#region src/fxCommands.ts
3177
- var Ut = G.fxCommands, Wt = G.instrumentFx.types, Gt = Object.entries(Wt).filter(([, e]) => e.verified).map(([e]) => e);
3178
- function Kt(e) {
3179
- let t = Gt.filter((t) => Wt[t].commands.includes(e));
3180
- return t.length === 0 || t.length === Gt.length ? null : t;
3605
+ var sn = W.fxCommands, cn = W.instrumentFx.types, ln = Object.entries(cn).filter(([, e]) => e.verified).map(([e]) => e);
3606
+ function un(e) {
3607
+ let t = ln.filter((t) => cn[t].commands.includes(e));
3608
+ return t.length === 0 || t.length === ln.length ? null : t;
3181
3609
  }
3182
- function qt(e) {
3610
+ function dn(e) {
3183
3611
  let t = e.toUpperCase();
3184
3612
  for (let e of [
3185
3613
  "sequencer",
3186
3614
  "instrument",
3187
3615
  "mixer"
3188
3616
  ]) {
3189
- let n = Ut[e][t];
3617
+ let n = sn[e][t];
3190
3618
  if (n) return {
3191
3619
  description: n.description,
3192
3620
  category: e,
3193
3621
  relative: n.relative === !0,
3194
- instrumentTypes: e === "instrument" ? Kt(t) : null,
3622
+ instrumentTypes: e === "instrument" ? un(t) : null,
3195
3623
  slot: null
3196
3624
  };
3197
3625
  }
3198
- let n = /^([A-Z]{2})([1-4])$/.exec(t), r = n ? Ut.modulation[n[1]] : void 0;
3626
+ let n = /^([A-Z]{2})([1-4])$/.exec(t), r = n ? sn.modulation[n[1]] : void 0;
3199
3627
  if (n && r) return {
3200
3628
  description: r.description.replace("modulator N", `modulator ${n[2]}`),
3201
3629
  category: "modulation",
@@ -3204,25 +3632,25 @@ function qt(e) {
3204
3632
  slot: Number(n[2])
3205
3633
  };
3206
3634
  }
3207
- function J(e) {
3208
- return qt(e)?.relative === !0;
3635
+ function q(e) {
3636
+ return dn(e)?.relative === !0;
3209
3637
  }
3210
- function Jt(e) {
3211
- let t = Wt[e.toLowerCase()];
3638
+ function fn(e) {
3639
+ let t = cn[e.toLowerCase()];
3212
3640
  return t ? {
3213
3641
  type: e.toLowerCase(),
3214
3642
  verified: t.verified,
3215
3643
  commands: [...t.commands]
3216
3644
  } : null;
3217
3645
  }
3218
- var Yt = G.instrumentFx.relativeCommands.description, Y = (e) => e.toString(16).toUpperCase().padStart(2, "0"), X = (e) => e > 127 ? e - 256 : e, Xt = (e) => `${e > 0 ? "+" : e < 0 ? "−" : "±"}${Math.abs(e)}`;
3219
- function Zt(e, t) {
3220
- return J(e) && t > 127 ? `${Y(t)} (${X(t)})` : Y(t);
3646
+ var pn = W.instrumentFx.relativeCommands.description, J = (e) => e.toString(16).toUpperCase().padStart(2, "0"), Y = (e) => e.toString(16).toUpperCase(), mn = (e) => e === 1 ? "once" : e === 2 ? "twice" : `${e} times`, X = (e) => e > 127 ? e - 256 : e, hn = (e) => `${e > 0 ? "+" : e < 0 ? "−" : "±"}${Math.abs(e)}`;
3647
+ function gn(e, t) {
3648
+ return q(e) && t > 127 ? `${J(t)} (${X(t)})` : J(t);
3221
3649
  }
3222
- function Qt(e) {
3223
- return e > 127 ? `−${Y(256 - e)}` : `+${Y(e)}`;
3650
+ function _n(e) {
3651
+ return e > 127 ? `−${J(256 - e)}` : `+${J(e)}`;
3224
3652
  }
3225
- var $t = [
3653
+ var vn = [
3226
3654
  "C-",
3227
3655
  "C#",
3228
3656
  "D-",
@@ -3236,16 +3664,16 @@ var $t = [
3236
3664
  "A#",
3237
3665
  "B-"
3238
3666
  ];
3239
- function en(e) {
3667
+ function yn(e) {
3240
3668
  let t = /^([A-G])([#-])(\d)$/.exec((e ?? "").trim().toUpperCase());
3241
3669
  if (!t) return null;
3242
- let n = $t.indexOf(`${t[1]}${t[2]}`);
3670
+ let n = vn.indexOf(`${t[1]}${t[2]}`);
3243
3671
  return n < 0 ? null : Number(t[3]) * 12 + n;
3244
3672
  }
3245
- function tn(e) {
3246
- return `${$t[(e % 12 + 12) % 12]}${Math.floor(e / 12)}`;
3673
+ function bn(e) {
3674
+ return `${vn[(e % 12 + 12) % 12]}${Math.floor(e / 12)}`;
3247
3675
  }
3248
- var nn = [
3676
+ var xn = [
3249
3677
  "C",
3250
3678
  "C#",
3251
3679
  "D",
@@ -3258,27 +3686,46 @@ var nn = [
3258
3686
  "A",
3259
3687
  "A#",
3260
3688
  "B"
3261
- ];
3262
- function rn(e, t, n = {}) {
3689
+ ], Sn = 6;
3690
+ function Cn(e) {
3691
+ return e === Sn ? "one row per step" : Sn % e === 0 ? `${Sn / e} rows per step` : e % Sn === 0 ? `one row every ${e / Sn} steps` : "";
3692
+ }
3693
+ function wn(e, t) {
3694
+ if (e === 0) return `${t} moves one row each time the instrument is triggered`;
3695
+ if (e <= 251) {
3696
+ let n = Cn(e);
3697
+ return `${t} moves one row every ${e} tick${e === 1 ? "" : "s"}${n ? ` (${n})` : ""}`;
3698
+ }
3699
+ return [
3700
+ "octave map: the octave played picks the table row (instrument tables only)",
3701
+ "velocity map: the velocity played picks the table row (instrument tables only)",
3702
+ "note map: the note played picks the table row (instrument tables only)",
3703
+ `${t} moves one row every 5 ms (200 Hz)`
3704
+ ][e - 252] ?? null;
3705
+ }
3706
+ function Tn(e, t, n = {}) {
3263
3707
  let r = e.toUpperCase(), i = t >> 4, a = t & 15, o = (e) => `${e} tick${e === 1 ? "" : "s"}`;
3264
3708
  switch (r) {
3265
3709
  case "ARP": {
3266
- let e = en(n.note), t = `+${i} and +${a} semitones`;
3267
- return e == null ? `plays the note, then ${t}` : `${t}: ${tn(e)} ${tn(e + i)} ${tn(e + a)}`;
3710
+ let e = yn(n.note), t = `+${i} and +${a} semitones`;
3711
+ return e == null ? `plays the note, then ${t}` : `${t}: ${bn(e)} ${bn(e + i)} ${bn(e + a)}`;
3268
3712
  }
3269
3713
  case "ARC": return `arp mode ${i}, speed ${o(a)}`;
3270
3714
  case "CHA": return n.inTable ? `everything left of it plays with probability ${t}/255 (00 never, FF always)` : `${i}/15 chance for what is left of it, ${a}/15 for what is right (0 never, F always)`;
3271
- case "DEL": return `delays this row by ${o(t)}`;
3715
+ case "DEL": return n.inTable ? `holds the table's playhead for ${o(t)}` : `delays this row by ${o(t)}`;
3272
3716
  case "KIL": return `stops the note after ${o(t)}`;
3273
3717
  case "OFF": return `note off after ${o(t)}`;
3274
3718
  case "PSL": return `portamento over ${o(t)}`;
3275
- case "GRV": return `track uses groove ${Y(t)}`;
3276
- case "GGR": return `all tracks use groove ${Y(t)}`;
3277
- case "INS": return `switches to instrument ${Y(t)}`;
3278
- case "TBL": return `instrument uses table ${Y(t)}`;
3279
- case "TBX": return t === 0 ? "stops the auxiliary table" : `plays table ${Y(t)} alongside the instrument table`;
3280
- case "THO": return `table jumps to position ${a.toString(16).toUpperCase()}`;
3281
- case "HOP": return n.inTable ? `jumps to row ${a.toString(16).toUpperCase()}, ${i} time${i === 1 ? "" : "s"}` : t === 255 ? "stops this track" : `jumps to row ${a.toString(16).toUpperCase()} of the next phrase`;
3719
+ case "GRV": return `track uses groove ${J(t)}`;
3720
+ case "GGR": return `all tracks use groove ${J(t)}`;
3721
+ case "INS": return `switches to instrument ${J(t)}`;
3722
+ case "TBL": return `instrument uses table ${J(t)}`;
3723
+ case "TBX": return t === 0 ? "stops the auxiliary table" : `plays table ${J(t)} alongside the instrument table`;
3724
+ case "THO": return n.inTable ? `every column of the table jumps to row ${Y(a)}` : `the instrument's table jumps to row ${Y(a)} (the aux table when a TBX is on this step)`;
3725
+ case "HOP": return n.inTable ? a === n.row ? i === 0 ? `this column stops on row ${Y(a)}` : `holds this column on row ${Y(a)} ${mn(i)}` : i === 0 ? `always jumps back to row ${Y(a)}: the rows in between loop` : `jumps to row ${Y(a)} ${mn(i)}, then the column runs on past it` : t === 255 ? "stops this track" : `jumps to row ${Y(a)} of the next phrase`;
3726
+ case "REP": return !n.inTable || t === 0 ? null : `plays the command above it ${t} more time${t === 1 ? "" : "s"}, one row each`;
3727
+ case "RTO": return n.inTable ? `repeats the relative command above it until it has added ${t > 127 ? `${J(t)} (${X(t)})` : J(t)} in total` : null;
3728
+ case "RMX": return `tracks to its left (selection ${Y(i)}) jump to phrase row ${Y(a)}`;
3282
3729
  case "RND": return `randomises the previous FX: left range ${i}, right range ${a} (0 none, F full)`;
3283
3730
  case "RNL": return `randomises the FX to its left: left range ${i}, right range ${a} (0 none, F full)`;
3284
3731
  case "RET": return a === 0 ? `single retrig after ${o(i)}` : `retrigs every ${o(a)}, volume ${i < 8 ? "falls" : "rises"} on each retrig (ramp ${i})`;
@@ -3287,51 +3734,47 @@ function rn(e, t, n = {}) {
3287
3734
  case "PBN": return t < 128 ? `pitch slides up by ${t}` : `pitch slides down by ${256 - t}`;
3288
3735
  case "SCA":
3289
3736
  case "SCG": {
3290
- let e = nn[i];
3737
+ let e = xn[i];
3291
3738
  return `${r === "SCA" ? "track" : "song"} scale ${a.toString(16).toUpperCase()}${e ? `, key ${e}` : ""}`;
3292
3739
  }
3293
- case "SNG": return `moves the song position by ${Xt(X(t))} row(s)`;
3294
- case "SED": return `random seed ${Y(t)}`;
3295
- case "NXT": return `plays instrument ${Y(t)} on the next track`;
3740
+ case "SNG": return `moves the song position by ${hn(X(t))} row(s)`;
3741
+ case "SED": return `random seed ${J(t)}`;
3742
+ case "NXT": return `plays instrument ${J(t)} on the next track`;
3296
3743
  case "MTT": {
3744
+ if (n.inTable) return "no effect in a table";
3297
3745
  let e = X(t);
3298
- return e === 0 ? "no timing shift" : `shifts the row by ${Xt(e)}/8 tick`;
3746
+ return e < 0 && n.row === 0 ? "no effect: a row cannot be moved earlier on the first step" : e === 0 ? "no timing shift" : `shifts the row by ${hn(e)}/8 tick`;
3299
3747
  }
3300
3748
  case "TPO": return `${t} BPM`;
3301
- case "TSP": return `transposes the song by ${Xt(X(t))} semitones`;
3302
- case "TIC": return t === 0 ? "table advances on each instrument trigger" : t <= 251 ? `table advances every ${o(t)}` : {
3303
- 252: "octave map: playing octave picks the table row",
3304
- 253: "velocity map",
3305
- 254: "note map",
3306
- 255: "table advances at 200 Hz"
3307
- }[t] ?? null;
3749
+ case "TSP": return `transposes the song by ${hn(X(t))} semitones`;
3750
+ case "TIC": return wn(t, n.inTable ? "this column" : "the instrument's table");
3308
3751
  case "XRZ": return t > 0 ? "reverb freeze on" : "reverb freeze off";
3309
3752
  }
3310
- if (J(r)) {
3753
+ if (q(r)) {
3311
3754
  let e = X(t);
3312
- return r === "PIT" ? e === 0 ? "no change" : `${Xt(e)} semitones` : e === 0 ? "no change" : Xt(e);
3755
+ return r === "PIT" ? e === 0 ? "no change" : `${hn(e)} semitones` : e === 0 ? "no change" : hn(e);
3313
3756
  }
3314
3757
  return null;
3315
3758
  }
3316
- function an(e, t, n) {
3759
+ function En(e, t, n) {
3317
3760
  let r = e + t & 255;
3318
3761
  return n != null && (t < 128 ? r < e || r > n : r > e || r < n) && (r = n), {
3319
3762
  value: r,
3320
3763
  atLimit: n != null && r === n
3321
3764
  };
3322
3765
  }
3323
- function on(e, t, n, r = !1) {
3766
+ function Dn(e, t, n, r = !1) {
3324
3767
  let i = null, a = null, o = null, s = null, c = r, l = !1, u = null, d = "none", f = Math.min(n, e.length - 1);
3325
3768
  for (let n = 0; n <= f; n += 1) {
3326
3769
  let f = e[n].fields[`fx${t}cmd`], p = e[n].fields[`fx${t}val`];
3327
3770
  if (d = "none", f && !f.isEmpty) {
3328
3771
  let e = f.rawValue.toUpperCase(), t = p && !p.isEmpty ? p.hexValue : null;
3329
- e === "REP" ? t ? !i && !r ? d = "orphan" : (d = c ? "repChange" : "rep", c || (u = n), c = !0, o = t, a != null && ({value: a, atLimit: l} = an(a, o, s))) : (d = "repStop", c = !1, o = null, l = !1) : e === "RTO" ? (d = "rto", s = t) : (d = c ? "replace" : "set", i = {
3772
+ e === "REP" ? t ? !i && !r ? d = "orphan" : (d = c ? "repChange" : "rep", c || (u = n), c = !0, o = t, a != null && ({value: a, atLimit: l} = En(a, o, s))) : (d = "repStop", c = !1, o = null, l = !1) : e === "RTO" ? (d = "rto", s = t) : (d = c ? "replace" : "set", i = {
3330
3773
  cmd: e,
3331
3774
  value: t,
3332
3775
  step: n
3333
3776
  }, a = t, c = !1, o = null, s = null, l = !1, u = null);
3334
- } else c && (d = "continue", a != null && o != null && ({value: a, atLimit: l} = an(a, o, s)));
3777
+ } else c && (d = "continue", a != null && o != null && ({value: a, atLimit: l} = En(a, o, s)));
3335
3778
  }
3336
3779
  return {
3337
3780
  lane: t,
@@ -3347,20 +3790,20 @@ function on(e, t, n, r = !1) {
3347
3790
  carried: c && !i
3348
3791
  };
3349
3792
  }
3350
- function sn(e) {
3351
- let t = e.source, n = (e) => e == null ? "??" : t ? Zt(t.cmd, e) : Y(e), r = e.increment == null ? "" : Qt(e.increment), i = e.atLimit && e.limit != null ? `, held at the RTO limit ${Y(e.limit)}` : "";
3793
+ function On(e) {
3794
+ let t = e.source, n = (e) => e == null ? "??" : t ? gn(t.cmd, e) : J(e), r = e.increment == null ? "" : _n(e.increment), i = e.atLimit && e.limit != null ? `, held at the RTO limit ${J(e.limit)}` : "";
3352
3795
  switch (e.event) {
3353
3796
  case "rep":
3354
3797
  case "repChange": return e.carried ? `REP ${r} per step continues the repeat from the previous phrase` : `${e.event === "rep" ? "repeats" : "now repeats"} ${t?.cmd} from ${n(t?.value ?? null)} (step ${t?.step.toString(16).toUpperCase()}), ${r} per step → ${n(e.value)} on this step${i}`;
3355
3798
  case "continue": return e.carried ? "REP from a previous phrase is still active in this lane (^^)" : `REP still active: ${t?.cmd} reaches ${n(e.value)} on this step (${r} per step since step ${e.repStartStep?.toString(16).toUpperCase()})${i}`;
3356
3799
  case "repStop": return t ? `REP00 stops the repeat of ${t.cmd}` : "REP00 stops the repeat";
3357
3800
  case "orphan": return "REP has no earlier command in this lane of the phrase to repeat";
3358
- case "rto": return e.active && e.limit != null ? `sets the value the running REP stops at (${Y(e.limit)})` : "sets the stop value for a REP";
3801
+ case "rto": return e.active && e.limit != null ? `sets the value the running REP stops at (${J(e.limit)})` : "sets the stop value for a REP";
3359
3802
  case "replace": return t ? `replaces the running REP (${t.cmd} is no longer repeated)` : null;
3360
3803
  default: return null;
3361
3804
  }
3362
3805
  }
3363
- function cn(e) {
3806
+ function kn(e) {
3364
3807
  let t = e.fields.inst;
3365
3808
  return t && !t.isEmpty ? "instrument" : [
3366
3809
  1,
@@ -3371,10 +3814,10 @@ function cn(e) {
3371
3814
  return n != null && !n.isEmpty && n.rawValue.toUpperCase() === "RET";
3372
3815
  }) ? "ret" : null;
3373
3816
  }
3374
- function ln(e, t, n = null) {
3817
+ function An(e, t, n = null) {
3375
3818
  let r = {}, i = null, a = null, o = Math.min(t, e.length - 1);
3376
3819
  for (let t = 0; t <= o; t += 1) {
3377
- let o = cn(e[t]);
3820
+ let o = kn(e[t]);
3378
3821
  if (o) {
3379
3822
  for (let e of Object.keys(r)) delete r[e];
3380
3823
  i = t, a = o;
@@ -3384,8 +3827,8 @@ function ln(e, t, n = null) {
3384
3827
  2,
3385
3828
  3
3386
3829
  ]) {
3387
- let s = on(e, o, t, n === o), c = s.source;
3388
- if (!c || s.carried || !J(c.cmd)) continue;
3830
+ let s = Dn(e, o, t, n === o), c = s.source;
3831
+ if (!c || s.carried || !q(c.cmd)) continue;
3389
3832
  let l = null, u = !1;
3390
3833
  if (s.event === "set" || s.event === "replace" ? l = c.value : (s.event === "rep" || s.event === "repChange" || s.event === "continue") && (l = s.value, u = !0), l == null) continue;
3391
3834
  let d = r[c.cmd];
@@ -3401,21 +3844,21 @@ function ln(e, t, n = null) {
3401
3844
  }
3402
3845
  return r;
3403
3846
  }
3404
- var un = (e) => e === 0 ? "±00" : `${e > 0 ? "+" : "−"}${Y(Math.abs(e))}`;
3405
- function dn(e) {
3847
+ var jn = (e) => e === 0 ? "±00" : `${e > 0 ? "+" : "−"}${J(Math.abs(e))}`;
3848
+ function Mn(e) {
3406
3849
  if (e.resetStep == null) return "the start of this phrase (earlier state unknown)";
3407
3850
  let t = e.resetStep.toString(16).toUpperCase();
3408
3851
  return e.resetBy === "ret" ? `the RET on step ${t}` : `the instrument number on step ${t}`;
3409
3852
  }
3410
- var fn = "stays until a note with an instrument number (I) or a RET resets it";
3411
- function pn(e) {
3412
- if (!e || e.count <= 1) return `relative: ${fn}`;
3413
- let t = e.viaRep ? ", including REP repeats" : "", n = e.total === 0 ? "has had no net effect" : `has moved by ${un(e.total)}`;
3414
- return `relative: ${e.cmd} ${n} over ${e.count} steps since ${dn(e)}${t}; ${fn}`;
3853
+ var Nn = "stays until a note with an instrument number (I) or a RET resets it";
3854
+ function Pn(e) {
3855
+ if (!e || e.count <= 1) return `relative: ${Nn}`;
3856
+ let t = e.viaRep ? ", including REP repeats" : "", n = e.total === 0 ? "has had no net effect" : `has moved by ${jn(e.total)}`;
3857
+ return `relative: ${e.cmd} ${n} over ${e.count} steps since ${Mn(e)}${t}; ${Nn}`;
3415
3858
  }
3416
- function mn(e) {
3859
+ function Fn(e) {
3417
3860
  let t = Object.values(e).filter((e) => e.total !== 0);
3418
- return t.length ? `triggers the instrument again: resets the relative changes still in force (${t.map((e) => `${e.cmd} ${un(e.total)}`).join(", ")})` : null;
3861
+ return t.length ? `triggers the instrument again: resets the relative changes still in force (${t.map((e) => `${e.cmd} ${jn(e.total)}`).join(", ")})` : null;
3419
3862
  }
3420
3863
  //#endregion
3421
3864
  //#region src/scaleInsight.ts
@@ -3432,7 +3875,7 @@ var Z = [
3432
3875
  "A",
3433
3876
  "A#",
3434
3877
  "B"
3435
- ], hn = [
3878
+ ], In = [
3436
3879
  {
3437
3880
  name: "Major (Ionian)",
3438
3881
  steps: [
@@ -3650,7 +4093,7 @@ var Z = [
3650
4093
  11
3651
4094
  ]
3652
4095
  }
3653
- ], gn = [
4096
+ ], Ln = [
3654
4097
  "the root",
3655
4098
  "a minor 2nd",
3656
4099
  "a major 2nd",
@@ -3664,26 +4107,26 @@ var Z = [
3664
4107
  "a minor 7th",
3665
4108
  "a major 7th"
3666
4109
  ];
3667
- function _n(e) {
4110
+ function Rn(e) {
3668
4111
  let t = e.replace(/[-\s]/g, "").toUpperCase();
3669
4112
  return Z.includes(t) ? t : null;
3670
4113
  }
3671
- function vn(e) {
4114
+ function zn(e) {
3672
4115
  let t = Z.indexOf(e);
3673
- return t <= 0 ? t === 0 ? "the first row (the scale root)" : e : `${gn[t]} (+${t} semitones above C)`;
4116
+ return t <= 0 ? t === 0 ? "the first row (the scale root)" : e : `${Ln[t]} (+${t} semitones above C)`;
3674
4117
  }
3675
- function yn(e) {
4118
+ function Bn(e) {
3676
4119
  let t = new Set(e);
3677
4120
  if (t.size === 0) return [];
3678
4121
  let n = [];
3679
- for (let e of hn) if (e.steps.length === t.size) for (let r = 0; r < 12; r += 1) e.steps.every((e) => t.has((e + r) % 12)) && n.push({
4122
+ for (let e of In) if (e.steps.length === t.size) for (let r = 0; r < 12; r += 1) e.steps.every((e) => t.has((e + r) % 12)) && n.push({
3680
4123
  root: r,
3681
4124
  name: e.name
3682
4125
  });
3683
4126
  return n.sort((e, t) => e.root - t.root), n.map((e) => e.root === 0 ? e.name : `${e.name} from ${Z[e.root]}`);
3684
4127
  }
3685
- function bn(e) {
3686
- let t = e.filter((e) => e.enabled === !0), n = t.map((e) => e.interval), r = yn(t.map((e) => e.semitone)), i = e.filter((e) => e.enabled === !0 && e.offset != null && e.offset !== 0).map((e) => ({
4128
+ function Vn(e) {
4129
+ let t = e.filter((e) => e.enabled === !0), n = t.map((e) => e.interval), r = Bn(t.map((e) => e.semitone)), i = e.filter((e) => e.enabled === !0 && e.offset != null && e.offset !== 0).map((e) => ({
3687
4130
  interval: e.interval,
3688
4131
  offset: e.offset
3689
4132
  }));
@@ -3692,47 +4135,47 @@ function bn(e) {
3692
4135
  enabled: n,
3693
4136
  shapes: r,
3694
4137
  detuned: i,
3695
- summary: xn({
4138
+ summary: Hn({
3696
4139
  enabled: n,
3697
4140
  shapes: r,
3698
4141
  detuned: i
3699
4142
  }).join(" — ")
3700
4143
  };
3701
4144
  }
3702
- function xn(e) {
4145
+ function Hn(e) {
3703
4146
  let { enabled: t, shapes: n, detuned: r } = e;
3704
4147
  if (!t.length) return ["no notes enabled"];
3705
4148
  let i = n[0] ?? `${t.length} notes, custom scale`;
3706
- return r.length && (i += `; detuned: ${r.map((e) => `${e.interval} ${Sn(e.offset)}`).join(", ")}`), [t.join(" "), i];
4149
+ return r.length && (i += `; detuned: ${r.map((e) => `${e.interval} ${Un(e.offset)}`).join(", ")}`), [t.join(" "), i];
3707
4150
  }
3708
- function Sn(e) {
4151
+ function Un(e) {
3709
4152
  return `${e > 0 ? "+" : e < 0 ? "−" : ""}${Math.abs(e).toFixed(2)} st`;
3710
4153
  }
3711
4154
  //#endregion
3712
4155
  //#region src/viewContext.ts
3713
- var Cn = /* @__PURE__ */ new Set([
4156
+ var Wn = /* @__PURE__ */ new Set([
3714
4157
  "--",
3715
4158
  "---",
3716
4159
  "---00",
3717
4160
  "----",
3718
4161
  "-"
3719
4162
  ]);
3720
- function wn(e) {
3721
- return !e || Cn.has(e) || /^-+$/.test(e);
4163
+ function Gn(e) {
4164
+ return !e || Wn.has(e) || /^-+$/.test(e);
3722
4165
  }
3723
- function Tn(e) {
4166
+ function Kn(e) {
3724
4167
  let t = e.trim();
3725
- if (!t || wn(t)) return null;
4168
+ if (!t || Gn(t)) return null;
3726
4169
  let n = parseInt(t, 16);
3727
4170
  return Number.isNaN(n) ? null : n;
3728
4171
  }
3729
- function En(e, t, n) {
4172
+ function qn(e, t, n) {
3730
4173
  return t >= e.length ? "" : e.slice(t, Math.min(t + n, e.length));
3731
4174
  }
3732
- function Dn(e) {
3733
- return On(e);
4175
+ function Jn(e) {
4176
+ return Yn(e);
3734
4177
  }
3735
- function On(e) {
4178
+ function Yn(e) {
3736
4179
  if (!e) return {
3737
4180
  viewId: null,
3738
4181
  isUnsaved: !1,
@@ -3745,7 +4188,7 @@ function On(e) {
3745
4188
  marker: r
3746
4189
  };
3747
4190
  }
3748
- function kn(e) {
4191
+ function Xn(e) {
3749
4192
  return e.length > 0 && /^[^a-zA-Z0-9 ]/.test(e[0]) ? {
3750
4193
  line: e.slice(1),
3751
4194
  offset: 1
@@ -3754,12 +4197,12 @@ function kn(e) {
3754
4197
  offset: 0
3755
4198
  };
3756
4199
  }
3757
- function An(e, t) {
3758
- let n = En(t, e.x, e.width).trim();
4200
+ function Zn(e, t) {
4201
+ let n = qn(t, e.x, e.width).trim();
3759
4202
  n.includes("^") && (e.type === "fxCommand" || e.type === "fxValue") && (n = "");
3760
- let r = wn(n);
4203
+ let r = Gn(n);
3761
4204
  if (e.format) {
3762
- let t = r ? null : Vt(n, e.format), i = e.type === "name" || e.type === "fileEntry", a = t?.text != null && !i ? t.text.toUpperCase() : t?.text ?? null;
4205
+ let t = r ? null : an(n, e.format), i = e.type === "name" || e.type === "fileEntry", a = t?.text != null && !i ? t.text.toUpperCase() : t?.text ?? null;
3763
4206
  return {
3764
4207
  key: e.key,
3765
4208
  label: e.label,
@@ -3774,8 +4217,8 @@ function An(e, t) {
3774
4217
  values: t?.values ?? null,
3775
4218
  parts: e.parts,
3776
4219
  unit: e.unit,
3777
- valueDescription: !r && Ht(e.options, a ?? n) || void 0,
3778
- fileKind: e.type === "fileEntry" ? jn(n) : void 0
4220
+ valueDescription: !r && on(e.options, a ?? n) || void 0,
4221
+ fileKind: e.type === "fileEntry" ? Qn(n) : void 0
3779
4222
  };
3780
4223
  }
3781
4224
  let i = [
@@ -3795,7 +4238,7 @@ function An(e, t) {
3795
4238
  if (a.includes(e.type)) {
3796
4239
  let e = parseInt(n, 10);
3797
4240
  o = Number.isNaN(e) ? null : e;
3798
- } else o = Tn(n);
4241
+ } else o = Kn(n);
3799
4242
  }
3800
4243
  return {
3801
4244
  key: e.key,
@@ -3807,20 +4250,20 @@ function An(e, t) {
3807
4250
  meta: e.meta
3808
4251
  };
3809
4252
  }
3810
- function jn(e) {
4253
+ function Qn(e) {
3811
4254
  if (e === "/..") return "parent";
3812
4255
  if (e.startsWith("/")) return "directory";
3813
4256
  let t = /\.([a-z0-9]+)$/i.exec(e)?.[1]?.toLowerCase();
3814
4257
  return t === "m8i" ? "instrumentPreset" : t === "wav" ? "sample" : t?.startsWith("m8") ? "m8file" : "file";
3815
4258
  }
3816
- function Mn(e, t) {
3817
- let n = En(e, t.x, t.width).trim();
4259
+ function $n(e, t) {
4260
+ let n = qn(e, t.x, t.width).trim();
3818
4261
  if (!n) return null;
3819
- if (t.parse === "hex") return Tn(n);
4262
+ if (t.parse === "hex") return Kn(n);
3820
4263
  let r = parseInt(n, 10);
3821
4264
  return Number.isNaN(r) ? null : r;
3822
4265
  }
3823
- function Nn(e, t) {
4266
+ function er(e, t) {
3824
4267
  for (let n of e) if (t >= n.x && t < n.x + n.width) return n;
3825
4268
  let n = null, r = 2;
3826
4269
  for (let i of e) {
@@ -3829,9 +4272,9 @@ function Nn(e, t) {
3829
4272
  }
3830
4273
  return n;
3831
4274
  }
3832
- function Pn(e, t) {
4275
+ function tr(e, t) {
3833
4276
  let n = {};
3834
- for (let r of e.columns ?? []) n[r.key] = An(r, t);
4277
+ for (let r of e.columns ?? []) n[r.key] = Zn(r, t);
3835
4278
  for (let e of Object.keys(n)) {
3836
4279
  let t = /^fx(\d)cmd$/.exec(e)?.[1], r = t ? n[`fx${t}val`] : void 0;
3837
4280
  r && n[e].isEmpty && (n[`fx${t}val`] = {
@@ -3840,27 +4283,27 @@ function Pn(e, t) {
3840
4283
  });
3841
4284
  }
3842
4285
  return {
3843
- rowIndex: e.rowField ? Mn(t, e.rowField) : null,
4286
+ rowIndex: e.rowField ? $n(t, e.rowField) : null,
3844
4287
  rowKey: e.rowField?.key ?? "row",
3845
4288
  rowId: null,
3846
4289
  fields: n
3847
4290
  };
3848
4291
  }
3849
- var Fn = (e) => e.replace(/\s+/g, ""), In = 1, Q = (e) => e.slice(1);
3850
- function Ln(e, t) {
4292
+ var nr = (e) => e.replace(/\s+/g, ""), rr = 1, Q = (e) => e.slice(1);
4293
+ function ir(e, t) {
3851
4294
  if (!t || !e.cursorPos || !e.currentLine) return !1;
3852
4295
  let n = t.lines[e.cursorPos.y];
3853
- return n != null && Fn(n) === Fn(e.currentLine);
4296
+ return n != null && nr(n) === nr(e.currentLine);
3854
4297
  }
3855
- var Rn = 16, zn = 4, Bn = 21;
3856
- function Vn(e, t) {
3857
- let n = G.views[e];
4298
+ var ar = 16, or = 4, sr = 21;
4299
+ function cr(e, t) {
4300
+ let n = W.views[e];
3858
4301
  if (n?.type !== "grid" || !n.rowField) return [];
3859
- let r = [], i = Math.min(Bn, t.lines.length - 1);
3860
- for (let e = zn; e <= i; e += 1) {
4302
+ let r = [], i = Math.min(sr, t.lines.length - 1);
4303
+ for (let e = or; e <= i; e += 1) {
3861
4304
  let i = Q(t.lines[e] ?? ""), a = n.rowField;
3862
- if (i.length <= a.x + a.width || !Hn(i[a.x + a.width])) continue;
3863
- let o = Pn(n, i);
4305
+ if (i.length <= a.x + a.width || !lr(i[a.x + a.width])) continue;
4306
+ let o = tr(n, i);
3864
4307
  o.rowIndex != null && r.push({
3865
4308
  ...o,
3866
4309
  screenRow: e
@@ -3868,11 +4311,11 @@ function Vn(e, t) {
3868
4311
  }
3869
4312
  return r;
3870
4313
  }
3871
- var Hn = (e) => e === " " || /^[^a-zA-Z0-9-]$/.test(e);
3872
- function Un(e, t) {
3873
- let n = G.views[e];
4314
+ var lr = (e) => e === " " || /^[^a-zA-Z0-9-]$/.test(e);
4315
+ function ur(e, t) {
4316
+ let n = W.views[e];
3874
4317
  if (n?.type !== "grid") return null;
3875
- let r = Vn(e, t), i = (e) => !!e && e !== " " && Hn(e);
4318
+ let r = cr(e, t), i = (e) => !!e && e !== " " && lr(e);
3876
4319
  if (e === "song") return {
3877
4320
  tracks: (n.columns ?? []).filter((e) => e.type === "chainRef").map((e) => r.find((n) => i(Q(t.lines[n.screenRow] ?? "")[e.x - 1]))?.rowIndex ?? null),
3878
4321
  row: null
@@ -3886,25 +4329,25 @@ function Un(e, t) {
3886
4329
  row: null
3887
4330
  };
3888
4331
  }
3889
- function Wn(e, t, n, r) {
4332
+ function dr(e, t, n, r) {
3890
4333
  let i = (t.cursorPos?.y ?? -1) - r;
3891
4334
  if (i < 0) return null;
3892
4335
  let a = [], o = null;
3893
- for (let t = 0; t < Rn; t += 1) {
4336
+ for (let t = 0; t < ar; t += 1) {
3894
4337
  let r = n.lines[i + t];
3895
4338
  if (r == null) return null;
3896
- let s = Q(r), c = Pn(e, s);
4339
+ let s = Q(r), c = tr(e, s);
3897
4340
  if (c.rowIndex !== t) return null;
3898
4341
  a.push(c);
3899
4342
  let l = s.indexOf("^^");
3900
- o == null && l >= 0 && (o = Gn(e, l));
4343
+ o == null && l >= 0 && (o = fr(e, l));
3901
4344
  }
3902
4345
  return {
3903
4346
  steps: a,
3904
4347
  carriedRepLane: o
3905
4348
  };
3906
4349
  }
3907
- function Gn(e, t) {
4350
+ function fr(e, t) {
3908
4351
  let n = [];
3909
4352
  for (let r = 1; r <= 3; r += 1) {
3910
4353
  let i = e.columns?.find((e) => e.key === `fx${r}cmd`), a = e.columns?.find((e) => e.key === `fx${r}val`);
@@ -3918,8 +4361,8 @@ function Gn(e, t) {
3918
4361
  let r = Math.min(...n.map((e) => e.dist)), i = n.filter((e) => e.dist === r);
3919
4362
  return r <= 1 && i.length === 1 ? i[0].lane : null;
3920
4363
  }
3921
- function Kn(e) {
3922
- let t = _n(e.fields.interval?.rawValue ?? "");
4364
+ function pr(e) {
4365
+ let t = Rn(e.fields.interval?.rawValue ?? "");
3923
4366
  if (!t) return null;
3924
4367
  let n = e.fields.en?.rawValue.toUpperCase(), r = parseFloat(e.fields.offset?.rawValue ?? "");
3925
4368
  return {
@@ -3929,15 +4372,15 @@ function Kn(e) {
3929
4372
  offset: Number.isNaN(r) ? null : r
3930
4373
  };
3931
4374
  }
3932
- function qn(e, t) {
4375
+ function mr(e, t) {
3933
4376
  let n = /* @__PURE__ */ new Map();
3934
4377
  for (let r of t.lines) {
3935
- let t = Pn(e, Q(r)), i = Kn(t), a = t.fields.en?.rawValue.toUpperCase();
4378
+ let t = tr(e, Q(r)), i = pr(t), a = t.fields.en?.rawValue.toUpperCase();
3936
4379
  !i || a !== "ON" && a !== "OFF" && a !== "--" || n.has(i.interval) || n.set(i.interval, i);
3937
4380
  }
3938
4381
  return n.size === Z.length ? Z.map((e) => n.get(e)) : null;
3939
4382
  }
3940
- function Jn(e, t, n, r) {
4383
+ function hr(e, t, n, r) {
3941
4384
  let i = {
3942
4385
  ...r,
3943
4386
  isGridView: !1,
@@ -3954,40 +4397,40 @@ function Jn(e, t, n, r) {
3954
4397
  resetsRelative: null,
3955
4398
  scaleNote: null,
3956
4399
  scale: null
3957
- }, a = e.cursorPos, o = a ? a.x - 1 : 0, s = a && Ln(e, t) ? t : null, c = s && a ? Q(s.lines[a.y] ?? "") : e.currentLine ?? "";
4400
+ }, a = e.cursorPos, o = a ? a.x - 1 : 0, s = a && ir(e, t) ? t : null, c = s && a ? Q(s.lines[a.y] ?? "") : e.currentLine ?? "";
3958
4401
  if (!c.trim()) return i;
3959
- let l = s && a ? s.lines.slice(0, a.y + 1).reverse().map(Q) : [c], u = r.viewName === "inst" ? Pt(s ? s.lines.map(Q) : [c]) : null, d = {
4402
+ let l = s && a ? s.lines.slice(0, a.y + 1).reverse().map(Q) : [c], u = r.viewName === "inst" ? Gt(s ? s.lines.map(Q) : [c]) : null, d = {
3960
4403
  instrumentKey: u?.key ?? null,
3961
4404
  entryText: e.textUnderCursor
3962
4405
  };
3963
4406
  if (!s && a && n.kind == null) for (let e = 0; e <= Math.min(o, 24); e += 1) {
3964
4407
  let t = " ".repeat(e) + c;
3965
- if (zt(n, t, {
4408
+ if (nn(n, t, {
3966
4409
  ...d,
3967
- section: jt(n, [t])
4410
+ section: Ht(n, [t])
3968
4411
  }).cells.some((e) => o >= e.x && o < e.x + e.width)) {
3969
4412
  c = t;
3970
4413
  break;
3971
4414
  }
3972
4415
  }
3973
- let f = jt(n, s ? l : [c]), { rowId: p, cells: m } = zt(n, c, {
4416
+ let f = Ht(n, s ? l : [c]), { rowId: p, cells: m } = nn(n, c, {
3974
4417
  ...d,
3975
4418
  section: f
3976
4419
  }), h = {};
3977
- for (let e of m) h[e.key] = An(e, c);
4420
+ for (let e of m) h[e.key] = Zn(e, c);
3978
4421
  let g = {
3979
4422
  rowIndex: null,
3980
4423
  rowKey: "param",
3981
4424
  rowId: p,
3982
4425
  fields: h
3983
- }, _ = null, v = a ? Nn(m, o) : null;
4426
+ }, _ = null, v = a ? er(m, o) : null;
3984
4427
  v && (_ = {
3985
4428
  ...h[v.key],
3986
4429
  viewName: r.viewName,
3987
4430
  rowIndex: null
3988
4431
  });
3989
4432
  let y = v?.meta?.zone;
3990
- return s && typeof y == "string" && (f = Mt(n, y, l) ?? f), {
4433
+ return s && typeof y == "string" && (f = Ut(n, y, l) ?? f), {
3991
4434
  ...i,
3992
4435
  section: f,
3993
4436
  instrumentType: u,
@@ -3996,10 +4439,10 @@ function Jn(e, t, n, r) {
3996
4439
  lineText: c.trim()
3997
4440
  };
3998
4441
  }
3999
- function Yn(e, t) {
4442
+ function gr(e, t) {
4000
4443
  let n = e.viewName;
4001
4444
  if (!n) return null;
4002
- let r = G.views[n] ?? null, i = r?.type === "grid", a = r?.title ?? null, o = r?.description ?? null, { viewId: s, isUnsaved: c } = On(e.viewTitle), l = {
4445
+ let r = W.views[n] ?? null, i = r?.type === "grid", a = r?.title ?? null, o = r?.description ?? null, { viewId: s, isUnsaved: c } = Yn(e.viewTitle), l = {
4003
4446
  viewName: n,
4004
4447
  viewTitle: a,
4005
4448
  viewDescription: o,
@@ -4018,8 +4461,8 @@ function Yn(e, t) {
4018
4461
  resetsRelative: null,
4019
4462
  scaleNote: null,
4020
4463
  scale: null
4021
- }, d = r?.type === "parameter" ? kt(n) : null;
4022
- if (d) return Jn(e, t, d, l);
4464
+ }, d = r?.type === "parameter" ? Bt(n) : null;
4465
+ if (d) return hr(e, t, d, l);
4023
4466
  if (!i || !r) return {
4024
4467
  ...u,
4025
4468
  isGridView: !1,
@@ -4033,11 +4476,11 @@ function Yn(e, t) {
4033
4476
  row: null,
4034
4477
  activeField: null
4035
4478
  };
4036
- let { line: p } = kn(f), m = e.cursorPos ? e.cursorPos.x - 1 : 0, h = Pn(r, p), g = n === "scale" && !_n(h.fields.interval?.rawValue ?? "") ? kt(n) : null;
4037
- if (g) return Jn(e, t, g, l);
4479
+ let { line: p } = Xn(f), m = e.cursorPos ? e.cursorPos.x - 1 : 0, h = tr(r, p), g = n === "scale" && !Rn(h.fields.interval?.rawValue ?? "") ? Bt(n) : null;
4480
+ if (g) return hr(e, t, g, l);
4038
4481
  let _ = null;
4039
4482
  if (e.cursorPos) {
4040
- let e = Nn(r.columns ?? [], m);
4483
+ let e = er(r.columns ?? [], m);
4041
4484
  if (e) {
4042
4485
  let t = h.fields[e.key];
4043
4486
  t && (_ = {
@@ -4048,20 +4491,20 @@ function Yn(e, t) {
4048
4491
  }
4049
4492
  }
4050
4493
  let v = null, y = null, b = null, x = null, S = null, C = null;
4051
- if (Ln(e, t)) {
4494
+ if (ir(e, t)) {
4052
4495
  if (n === "phrase" && h.rowIndex != null) {
4053
- let n = Wn(r, e, t, h.rowIndex);
4496
+ let n = dr(r, e, t, h.rowIndex);
4054
4497
  if (n) {
4055
4498
  v = n.steps, y = n.carriedRepLane;
4056
4499
  let e = _ ? /^fx(\d)(cmd|val)$/.exec(_.key)?.[1] : void 0;
4057
- e && (b = on(v, Number(e), h.rowIndex, y === Number(e))), x = ln(v, h.rowIndex, y), h.rowIndex > 0 && cn(h) && (S = ln(v, h.rowIndex - 1, y));
4500
+ e && (b = Dn(v, Number(e), h.rowIndex, y === Number(e))), x = An(v, h.rowIndex, y), h.rowIndex > 0 && kn(h) && (S = An(v, h.rowIndex - 1, y));
4058
4501
  }
4059
4502
  } else if (n === "scale") {
4060
- let e = qn(r, t);
4061
- e && (C = bn(e));
4503
+ let e = mr(r, t);
4504
+ e && (C = Vn(e));
4062
4505
  }
4063
4506
  }
4064
- let w = n === "scale" ? Kn(h) : null;
4507
+ let w = n === "scale" ? pr(h) : null;
4065
4508
  return {
4066
4509
  ...u,
4067
4510
  isGridView: !0,
@@ -4077,25 +4520,25 @@ function Yn(e, t) {
4077
4520
  scale: C
4078
4521
  };
4079
4522
  }
4080
- function Xn(e) {
4081
- if (e.isParameterView) return ir(e);
4523
+ function _r(e) {
4524
+ if (e.isParameterView) return Er(e);
4082
4525
  if (e.viewName === "scale") {
4083
- let t = ar(e);
4526
+ let t = Dr(e);
4084
4527
  if (t) return t;
4085
4528
  }
4086
4529
  if (!e.activeField) return $(["location", e.viewTitle ?? e.viewName]);
4087
- let { activeField: t, row: n } = e, r = $(["field", `${t.label}: ${sr(t)}`]), i = (t.type === "fxCommand" || t.type === "fxValue") && n ? Qn(e, t, n) : tr(e, t), a = i.length > 0;
4530
+ let { activeField: t, row: n } = e, r = $(["field", `${t.label}: ${kr(t)}`]), i = (t.type === "fxCommand" || t.type === "fxValue") && n ? yr(e, t, n) : [...Sr(e, t), ...Cr(e, t)], a = i.length > 0;
4088
4531
  if (t.type === "fxCommand" && !t.isEmpty) {
4089
- let e = a && /^(REP|RTO)$/i.test(t.rawValue) ? void 0 : qt(t.rawValue);
4532
+ let e = a && /^(REP|RTO)$/i.test(t.rawValue) ? void 0 : dn(t.rawValue);
4090
4533
  e && r.push({
4091
4534
  kind: "fxInfo",
4092
- text: `(${$n(e)})`
4535
+ text: `(${br(e)})`
4093
4536
  });
4094
4537
  }
4095
4538
  if (t.type === "fxValue" && n) {
4096
4539
  let e = t.key.replace("val", "cmd"), i = n.fields[e];
4097
4540
  if (i && !i.isEmpty) {
4098
- let e = a && /^(REP|RTO)$/i.test(i.rawValue) ? void 0 : qt(i.rawValue), t = e ? `${i.rawValue} (${$n(e)})` : i.rawValue;
4541
+ let e = a && /^(REP|RTO)$/i.test(i.rawValue) ? void 0 : dn(i.rawValue), t = e ? `${i.rawValue} (${br(e)})` : i.rawValue;
4099
4542
  r.push({
4100
4543
  kind: "fxTarget",
4101
4544
  text: `for ${t}`
@@ -4114,8 +4557,8 @@ function Xn(e) {
4114
4557
  }
4115
4558
  return r;
4116
4559
  }
4117
- function Zn(e) {
4118
- return Xn(e).map((e) => e.text).join(" — ");
4560
+ function vr(e) {
4561
+ return _r(e).map((e) => e.text).join(" — ");
4119
4562
  }
4120
4563
  function $(...e) {
4121
4564
  return e.flatMap(([e, t]) => t ? [{
@@ -4123,32 +4566,33 @@ function $(...e) {
4123
4566
  text: t
4124
4567
  }] : []);
4125
4568
  }
4126
- function Qn(e, t, n) {
4569
+ function yr(e, t, n) {
4127
4570
  let r = /^fx(\d)/.exec(t.key)?.[1];
4128
4571
  if (!r) return [];
4129
4572
  let i = n.fields[`fx${r}cmd`], a = n.fields[`fx${r}val`];
4130
4573
  if (!i || i.isEmpty) {
4131
4574
  if (e.fxLane?.event !== "continue") return [];
4132
- let t = e.fxLane.source?.cmd, n = t && J(t) ? pn(e.relativeFx?.[t]) : null;
4133
- return $(["fxLane", sn(e.fxLane)], ["relative", n]);
4575
+ let t = e.fxLane.source?.cmd, n = t && q(t) ? Pn(e.relativeFx?.[t]) : null;
4576
+ return $(["fxLane", On(e.fxLane)], ["relative", n]);
4134
4577
  }
4135
- let o = i.rawValue.toUpperCase(), s = e.fxLane && e.fxLane.event !== "none" && e.fxLane.event !== "set" ? sn(e.fxLane) : null;
4136
- if (o === "REP" || o === "RTO") {
4137
- let t = e.fxLane?.source?.cmd, n = o === "REP" && e.fxLane?.active && t && J(t) ? pn(e.relativeFx?.[t]) : null;
4578
+ let o = i.rawValue.toUpperCase(), s = e.fxLane && e.fxLane.event !== "none" && e.fxLane.event !== "set" ? On(e.fxLane) : null;
4579
+ if ((o === "REP" || o === "RTO") && e.viewName !== "table") {
4580
+ let t = e.fxLane?.source?.cmd, n = o === "REP" && e.fxLane?.active && t && q(t) ? Pn(e.relativeFx?.[t]) : null;
4138
4581
  return $(["fxLane", s], ["relative", n]);
4139
4582
  }
4140
4583
  if (!a || a.isEmpty || a.hexValue == null) return $(["fxLane", s]);
4141
- let c = t.type === "fxCommand" ? `value ${Zt(o, a.hexValue)}: ` : "", l = rn(o, a.hexValue, {
4584
+ let c = t.type === "fxCommand" ? `value ${gn(o, a.hexValue)}: ` : "", l = Tn(o, a.hexValue, {
4142
4585
  note: n.fields.note?.isEmpty ? null : n.fields.note?.rawValue,
4143
- inTable: e.viewName === "table"
4586
+ inTable: e.viewName === "table",
4587
+ row: n.rowIndex
4144
4588
  }), u = null;
4145
- return J(o) ? u = pn(e.relativeFx?.[o]) : o === "RET" && e.resetsRelative && (u = mn(e.resetsRelative)), $(["meaning", l ? `${c}${l}` : null], ["fxLane", s], ["relative", u]);
4589
+ return q(o) ? u = Pn(e.relativeFx?.[o]) : o === "RET" && e.resetsRelative && (u = Fn(e.resetsRelative)), $(["meaning", l ? `${c}${l}` : null], ["fxLane", s], ["relative", u]);
4146
4590
  }
4147
- function $n(e) {
4591
+ function br(e) {
4148
4592
  let t = e.description.replace(/\.$/, "");
4149
- return e.instrumentTypes ? `${t}; offered by ${e.instrumentTypes.map((e) => er[e] ?? e).join(", ")} instruments` : t;
4593
+ return e.instrumentTypes ? `${t}; offered by ${e.instrumentTypes.map((e) => xr[e] ?? e).join(", ")} instruments` : t;
4150
4594
  }
4151
- var er = {
4595
+ var xr = {
4152
4596
  sampler: "Sampler",
4153
4597
  macrosynth: "Macrosynth",
4154
4598
  fmsynth: "FM Synth",
@@ -4157,14 +4601,17 @@ var er = {
4157
4601
  external: "External",
4158
4602
  midiout: "MIDI Out"
4159
4603
  };
4160
- function tr(e, t) {
4161
- return t.type !== "instrumentRef" || t.isEmpty || !e.resetsRelative ? [] : $(["relative", mn(e.resetsRelative)]);
4604
+ function Sr(e, t) {
4605
+ return t.type !== "instrumentRef" || t.isEmpty || !e.resetsRelative ? [] : $(["relative", Fn(e.resetsRelative)]);
4162
4606
  }
4163
- var nr = {
4607
+ function Cr(e, t) {
4608
+ return e.viewName !== "table" || t.key !== "volume" || t.isEmpty || t.hexValue == null || t.hexValue > 127 ? [] : $(["meaning", `plays at ${Math.round(t.hexValue / 127 * 100)}% of the step's velocity`]);
4609
+ }
4610
+ var wr = {
4164
4611
  MODFX: "ModFX",
4165
4612
  DELAY: "Delay",
4166
4613
  REVERB: "Reverb"
4167
- }, rr = {
4614
+ }, Tr = {
4168
4615
  parent: "goes up to the parent directory",
4169
4616
  directory: "directory: [EDIT] opens it",
4170
4617
  instrumentPreset: "instrument preset: [EDIT] selects it",
@@ -4172,38 +4619,38 @@ var nr = {
4172
4619
  m8file: "M8 file: [EDIT] selects it",
4173
4620
  file: "file: [EDIT] selects it"
4174
4621
  };
4175
- function ir(e) {
4622
+ function Er(e) {
4176
4623
  let t = `${e.viewTitle ?? e.viewName}${e.viewId ? ` ${e.viewId}` : ""}`, n = e.activeField;
4177
4624
  if (!n) return $(["location", t], ["line", e.lineText]);
4178
- let r = e.instrumentType?.key ? Ft(e.instrumentType.key) : null;
4179
- return $(["field", `${n.label}: ${sr(n)}`], ["optionInfo", n.valueDescription], ["fieldInfo", n.description], ["fileInfo", n.fileKind && rr[n.fileKind]], ["instrument", e.instrumentType && `${r?.name ?? e.instrumentType.text} instrument`], ["section", e.section && (nr[e.section] ?? e.section)], ["location", t]);
4625
+ let r = e.instrumentType?.key ? Kt(e.instrumentType.key) : null;
4626
+ return $(["field", `${n.label}: ${kr(n)}`], ["optionInfo", n.valueDescription], ["fieldInfo", n.description], ["fileInfo", n.fileKind && Tr[n.fileKind]], ["instrument", e.instrumentType && `${r?.name ?? e.instrumentType.text} instrument`], ["section", e.section && (wr[e.section] ?? e.section)], ["location", t]);
4180
4627
  }
4181
- function ar(e) {
4628
+ function Dr(e) {
4182
4629
  let t = `${e.viewTitle ?? e.viewName}${e.viewId ? ` ${e.viewId}` : ""}`, n = e.activeField, r = e.scaleNote;
4183
4630
  if (!n || !r) return e.lineText ? $(["location", t], ["line", e.lineText]) : null;
4184
4631
  let { interval: i, enabled: a, offset: o } = r, s;
4185
4632
  if (n.key === "offset") {
4186
- let e = o == null ? `detune of ${i}` : o === 0 ? `${i} is not detuned` : `${i} is detuned by ${Sn(o)}`;
4633
+ let e = o == null ? `detune of ${i}` : o === 0 ? `${i} is not detuned` : `${i} is detuned by ${Un(o)}`;
4187
4634
  s = $(["field", `${n.label}: ${n.rawValue || "(empty)"}`], ["meaning", e], ["meaning", a === !1 ? "(note is OFF)" : null]);
4188
4635
  } else {
4189
4636
  let e = a === !0 ? "is in" : a === !1 ? "is not in" : "is not yet set in";
4190
- s = $(["field", `${n.label}: ${n.key === "en" ? n.rawValue.toUpperCase() || "(unset)" : i}`], ["meaning", i], ["meaning", vn(i)], ["meaning", `${e} the scale`]);
4637
+ s = $(["field", `${n.label}: ${n.key === "en" ? n.rawValue.toUpperCase() || "(unset)" : i}`], ["meaning", i], ["meaning", zn(i)], ["meaning", `${e} the scale`]);
4191
4638
  }
4192
4639
  if (e.scale) {
4193
- let [t, n] = xn(e.scale);
4640
+ let [t, n] = Hn(e.scale);
4194
4641
  s.push(...$(["scale", `Scale: ${t}`], ["scale", n]));
4195
4642
  }
4196
4643
  return [...s, ...$(["location", `${t} row ${i}`])];
4197
4644
  }
4198
- function or(e) {
4645
+ function Or(e) {
4199
4646
  let t = e.unit ?? "";
4200
4647
  if (e.values && e.parts) {
4201
4648
  let n = e.hexValue != null, r = e.values.map((e) => n ? e.toString(16).toUpperCase().padStart(2, "0") : String(e)), i = e.parts.map((e, n) => `${e} ${r[n] ?? "?"}${t}`).join(", ");
4202
- return `${r.join(":")} (${i})`;
4649
+ return `${r.join(e.rawValue.includes("/") ? "/" : ":")} (${i})`;
4203
4650
  }
4204
4651
  return e.hexValue == null ? e.numericValue == null ? e.text ?? e.rawValue : `${e.rawValue}${t}` : `${e.rawValue.slice(0, 2)}h (dec ${e.hexValue})${e.text ? ` ${e.text}` : ""}`;
4205
4652
  }
4206
- function sr(e) {
4653
+ function kr(e) {
4207
4654
  if (e.isEmpty) return "(empty)";
4208
4655
  let t = e.type === "name" || e.type === "fileEntry" ? e : {
4209
4656
  ...e,
@@ -4230,26 +4677,26 @@ function sr(e) {
4230
4677
  case "fileEntry": return t.text ?? t.rawValue;
4231
4678
  case "name": return `"${t.rawValue}"`;
4232
4679
  case "option": return t.hexValue != null && t.text ? `${t.rawValue.slice(0, 2)} ${t.text}` : t.text ?? t.rawValue;
4233
- case "parameter": return or(t);
4234
- case "noteInterval": return `Interval ${_n(t.rawValue) ?? t.rawValue}`;
4680
+ case "parameter": return Or(t);
4681
+ case "noteInterval": return `Interval ${Rn(t.rawValue) ?? t.rawValue}`;
4235
4682
  case "semitoneOffset": return t.isEmpty ? "(empty)" : `${t.rawValue} st`;
4236
4683
  default: return t.rawValue;
4237
4684
  }
4238
4685
  }
4239
4686
  //#endregion
4240
4687
  //#region src/client.ts
4241
- var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4688
+ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4242
4689
  let t = e.remoteHandle();
4243
4690
  return {
4244
4691
  call: (e, ...n) => t.call(e, ...n),
4245
4692
  on: (e, n) => (t.addEventListener(e, n), () => t.removeEventListener(e, n)),
4246
4693
  close: () => e.close()
4247
4694
  };
4248
- }, dr = (e) => ({
4695
+ }, Nr = (e) => ({
4249
4696
  call: (t, ...n) => e.methods[t](...n),
4250
4697
  on: (t, n) => e.on(t, n),
4251
4698
  close: () => e.dispose()
4252
- }), fr = (e) => {
4699
+ }), Pr = (e) => {
4253
4700
  let t = [];
4254
4701
  return {
4255
4702
  messenger: {
@@ -4263,21 +4710,21 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4263
4710
  for (let e of t.splice(0)) e();
4264
4711
  }
4265
4712
  };
4266
- }, pr = () => {
4713
+ }, Fr = () => {
4267
4714
  if (typeof window > "u") return !1;
4268
4715
  try {
4269
4716
  return window.parent !== window;
4270
4717
  } catch {
4271
4718
  return !0;
4272
4719
  }
4273
- }, mr = (e) => e instanceof DOMException && (e.name === "NotFoundError" || e.name === "AbortError"), hr = () => {
4720
+ }, Ir = (e) => e instanceof DOMException && (e.name === "NotFoundError" || e.name === "AbortError"), Lr = () => {
4274
4721
  let e = typeof navigator > "u" ? null : navigator;
4275
4722
  return {
4276
4723
  serial: !!e && "serial" in e,
4277
4724
  usb: !!e && "usb" in e,
4278
4725
  midi: !!e && "requestMIDIAccess" in e
4279
4726
  };
4280
- }, gr = () => ({
4727
+ }, Rr = () => ({
4281
4728
  viewName: null,
4282
4729
  viewTitle: null,
4283
4730
  minimapKey: null,
@@ -4294,9 +4741,9 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4294
4741
  fontMode: null,
4295
4742
  systemInfo: null,
4296
4743
  macroRunning: !1
4297
- }), _r = class {
4744
+ }), zr = class {
4298
4745
  constructor(e = {}) {
4299
- this.backend = null, this.backendUnsubscribers = [], this.device = null, this._mode = null, this.audio = null, this.connecting = null, this.stateCallbacks = /* @__PURE__ */ new Set(), this.viewCallbacks = /* @__PURE__ */ new Set(), this.cursorCallbacks = /* @__PURE__ */ new Set(), this.textCallbacks = /* @__PURE__ */ new Set(), this.keyCallbacks = /* @__PURE__ */ new Set(), this.transportCallbacks = /* @__PURE__ */ new Set(), this.connectionCallbacks = /* @__PURE__ */ new Set(), this._state = gr(), this._screen = null, this.screenRefreshing = !1, this.screenDirty = !1, this.config = e;
4746
+ this.backend = null, this.backendUnsubscribers = [], this.device = null, this._mode = null, this.audio = null, this.connecting = null, this.stateCallbacks = /* @__PURE__ */ new Set(), this.viewCallbacks = /* @__PURE__ */ new Set(), this.cursorCallbacks = /* @__PURE__ */ new Set(), this.textCallbacks = /* @__PURE__ */ new Set(), this.keyCallbacks = /* @__PURE__ */ new Set(), this.transportCallbacks = /* @__PURE__ */ new Set(), this.connectionCallbacks = /* @__PURE__ */ new Set(), this._state = Rr(), this._screen = null, this.screenRefreshing = !1, this.screenDirty = !1, this.config = e;
4300
4747
  }
4301
4748
  get state() {
4302
4749
  return this._state;
@@ -4318,11 +4765,11 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4318
4765
  }
4319
4766
  async resolveMode() {
4320
4767
  let e = this.config.mode ?? "auto";
4321
- if (e === "standalone" || e === "auto" && !pr()) {
4768
+ if (e === "standalone" || e === "auto" && !Fr()) {
4322
4769
  await this.startStandalone();
4323
4770
  return;
4324
4771
  }
4325
- let t = typeof window < "u" && window.name === "yam8d-external-app", n = e === "host" || t ? null : this.config.handshakeTimeoutMs ?? lr;
4772
+ let t = typeof window < "u" && window.name === "yam8d-external-app", n = e === "host" || t ? null : this.config.handshakeTimeoutMs ?? jr;
4326
4773
  await this.handshake(n) || (this.config.debug && console.log("[M8 SDK] No yam8d host answered, switching to standalone mode"), await this.startStandalone());
4327
4774
  }
4328
4775
  async handshake(r) {
@@ -4334,10 +4781,10 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4334
4781
  this.config.debug && (i = t(i, (e, ...t) => {
4335
4782
  console.log("[M8 SDK]", e, ...t);
4336
4783
  }));
4337
- let a = fr(i), o = e(a.messenger, { ping: async () => "pong" }), s, c = new Promise((e) => {
4784
+ let a = Pr(i), o = e(a.messenger, { ping: async () => "pong" }), s, c = new Promise((e) => {
4338
4785
  r !== null && (s = setTimeout(() => e(null), r));
4339
4786
  }), l = await Promise.race([o, c]);
4340
- return clearTimeout(s), l ? (this._mode = "host", await this.attach(ur(l)), l.localHandle().emit("ready", void 0), !0) : (a.dispose(), !1);
4787
+ return clearTimeout(s), l ? (this._mode = "host", await this.attach(Mr(l)), l.localHandle().emit("ready", void 0), !0) : (a.dispose(), !1);
4341
4788
  }
4342
4789
  async startStandalone() {
4343
4790
  if (this._mode = "standalone", this.config.autoConnect !== !1) try {
@@ -4363,14 +4810,14 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4363
4810
  });
4364
4811
  break;
4365
4812
  } catch (r) {
4366
- if (a = r, t && !mr(r) && (i = await ee(() => n.connect({
4813
+ if (a = r, t && !Ir(r) && (i = await ee(() => n.connect({
4367
4814
  transport: e,
4368
4815
  prompt: !1
4369
4816
  }), { maxAttempts: 2 }), i)) break;
4370
4817
  }
4371
4818
  if (!i) throw a;
4372
- let o = Ct(i, { debug: this.config.debug });
4373
- this.device = i, await this.attach(dr(o)), i.commands.resetScreen();
4819
+ let o = Pt(i, { debug: this.config.debug });
4820
+ this.device = i, await this.attach(Nr(o)), i.commands.resetScreen();
4374
4821
  let s = i;
4375
4822
  i.closed.then(() => {
4376
4823
  this.device === s && (this.config.debug && console.log("[M8 SDK] Device connection ended"), this.device = null, this.detach());
@@ -4520,7 +4967,7 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4520
4967
  }, e;
4521
4968
  }
4522
4969
  async captureSnapshot() {
4523
- let [e, t] = await Promise.all([this.fetchState(), this.getScreen()]), n = Yn(e, t);
4970
+ let [e, t] = await Promise.all([this.fetchState(), this.getScreen()]), n = gr(e, t);
4524
4971
  return {
4525
4972
  capturedAt: Date.now(),
4526
4973
  state: {
@@ -4533,8 +4980,8 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4533
4980
  deviceModel: e.deviceModel
4534
4981
  },
4535
4982
  semantic: n,
4536
- description: n ? Zn(n) : null,
4537
- descriptionParts: n ? Xn(n) : null,
4983
+ description: n ? vr(n) : null,
4984
+ descriptionParts: n ? _r(n) : null,
4538
4985
  screen: t
4539
4986
  };
4540
4987
  }
@@ -4563,23 +5010,23 @@ var cr = "yam8d-external-app", lr = 3e3, ur = (e) => {
4563
5010
  this.audio?.disconnect(), this.closeDevice(), this.detach(), this.stateCallbacks.clear(), this.viewCallbacks.clear(), this.cursorCallbacks.clear(), this.textCallbacks.clear(), this.keyCallbacks.clear(), this.transportCallbacks.clear(), this.connectionCallbacks.clear();
4564
5011
  }
4565
5012
  getSemanticContext() {
4566
- return Yn(this._state, this._screen);
5013
+ return gr(this._state, this._screen);
4567
5014
  }
4568
5015
  describeContext() {
4569
- let e = Yn(this._state, this._screen);
4570
- return e ? Zn(e) : null;
5016
+ let e = gr(this._state, this._screen);
5017
+ return e ? vr(e) : null;
4571
5018
  }
4572
5019
  describeContextParts() {
4573
- let e = Yn(this._state, this._screen);
4574
- return e ? Xn(e) : null;
5020
+ let e = gr(this._state, this._screen);
5021
+ return e ? _r(e) : null;
4575
5022
  }
4576
5023
  };
4577
- async function vr(e = {}) {
4578
- let t = new _r(e);
5024
+ async function Br(e = {}) {
5025
+ let t = new zr(e);
4579
5026
  return await t.connect(), t;
4580
5027
  }
4581
- function yr(e = {}) {
4582
- let t = new _r(e);
5028
+ function Vr(e = {}) {
5029
+ let t = new zr(e);
4583
5030
  return {
4584
5031
  client: t,
4585
5032
  connect: async () => {
@@ -4587,10 +5034,10 @@ function yr(e = {}) {
4587
5034
  }
4588
5035
  };
4589
5036
  }
4590
- var br = {
4591
- createM8Client: vr,
4592
- createM8ClientSync: yr
4593
- }, xr = [
5037
+ var Hr = {
5038
+ createM8Client: Br,
5039
+ createM8ClientSync: Vr
5040
+ }, Ur = [
4594
5041
  {
4595
5042
  role: "background",
4596
5043
  label: "BACKGROUND"
@@ -4643,7 +5090,7 @@ var br = {
4643
5090
  role: "meterPeak",
4644
5091
  label: "METER PEAK"
4645
5092
  }
4646
- ], Sr = "themesettings", Cr = (e) => e.replace(/\s+/g, "").toUpperCase(), wr = (e) => Math.max(0, Math.min(255, Math.round(e))).toString(16).padStart(2, "0"), Tr = (e, t, n) => `#${wr(e)}${wr(t)}${wr(n)}`, Er = (e, t, n) => {
5093
+ ], Wr = "themesettings", Gr = (e) => e.replace(/\s+/g, "").toUpperCase(), Kr = (e) => Math.max(0, Math.min(255, Math.round(e))).toString(16).padStart(2, "0"), qr = (e, t, n) => `#${Kr(e)}${Kr(t)}${Kr(n)}`, Jr = (e, t, n) => {
4647
5094
  let r = e / 256 * 6, i = t / 255, a = n / 255, o = Math.floor(r) % 6, s = r - Math.floor(r), c = a * (1 - i), l = a * (1 - s * i), u = a * (1 - (1 - s) * i), [d, f, p] = [
4648
5095
  [
4649
5096
  a,
@@ -4676,14 +5123,14 @@ var br = {
4676
5123
  l
4677
5124
  ]
4678
5125
  ][o];
4679
- return Tr(d * 255, f * 255, p * 255);
4680
- }, Dr = new Map(xr.map(({ role: e, label: t }) => [Cr(t), e]));
4681
- function Or(e) {
5126
+ return qr(d * 255, f * 255, p * 255);
5127
+ }, Yr = new Map(Ur.map(({ role: e, label: t }) => [Gr(t), e]));
5128
+ function Xr(e) {
4682
5129
  let t = "rgb", n = null, r = {};
4683
5130
  for (let i of e.lines) {
4684
5131
  let e = i.replace(/[{}]/g, "0"), a = /^\s*MODE\s+([A-Z])\s+([A-Z])\s+([A-Z])\b/i.exec(e);
4685
5132
  if (a) {
4686
- t = Cr(a.slice(1).join("")) === "HSV" ? "hsv" : "rgb";
5133
+ t = Gr(a.slice(1).join("")) === "HSV" ? "hsv" : "rgb";
4687
5134
  continue;
4688
5135
  }
4689
5136
  let o = /^\s*THEME\s+NAME\s+(\S.*?)\s*$/i.exec(e);
@@ -4694,23 +5141,23 @@ function Or(e) {
4694
5141
  }
4695
5142
  let s = /^\s*([A-Z][A-Z:/. ]*?)\s+([0-9A-F]{2})\s+([0-9A-F]{2})\s+([0-9A-F]{2})(?:\s|$)/i.exec(e);
4696
5143
  if (!s) continue;
4697
- let c = Dr.get(Cr(s[1]));
5144
+ let c = Yr.get(Gr(s[1]));
4698
5145
  if (!c || r[c]) continue;
4699
5146
  let [l, u, d] = s.slice(2, 5).map((e) => parseInt(e, 16));
4700
- r[c] = t === "hsv" ? Er(l, u, d) : Tr(l, u, d);
5147
+ r[c] = t === "hsv" ? Jr(l, u, d) : qr(l, u, d);
4701
5148
  }
4702
- return xr.some(({ role: e }) => !r[e]) ? null : {
5149
+ return Ur.some(({ role: e }) => !r[e]) ? null : {
4703
5150
  colors: r,
4704
5151
  mode: t,
4705
5152
  name: n
4706
5153
  };
4707
5154
  }
4708
- var kr = (e) => e ? Tr(e.r, e.g, e.b) : void 0;
4709
- function Ar(e) {
4710
- let t = {}, n = kr(e.backgroundColor), r = kr(e.titleColor);
5155
+ var Zr = (e) => e ? qr(e.r, e.g, e.b) : void 0;
5156
+ function Qr(e) {
5157
+ let t = {}, n = Zr(e.backgroundColor), r = Zr(e.titleColor);
4711
5158
  return n && (t.background = n), r && (t.textTitles = r), t;
4712
5159
  }
4713
5160
  //#endregion
4714
- export { I as M8KeyMask, j as M8_AUDIO_CAPTURE_CONSTRAINTS, xr as M8_THEME_ROLES, Sr as M8_THEME_VIEW, Yt as RELATIVE_FX_RULE, In as SCREEN_MARGIN, cr as YAM8D_FRAME_NAME, F as createM8Audio, vr as createM8Client, yr as createM8ClientSync, A as createM8Connection, Ct as createM8Engine, br as default, Zn as describeContext, Xn as describeContextParts, sr as formatFieldValue, hr as getDeviceSupport, Jt as getInstrumentFxCommands, P as getM8InputStream, Yn as getSemanticContext, qt as lookupFxCommand, Ar as observeThemeColors, Vn as parseGridScreen, Or as parseThemeScreen, Dn as parseViewTitle, te as pressKeys, Un as readPlayPositions };
5161
+ export { I as M8KeyMask, j as M8_AUDIO_CAPTURE_CONSTRAINTS, Ur as M8_THEME_ROLES, Wr as M8_THEME_VIEW, pn as RELATIVE_FX_RULE, rr as SCREEN_MARGIN, Ar as YAM8D_FRAME_NAME, F as createM8Audio, Br as createM8Client, Vr as createM8ClientSync, A as createM8Connection, Pt as createM8Engine, Hr as default, vr as describeContext, _r as describeContextParts, kr as formatFieldValue, Lr as getDeviceSupport, fn as getInstrumentFxCommands, P as getM8InputStream, gr as getSemanticContext, dn as lookupFxCommand, Qr as observeThemeColors, cr as parseGridScreen, Xr as parseThemeScreen, Jn as parseViewTitle, te as pressKeys, ur as readPlayPositions };
4715
5162
 
4716
5163
  //# sourceMappingURL=index.js.map