@yam8d/m8-sdk 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +803 -800
- package/dist/index.js +1238 -803
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/types/client.d.ts +1 -1
- package/types/engine/cursorMap.d.ts +60 -0
- package/types/engine/cursorNavigator.d.ts +15 -8
- package/types/engine/cursorTargets.d.ts +86 -0
- package/types/engine/engine.d.ts +5 -2
- package/types/engine/host/hostMethods.d.ts +1 -1
- package/types/engine/index.d.ts +1 -0
- package/types/engine/viewNavigation.d.ts +1 -1
- package/types/parameterViews.d.ts +9 -0
- package/types/types.d.ts +2 -1
package/dist/index.js
CHANGED
|
@@ -521,7 +521,7 @@ var D = (e) => {
|
|
|
521
521
|
serial: "Web Serial",
|
|
522
522
|
usb: "WebUSB",
|
|
523
523
|
midi: "Web MIDI"
|
|
524
|
-
},
|
|
524
|
+
}, ee = () => {
|
|
525
525
|
let e = "requestMIDIAccess" in navigator, t = "serial" in navigator, n = "usb" in navigator;
|
|
526
526
|
if (!e && !t && !n) return { browserSupport: !1 };
|
|
527
527
|
let r = async (e) => {
|
|
@@ -624,26 +624,26 @@ var D = (e) => {
|
|
|
624
624
|
};
|
|
625
625
|
}
|
|
626
626
|
};
|
|
627
|
-
},
|
|
627
|
+
}, te = async (e, { maxAttempts: t = 3, retryDelayMs: n = 1500 } = {}) => {
|
|
628
628
|
for (let r = 1; r <= t; r++) try {
|
|
629
629
|
return await e();
|
|
630
630
|
} catch (e) {
|
|
631
631
|
console.warn(`Connection attempt ${r}/${t} failed:`, e), r < t ? await new Promise((e) => setTimeout(e, n)) : console.error("Could not connect to M8 after all attempts:", e);
|
|
632
632
|
}
|
|
633
|
-
},
|
|
633
|
+
}, A = {
|
|
634
634
|
autoGainControl: !1,
|
|
635
635
|
channelCount: { ideal: 2 },
|
|
636
636
|
echoCancellation: !1,
|
|
637
637
|
noiseSuppression: !1
|
|
638
|
-
},
|
|
638
|
+
}, j = async () => {
|
|
639
639
|
let e = (await navigator.mediaDevices.enumerateDevices()).filter((e) => e.kind === "audioinput" && e.label.includes("M8") && e.deviceId !== "default" && e.deviceId !== "communications");
|
|
640
640
|
return e.length > 1 && console.warn("Suspicious: More than one M8 audio input found"), e[0] ?? null;
|
|
641
|
-
},
|
|
641
|
+
}, M = async () => navigator.mediaDevices?.enumerateDevices ? j() : null, N = async ({ requireM8: e = !1 } = {}) => {
|
|
642
642
|
if (!navigator.mediaDevices?.getUserMedia) throw Error("Audio capture is not supported in this browser");
|
|
643
|
-
let t = await
|
|
643
|
+
let t = await M();
|
|
644
644
|
if (!t) {
|
|
645
|
-
let n = await navigator.mediaDevices.getUserMedia({ audio:
|
|
646
|
-
if (t = await
|
|
645
|
+
let n = await navigator.mediaDevices.getUserMedia({ audio: A });
|
|
646
|
+
if (t = await M(), !t) {
|
|
647
647
|
if (!e) return n;
|
|
648
648
|
for (let e of n.getTracks()) e.stop();
|
|
649
649
|
throw Error("No M8 audio input found (is the M8 connected over USB?)");
|
|
@@ -651,10 +651,10 @@ var D = (e) => {
|
|
|
651
651
|
for (let e of n.getTracks()) e.stop();
|
|
652
652
|
}
|
|
653
653
|
return navigator.mediaDevices.getUserMedia({ audio: {
|
|
654
|
-
...
|
|
654
|
+
...A,
|
|
655
655
|
deviceId: { exact: t.deviceId }
|
|
656
656
|
} });
|
|
657
|
-
},
|
|
657
|
+
}, P = () => {
|
|
658
658
|
let e = null, t = null, n = null, r = null, i = (e) => {
|
|
659
659
|
if (e.state !== "suspended" || typeof window > "u") return;
|
|
660
660
|
let t = () => {
|
|
@@ -666,7 +666,7 @@ var D = (e) => {
|
|
|
666
666
|
let r = e;
|
|
667
667
|
e = null, t = null, n = null, await r?.close().catch(() => void 0);
|
|
668
668
|
}, o = async (r) => {
|
|
669
|
-
let o = await
|
|
669
|
+
let o = await N({ requireM8: r }), s = new AudioContext(), c = s.createMediaStreamSource(o), l = s.createMediaStreamDestination();
|
|
670
670
|
c.connect(s.destination), c.connect(l), e = s, t = o, n = l.stream;
|
|
671
671
|
for (let e of o.getAudioTracks()) e.addEventListener("ended", () => {
|
|
672
672
|
t === o && a();
|
|
@@ -688,7 +688,437 @@ var D = (e) => {
|
|
|
688
688
|
return e.length <= 0 ? null : new MediaStream(e.map((e) => e.clone()));
|
|
689
689
|
}
|
|
690
690
|
};
|
|
691
|
+
}, ne = [
|
|
692
|
+
"up",
|
|
693
|
+
"down",
|
|
694
|
+
"left",
|
|
695
|
+
"right"
|
|
696
|
+
], re = 64, F = (e) => `${e.x},${e.y},${e.w},${e.h}`, ie = (e) => {
|
|
697
|
+
let [t, n, r, i] = e.split(",").map(Number);
|
|
698
|
+
return [
|
|
699
|
+
t,
|
|
700
|
+
n,
|
|
701
|
+
r,
|
|
702
|
+
i
|
|
703
|
+
].every(Number.isFinite) ? {
|
|
704
|
+
x: t,
|
|
705
|
+
y: n,
|
|
706
|
+
w: r,
|
|
707
|
+
h: i
|
|
708
|
+
} : null;
|
|
709
|
+
}, ae = () => ({
|
|
710
|
+
cells: /* @__PURE__ */ new Map(),
|
|
711
|
+
moves: /* @__PURE__ */ new Map(),
|
|
712
|
+
unreachable: /* @__PURE__ */ new Set(),
|
|
713
|
+
suspects: /* @__PURE__ */ new Set()
|
|
714
|
+
}), oe = (e = {}) => {
|
|
715
|
+
let t = /* @__PURE__ */ new Map(), n = () => e.onChange?.(), r = (e) => {
|
|
716
|
+
let n = t.get(e);
|
|
717
|
+
for (n ? t.delete(e) : n = ae(), t.set(e, n); t.size > re;) {
|
|
718
|
+
let e = t.keys().next().value;
|
|
719
|
+
if (e === void 0) break;
|
|
720
|
+
t.delete(e);
|
|
721
|
+
}
|
|
722
|
+
return n;
|
|
723
|
+
}, i = (e, t) => {
|
|
724
|
+
let n = F(t);
|
|
725
|
+
return !e.cells.has(n) && (e.cells.set(n, {
|
|
726
|
+
x: t.x,
|
|
727
|
+
y: t.y,
|
|
728
|
+
w: t.w,
|
|
729
|
+
h: t.h
|
|
730
|
+
}), !0);
|
|
731
|
+
};
|
|
732
|
+
if (e.data?.version === 1) for (let t of e.data.layouts ?? []) {
|
|
733
|
+
let e = r(t.key);
|
|
734
|
+
for (let n of t.cells ?? []) {
|
|
735
|
+
let t = ie(n);
|
|
736
|
+
t && i(e, t);
|
|
737
|
+
}
|
|
738
|
+
for (let [n, r, i] of t.moves ?? []) {
|
|
739
|
+
if (!ne.includes(r)) continue;
|
|
740
|
+
let t = e.moves.get(n) ?? {};
|
|
741
|
+
t[r] = i, e.moves.set(n, t);
|
|
742
|
+
}
|
|
743
|
+
for (let n of t.unreachable ?? []) e.unreachable.add(n);
|
|
744
|
+
for (let n of t.suspects ?? []) e.suspects.add(n);
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
get: (e) => t.get(e),
|
|
748
|
+
recordCell: (e, t) => {
|
|
749
|
+
i(r(e), t) && n();
|
|
750
|
+
},
|
|
751
|
+
recordMove: (e, t, a, o) => {
|
|
752
|
+
let s = r(e);
|
|
753
|
+
i(s, t), o && i(s, o);
|
|
754
|
+
let c = F(t), l = o ? F(o) : null, u = s.moves.get(c) ?? {};
|
|
755
|
+
u[a] !== l && (u[a] = l, s.moves.set(c, u), n());
|
|
756
|
+
},
|
|
757
|
+
markUnreachable: (e, t) => {
|
|
758
|
+
let i = r(e);
|
|
759
|
+
i.unreachable.has(t) || (i.suspects.delete(t) ? i.unreachable.add(t) : i.suspects.add(t), n());
|
|
760
|
+
},
|
|
761
|
+
toJSON: () => ({
|
|
762
|
+
version: 1,
|
|
763
|
+
layouts: [...t.entries()].map(([e, t]) => ({
|
|
764
|
+
key: e,
|
|
765
|
+
cells: [...t.cells.keys()],
|
|
766
|
+
moves: [...t.moves.entries()].flatMap(([e, t]) => ne.filter((e) => t[e] !== void 0).map((n) => [
|
|
767
|
+
e,
|
|
768
|
+
n,
|
|
769
|
+
t[n] ?? null
|
|
770
|
+
])),
|
|
771
|
+
unreachable: [...t.unreachable],
|
|
772
|
+
suspects: [...t.suspects]
|
|
773
|
+
}))
|
|
774
|
+
})
|
|
775
|
+
};
|
|
776
|
+
}, se = (e, t, n) => t === "up" ? n.y > e.y : t === "down" ? n.y < e.y : t === "left" ? n.x > e.x : n.x < e.x, ce = 1.01, le = (e, t, n, r) => Math.min(t, r) - Math.max(e, n), ue = (e, t, n) => {
|
|
777
|
+
let r = t.x + t.w / 2, i = t.y + t.h / 2, a = null;
|
|
778
|
+
for (let [o, s] of e.cells) {
|
|
779
|
+
let e = s.x + s.w / 2 - r, c = s.y + s.h / 2 - i, l, u;
|
|
780
|
+
if (n === "up" || n === "down") {
|
|
781
|
+
if (l = n === "down" ? c : -c, u = Math.abs(e), le(t.x, t.x + t.w, s.x, s.x + s.w) <= 0) continue;
|
|
782
|
+
} else if (l = n === "right" ? e : -e, u = Math.abs(c), le(t.y, t.y + t.h, s.y, s.y + s.h) < Math.min(t.h, s.h) / 2) continue;
|
|
783
|
+
l < Math.min(t.h, s.h) / 2 || (!a || l < a.along - .5 || Math.abs(l - a.along) <= .5 && u < a.across) && (a = {
|
|
784
|
+
id: o,
|
|
785
|
+
along: l,
|
|
786
|
+
across: u
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return a?.id ?? null;
|
|
790
|
+
}, de = (e, t, n, r = /* @__PURE__ */ new Set(), i = () => !1) => {
|
|
791
|
+
if (n(t)) return [];
|
|
792
|
+
let a = /* @__PURE__ */ new Map([[t, 0]]), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set([t]);
|
|
793
|
+
for (; c.size > 0;) {
|
|
794
|
+
let l = "", u = Infinity;
|
|
795
|
+
for (let e of c) {
|
|
796
|
+
let t = a.get(e) ?? Infinity;
|
|
797
|
+
t < u && (u = t, l = e);
|
|
798
|
+
}
|
|
799
|
+
if (c.delete(l), s.has(l)) continue;
|
|
800
|
+
if (s.add(l), n(l)) {
|
|
801
|
+
let e = [];
|
|
802
|
+
for (let n = l; n !== t;) {
|
|
803
|
+
let t = o.get(n);
|
|
804
|
+
e.unshift({
|
|
805
|
+
dir: t.dir,
|
|
806
|
+
to: n,
|
|
807
|
+
guessed: t.guessed
|
|
808
|
+
}), n = t.from;
|
|
809
|
+
}
|
|
810
|
+
return e;
|
|
811
|
+
}
|
|
812
|
+
let d = e.cells.get(l), f = e.moves.get(l) ?? {};
|
|
813
|
+
for (let t of ne) {
|
|
814
|
+
if (r.has(`${l}:${t}`) || d && i(d, t)) continue;
|
|
815
|
+
let n = f[t];
|
|
816
|
+
if (n === null) continue;
|
|
817
|
+
let p = n === void 0, m = p ? d ? ue(e, d, t) : null : n;
|
|
818
|
+
if (!m || s.has(m)) continue;
|
|
819
|
+
let h = e.cells.get(m);
|
|
820
|
+
if (!p && d && h && se(d, t, h)) continue;
|
|
821
|
+
let g = u + (p ? ce : 1);
|
|
822
|
+
g < (a.get(m) ?? Infinity) && (a.set(m, g), o.set(m, {
|
|
823
|
+
from: l,
|
|
824
|
+
dir: t,
|
|
825
|
+
guessed: p
|
|
826
|
+
}), c.add(m));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return null;
|
|
691
830
|
}, I = {
|
|
831
|
+
version: "1.0",
|
|
832
|
+
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.",
|
|
833
|
+
notes: {
|
|
834
|
+
fontMode: "All x/width values assume font mode 0 (standard, 12×14 px cells, 40 cols × ~22 rows). Font modes 1 (bold uppercase) and 2 (large) may shift column positions.",
|
|
835
|
+
playbackIndicator: "The M8 renders a '>' character one column to the left of the first data column when that step/row/track is currently playing. This character is part of the text stream; the editing cursor is a separate rect overlay.",
|
|
836
|
+
screenGeometry: "The screen is 40 columns x 24 rows. Every row has a one-column left margin (the playback marker is drawn in it), so x positions in this schema count from column 1 of the raw screen and cursorPos.x is the schema x + 1. Titles are on row 3, content starts on row 5. The text stream is lower case ('tempo', 'b-4', 'ff').",
|
|
837
|
+
emptyCell: "An empty or unused cell displays '--' (2 chars) or '---' (3 chars) or '---00' (command+value). Parsed hexValue will be null for these.",
|
|
838
|
+
relativeHex: "Transpose and some FX values use relative hex: 00 = no change, 01-7F = +1-127, FF-80 = -1-128. See 'Relative and Absolute FX Commands' in the manual.",
|
|
839
|
+
rowIndex: "Row/step indices are parsed from the leftmost characters of currentLine. They are hex for all grid views.",
|
|
840
|
+
titleAsterisk: "A '*' next to the item ID in the screen title (e.g. 'PHRASE 03*') indicates the item is used elsewhere in the chain or song (phrase view); on other views it marks unsaved changes. See 'An asterisk next to the phrase number' in the manual."
|
|
841
|
+
},
|
|
842
|
+
fieldTypes: {
|
|
843
|
+
chainRef: "Reference to a chain number (hex 00-FF). '--' means empty/unused.",
|
|
844
|
+
phraseRef: "Reference to a phrase number (hex 00-FF). '--' means empty/unused.",
|
|
845
|
+
note: "Musical note in M8 format: [A-G][#|-][0-9] e.g. 'C-4', 'A#3'. '---' = empty. 'OFF' = note-off.",
|
|
846
|
+
velocity: "Note velocity 00-7F (hex), from 00 (silence) to 7F (maximum). Default is 64 (hex). '--' = inherit.",
|
|
847
|
+
instrumentRef: "Instrument number 00-7F (hex). '--' = no retrigger (keep current instrument).",
|
|
848
|
+
fxCommand: "3-character sequencer or instrument FX command name, e.g. 'KIL', 'ARP', 'DEL'. '---' = none.",
|
|
849
|
+
fxValue: "2-char hex value 00-FF accompanying an FX command. Interpretation depends on the command.",
|
|
850
|
+
transpose: "Semitone or tick transpose. Relative: 00=none, 01-7F=+1-+127, FF-80=-1--128.",
|
|
851
|
+
ticks: "Number of ticks this step consumes in a groove (hex 01-FF). '00' skips the step. '--' = empty (groove loops at this point).",
|
|
852
|
+
volume: "Volume multiplier 00-FF (hex). Multiplied with the phrase-row velocity. '--' = no override.",
|
|
853
|
+
ppq: "Pulses Per Quarter note: resolution of the groove clock (decimal). One of 24 (default), 48, 96 or 192. Only present on groove row 0; absent on all other rows.",
|
|
854
|
+
instrumentName: "Instrument name string (up to 12 characters). Text field, no hex value.",
|
|
855
|
+
eqSlot: "EQ slot assignment (hex). '--' = no EQ applied to this instrument.",
|
|
856
|
+
rowIndex: "The positional index of this row in the current view (song row, chain position, phrase step, etc.).",
|
|
857
|
+
noteInterval: "Scale row label: one of the 12 note intervals C, C#, D, D#, E, F, F#, G, G#, A, A#, B (sharps use '#', naturals may render a dim '-' pad, e.g. 'C-').",
|
|
858
|
+
onOff: "Toggle field displayed as 'ON' or 'OFF'; the scale view 'EN' column shows '--' for a note out of the scale. Used by the scale view 'EN' column.",
|
|
859
|
+
semitoneOffset: "Signed semitone offset in decimal notation with two decimals (-24.00 to +24.00), e.g. '00.00', '-00.50', '12.00'. Used by the scale view 'OFFSET' column.",
|
|
860
|
+
swing: "Groove swing amount as a percentage with one decimal, e.g. '50.0%' = straight timing, higher = swung. Shown on every second groove row (0, 2, 4, …): the swing of that pair of steps (ticks 07,05 → 58.3%).",
|
|
861
|
+
instrumentType: "Instrument engine chosen with the TYPE field of the Instrument view: WAVSYNTH, MACROSYN, SAMPLER, FMSYNTH, HYPERSYNTH, EXTERNAL INST, MIDI OUT (NONE for an unused slot). Text value; changes which parameters the view shows.",
|
|
862
|
+
name: "Free-text name field (instrument, song, sample, directory). Up to 12 characters; a row of dashes means unnamed.",
|
|
863
|
+
parameter: "Editable value of a named parameter (CUTOFF, TEMPO, GAIN, …). Its 'format' says how to read it: hex byte, 'XX:YY' pair, decimal, decimal with a fraction, or free text.",
|
|
864
|
+
option: "Choice from a list, shown as its name, optionally preceded by its index in hex (e.g. '00CHORUS', 'STEREO', 'BELL'). hexValue holds the index when there is one, text holds the name.",
|
|
865
|
+
action: "Button-like text that does something when [EDIT] is pressed on it (LOAD, SAVE, RENDER, SETTINGS, VIEW INST. POOL, EQ, …). The text is the caption.",
|
|
866
|
+
fileEntry: "Row of a file browser: '/name' is a directory, '/..' the parent directory, anything else a file (e.g. 'Bass.m8i')."
|
|
867
|
+
},
|
|
868
|
+
views: /* @__PURE__ */ JSON.parse("{\"song\":{\"title\":\"Song View\",\"type\":\"grid\",\"description\":\"Arranges chains across 8 independent tracks to form the song structure. Each row is a song position (up to 256 rows); each column is a track. Each track advances vertically until it reaches an empty column ('--'), then loops back to row 00. Chains containing no notes are grayed out; chains '00' or 'FE' are commonly used as silent spacers. On row '00', [UP][UP] enters track reordering mode.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"songRow\",\"description\":\"Song row 00-FF. Each track loops back to row 00 when it reaches '--'.\"},\"columns\":[{\"x\":3,\"width\":2,\"key\":\"track1\",\"type\":\"chainRef\",\"label\":\"Track 1\",\"meta\":{\"trackIndex\":1}},{\"x\":6,\"width\":2,\"key\":\"track2\",\"type\":\"chainRef\",\"label\":\"Track 2\",\"meta\":{\"trackIndex\":2}},{\"x\":9,\"width\":2,\"key\":\"track3\",\"type\":\"chainRef\",\"label\":\"Track 3\",\"meta\":{\"trackIndex\":3}},{\"x\":12,\"width\":2,\"key\":\"track4\",\"type\":\"chainRef\",\"label\":\"Track 4\",\"meta\":{\"trackIndex\":4}},{\"x\":15,\"width\":2,\"key\":\"track5\",\"type\":\"chainRef\",\"label\":\"Track 5\",\"meta\":{\"trackIndex\":5}},{\"x\":18,\"width\":2,\"key\":\"track6\",\"type\":\"chainRef\",\"label\":\"Track 6\",\"meta\":{\"trackIndex\":6}},{\"x\":21,\"width\":2,\"key\":\"track7\",\"type\":\"chainRef\",\"label\":\"Track 7\",\"meta\":{\"trackIndex\":7}},{\"x\":24,\"width\":2,\"key\":\"track8\",\"type\":\"chainRef\",\"label\":\"Track 8\",\"meta\":{\"trackIndex\":8}}]},\"chain\":{\"title\":\"Chain View\",\"type\":\"grid\",\"description\":\"An ordered list of up to 16 phrases (256 steps). The first '--' phrase ends the chain and the song continues on to the next chain in the song view. The 'TSP' column optionally transposes every note in the phrase in semitones (relative hex).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"chainPos\",\"description\":\"Position within the chain (0-F, max 16 entries).\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"phrase\",\"type\":\"phraseRef\",\"label\":\"Phrase (PH)\",\"description\":\"Phrase number to play at this chain position.\"},{\"x\":5,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (TSP)\",\"description\":\"Optional note transpose in semitones applied to every note in the phrase (relative hex).\"}]},\"phrase\":{\"title\":\"Phrase View\",\"type\":\"grid\",\"description\":\"A 16-step sequence. Each step holds a note, velocity, instrument assignment, and up to 3 FX command+value pairs. Notes cover 11 octaves (the full 128 MIDI note range); velocity defaults to 64 (hex); an empty instrument column plays the note without retriggering the instrument. A '*' next to the phrase number indicates the phrase is used elsewhere in the chain or song.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Step index 0-F (16th note by default; altered by groove).\"},\"columns\":[{\"x\":2,\"width\":3,\"key\":\"note\",\"type\":\"note\",\"label\":\"Note (N)\"},{\"x\":6,\"width\":2,\"key\":\"vel\",\"type\":\"velocity\",\"label\":\"Velocity (V)\"},{\"x\":9,\"width\":2,\"key\":\"inst\",\"type\":\"instrumentRef\",\"label\":\"Instrument (I)\"},{\"x\":12,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\"},{\"x\":15,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\"},{\"x\":18,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\"},{\"x\":21,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\"},{\"x\":24,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\"},{\"x\":27,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\"}]},\"table\":{\"title\":\"Table View\",\"type\":\"grid\",\"description\":\"A 16-step mini-sequencer that runs alongside an instrument. Used for arpeggios, multi-stage envelopes, and automated FX. The 'N' column transposes the instrument for the step (relative hex); 'V' multiplies with the phrase-row velocity. Each instrument has a dedicated table plus 128 assignable tables (TBL command). FX columns can run at independent speeds via TIC; a TIC on the last table row affects all rows. HOP shortens or loops the table.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Table step 0-F. Loops from bottom unless a HOP command redirects.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (N)\",\"description\":\"Transposes the instrument for this step (relative hex). '--' = no transpose.\"},{\"x\":5,\"width\":2,\"key\":\"volume\",\"type\":\"volume\",\"label\":\"Volume (V)\",\"description\":\"Volume multiplier applied on top of the phrase velocity (hex). '--' = no override.\"},{\"x\":8,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\",\"description\":\"3-char command name for table FX slot 1.\"},{\"x\":11,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\",\"description\":\"Value for table FX1.\"},{\"x\":14,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\",\"description\":\"3-char command name for table FX slot 2.\"},{\"x\":17,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\",\"description\":\"Value for table FX2.\"},{\"x\":20,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\",\"description\":\"3-char command name for table FX slot 3.\"},{\"x\":23,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\",\"description\":\"Value for table FX3.\"}]},\"groove\":{\"title\":\"Groove View\",\"type\":\"grid\",\"description\":\"Defines the timing of each phrase step in ticks. Used to create swing, shuffle, triplets, and other rhythmic feels. At the default 24 PPQ a step is 6 ticks and a full phrase totals 96 ticks. The groove loops when a '--' entry is reached; a '00' entry skips the phrase step. Row 0 also holds the PPQ resolution (24/48/96/192) and the SWING percentage; editing the row 0 tick value alters rows 0 and 1 together (a common swing setting is 07,05 or 08,04).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Groove step 0-F. The groove loops when a '--' entry is reached.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"ticks\",\"type\":\"ticks\",\"label\":\"Ticks (TIC)\",\"description\":\"Number of ticks this step takes. Default is 06 (at 24 PPQN = 1 sixteenth note). '00' skips the step.\"},{\"x\":6,\"width\":2,\"key\":\"ppq\",\"type\":\"ppq\",\"label\":\"PPQ\",\"description\":\"Pulses Per Quarter note: 24, 48, 96 or 192. Only present on groove row 0; absent on all other rows.\"},{\"x\":10,\"width\":5,\"key\":\"swing\",\"type\":\"swing\",\"label\":\"Swing\",\"description\":\"Swing amount as a percentage (e.g. '50.0%' = straight). Shown on every second row (0, 2, 4, …) for that pair of steps; empty on the others.\"}]},\"scale\":{\"title\":\"Scale View\",\"type\":\"grid\",\"description\":\"Defines and quantizes note-related events (project/chain transpose, note entry, ARP, PIT, and the MIDIOUT instrument CHD and ADD commands). Up to 16 user-defined scales per project; scale '00' is the default for all 8 tracks and can be overridden per track with SCA/SCG FX commands. Each of the 12 note intervals can be enabled ('EN' = ON) with an optional detuning offset (-24.00 to +24.00 semitones); a note out of the scale reads '-- -- --'. Rows that are not column-mapped (Model:02, fw 6.5): KEY under the title (global root note for the default scale, the same on every scale), then after the intervals TUNE (A4 reference in Hz, e.g. '440.00'), NAME (up to 16 characters, 92 factory scales load from /Scales/Factory), and the LOAD/SAVE actions.\",\"columns\":[{\"x\":0,\"width\":2,\"key\":\"interval\",\"type\":\"noteInterval\",\"label\":\"Note Interval\",\"description\":\"Note interval row label: C, C#, D, D#, E, F, F#, G, G#, A, A#, B.\"},{\"x\":3,\"width\":3,\"key\":\"en\",\"type\":\"onOff\",\"label\":\"Enabled (EN)\",\"description\":\"Enables this note interval in the scale: 'ON', or '--' when the note is out of the scale (the device draws the row '-- -- --').\"},{\"x\":6,\"width\":6,\"key\":\"offset\",\"type\":\"semitoneOffset\",\"label\":\"Offset (OFFSET)\",\"description\":\"Detune offset in semitones, decimal with two decimals (-24.00 to +24.00), e.g. '00.00'.\"}]},\"instrumentpool\":{\"title\":\"Instrument Pool\",\"type\":\"grid\",\"description\":\"Lists all 128 instrument slots with their mix routing. Each row shows the instrument name (up to 12 chars, '------------' = empty slot) and its dry/FX send levels. Mixer-related settings can be adjusted per instrument here; [EDIT] on an empty slot loads an instrument or sample.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"instIndex\",\"description\":\"Instrument slot index 00-7F.\"},\"columns\":[{\"x\":3,\"width\":12,\"key\":\"name\",\"type\":\"instrumentName\",\"label\":\"Instrument Name\",\"description\":\"Name of the instrument in this slot (up to 12 chars). '------------' = empty slot.\"},{\"x\":16,\"width\":2,\"key\":\"dry\",\"type\":\"volume\",\"label\":\"Dry (DRY)\",\"description\":\"Direct output volume for this instrument (hex 00-FF).\"},{\"x\":19,\"width\":2,\"key\":\"mx\",\"type\":\"volume\",\"label\":\"ModFX (MX)\",\"description\":\"ModFX send level (hex 00-FF).\"},{\"x\":22,\"width\":2,\"key\":\"de\",\"type\":\"volume\",\"label\":\"Delay (DE)\",\"description\":\"Delay send level (hex 00-FF).\"},{\"x\":25,\"width\":2,\"key\":\"rv\",\"type\":\"volume\",\"label\":\"Reverb (RV)\",\"description\":\"Reverb send level (hex 00-FF).\"},{\"x\":28,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ slot assignment. '--' = no EQ.\"}]},\"inst\":{\"title\":\"Instrument View\",\"type\":\"parameter\",\"description\":\"Per-instrument parameters for the selected slot: TYPE (Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out), LOAD/SAVE presets, NAME, TRANSP. (enable/disable transposition from scales and transpose columns), TABLE TIC (default table speed), and EQ slot, plus the synth engine parameters. Almost every parameter has an associated FX command; highlighting a parameter and navigating to the phrase or table view sets that parameter's FX command as default.\"},\"instmods\":{\"title\":\"Instrument Modulation View\",\"type\":\"parameter\",\"description\":\"Four configurable modulation slots per instrument: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope, or TRACKING, each with a destination parameter (DEST) and optional source (SRC).\"},\"mixer\":{\"title\":\"Mixer View\",\"type\":\"parameter\",\"description\":\"Mix levels for the 8 tracks, the 3 send effects (MX/DE/RE), the analog input (INPUT, with dual-mono mode) and USB input, the DJ filter (DJF), OTT compression, main EQ, limiter (LIM) and main volume (MIX). All signals are summed through OTT, main EQ, limiter, DJ filter and main volume. Most values are also exposed as FX commands (VMV, VMX, VDE, VRE, VT1-8, DJC/DJR/DJT, IVO/IMX/IDE/IRV, USB, ...).\"},\"project\":{\"title\":\"Project View\",\"type\":\"parameter\",\"description\":\"Song-wide settings such as tempo, project transpose (identical to the TSP FX command), and LIVE QUANTIZE behavior for cueing tracks in live mode. [SHIFT]+[OPTION] stores a song snapshot; [SHIFT]+[EDIT] recalls it.\"},\"effectsettings\":{\"title\":\"Effect Settings View\",\"type\":\"parameter\",\"description\":\"Settings for the 3 send effects: ModFX type/depth/frequency/width, Delay time/feedback/width, and Reverb size/decay/modulation/width/freeze — all addressable via the XMT-XMW, XDT-XDR and XRS-XRZ FX commands.\"},\"mixeq\":{\"title\":\"Mix EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the main mix EQ slot (assign with the EQM FX command). Selecting EQ in the Mixer view and pressing [EDIT] or [SHIFT]+[RIGHT] opens this view.\"},\"modfxeq\":{\"title\":\"ModFX EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the ModFX send path.\"},\"delayeq\":{\"title\":\"Delay EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Delay send path.\"},\"reverbeq\":{\"title\":\"Reverb EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Reverb send path.\"},\"mixscope\":{\"title\":\"Mix Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the main mix activity. Opened from the Mixer view with MIX selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"limiterscope\":{\"title\":\"Limiter Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the limiter's compression activity (the white line on the MIX indicator). Opened from the Mixer view with LIM selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"systemsettings\":{\"title\":\"System Settings View\",\"type\":\"parameter\",\"description\":\"System-level settings for the device.\"},\"loadinstrument\":{\"title\":\"Load Instrument\",\"type\":\"parameter\",\"description\":\"File browser for loading an instrument preset from the SD card ([EDIT] on an empty instrument slot, or LOAD in the Instrument view).\"},\"selectsavedirectory\":{\"title\":\"Select Save Directory\",\"type\":\"parameter\",\"description\":\"File browser directory-selection step when saving a preset or project.\"},\"createdirectory\":{\"title\":\"Create Directory\",\"type\":\"parameter\",\"description\":\"File browser dialog for creating a new directory on the SD card.\"}}"),
|
|
869
|
+
fxCommands: /* @__PURE__ */ JSON.parse("{\"sequencer\":{\"ARP\":{\"description\":\"Arpeggio: plays 3 notes rapidly. X=first interval semitones, Y=second interval semitones.\"},\"ARC\":{\"description\":\"Arpeggio Config: X=direction and number of octaves, Y=speed in ticks.\"},\"CHA\":{\"description\":\"Chance: X=probability left of command (0=never, F=always), Y=probability right. In a table: probability for everything to the left of CHA from 00 (never) to FF (always).\"},\"DEL\":{\"description\":\"Delay: in phrase delays entire row by XX ticks; in table delays table playhead.\"},\"GRV\":{\"description\":\"Groove: sets groove number XX for the current track.\"},\"GGR\":{\"description\":\"Global Groove: sets groove XX for all tracks.\"},\"HOP\":{\"description\":\"Hop: in phrase jumps to row Y of next phrase; HOPFF stops track. In table loops to row Y X times.\"},\"INS\":{\"description\":\"Instrument: changes active instrument to XX as an FX command.\"},\"KIL\":{\"description\":\"Kill: stops the playing instrument after XX ticks.\"},\"RND\":{\"description\":\"Random: randomises the preceding FX command. X=left range, Y=right range (0=none, F=full).\"},\"RNL\":{\"description\":\"Randomise Left: randomises the FX command to the left (X=left range, Y=right range). In the first column RNL randomises the note and instrument number in phrase, or note and velocity in table.\"},\"RET\":{\"description\":\"Retrig: retriggers current row. Y≠0: retrig every Y ticks with X volume ramp. Y=0: single retrig after X ticks.\"},\"REP\":{\"description\":\"Repeat: repeats and increments the last FX command by XX each step. Active until a new command or REP00 stops it; it does not need to appear on every row. In a table: loops the last row XX times. A double-caret '^^' appears next to an FX lane while a REP from a previous phrase is active.\"},\"RTO\":{\"description\":\"Repeat To: use after REP to hold at the given value (the min/max REP stops at). In a table, used alone: loops until the relative value is reached.\"},\"RMX\":{\"description\":\"Remix: repositions the phrase playhead (Y) on tracks to the left (selected by X).\"},\"NTH\":{\"description\":\"Nth Trigger: conditional trigger on the loop count. X=affects what is left of the command, Y=what is right (in a table: what is left).\"},\"PSL\":{\"description\":\"Pitch Slide (portamento): slides to the given note over XX ticks.\"},\"PBN\":{\"description\":\"Pitch Bend: continuous pitch slide up (00-7F) or down (80-FF).\"},\"PVB\":{\"description\":\"Vibrato: X=speed, Y=depth.\"},\"PVX\":{\"description\":\"Extreme Vibrato: like PVB but with higher range.\"},\"SCA\":{\"description\":\"Track Scale: X=key signature, Y=scale number for this track.\"},\"SCG\":{\"description\":\"Global Scale: X=key, Y=scale number for the whole song.\"},\"SNG\":{\"description\":\"Song Hop: moves this track's song position by XX rows relative to current. Ignored if the resulting song position is not playable.\"},\"SED\":{\"description\":\"Random Seed: sets the random seed XX for this track.\"},\"TBL\":{\"description\":\"Table: assigns table number XX to the current instrument.\"},\"THO\":{\"description\":\"Table Hop: jumps all table columns to position 0X.\"},\"TIC\":{\"description\":\"Table Tick: sets the table tick rate. 00 = increment row on each instrument trigger; 01-FB = ticks per row; FC = Octave Map (playing octave maps to table row); FD = Velocity Map; FE = Note Map (pair with HOP00 on row 0C to limit to 12 notes/octave); FF = increment at 200 Hz. In a table each FX column can run independently; a TIC on the last table row affects all rows.\"},\"TBX\":{\"description\":\"Aux Table: runs table XX in parallel with the instrument table. 00=stop aux table. A TIC in the same row as a TBX affects the auxiliary table's tick rate.\"},\"TPO\":{\"description\":\"Tempo: sets song BPM to XX. In a table the tempo is relative.\"},\"TSP\":{\"description\":\"Global Song Transpose: transposes the entire song by XX semitones. Identical to the transpose value in the Project View.\"},\"NXT\":{\"description\":\"Next Track Trigger: plays instrument XX on the track to the right using the current note/velocity. Does not work on track 8.\"},\"OFF\":{\"description\":\"Note Off: stops the playing instrument after XX ticks. Triggers ADSR release stage.\"},\"MTT\":{\"description\":\"Micro Time: adjusts row playback timing in positive or negative sub-tick (1/8 tick) amounts. Negative amounts do not work on the first phrase row. Currently has no action in tables.\"}},\"instrument\":{\"VOL\":{\"description\":\"Volume (relative).\",\"relative\":true},\"PIT\":{\"description\":\"Pitch in semitones (relative).\",\"relative\":true},\"FIN\":{\"description\":\"Fine tune: pitch shift of up to about one semitone (relative).\",\"relative\":true},\"PLY\":{\"description\":\"Sampler play mode (FWD, REV, loop modes…).\"},\"STA\":{\"description\":\"Sampler start position.\"},\"LOP\":{\"description\":\"Sampler loop start (relative).\",\"relative\":true},\"LEN\":{\"description\":\"Sampler length (relative).\",\"relative\":true},\"DEG\":{\"description\":\"Degrade amount (relative).\",\"relative\":true},\"SLI\":{\"description\":\"Sampler slice to play.\"},\"ERR\":{\"description\":\"Error: randomizes pitch and parameters by the given amount.\"},\"FLT\":{\"description\":\"Filter type.\"},\"FIL\":{\"description\":\"Filter type.\"},\"ALG\":{\"description\":\"FM algorithm.\"},\"FM1\":{\"description\":\"FM modulation amount 1 (relative).\",\"relative\":true},\"FM2\":{\"description\":\"FM modulation amount 2 (relative).\",\"relative\":true},\"FM3\":{\"description\":\"FM modulation amount 3 (relative).\",\"relative\":true},\"FM4\":{\"description\":\"FM modulation amount 4 (relative).\",\"relative\":true},\"SNC\":{\"description\":\"Sync: on an FM synth the FM sync phase position; on a Hypersynth it offsets the sync of the oscillators by the given amount.\"},\"CRD\":{\"description\":\"Hypersynth chord set.\"},\"CVO\":{\"description\":\"Hypersynth chord voice offset: X=voice, Y=offset.\"},\"SWM\":{\"description\":\"Hypersynth oscillator swarm (relative).\",\"relative\":true},\"WID\":{\"description\":\"Hypersynth stereo width (relative).\",\"relative\":true},\"SUB\":{\"description\":\"Hypersynth sub-oscillator amount (relative).\",\"relative\":true},\"OSC\":{\"description\":\"Macrosynth oscillator shape.\"},\"TBR\":{\"description\":\"Macrosynth timbre (relative).\",\"relative\":true},\"COL\":{\"description\":\"Macrosynth color (relative).\",\"relative\":true},\"RED\":{\"description\":\"Macrosynth bit reduction (relative).\",\"relative\":true},\"TRG\":{\"description\":\"Macrosynth trigger/strike.\"},\"CUT\":{\"description\":\"Filter cutoff (relative).\",\"relative\":true},\"RES\":{\"description\":\"Filter resonance (relative).\",\"relative\":true},\"AMP\":{\"description\":\"Amp level (relative).\",\"relative\":true},\"LIM\":{\"description\":\"Limit mode: how the amp stage clips.\"},\"PAN\":{\"description\":\"Pan (relative).\",\"relative\":true},\"DRY\":{\"description\":\"Dry volume: the instrument's level in the main mix.\"},\"SMX\":{\"description\":\"Send to ModFX.\"},\"SDL\":{\"description\":\"Send to Delay.\"},\"SRV\":{\"description\":\"Send to Reverb.\"},\"CHD\":{\"description\":\"Chord (MIDIOUT instrument): triggers a chord. Quantized by the active scale.\"},\"ADD\":{\"description\":\"Add Note (MIDIOUT instrument): adds a note to the chord. Quantized by the active scale.\"}},\"modulation\":{\"EA\":{\"description\":\"Envelope amount of modulator N (relative).\",\"relative\":true},\"AT\":{\"description\":\"Envelope attack of modulator N.\"},\"HO\":{\"description\":\"Envelope hold of modulator N.\"},\"DE\":{\"description\":\"Envelope decay of modulator N.\"},\"ET\":{\"description\":\"Envelope trigger of modulator N: retriggers the envelope.\"},\"LA\":{\"description\":\"LFO amount of modulator N (relative).\",\"relative\":true},\"LO\":{\"description\":\"LFO oscillator shape of modulator N.\"},\"LS\":{\"description\":\"LFO trigger settings of modulator N.\"},\"LF\":{\"description\":\"LFO frequency of modulator N.\"},\"LT\":{\"description\":\"LFO trigger of modulator N: at the given offset, resets the LFO to that position.\"}},\"mixer\":{\"VMV\":{\"description\":\"Main Volume: sets the main song output volume.\"},\"VMX\":{\"description\":\"ModFX Volume.\"},\"VDE\":{\"description\":\"Delay Volume.\"},\"VRE\":{\"description\":\"Reverb Volume.\"},\"VT1\":{\"description\":\"Track 1 Volume.\"},\"VT2\":{\"description\":\"Track 2 Volume.\"},\"VT3\":{\"description\":\"Track 3 Volume.\"},\"VT4\":{\"description\":\"Track 4 Volume.\"},\"VT5\":{\"description\":\"Track 5 Volume.\"},\"VT6\":{\"description\":\"Track 6 Volume.\"},\"VT7\":{\"description\":\"Track 7 Volume.\"},\"VT8\":{\"description\":\"Track 8 Volume.\"},\"DJC\":{\"description\":\"DJ Filter Cutoff.\"},\"DJR\":{\"description\":\"DJ Filter Resonance.\"},\"DJT\":{\"description\":\"DJ Filter Type.\"},\"EQM\":{\"description\":\"Assign main mix EQ slot.\"},\"EQI\":{\"description\":\"Assign current instrument EQ slot.\"},\"XMT\":{\"description\":\"Send Effect: ModFX mod type and phase position (Effect Settings view).\"},\"XMM\":{\"description\":\"Send Effect: ModFX mod depth (Effect Settings view).\"},\"XMF\":{\"description\":\"Send Effect: ModFX mod frequency (Effect Settings view).\"},\"XMW\":{\"description\":\"Send Effect: ModFX stereo width (Effect Settings view).\"},\"XMR\":{\"description\":\"Send Effect: ModFX to reverb mix (Effect Settings view).\"},\"XDT\":{\"description\":\"Send Effect: Delay time — X = left, Y = right, in large increments (Effect Settings view).\"},\"XDF\":{\"description\":\"Send Effect: Delay feedback (Effect Settings view).\"},\"XDW\":{\"description\":\"Send Effect: Delay stereo width (Effect Settings view).\"},\"XDR\":{\"description\":\"Send Effect: Delay to reverb mix (Effect Settings view).\"},\"XRS\":{\"description\":\"Send Effect: Reverb room size (Effect Settings view).\"},\"XRD\":{\"description\":\"Send Effect: Reverb decay (Effect Settings view).\"},\"XRM\":{\"description\":\"Send Effect: Reverb mod depth (Effect Settings view).\"},\"XRF\":{\"description\":\"Send Effect: Reverb mod frequency (Effect Settings view).\"},\"XRW\":{\"description\":\"Send Effect: Reverb stereo width (Effect Settings view).\"},\"XRZ\":{\"description\":\"Send Effect: Reverb freeze — enabled with a value greater than 00, disabled at 00.\"},\"VIN\":{\"description\":\"Line Input Mixer: analog input volume (Mixer view).\"},\"XRH\":{\"description\":\"Send Effect: Reverb shimmer (Effect Settings view).\"},\"OTT\":{\"description\":\"OTT mix amount (Mixer OTT settings; OTC and OTI default to 80).\"},\"OTC\":{\"description\":\"OTT color (default 80).\"},\"OTI\":{\"description\":\"OTT time (default 80).\"},\"IVO\":{\"description\":\"Alias of VIN as named in the manual: analog input volume (Mixer view).\"},\"IMX\":{\"description\":\"Line Input Mixer: analog input ModFX send (Mixer view).\"},\"IDE\":{\"description\":\"Line Input Mixer: analog input delay send (Mixer view).\"},\"IRV\":{\"description\":\"Line Input Mixer: analog input reverb send (Mixer view). Also referenced as 'IRE' in the manual text.\"},\"IRE\":{\"description\":\"Alias of IRV: analog input reverb send (Mixer view).\"},\"IV2\":{\"description\":\"Line Input Mixer: volume for the 2nd input (right channel in dual-mono input mode, set in the Mixer view).\"},\"IM2\":{\"description\":\"Line Input Mixer: ModFX send for the 2nd input (right channel in dual-mono input mode).\"},\"ID2\":{\"description\":\"Line Input Mixer: delay send for the 2nd input (right channel in dual-mono input mode).\"},\"IR2\":{\"description\":\"Line Input Mixer: reverb send for the 2nd input (right channel in dual-mono input mode).\"},\"USB\":{\"description\":\"USB input volume (Mixer view).\"}}}"),
|
|
870
|
+
instrumentFx: {
|
|
871
|
+
_note: "Instrument commands offered by [EDIT]+[UP/DOWN] (Effect Command Help view) in the Current Instrument section depend on the instrument type; the Instrument Mods section depends on the modulators of that instrument, and Sequencer FX and Mixer/Effects are the same for every type. Lists marked verified were read from a real M8 (Model:02). Wavsynth, External and MIDI Out were not available on that device: their lists are not known here.",
|
|
872
|
+
types: {
|
|
873
|
+
sampler: {
|
|
874
|
+
verified: !0,
|
|
875
|
+
commands: [
|
|
876
|
+
"VOL",
|
|
877
|
+
"PIT",
|
|
878
|
+
"FIN",
|
|
879
|
+
"PLY",
|
|
880
|
+
"STA",
|
|
881
|
+
"LOP",
|
|
882
|
+
"LEN",
|
|
883
|
+
"DEG",
|
|
884
|
+
"FLT",
|
|
885
|
+
"CUT",
|
|
886
|
+
"RES",
|
|
887
|
+
"AMP",
|
|
888
|
+
"LIM",
|
|
889
|
+
"PAN",
|
|
890
|
+
"SLI",
|
|
891
|
+
"ERR",
|
|
892
|
+
"DRY",
|
|
893
|
+
"SMX",
|
|
894
|
+
"SDL",
|
|
895
|
+
"SRV"
|
|
896
|
+
]
|
|
897
|
+
},
|
|
898
|
+
macrosynth: {
|
|
899
|
+
verified: !0,
|
|
900
|
+
commands: [
|
|
901
|
+
"VOL",
|
|
902
|
+
"PIT",
|
|
903
|
+
"FIN",
|
|
904
|
+
"OSC",
|
|
905
|
+
"TBR",
|
|
906
|
+
"COL",
|
|
907
|
+
"DEG",
|
|
908
|
+
"RED",
|
|
909
|
+
"FIL",
|
|
910
|
+
"CUT",
|
|
911
|
+
"RES",
|
|
912
|
+
"AMP",
|
|
913
|
+
"LIM",
|
|
914
|
+
"PAN",
|
|
915
|
+
"TRG",
|
|
916
|
+
"ERR",
|
|
917
|
+
"DRY",
|
|
918
|
+
"SMX",
|
|
919
|
+
"SDL",
|
|
920
|
+
"SRV"
|
|
921
|
+
]
|
|
922
|
+
},
|
|
923
|
+
fmsynth: {
|
|
924
|
+
verified: !0,
|
|
925
|
+
commands: [
|
|
926
|
+
"VOL",
|
|
927
|
+
"PIT",
|
|
928
|
+
"FIN",
|
|
929
|
+
"ALG",
|
|
930
|
+
"FM1",
|
|
931
|
+
"FM2",
|
|
932
|
+
"FM3",
|
|
933
|
+
"FM4",
|
|
934
|
+
"FIL",
|
|
935
|
+
"CUT",
|
|
936
|
+
"RES",
|
|
937
|
+
"AMP",
|
|
938
|
+
"LIM",
|
|
939
|
+
"PAN",
|
|
940
|
+
"SNC",
|
|
941
|
+
"ERR",
|
|
942
|
+
"DRY",
|
|
943
|
+
"SMX",
|
|
944
|
+
"SDL",
|
|
945
|
+
"SRV"
|
|
946
|
+
]
|
|
947
|
+
},
|
|
948
|
+
hypersynth: {
|
|
949
|
+
verified: !0,
|
|
950
|
+
commands: [
|
|
951
|
+
"VOL",
|
|
952
|
+
"PIT",
|
|
953
|
+
"FIN",
|
|
954
|
+
"CRD",
|
|
955
|
+
"CVO",
|
|
956
|
+
"SWM",
|
|
957
|
+
"WID",
|
|
958
|
+
"SUB",
|
|
959
|
+
"FIL",
|
|
960
|
+
"CUT",
|
|
961
|
+
"RES",
|
|
962
|
+
"AMP",
|
|
963
|
+
"LIM",
|
|
964
|
+
"PAN",
|
|
965
|
+
"SNC",
|
|
966
|
+
"ERR",
|
|
967
|
+
"DRY",
|
|
968
|
+
"SMX",
|
|
969
|
+
"SDL",
|
|
970
|
+
"SRV"
|
|
971
|
+
]
|
|
972
|
+
},
|
|
973
|
+
midiout: {
|
|
974
|
+
verified: !1,
|
|
975
|
+
commands: ["CHD", "ADD"]
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
modulation: {
|
|
979
|
+
_note: "Modulator slot N (1-4) offers the commands of its kind. Seen: an envelope slot (AHD) offers EA AT HO DE ET, an LFO slot offers LA LO LS LF LT. Slot kinds vary per instrument (e.g. Sampler 00: env, env, LFO, LFO; Hypersynth 01: env, LFO, LFO, LFO). Other modulator kinds were not captured.",
|
|
980
|
+
envelope: [
|
|
981
|
+
"EA",
|
|
982
|
+
"AT",
|
|
983
|
+
"HO",
|
|
984
|
+
"DE",
|
|
985
|
+
"ET"
|
|
986
|
+
],
|
|
987
|
+
lfo: [
|
|
988
|
+
"LA",
|
|
989
|
+
"LO",
|
|
990
|
+
"LS",
|
|
991
|
+
"LF",
|
|
992
|
+
"LT"
|
|
993
|
+
]
|
|
994
|
+
},
|
|
995
|
+
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." }
|
|
996
|
+
}
|
|
997
|
+
}, fe = 3, pe = 1.5, me = 12, he = 34, ge = Object.fromEntries(Object.entries(I.views).flatMap(([e, t]) => t.rowField ? [[e, t.rowField.width]] : [])), _e = (e, t) => {
|
|
998
|
+
let n = e ? ge[e] : void 0;
|
|
999
|
+
if (!n) return null;
|
|
1000
|
+
let r = -1, i = -1;
|
|
1001
|
+
return t.forEach((e, t) => {
|
|
1002
|
+
/^[0-9a-f]+$/i.test(e.slice(1, 1 + n)) && (e[1 + n] ?? " ") === " " && (r < 0 && (r = t), i = t);
|
|
1003
|
+
}), r < 0 ? null : {
|
|
1004
|
+
first: r,
|
|
1005
|
+
last: i,
|
|
1006
|
+
numberWidth: n
|
|
1007
|
+
};
|
|
1008
|
+
}, ve = (e, t) => {
|
|
1009
|
+
let n = Math.round((e.x - t.offX) / t.cellW), r = Math.round((e.y - t.offY) / t.cellH), i = Math.max(1, Math.round(e.w / t.cellW));
|
|
1010
|
+
return {
|
|
1011
|
+
x: n * t.cellW + t.offX,
|
|
1012
|
+
y: r * t.cellH + t.offY,
|
|
1013
|
+
w: i * t.cellW,
|
|
1014
|
+
h: t.cellH
|
|
1015
|
+
};
|
|
1016
|
+
}, ye = (e, t) => Math.round((e.y - t.offY) / t.cellH), be = (e, t) => Math.round(e / t.cellW) >= 40 ? he : null, xe = (e, t) => e.map((e, n) => {
|
|
1017
|
+
let r = (t === null ? e : e.slice(0, t)).trim().split(/\s+/)[0] ?? "";
|
|
1018
|
+
return r.length >= 2 && /[g-z]/i.test(r) ? `${n}${r.toLowerCase()}` : "";
|
|
1019
|
+
}).filter(Boolean).join(" "), Se = (e, t, n) => ({
|
|
1020
|
+
x: t * n.cellW + n.offX,
|
|
1021
|
+
y: e * n.cellH + n.offY,
|
|
1022
|
+
w: n.cellW,
|
|
1023
|
+
h: n.cellH
|
|
1024
|
+
}), Ce = (e, t, n, r = () => !1) => {
|
|
1025
|
+
let i = [];
|
|
1026
|
+
return e.forEach((e, a) => {
|
|
1027
|
+
let o = [...` ${(n === null ? e : e.slice(0, n)).slice(1)}`].map((e, t) => e !== " " && r(a, t) ? " " : e).join("");
|
|
1028
|
+
for (let e of o.matchAll(/\S+/g)) {
|
|
1029
|
+
let n = e.index ?? 0, r = e[0];
|
|
1030
|
+
i.push({
|
|
1031
|
+
key: `${a}:${n}:${r.toLowerCase()}`,
|
|
1032
|
+
rect: {
|
|
1033
|
+
x: n * t.cellW + t.offX,
|
|
1034
|
+
y: a * t.cellH + t.offY,
|
|
1035
|
+
w: r.length * t.cellW,
|
|
1036
|
+
h: t.cellH
|
|
1037
|
+
},
|
|
1038
|
+
row: a,
|
|
1039
|
+
col: n,
|
|
1040
|
+
text: r
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}), i;
|
|
1044
|
+
}, we = (e, t, n) => {
|
|
1045
|
+
let r = ye(e, n), i = Math.round((e.x - n.offX) / n.cellW), a = Math.floor((e.x + e.w - 1 - n.offX) / n.cellW);
|
|
1046
|
+
return (t[r] ?? "").slice(Math.max(0, i), a + 1).trim().length > 0;
|
|
1047
|
+
}, Te = (e, t, n, r) => Math.max(0, Math.min(t, r) - Math.max(e, n)), Ee = (e, t, n) => Te(e.x, e.x + e.w, t.x, t.x + t.w) >= Math.min(n.cellW, t.w) / 2, De = (e, t, n) => Te(e.y, e.y + e.h, t.y, t.y + t.h) >= Math.min(n.cellH, t.h) / 2, Oe = (e, t, n) => Ee(e, t, n) && De(e, t, n), ke = (e) => ({
|
|
1048
|
+
x: e.x + e.w / 2,
|
|
1049
|
+
y: e.y + e.h / 2
|
|
1050
|
+
}), L = (e, t) => {
|
|
1051
|
+
let n = ke(e), r = ke(t), i = Math.max(0, Math.abs(n.x - r.x) - (e.w + t.w) / 2 + fe), a = Math.max(0, Math.abs(n.y - r.y) - (e.h + t.h) / 2 + fe);
|
|
1052
|
+
return Math.hypot(i, a) + .01 * Math.hypot(n.x - r.x, n.y - r.y);
|
|
1053
|
+
}, Ae = (e, t) => ({
|
|
1054
|
+
dx: Math.max(0, t.x - e.x, e.x - (t.x + t.w)),
|
|
1055
|
+
dy: Math.max(0, t.y - e.y, e.y - (t.y + t.h))
|
|
1056
|
+
}), je = (e) => ({
|
|
1057
|
+
kind: "point",
|
|
1058
|
+
rect: {
|
|
1059
|
+
x: e.x,
|
|
1060
|
+
y: e.y,
|
|
1061
|
+
w: 1,
|
|
1062
|
+
h: 1
|
|
1063
|
+
}
|
|
1064
|
+
}), Me = (e, t, n) => e.kind === "cell" && F(t) === e.id || Oe(t, e.rect, n), Ne = (e, t, n, r, i = null) => {
|
|
1065
|
+
let a = (e) => !i || e >= i.first && e <= i.last, o = [];
|
|
1066
|
+
for (let [r, i] of e?.cells ?? []) a(ye(i, n)) && we(i, t, n) && o.push({
|
|
1067
|
+
id: r,
|
|
1068
|
+
rect: i
|
|
1069
|
+
});
|
|
1070
|
+
let s = (e, t) => {
|
|
1071
|
+
let r = Se(e, t, n);
|
|
1072
|
+
return o.some((e) => Oe(e.rect, r, n));
|
|
1073
|
+
}, c = (e) => !!i && e.col === 1 && e.text.length <= i.numberWidth;
|
|
1074
|
+
return {
|
|
1075
|
+
cells: o,
|
|
1076
|
+
runs: Ce(t, n, r, s).filter((e) => a(e.row) && !c(e))
|
|
1077
|
+
};
|
|
1078
|
+
}, Pe = (e, t, n, r, i, a = null, o = /* @__PURE__ */ new Set(), s = !1) => {
|
|
1079
|
+
let { cells: c, runs: l } = Ne(t, n, r, i, a), u = c.map((e) => ({
|
|
1080
|
+
goal: {
|
|
1081
|
+
kind: "cell",
|
|
1082
|
+
...e
|
|
1083
|
+
},
|
|
1084
|
+
area: e.rect
|
|
1085
|
+
}));
|
|
1086
|
+
for (let n of l) {
|
|
1087
|
+
if (t?.unreachable.has(n.key) || o.has(n.key)) continue;
|
|
1088
|
+
let i = Math.min(n.col + n.text.length - 1, Math.max(n.col, Math.floor((e.x - r.offX) / r.cellW)));
|
|
1089
|
+
u.push({
|
|
1090
|
+
goal: {
|
|
1091
|
+
kind: "text",
|
|
1092
|
+
key: n.key,
|
|
1093
|
+
rect: Se(n.row, i, r),
|
|
1094
|
+
run: n.rect,
|
|
1095
|
+
row: n.row
|
|
1096
|
+
},
|
|
1097
|
+
area: n.rect
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
let d = n[Math.floor((e.y - r.offY) / r.cellH)] ?? "", f = (e) => e > 0 && (d[e] ?? " ") !== " ", p = Math.floor((e.x - r.offX) / r.cellW);
|
|
1101
|
+
for (; f(p) && (f(p + 1) || f(p + 2));) p += f(p + 1) ? 1 : 2;
|
|
1102
|
+
let m = (t) => {
|
|
1103
|
+
let { dx: n, dy: i } = Ae(e, t);
|
|
1104
|
+
if (i > 0 || n > me * r.cellW) return !1;
|
|
1105
|
+
let a = Math.round((t.x - r.offX) / r.cellW);
|
|
1106
|
+
if (a <= p) return !0;
|
|
1107
|
+
for (let e = p + 1; e < a; e++) if (f(e)) return !1;
|
|
1108
|
+
return !0;
|
|
1109
|
+
}, h = u.some(({ area: e }) => m(e)), g = u.some(({ goal: e }) => e.kind === "cell"), _ = null;
|
|
1110
|
+
for (let { goal: t, area: n } of u) {
|
|
1111
|
+
let { dx: r, dy: i } = Ae(e, n);
|
|
1112
|
+
if (h && !m(n) || s && !h && g && t.kind !== "cell") continue;
|
|
1113
|
+
let a = n.x > e.x, o = Math.hypot(r, i * pe) - (t.kind === "cell" ? .001 : 0) - (a ? .5 : 0), c = ke(n), l = Math.hypot(c.x - e.x, c.y - e.y);
|
|
1114
|
+
(!_ || o < _.score - 1e-6 || Math.abs(o - _.score) <= 1e-6 && l < _.tie) && (_ = {
|
|
1115
|
+
goal: t,
|
|
1116
|
+
score: o,
|
|
1117
|
+
tie: l
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
return _?.goal ?? je(e);
|
|
1121
|
+
}, R = {
|
|
692
1122
|
Left: 128,
|
|
693
1123
|
Up: 64,
|
|
694
1124
|
Down: 32,
|
|
@@ -697,160 +1127,256 @@ var D = (e) => {
|
|
|
697
1127
|
Right: 4,
|
|
698
1128
|
Opt: 2,
|
|
699
1129
|
Edit: 1
|
|
700
|
-
},
|
|
1130
|
+
}, Fe = ({ left: e, right: t, up: n, down: r, shift: i, play: a, opt: o, edit: s }) => {
|
|
701
1131
|
let c = 0;
|
|
702
|
-
return e && (c |=
|
|
703
|
-
},
|
|
704
|
-
up:
|
|
705
|
-
down:
|
|
706
|
-
left:
|
|
707
|
-
right:
|
|
708
|
-
},
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
}, R = (e, t) => e ? e.rx === t.rx && e.ry === t.ry && e.rw === t.rw && e.rh === t.rh : !1, ce = (e) => ({
|
|
716
|
-
x: e.rx + e.rw / 2,
|
|
717
|
-
y: e.ry + e.rh / 2
|
|
718
|
-
}), le = (e, t) => t.x >= e.rx - L && t.x <= e.rx + e.rw + L, ue = (e, t) => t.y >= e.ry - L && t.y <= e.ry + e.rh + L, de = (e, t) => {
|
|
719
|
-
let n = ce(e), r = n.x - t.x, i = n.y - t.y;
|
|
720
|
-
return Math.sqrt(r * r + i * i);
|
|
721
|
-
}, fe = (e, t, n) => e === "vertical" ? n.y < t.ry - L ? "up" : n.y > t.ry + t.rh + L ? "down" : null : n.x < t.rx - L ? "left" : n.x > t.rx + t.rw + L ? "right" : null, pe = ({ bus: e, store: t }) => {
|
|
722
|
-
let n = null, r = null, i = 0, a = 0, o = !1, s = "vertical", c = {
|
|
723
|
-
vertical: !1,
|
|
724
|
-
horizontal: !1
|
|
725
|
-
}, l = null, u = null, d = !1, f = null, p = [], m = [], h = [], g = () => {
|
|
726
|
-
f &&= (clearTimeout(f), null);
|
|
727
|
-
}, _ = () => {
|
|
728
|
-
g(), f = setTimeout(() => {
|
|
729
|
-
O("timeout");
|
|
730
|
-
}, ae);
|
|
731
|
-
}, v = () => {
|
|
732
|
-
r = null, s = "vertical", c = {
|
|
733
|
-
vertical: !1,
|
|
734
|
-
horizontal: !1
|
|
735
|
-
}, l = null, u = null, d = !1, p = [], m = [], h = [], o = !1, a = i & ~re, g();
|
|
736
|
-
}, y = () => {
|
|
737
|
-
g(), n &&= (n(), null), v();
|
|
738
|
-
}, b = () => {
|
|
1132
|
+
return e && (c |= R.Left), t && (c |= R.Right), n && (c |= R.Up), r && (c |= R.Down), i && (c |= R.Shift), a && (c |= R.Play), o && (c |= R.Opt), s && (c |= R.Edit), c;
|
|
1133
|
+
}, Ie = {
|
|
1134
|
+
up: R.Up,
|
|
1135
|
+
down: R.Down,
|
|
1136
|
+
left: R.Left,
|
|
1137
|
+
right: R.Right
|
|
1138
|
+
}, Le = R.Up | R.Down | R.Left | R.Right, Re = R.Edit | R.Opt | R.Shift | R.Play, ze = Le | R.Edit | R.Opt | R.Shift, Be = 48, Ve = 40, He = 16, Ue = 4, We = 150, Ge = 300, Ke = 200, qe = (e, t) => !!e && !!t && e.x === t.x && e.y === t.y && e.w === t.w && e.h === t.h, Je = (e) => {
|
|
1139
|
+
if (e & Re) return null;
|
|
1140
|
+
let t = Object.keys(Ie).filter((t) => e & Ie[t]);
|
|
1141
|
+
return t.length === 1 ? t[0] : null;
|
|
1142
|
+
}, Ye = ({ bus: e, store: t, getScreenLines: n = () => [], cursorMap: r = oe() }) => {
|
|
1143
|
+
let i = null, a = 0, o = 0, s = !1, c = null, l = null, u = () => t.get("cellMetrics"), d = () => be(t.get("systemInfo")?.screenWidth ?? 480, u()), f = () => {
|
|
739
1144
|
let e = t.get("cursorRect");
|
|
740
|
-
return e ?
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1145
|
+
return e ? ve(e, u()) : null;
|
|
1146
|
+
}, p = () => {
|
|
1147
|
+
let e = t.get("viewName");
|
|
1148
|
+
if (!e || t.get("selectionMode")) return null;
|
|
1149
|
+
let r = t.get("systemInfo");
|
|
1150
|
+
return [
|
|
1151
|
+
e,
|
|
1152
|
+
r?.model ?? t.get("deviceModel") ?? "?",
|
|
1153
|
+
r?.fontMode ?? t.get("fontMode") ?? "?",
|
|
1154
|
+
xe(n(), d())
|
|
1155
|
+
].join("|");
|
|
1156
|
+
}, m = () => {
|
|
1157
|
+
c &&= (clearTimeout(c), null);
|
|
1158
|
+
}, h = (e = We) => {
|
|
1159
|
+
m(), c = setTimeout(() => {
|
|
1160
|
+
c = null, E("timeout");
|
|
1161
|
+
}, e);
|
|
1162
|
+
}, g = (e) => {
|
|
1163
|
+
if (!i) return;
|
|
1164
|
+
let { resolve: t } = i;
|
|
1165
|
+
i.goal, f(), i = null, m(), s = !1, o = a & ~ze, t(e);
|
|
1166
|
+
}, _ = (t) => {
|
|
1167
|
+
e.commands.sendKeys(o | Ie[t]), e.commands.sendKeys(o);
|
|
1168
|
+
}, v = (e, t, n) => e.goal.kind === "cell" ? t === e.goal.id : !!n && Me(e.goal, n, u()), y = (e, t) => {
|
|
1169
|
+
let n = [], r = e.y + e.h / 2, i = e.x + e.w / 2;
|
|
1170
|
+
return De(e, t.rect, u()) || n.push(t.rect.y + t.rect.h / 2 < r ? "up" : "down"), Ee(e, t.rect, u()) || n.push(t.rect.x + t.rect.w / 2 < i ? "left" : "right"), n;
|
|
1171
|
+
}, b = (e, t, n) => {
|
|
1172
|
+
if (!e.grid) return !1;
|
|
1173
|
+
let r = ye(t, u());
|
|
1174
|
+
return n === "up" && r <= e.grid.first || n === "down" && r >= e.grid.last;
|
|
746
1175
|
}, x = (e, t) => {
|
|
747
|
-
let n =
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1176
|
+
let n = F(t), i = e.layoutKey ? r.get(e.layoutKey) : void 0;
|
|
1177
|
+
if (i) {
|
|
1178
|
+
let t = de(i, n, (t) => v(e, t, i.cells.get(t)), e.distrust, (t, n) => b(e, t, n))?.[0];
|
|
1179
|
+
if (t && !(t.guessed && (e.explored >= Ve || e.tried.has(`${n}:${t.dir}`)))) return {
|
|
1180
|
+
dir: t.dir,
|
|
1181
|
+
expected: t.to,
|
|
1182
|
+
guessed: t.guessed
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
let a = i?.moves.get(n) ?? {}, o = L(t, e.goal.rect), s = null;
|
|
1186
|
+
for (let r of y(t, e.goal)) {
|
|
1187
|
+
let c = `${n}:${r}`;
|
|
1188
|
+
if (e.tried.has(c) || b(e, t, r)) continue;
|
|
1189
|
+
let l = e.distrust.has(c) ? void 0 : a[r];
|
|
1190
|
+
if (l === null) s ??= {
|
|
1191
|
+
dir: r,
|
|
1192
|
+
expected: null,
|
|
1193
|
+
guessed: !1
|
|
1194
|
+
};
|
|
1195
|
+
else if (l !== void 0) {
|
|
1196
|
+
let n = i?.cells.get(l);
|
|
1197
|
+
if (n && !se(t, r, n) && L(n, e.goal.rect) < o) return {
|
|
1198
|
+
dir: r,
|
|
1199
|
+
expected: l,
|
|
1200
|
+
guessed: !1
|
|
1201
|
+
};
|
|
1202
|
+
} else return e.explored >= Ve ? null : {
|
|
1203
|
+
dir: r,
|
|
1204
|
+
expected: void 0,
|
|
1205
|
+
guessed: !0
|
|
1206
|
+
};
|
|
771
1207
|
}
|
|
772
|
-
return
|
|
773
|
-
},
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
},
|
|
781
|
-
|
|
1208
|
+
return s;
|
|
1209
|
+
}, S = (e, t, n) => {
|
|
1210
|
+
_(n.dir), e.steps += 1, n.guessed && (e.explored += 1), e.tried.add(`${F(t)}:${n.dir}`), e.pending = {
|
|
1211
|
+
from: t,
|
|
1212
|
+
dir: n.dir,
|
|
1213
|
+
expected: n.expected,
|
|
1214
|
+
guessed: n.guessed
|
|
1215
|
+
}, s = !0, n.dir, n.expected, h();
|
|
1216
|
+
}, C = (e, t, i) => {
|
|
1217
|
+
if (e.phase === "seek") {
|
|
1218
|
+
if (e.goal.kind === "text" && Oe(t, e.goal.run, u())) {
|
|
1219
|
+
g(!0);
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
if (i && e.goal.kind === "text" && e.layoutKey) {
|
|
1223
|
+
let { row: i, run: a } = e.goal, o = De(t, a, u()), s = a.x + a.w / 2 < t.x + t.w / 2, c = ye(t, u()), l = i < c ? "up" : "down", f = Math.abs(i - c) === 1 && r.get(e.layoutKey)?.moves.get(F(t))?.[l] === null, p = o || f ? Ne(r.get(e.layoutKey), n(), u(), d(), e.grid).runs.filter((e) => e.row === i).filter((e) => !o || (s ? e.rect.x + e.rect.w <= t.x + 3 : e.rect.x >= t.x + t.w - 3)).map((e) => e.key) : [];
|
|
1224
|
+
p.includes(e.goal.key) || p.push(e.goal.key);
|
|
1225
|
+
for (let t of p) e.failedTexts.add(t), r.markUnreachable(e.layoutKey, t);
|
|
1226
|
+
let m = Pe(e.point, r.get(e.layoutKey), n(), u(), d(), e.grid, e.failedTexts, !0);
|
|
1227
|
+
if (e.goals < Ue && m.kind !== "point" && m.rect !== e.goal.rect) {
|
|
1228
|
+
e.goal, e.goal = m, e.goals += 1, e.explored = 0, e.tried.clear(), w(e);
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
let a = {
|
|
1233
|
+
x: e.point.x,
|
|
1234
|
+
y: e.point.y,
|
|
1235
|
+
w: 1,
|
|
1236
|
+
h: 1
|
|
1237
|
+
}, o = Ne(e.layoutKey ? r.get(e.layoutKey) : void 0, n(), u(), d(), e.grid).cells, s = t;
|
|
1238
|
+
if (o.length > 0) for (let e of o) L(e.rect, a) < L(s, a) - 1e-6 && (s = e.rect);
|
|
1239
|
+
else for (let t of e.visited.values()) L(t, e.goal.rect) < L(s, e.goal.rect) - 1e-6 && (s = t);
|
|
1240
|
+
if (!qe(s, t)) {
|
|
1241
|
+
e.goal, e.phase = "return", e.goal = {
|
|
1242
|
+
kind: "cell",
|
|
1243
|
+
id: F(s),
|
|
1244
|
+
rect: s
|
|
1245
|
+
}, e.steps = 0, e.explored = 0, e.tried.clear(), w(e);
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
g(!1);
|
|
782
1250
|
};
|
|
783
|
-
function
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
let n = r;
|
|
788
|
-
if ((m.length === 0 || !R(m[m.length - 1].rect, t)) && x(t, n), T()) {
|
|
789
|
-
w(S(!0)), y();
|
|
1251
|
+
function w(e) {
|
|
1252
|
+
let t = f();
|
|
1253
|
+
if (!t) {
|
|
1254
|
+
g(!1);
|
|
790
1255
|
return;
|
|
791
1256
|
}
|
|
792
|
-
if (
|
|
793
|
-
|
|
1257
|
+
if (Me(e.goal, t, u())) {
|
|
1258
|
+
g(e.phase === "seek");
|
|
794
1259
|
return;
|
|
795
1260
|
}
|
|
796
|
-
if (
|
|
797
|
-
|
|
1261
|
+
if (e.steps >= (e.phase === "seek" ? Be : He)) {
|
|
1262
|
+
C(e, t, !1);
|
|
798
1263
|
return;
|
|
799
1264
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
let
|
|
808
|
-
|
|
1265
|
+
let n = x(e, t);
|
|
1266
|
+
n ? S(e, t, n) : C(e, t, e.explored < Ve);
|
|
1267
|
+
}
|
|
1268
|
+
let T = (e, t, n) => {
|
|
1269
|
+
e.pending = null, m(), s = !1;
|
|
1270
|
+
let i = p();
|
|
1271
|
+
e.layoutKey && i === e.layoutKey && r.recordMove(e.layoutKey, t.from, t.dir, n);
|
|
1272
|
+
let a = n ? F(n) : null;
|
|
1273
|
+
!t.guessed && t.expected !== void 0 && a !== t.expected && e.distrust.add(`${F(t.from)}:${t.dir}`), n && e.visited.set(F(n), n);
|
|
1274
|
+
};
|
|
1275
|
+
function E(e) {
|
|
1276
|
+
let n = i;
|
|
1277
|
+
if (!n) return;
|
|
1278
|
+
let r = f();
|
|
1279
|
+
if (!r) g(!1);
|
|
809
1280
|
else {
|
|
810
|
-
if (
|
|
811
|
-
|
|
812
|
-
|
|
1281
|
+
if (n.pending) {
|
|
1282
|
+
if (!qe(n.pending.from, r)) T(n, n.pending, r);
|
|
1283
|
+
else if (e === "timeout" && !n.pending.graced) {
|
|
1284
|
+
n.pending.graced = !0, h(Ge);
|
|
1285
|
+
return;
|
|
1286
|
+
} else if (e === "timeout") T(n, n.pending, null);
|
|
1287
|
+
else return;
|
|
813
1288
|
}
|
|
814
|
-
w(
|
|
1289
|
+
t.get("viewName") === n.viewName ? w(n) : g(!1);
|
|
815
1290
|
}
|
|
816
1291
|
}
|
|
817
|
-
let
|
|
818
|
-
|
|
1292
|
+
let D = (e) => {
|
|
1293
|
+
if (a = e.keys ?? 0, i) {
|
|
1294
|
+
l = null, s && (s = !1, i.pending && h());
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
o = a & ~ze;
|
|
1298
|
+
let t = Je(a);
|
|
1299
|
+
t ? (!l || l.dir !== t || l.releasedAt !== null) && (l = {
|
|
1300
|
+
dir: t,
|
|
1301
|
+
from: f(),
|
|
1302
|
+
layoutKey: p(),
|
|
1303
|
+
releasedAt: null
|
|
1304
|
+
}) : a === 0 && l ? l.releasedAt ??= Date.now() : l = null;
|
|
1305
|
+
}, O = () => {
|
|
1306
|
+
if (i) {
|
|
1307
|
+
E("rect");
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
let e = f(), t = p();
|
|
1311
|
+
if (!e || !t) {
|
|
1312
|
+
l = null;
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
let a = l;
|
|
1316
|
+
if (a?.from && a.layoutKey === t && (a.releasedAt === null || Date.now() - a.releasedAt <= Ke)) {
|
|
1317
|
+
let i = n();
|
|
1318
|
+
!qe(a.from, e) && we(a.from, i, u()) && we(e, i, u()) && r.recordMove(t, a.from, a.dir, e), a.from = e;
|
|
1319
|
+
} else l = null;
|
|
819
1320
|
};
|
|
820
|
-
e.protocol.eventBus.on("key",
|
|
821
|
-
let
|
|
822
|
-
r && O("rect");
|
|
823
|
-
});
|
|
1321
|
+
e.protocol.eventBus.on("key", D);
|
|
1322
|
+
let k = t.sub("cursorRect", O);
|
|
824
1323
|
return {
|
|
825
|
-
navigateTo: (e) => new Promise((
|
|
826
|
-
|
|
827
|
-
|
|
1324
|
+
navigateTo: (e) => new Promise((s) => {
|
|
1325
|
+
let m = f();
|
|
1326
|
+
if (!t.get("cursorPos") || !m) {
|
|
1327
|
+
s(!1);
|
|
828
1328
|
return;
|
|
829
1329
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1330
|
+
let g = i?.pending ?? null;
|
|
1331
|
+
if (i) {
|
|
1332
|
+
let e = i;
|
|
1333
|
+
i = null, e.resolve(!1);
|
|
1334
|
+
}
|
|
1335
|
+
let _ = p(), v = _ ? r.get(_) : void 0, y = n(), b = _e(t.get("viewName"), y), x = t.get("selectionMode") ? je(e) : Pe(e, v, y, u(), d(), b);
|
|
1336
|
+
_ && we(m, y, u()) && r.recordCell(_, m), g || (o = a & ~ze), l = null;
|
|
1337
|
+
let S = {
|
|
1338
|
+
point: e,
|
|
1339
|
+
grid: b,
|
|
1340
|
+
goal: x,
|
|
1341
|
+
goals: 1,
|
|
1342
|
+
phase: "seek",
|
|
1343
|
+
viewName: t.get("viewName"),
|
|
1344
|
+
layoutKey: _,
|
|
1345
|
+
steps: 0,
|
|
1346
|
+
explored: 0,
|
|
1347
|
+
tried: /* @__PURE__ */ new Set(),
|
|
1348
|
+
distrust: /* @__PURE__ */ new Set(),
|
|
1349
|
+
visited: /* @__PURE__ */ new Map([[F(m), m]]),
|
|
1350
|
+
failedTexts: /* @__PURE__ */ new Set(),
|
|
1351
|
+
pending: g ? {
|
|
1352
|
+
...g,
|
|
1353
|
+
expected: void 0,
|
|
1354
|
+
guessed: !0
|
|
1355
|
+
} : null,
|
|
1356
|
+
resolve: s
|
|
1357
|
+
};
|
|
1358
|
+
i = S, g ? c || h() : w(S);
|
|
833
1359
|
}),
|
|
834
1360
|
dispose: () => {
|
|
835
|
-
|
|
1361
|
+
g(!1), e.protocol.eventBus.off("key", D), k();
|
|
836
1362
|
}
|
|
837
1363
|
};
|
|
838
|
-
}, z = (e) => e?.trim().toLowerCase() ?? "",
|
|
1364
|
+
}, z = (e) => e?.trim().toLowerCase() ?? "", Xe = /\.m[a-z0-9]{2}$/i, Ze = (e) => [
|
|
839
1365
|
e.viewName ?? "",
|
|
840
1366
|
e.viewTitle ?? "",
|
|
841
1367
|
e.cursorPos?.x ?? "",
|
|
842
1368
|
e.cursorPos?.y ?? "",
|
|
843
1369
|
e.textUnderCursor ?? "",
|
|
844
1370
|
e.currentLine ?? ""
|
|
845
|
-
].join("|"),
|
|
1371
|
+
].join("|"), Qe = (e) => e.textUnderCursor?.trim() || e.currentLine?.trim() || null, $e = (e) => {
|
|
846
1372
|
let t = z(e.viewName), n = z(e.viewTitle);
|
|
847
1373
|
return t.includes("load") || t.includes("save") || n.includes("load") || n.includes("save");
|
|
848
|
-
},
|
|
1374
|
+
}, et = (e) => z(e) === "/..", tt = (e) => !!e?.trim().startsWith("/"), nt = (e) => !!e && !tt(e) && Xe.test(e.trim()), rt = (e) => {
|
|
849
1375
|
let t = e?.trim() ?? "";
|
|
850
1376
|
if (!t) return [];
|
|
851
|
-
let n = z(t), r = z(t.replace(
|
|
1377
|
+
let n = z(t), r = z(t.replace(Xe, ""));
|
|
852
1378
|
return Array.from(new Set([n, r].filter(Boolean)));
|
|
853
|
-
},
|
|
1379
|
+
}, it = (e) => et(e) ? "parent" : tt(e) ? /^\/[^/]+$/.test(e.trim()) ? "dir" : "other" : nt(e) ? "file" : "other", at = (e, t) => {
|
|
854
1380
|
let n = (t) => e[t]?.trim() ?? "", r = t.cursorPos?.y ?? null, i = z(t.viewTitle), a = 0, o = e.length - 1;
|
|
855
1381
|
if (r !== null && n(r)) {
|
|
856
1382
|
for (a = r; a > 0 && n(a - 1);) --a;
|
|
@@ -863,7 +1389,7 @@ var D = (e) => {
|
|
|
863
1389
|
row: e,
|
|
864
1390
|
text: t,
|
|
865
1391
|
key: z(t),
|
|
866
|
-
kind:
|
|
1392
|
+
kind: it(t)
|
|
867
1393
|
});
|
|
868
1394
|
}
|
|
869
1395
|
let c = t.textUnderCursor?.trim();
|
|
@@ -874,7 +1400,7 @@ var D = (e) => {
|
|
|
874
1400
|
hasParent: s.some((e) => e.kind === "parent"),
|
|
875
1401
|
consistent: !c || r === null || n(r).includes(c)
|
|
876
1402
|
};
|
|
877
|
-
},
|
|
1403
|
+
}, ot = (e, t) => {
|
|
878
1404
|
if (t.length === 0) return {
|
|
879
1405
|
order: e,
|
|
880
1406
|
consistent: !0
|
|
@@ -908,22 +1434,22 @@ var D = (e) => {
|
|
|
908
1434
|
order: i,
|
|
909
1435
|
consistent: !0
|
|
910
1436
|
};
|
|
911
|
-
},
|
|
1437
|
+
}, st = (e) => e.split(/[^a-z0-9]+/).filter((e) => e.length >= 3), ct = /* @__PURE__ */ new Set([
|
|
912
1438
|
"load",
|
|
913
1439
|
"save",
|
|
914
1440
|
"select",
|
|
915
1441
|
"create",
|
|
916
1442
|
"directory",
|
|
917
1443
|
"new"
|
|
918
|
-
]),
|
|
919
|
-
target:
|
|
920
|
-
title:
|
|
921
|
-
}),
|
|
1444
|
+
]), lt = (e, t) => ({
|
|
1445
|
+
target: st(z(e).replace(Xe, "")),
|
|
1446
|
+
title: st(z(t)).filter((e) => !ct.has(e))
|
|
1447
|
+
}), ut = (e, t) => {
|
|
922
1448
|
let n = e.replace(/^\//, "");
|
|
923
1449
|
if (n.length < 3) return 0;
|
|
924
1450
|
let r = (e) => e.includes(n) || n.includes(e);
|
|
925
1451
|
return t.target.some(r) ? 2 : +!!t.title.some(r);
|
|
926
|
-
}, B = (e) => new Promise((t) => setTimeout(t, e)),
|
|
1452
|
+
}, B = (e) => new Promise((t) => setTimeout(t, e)), dt = ({ getKeySink: e, store: t, debugLog: n, getScreenLines: r }) => {
|
|
927
1453
|
let i = () => !!e(), a = (t) => {
|
|
928
1454
|
e()?.sendKeys(t);
|
|
929
1455
|
}, o = async (e, t = 50) => {
|
|
@@ -965,13 +1491,13 @@ var D = (e) => {
|
|
|
965
1491
|
}),
|
|
966
1492
|
getFileBrowserSnapshot: c,
|
|
967
1493
|
waitForFileBrowserSnapshotChange: (e, n = 500) => new Promise((r) => {
|
|
968
|
-
let i = !1, a = null, o = null, s = null, l = null, u = null, d = null, f =
|
|
1494
|
+
let i = !1, a = null, o = null, s = null, l = null, u = null, d = null, f = Ze(e), p = (e) => {
|
|
969
1495
|
i || (i = !0, d !== null && clearTimeout(d), a?.(), o?.(), s?.(), l?.(), u?.(), r(e));
|
|
970
1496
|
}, m = () => {
|
|
971
1497
|
let e = c();
|
|
972
|
-
|
|
1498
|
+
Ze(e) !== f && p(e);
|
|
973
1499
|
}, h = c();
|
|
974
|
-
|
|
1500
|
+
Ze(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
1501
|
p(c());
|
|
976
1502
|
}, n)) : p(h);
|
|
977
1503
|
}),
|
|
@@ -979,7 +1505,7 @@ var D = (e) => {
|
|
|
979
1505
|
};
|
|
980
1506
|
}, V = (e, t) => {
|
|
981
1507
|
t === void 0 ? console.log("[M8SDK browseFile]", e) : console.log("[M8SDK browseFile]", e, t);
|
|
982
|
-
},
|
|
1508
|
+
}, ft = 8, pt = 8, mt = 64, ht = 512, gt = () => ({
|
|
983
1509
|
order: [],
|
|
984
1510
|
kinds: /* @__PURE__ */ new Map(),
|
|
985
1511
|
names: /* @__PURE__ */ new Map(),
|
|
@@ -989,40 +1515,40 @@ var D = (e) => {
|
|
|
989
1515
|
sorted: !1,
|
|
990
1516
|
visited: /* @__PURE__ */ new Set(),
|
|
991
1517
|
rejected: /* @__PURE__ */ new Set()
|
|
992
|
-
}),
|
|
1518
|
+
}), _t = (e) => e === "up" ? "down" : "up", vt = (e) => {
|
|
993
1519
|
let { hasBus: t, pressAndRelease: n, getFileBrowserSnapshot: r, waitForFileBrowserSnapshotChange: i, getScreenLines: a } = e, o = /* @__PURE__ */ new Map(), s = null, c = null;
|
|
994
1520
|
return async (e, l = !0) => {
|
|
995
1521
|
if (!t()) return !1;
|
|
996
1522
|
let u = z(e);
|
|
997
1523
|
if (!u) return !1;
|
|
998
|
-
let d =
|
|
1524
|
+
let d = Xe.test(u), f = lt(e, r().viewTitle);
|
|
999
1525
|
V("start", {
|
|
1000
1526
|
targetText: e,
|
|
1001
1527
|
normalizedTarget: u,
|
|
1002
1528
|
exact: l
|
|
1003
1529
|
});
|
|
1004
|
-
let p = () =>
|
|
1530
|
+
let p = () => $e(r()), m = async (e, t = 350) => {
|
|
1005
1531
|
let a = r();
|
|
1006
1532
|
await n(e, 35);
|
|
1007
1533
|
let o = await i(a, t);
|
|
1008
1534
|
return {
|
|
1009
|
-
changed:
|
|
1535
|
+
changed: Ze(o) !== Ze(a),
|
|
1010
1536
|
snapshot: o
|
|
1011
1537
|
};
|
|
1012
|
-
}, h = (e) => e === "up" ?
|
|
1538
|
+
}, h = (e) => e === "up" ? R.Up : R.Down, g = (e) => R.Opt | h(e), _ = async () => {
|
|
1013
1539
|
let e = r();
|
|
1014
|
-
V("apply alphabetical sort"), await n(
|
|
1015
|
-
}, v = (e) =>
|
|
1540
|
+
V("apply alphabetical sort"), await n(R.Shift | R.Opt, 35), await B(180), await i(e, 300);
|
|
1541
|
+
}, v = (e) => nt(e) ? rt(e).some((e) => l ? e === u : e.includes(u)) : !1, y = (e) => {
|
|
1016
1542
|
let t = e.trim();
|
|
1017
|
-
return d ? t : t.replace(
|
|
1543
|
+
return d ? t : t.replace(Xe, "");
|
|
1018
1544
|
}, b = (e) => z(y(e)), x = y(e), S = async () => {
|
|
1019
1545
|
for (let e = 0;; e += 1) {
|
|
1020
|
-
let t =
|
|
1546
|
+
let t = at(a(), r());
|
|
1021
1547
|
if (t.consistent || e >= 2) return t;
|
|
1022
1548
|
await B(40);
|
|
1023
1549
|
}
|
|
1024
1550
|
}, C = (e, t) => {
|
|
1025
|
-
let n = t.entries.map((e) => e.key), i =
|
|
1551
|
+
let n = t.entries.map((e) => e.key), i = ot(e.order, n);
|
|
1026
1552
|
e.order = i.order, i.consistent || (e.top = !1, e.bottom = !1);
|
|
1027
1553
|
for (let n of t.entries) e.kinds.set(n.key, n.kind), e.names.set(n.key, n.text);
|
|
1028
1554
|
let a = z(r().viewName);
|
|
@@ -1031,10 +1557,10 @@ var D = (e) => {
|
|
|
1031
1557
|
let e = o.get(a);
|
|
1032
1558
|
(e === void 0 || n.length < e) && o.set(a, n.length);
|
|
1033
1559
|
}
|
|
1034
|
-
n.length < Math.max(
|
|
1560
|
+
n.length < Math.max(pt, o.get(a) ?? 0) && (e.top = !0, e.bottom = !0);
|
|
1035
1561
|
}, w = async (e, t, n) => {
|
|
1036
1562
|
let r = (await m(g(n), 280)).changed, i = r, a = !1;
|
|
1037
|
-
if (!r && s !== !0) for (let e = 0; e <
|
|
1563
|
+
if (!r && s !== !0) for (let e = 0; e < ft; e += 1) {
|
|
1038
1564
|
if (!(await m(h(n), 250)).changed) {
|
|
1039
1565
|
a = !0;
|
|
1040
1566
|
break;
|
|
@@ -1046,7 +1572,7 @@ var D = (e) => {
|
|
|
1046
1572
|
let o = await S();
|
|
1047
1573
|
C(e, o);
|
|
1048
1574
|
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 <
|
|
1575
|
+
return r && c >= 0 && l >= 0 && u > 0 && u < ft && (s = !0), o;
|
|
1050
1576
|
}, T = (e, t, n) => {
|
|
1051
1577
|
if (n === "/..") return "up";
|
|
1052
1578
|
let r = e.order.indexOf(n), i = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1;
|
|
@@ -1060,7 +1586,7 @@ var D = (e) => {
|
|
|
1060
1586
|
let t = n.row - r.cursorRow;
|
|
1061
1587
|
if (t === 0) return r;
|
|
1062
1588
|
let i = t < 0 ? "up" : "down";
|
|
1063
|
-
if (!(await m(Math.abs(t) >=
|
|
1589
|
+
if (!(await m(Math.abs(t) >= ft ? g(i) : h(i), 250)).changed) return null;
|
|
1064
1590
|
r = await S(), C(e, r);
|
|
1065
1591
|
continue;
|
|
1066
1592
|
}
|
|
@@ -1068,17 +1594,17 @@ var D = (e) => {
|
|
|
1068
1594
|
let o = await w(e, r, i);
|
|
1069
1595
|
if (o) r = o;
|
|
1070
1596
|
else if (a) return null;
|
|
1071
|
-
else a = !0, i =
|
|
1597
|
+
else a = !0, i = _t(i);
|
|
1072
1598
|
}
|
|
1073
1599
|
return null;
|
|
1074
1600
|
}, D = async (e, t) => {
|
|
1075
1601
|
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
1602
|
if (!i) return "none";
|
|
1077
|
-
let a = await E(e, i.key, t), o =
|
|
1603
|
+
let a = await E(e, i.key, t), o = Qe(r());
|
|
1078
1604
|
return a && v(o) ? (V("match found", {
|
|
1079
1605
|
entry: o,
|
|
1080
1606
|
y: a.cursorRow
|
|
1081
|
-
}), await m(
|
|
1607
|
+
}), await m(R.Edit, 500), "found") : (V("match not reachable", { candidate: i.text }), e.rejected.add(i.key), "failed");
|
|
1082
1608
|
}, O = (e) => e.order.filter((t) => e.kinds.get(t) === "file").map((t) => {
|
|
1083
1609
|
let n = e.names.get(t) ?? "";
|
|
1084
1610
|
return {
|
|
@@ -1101,27 +1627,27 @@ var D = (e) => {
|
|
|
1101
1627
|
};
|
|
1102
1628
|
}), r = n.findIndex((e) => e.kind === "file");
|
|
1103
1629
|
r < 0 || (n.slice(r).some((e) => e.kind === "dir") ? c = !1 : n.some((e, t) => e.kind === "dir" && n.slice(t + 1).some((t) => t.kind === "file" && e.ci > t.ci && e.raw > t.raw)) && c === null && (c = !0));
|
|
1104
|
-
},
|
|
1630
|
+
}, ee = (e) => !l || !e.sorted || c !== !0 || !e.top || e.bottom ? !1 : O(e).some((e) => /^[a-z0-9]/.test(e.ci) && e.ci > u && e.raw > x && e.raw > u), te = (e, t) => {
|
|
1105
1631
|
if (!l || !e.sorted) return null;
|
|
1106
1632
|
let n = t.entries.filter((e) => e.kind === "file").map((e) => b(e.text)), r = n[0], i = n[n.length - 1];
|
|
1107
1633
|
return r === void 0 || i === void 0 ? null : u < r ? "up" : u > i ? "down" : null;
|
|
1108
|
-
},
|
|
1634
|
+
}, A = (e, t) => e.top === e.bottom ? te(e, t) ?? "up" : e.top ? "down" : "up", j = (e, t) => {
|
|
1109
1635
|
let n = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1, r = null;
|
|
1110
1636
|
for (let [i, a] of e.kinds) {
|
|
1111
1637
|
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 =
|
|
1638
|
+
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 = ut(i, f) * 1e4 - c;
|
|
1113
1639
|
(!r || l > r.rank) && (r = {
|
|
1114
1640
|
key: i,
|
|
1115
1641
|
rank: l
|
|
1116
1642
|
});
|
|
1117
1643
|
}
|
|
1118
1644
|
return r?.key ?? null;
|
|
1119
|
-
},
|
|
1120
|
-
if (e >
|
|
1645
|
+
}, M = async (e, t) => await E(e, "/..", t) ? (V("navigate to parent directory"), (await m(R.Edit, 500)).changed && p()) : (V("parent directory entry unreachable"), !1), N = async (e) => {
|
|
1646
|
+
if (e > mt || !p()) return V("search aborted", {
|
|
1121
1647
|
depth: e,
|
|
1122
1648
|
active: p()
|
|
1123
1649
|
}), "aborted";
|
|
1124
|
-
let t =
|
|
1650
|
+
let t = gt(), n = await S();
|
|
1125
1651
|
C(t, n), V("search directory", {
|
|
1126
1652
|
depth: e,
|
|
1127
1653
|
entries: n.entries.length,
|
|
@@ -1130,12 +1656,12 @@ var D = (e) => {
|
|
|
1130
1656
|
for (let r = 0;; r += 1) {
|
|
1131
1657
|
let i = await D(t, n);
|
|
1132
1658
|
if (i === "found") return "found";
|
|
1133
|
-
if (i === "failed" && (n = await S(), C(t, n)), k(t),
|
|
1134
|
-
let a = await w(t, n,
|
|
1659
|
+
if (i === "failed" && (n = await S(), C(t, n)), k(t), ee(t) && (V("rest of listing is past the target", { depth: e }), t.bottom = !0), t.top && t.bottom || r >= ht) break;
|
|
1660
|
+
let a = await w(t, n, A(t, n));
|
|
1135
1661
|
a && (n = a);
|
|
1136
1662
|
}
|
|
1137
1663
|
for (;;) {
|
|
1138
|
-
let r =
|
|
1664
|
+
let r = j(t, n);
|
|
1139
1665
|
if (!r) break;
|
|
1140
1666
|
if (t.visited.add(r), !await E(t, r, n)) {
|
|
1141
1667
|
V("directory unreachable", { directory: r }), n = await S(), C(t, n);
|
|
@@ -1145,27 +1671,27 @@ var D = (e) => {
|
|
|
1145
1671
|
directory: r,
|
|
1146
1672
|
depth: e
|
|
1147
1673
|
});
|
|
1148
|
-
let i = await m(
|
|
1674
|
+
let i = await m(R.Edit, 500);
|
|
1149
1675
|
if (!p()) return "aborted";
|
|
1150
1676
|
if (!i.changed) {
|
|
1151
1677
|
n = await S(), C(t, n);
|
|
1152
1678
|
continue;
|
|
1153
1679
|
}
|
|
1154
|
-
let a = await
|
|
1680
|
+
let a = await N(e + 1);
|
|
1155
1681
|
if (a !== "missed") return a;
|
|
1156
1682
|
n = await S(), C(t, n);
|
|
1157
1683
|
}
|
|
1158
|
-
return e === 0 || await
|
|
1684
|
+
return e === 0 || await M(t, n) ? "missed" : "aborted";
|
|
1159
1685
|
};
|
|
1160
1686
|
if (!p()) return console.warn("[M8SDK] browseFile requires a file browser view (load/save)"), !1;
|
|
1161
1687
|
if (a().length === 0) return console.warn("[M8SDK] browseFile needs the screen reader, which is not available"), !1;
|
|
1162
|
-
let
|
|
1163
|
-
return
|
|
1688
|
+
let P = await N(0);
|
|
1689
|
+
return P !== "found" && V("search failed", {
|
|
1164
1690
|
targetText: e,
|
|
1165
|
-
outcome:
|
|
1166
|
-
}),
|
|
1691
|
+
outcome: P
|
|
1692
|
+
}), P === "found";
|
|
1167
1693
|
};
|
|
1168
|
-
},
|
|
1694
|
+
}, H = (e) => {
|
|
1169
1695
|
if (!e) return null;
|
|
1170
1696
|
let t = e.trim();
|
|
1171
1697
|
if (t === "--") return 0;
|
|
@@ -1173,7 +1699,7 @@ var D = (e) => {
|
|
|
1173
1699
|
if (!n) return null;
|
|
1174
1700
|
let r = parseInt(n[0], 16);
|
|
1175
1701
|
return Number.isNaN(r) ? null : r;
|
|
1176
|
-
},
|
|
1702
|
+
}, yt = (e) => {
|
|
1177
1703
|
if (!e) return null;
|
|
1178
1704
|
let t = e.trim();
|
|
1179
1705
|
if (t === "--") return 0;
|
|
@@ -1181,11 +1707,11 @@ var D = (e) => {
|
|
|
1181
1707
|
if (!n) return null;
|
|
1182
1708
|
let r = Number.parseInt(n[0], 10);
|
|
1183
1709
|
return Number.isNaN(r) ? null : r;
|
|
1184
|
-
},
|
|
1710
|
+
}, bt = /[+-]?\d+\.\d+/g, xt = (e) => {
|
|
1185
1711
|
if (!e) return [];
|
|
1186
1712
|
let t = [];
|
|
1187
|
-
|
|
1188
|
-
let n =
|
|
1713
|
+
bt.lastIndex = 0;
|
|
1714
|
+
let n = bt.exec(e);
|
|
1189
1715
|
for (; n !== null;) {
|
|
1190
1716
|
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
1717
|
!Number.isNaN(l) && !Number.isNaN(u) && t.push({
|
|
@@ -1196,16 +1722,16 @@ var D = (e) => {
|
|
|
1196
1722
|
decScale: d,
|
|
1197
1723
|
tickHundredths: f,
|
|
1198
1724
|
ticks: (i ? -1 : 1) * Math.round(p / f)
|
|
1199
|
-
}), n =
|
|
1725
|
+
}), n = bt.exec(e);
|
|
1200
1726
|
}
|
|
1201
1727
|
return t;
|
|
1202
|
-
},
|
|
1728
|
+
}, St = (e, t) => {
|
|
1203
1729
|
if (e.length <= 1) return 0;
|
|
1204
1730
|
let n = t?.trim() ?? "";
|
|
1205
1731
|
if (!n) return 0;
|
|
1206
1732
|
let r = e.findIndex((e) => e.raw === n || e.raw.replace(/^[+-]/, "") === n || `${e.negative ? "-" : ""}${e.intDigits}` === n || e.intDigits === n || e.decDigits === n);
|
|
1207
1733
|
return r >= 0 ? r : 0;
|
|
1208
|
-
},
|
|
1734
|
+
}, Ct = (e, t) => {
|
|
1209
1735
|
if (!e) return null;
|
|
1210
1736
|
let n = t?.trim() ?? "";
|
|
1211
1737
|
if (!n) return null;
|
|
@@ -1219,13 +1745,13 @@ var D = (e) => {
|
|
|
1219
1745
|
if (r && !t) return "dec";
|
|
1220
1746
|
}
|
|
1221
1747
|
return null;
|
|
1222
|
-
},
|
|
1748
|
+
}, wt = (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, Tt = (e, t, n) => {
|
|
1223
1749
|
let r = n?.trim() ?? "";
|
|
1224
|
-
return !r || !e[t] ||
|
|
1225
|
-
},
|
|
1750
|
+
return !r || !e[t] || wt(e[t], r) ? !1 : e.some((e, n) => n !== t && wt(e, r));
|
|
1751
|
+
}, U = (e, t) => {
|
|
1226
1752
|
let n = Math.round(e * t), r = n < 0 ? "-" : "", i = Math.abs(n), a = Math.floor(i / 100);
|
|
1227
1753
|
return `${r}${a}.${(i - a * 100).toString().padStart(2, "0")}`;
|
|
1228
|
-
},
|
|
1754
|
+
}, Et = [
|
|
1229
1755
|
"C-",
|
|
1230
1756
|
"C#",
|
|
1231
1757
|
"D-",
|
|
@@ -1238,35 +1764,35 @@ var D = (e) => {
|
|
|
1238
1764
|
"A-",
|
|
1239
1765
|
"A#",
|
|
1240
1766
|
"B-"
|
|
1241
|
-
],
|
|
1767
|
+
], W = (e) => {
|
|
1242
1768
|
if (!e) return null;
|
|
1243
1769
|
let t = e.trim().toUpperCase().match(/([A-G][#-][0-9A-F])/);
|
|
1244
1770
|
if (!t) return null;
|
|
1245
|
-
let n = t[1], r = n.slice(0, 2), i = Number.parseInt(n.slice(2, 3), 16), a =
|
|
1771
|
+
let n = t[1], r = n.slice(0, 2), i = Number.parseInt(n.slice(2, 3), 16), a = Et.indexOf(r);
|
|
1246
1772
|
return a < 0 || Number.isNaN(i) ? null : {
|
|
1247
1773
|
label: n,
|
|
1248
1774
|
noteName: r,
|
|
1249
1775
|
octave: i,
|
|
1250
1776
|
semitoneIndex: i * 12 + a
|
|
1251
1777
|
};
|
|
1252
|
-
},
|
|
1778
|
+
}, Dt = (e) => {
|
|
1253
1779
|
let { store: t, debugLog: n, hasBus: r, sendKeys: i, pressAndRelease: a, waitForTextUnderCursorChange: o, waitForCurrentLineChange: s } = e;
|
|
1254
1780
|
return async (e) => {
|
|
1255
1781
|
if (!r() || !Number.isFinite(e)) return !1;
|
|
1256
1782
|
let c = 0, l = 1, u = [], d = () => {
|
|
1257
|
-
let e =
|
|
1783
|
+
let e = xt(t.get("currentLine"));
|
|
1258
1784
|
if (u = e, !e.length) return null;
|
|
1259
1785
|
c = Math.min(c, e.length - 1);
|
|
1260
1786
|
let n = e[c];
|
|
1261
1787
|
return n.tickHundredths > 0 && (l = n.tickHundredths), n;
|
|
1262
|
-
}, f =
|
|
1788
|
+
}, f = xt(t.get("currentLine"));
|
|
1263
1789
|
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 =
|
|
1790
|
+
c = St(f, t.get("textUnderCursor")), f.length > 1 && n("[M8SDK] Multiple float values on line, editing:", f[c]?.raw);
|
|
1265
1791
|
let p = f[c];
|
|
1266
1792
|
l = p.tickHundredths;
|
|
1267
1793
|
let m = Math.round(Math.round(e * 100) / l);
|
|
1268
|
-
if (p.ticks === m) return n("[M8SDK] Already at target float value:",
|
|
1269
|
-
n("[M8SDK] Setting float value from",
|
|
1794
|
+
if (p.ticks === m) return n("[M8SDK] Already at target float value:", U(m, l)), !0;
|
|
1795
|
+
n("[M8SDK] Setting float value from", U(p.ticks, l), "to", U(m, l)), await a(R.Edit), await B(80);
|
|
1270
1796
|
let h = () => ({
|
|
1271
1797
|
upDown: {
|
|
1272
1798
|
mag: null,
|
|
@@ -1280,19 +1806,19 @@ var D = (e) => {
|
|
|
1280
1806
|
full: h(),
|
|
1281
1807
|
int: h(),
|
|
1282
1808
|
dec: h()
|
|
1283
|
-
}, _ = p.decScale, v = Date.now(), y =
|
|
1809
|
+
}, _ = p.decScale, v = Date.now(), y = Ct(p, t.get("textUnderCursor")) ?? "int";
|
|
1284
1810
|
n("[M8SDK] Float cursor focus:", y);
|
|
1285
1811
|
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
|
-
let n = e === "upDown" ?
|
|
1287
|
-
return
|
|
1288
|
-
},
|
|
1812
|
+
let n = e === "upDown" ? R.Up : R.Right, r = e === "upDown" ? R.Down : R.Left;
|
|
1813
|
+
return R.Edit | (t >= 0 ? n : r);
|
|
1814
|
+
}, ee = async (e, n, r) => {
|
|
1289
1815
|
let a = g[y][e], o = k(e, n * a.bias), c = t.get("currentLine");
|
|
1290
1816
|
for (let e = 0; e < r; e++) i(o), await B(22);
|
|
1291
1817
|
await s(c, 350);
|
|
1292
|
-
},
|
|
1293
|
-
let n = () =>
|
|
1818
|
+
}, te = async (e) => {
|
|
1819
|
+
let n = () => Ct(d(), t.get("textUnderCursor"));
|
|
1294
1820
|
if (n() === e) return y = e, !0;
|
|
1295
|
-
let r = t.get("textUnderCursor"), i = e === "dec" ?
|
|
1821
|
+
let r = t.get("textUnderCursor"), i = e === "dec" ? R.Right : R.Left, s = e === "dec" ? R.Left : R.Right;
|
|
1296
1822
|
if (await a(i, 35), await o(r, 250), n() === e) return y = e, !0;
|
|
1297
1823
|
await a(s, 35), await o(r, 250);
|
|
1298
1824
|
let c = n() === e;
|
|
@@ -1304,7 +1830,7 @@ var D = (e) => {
|
|
|
1304
1830
|
console.warn("[M8SDK] Float value no longer parseable while editing:", t.get("currentLine")), O = !0;
|
|
1305
1831
|
break;
|
|
1306
1832
|
}
|
|
1307
|
-
if (
|
|
1833
|
+
if (Tt(u, c, t.get("textUnderCursor"))) {
|
|
1308
1834
|
console.warn("[M8SDK] Float cursor drifted to another field, aborting to avoid editing the wrong value"), x = !0, O = !0;
|
|
1309
1835
|
break;
|
|
1310
1836
|
}
|
|
@@ -1322,8 +1848,8 @@ var D = (e) => {
|
|
|
1322
1848
|
}
|
|
1323
1849
|
if (C = S, S = e.ticks, y !== "full" && !b) {
|
|
1324
1850
|
let e = i >= _ ? "int" : "dec";
|
|
1325
|
-
if (e !== y && !await
|
|
1326
|
-
if (
|
|
1851
|
+
if (e !== y && !await te(e)) {
|
|
1852
|
+
if (Tt(u, c, t.get("textUnderCursor"))) {
|
|
1327
1853
|
console.warn("[M8SDK] Float cursor drifted to another field, aborting to avoid editing the wrong value"), x = !0, O = !0;
|
|
1328
1854
|
break;
|
|
1329
1855
|
}
|
|
@@ -1332,21 +1858,21 @@ var D = (e) => {
|
|
|
1332
1858
|
}
|
|
1333
1859
|
let s = g[y], f = y === "int" ? Math.max(32, 3 * _) : y === "full" ? _ : 8, p, h = 1;
|
|
1334
1860
|
s.upDown.mag !== null && s.upDown.mag > 0 && i >= s.upDown.mag ? (p = "upDown", h = Math.min(Math.max(1, Math.floor(i / s.upDown.mag)), 96)) : s.upDown.mag === null && i >= f ? (p = "upDown", h = 1) : s.leftRight.mag !== null && s.leftRight.mag > 0 ? (p = "leftRight", h = Math.min(Math.max(1, Math.floor(i / s.leftRight.mag)), 96)) : (p = "leftRight", h = 1);
|
|
1335
|
-
let v = r > 0 ? 1 : -1, k = e.ticks,
|
|
1336
|
-
await
|
|
1337
|
-
let
|
|
1338
|
-
if (!
|
|
1861
|
+
let v = r > 0 ? 1 : -1, k = e.ticks, A = y, j = p;
|
|
1862
|
+
await ee(p, v, h);
|
|
1863
|
+
let M = d();
|
|
1864
|
+
if (!M) {
|
|
1339
1865
|
O = !0;
|
|
1340
1866
|
break;
|
|
1341
1867
|
}
|
|
1342
|
-
let
|
|
1343
|
-
if (
|
|
1344
|
-
E += 1, E >= 4 && (console.warn(`[M8SDK] Float value stopped reacting at ${
|
|
1868
|
+
let N = M.ticks - k;
|
|
1869
|
+
if (N === 0) {
|
|
1870
|
+
E += 1, E >= 4 && (console.warn(`[M8SDK] Float value stopped reacting at ${U(M.ticks, l)} (target ${U(m, l)} may be out of range)`), O = !0);
|
|
1345
1871
|
continue;
|
|
1346
1872
|
}
|
|
1347
1873
|
E = 0;
|
|
1348
|
-
let
|
|
1349
|
-
|
|
1874
|
+
let P = g[A][j];
|
|
1875
|
+
P.mag = Math.max(1, Math.round(Math.abs(N) / h)), Math.sign(N) !== Math.sign(r) && (n(`[M8SDK] Float ${j} arrows move oppositely in '${A}' focus, inverting mapping`), P.bias *= -1, C = null);
|
|
1350
1876
|
}
|
|
1351
1877
|
if (!D && !x) {
|
|
1352
1878
|
let e = Date.now();
|
|
@@ -1354,20 +1880,20 @@ var D = (e) => {
|
|
|
1354
1880
|
let e = d();
|
|
1355
1881
|
if (!e || e.ticks === w) break;
|
|
1356
1882
|
let t = w - e.ticks, n = Math.abs(t);
|
|
1357
|
-
y !== "full" && !b && n >= _ != (y === "int") && (await
|
|
1883
|
+
y !== "full" && !b && n >= _ != (y === "int") && (await te(n >= _ ? "int" : "dec") || (b = !0));
|
|
1358
1884
|
let r = g[y], i, a = 1;
|
|
1359
1885
|
r.upDown.mag !== null && r.upDown.mag > 0 && n >= r.upDown.mag ? (i = "upDown", a = Math.min(Math.max(1, Math.floor(n / r.upDown.mag)), 16)) : r.leftRight.mag !== null && r.leftRight.mag > 0 ? (i = "leftRight", a = Math.min(Math.max(1, Math.floor(n / r.leftRight.mag)), 16)) : i = "leftRight";
|
|
1360
1886
|
let o = e.ticks;
|
|
1361
|
-
await
|
|
1887
|
+
await ee(i, t > 0 ? 1 : -1, a);
|
|
1362
1888
|
let s = d();
|
|
1363
1889
|
if (!s || s.ticks === o) break;
|
|
1364
1890
|
}
|
|
1365
1891
|
}
|
|
1366
|
-
x || await a(
|
|
1367
|
-
let
|
|
1368
|
-
return n(`[M8SDK] Float setting ${
|
|
1892
|
+
x || await a(R.Edit);
|
|
1893
|
+
let A = d(), j = !!A && A.ticks === m;
|
|
1894
|
+
return n(`[M8SDK] Float setting ${j ? "succeeded" : "failed"}: "${A?.raw ?? "N/A"}" (${A ? U(A.ticks, l) : "N/A"})`), j;
|
|
1369
1895
|
};
|
|
1370
|
-
},
|
|
1896
|
+
}, Ot = {
|
|
1371
1897
|
song: [
|
|
1372
1898
|
{
|
|
1373
1899
|
to: "project",
|
|
@@ -1820,7 +2346,7 @@ var D = (e) => {
|
|
|
1820
2346
|
via: "exit",
|
|
1821
2347
|
keys: [2, 0]
|
|
1822
2348
|
}]
|
|
1823
|
-
},
|
|
2349
|
+
}, kt = { 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(".") }, At = (e) => {
|
|
1824
2350
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
1825
2351
|
for (let [r, i] of Object.entries(e)) {
|
|
1826
2352
|
let e = r.toLowerCase(), a = [], o = /* @__PURE__ */ new Map();
|
|
@@ -1850,20 +2376,20 @@ var D = (e) => {
|
|
|
1850
2376
|
graph: t,
|
|
1851
2377
|
edgeKeys: n
|
|
1852
2378
|
};
|
|
1853
|
-
},
|
|
1854
|
-
if (!
|
|
1855
|
-
let e = Array.isArray(
|
|
1856
|
-
|
|
2379
|
+
}, jt = null, Mt = null, Nt = () => (jt ??= At(Ot), jt), Pt = () => {
|
|
2380
|
+
if (!Mt) {
|
|
2381
|
+
let e = Array.isArray(kt.views) ? kt.views : [];
|
|
2382
|
+
Mt = new Set(e.map((e) => String(e).trim().toLowerCase()).filter((e) => !!e));
|
|
1857
2383
|
}
|
|
1858
|
-
return
|
|
1859
|
-
},
|
|
2384
|
+
return Mt;
|
|
2385
|
+
}, Ft = 50, It = {
|
|
1860
2386
|
x: 0,
|
|
1861
2387
|
y: 0,
|
|
1862
2388
|
w: 1,
|
|
1863
2389
|
h: 1
|
|
1864
|
-
},
|
|
2390
|
+
}, Lt = class {
|
|
1865
2391
|
constructor() {
|
|
1866
|
-
this.assemblies = [], this.selectionRectSamples = [], this.lastCursor =
|
|
2392
|
+
this.assemblies = [], this.selectionRectSamples = [], this.lastCursor = It, 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;
|
|
1867
2393
|
}
|
|
1868
2394
|
processRect(e) {
|
|
1869
2395
|
if (!e) return {
|
|
@@ -2055,7 +2581,7 @@ var D = (e) => {
|
|
|
2055
2581
|
].filter((e) => e.foundVert || e.foundTip).length >= 2;
|
|
2056
2582
|
}
|
|
2057
2583
|
buildCursorFromAssembly(e) {
|
|
2058
|
-
if (!e.tr || !e.bl || !e.br) return
|
|
2584
|
+
if (!e.tr || !e.bl || !e.br) return It;
|
|
2059
2585
|
let t = e.tr.x - e.tl.x - 1, n = e.bl.y - e.tl.y - 1;
|
|
2060
2586
|
return {
|
|
2061
2587
|
x: e.tl.x + 1,
|
|
@@ -2072,19 +2598,19 @@ var D = (e) => {
|
|
|
2072
2598
|
h: 1
|
|
2073
2599
|
}, this.lastSelectionCursor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0;
|
|
2074
2600
|
}
|
|
2075
|
-
},
|
|
2601
|
+
}, Rt = (e) => e ? `#${[
|
|
2076
2602
|
e.r,
|
|
2077
2603
|
e.g,
|
|
2078
2604
|
e.b
|
|
2079
2605
|
].map((e) => e.toString(16).padStart(2, "0")).join("")}` : null;
|
|
2080
|
-
function
|
|
2081
|
-
let n = new
|
|
2606
|
+
function zt(e, t) {
|
|
2607
|
+
let n = new Lt();
|
|
2082
2608
|
class r {
|
|
2083
2609
|
constructor() {
|
|
2084
2610
|
this.rowChars = /* @__PURE__ */ new Map(), this.timer = null, this.knownViews = [], this.earlyAccepted = !1;
|
|
2085
2611
|
}
|
|
2086
2612
|
init() {
|
|
2087
|
-
this.knownViews = Array.from(
|
|
2613
|
+
this.knownViews = Array.from(Pt());
|
|
2088
2614
|
}
|
|
2089
2615
|
normForCompare(e) {
|
|
2090
2616
|
return e = e.includes("live") ? "song" : e, e.toLowerCase().trim().replace(/[{}]/g, "0");
|
|
@@ -2128,7 +2654,7 @@ function it(e, t) {
|
|
|
2128
2654
|
bg: e.background
|
|
2129
2655
|
}), this.tryEarlyAccept(o), this.timer && clearTimeout(this.timer), this.timer = setTimeout(() => {
|
|
2130
2656
|
this.emitStable();
|
|
2131
|
-
},
|
|
2657
|
+
}, Ft)));
|
|
2132
2658
|
}
|
|
2133
2659
|
reset() {
|
|
2134
2660
|
this.rowChars.clear(), this.timer &&= (clearTimeout(this.timer), null), this.earlyAccepted = !1;
|
|
@@ -2207,7 +2733,7 @@ function it(e, t) {
|
|
|
2207
2733
|
let e = this.grid.get(i), a = [], o = [];
|
|
2208
2734
|
for (let n = 0; n < t; n += 1) {
|
|
2209
2735
|
let t = e?.get(n);
|
|
2210
|
-
a.push(
|
|
2736
|
+
a.push(Rt(t?.fg)), o.push(Rt(t?.bg));
|
|
2211
2737
|
}
|
|
2212
2738
|
n.push(a), r.push(o);
|
|
2213
2739
|
}
|
|
@@ -2234,7 +2760,7 @@ function it(e, t) {
|
|
|
2234
2760
|
if (!e) return;
|
|
2235
2761
|
Date.now() < s && console.log(`[rect] type=${e.type} pos=(${e.pos.x},${e.pos.y}) size=${e.size.width}x${e.size.height}`);
|
|
2236
2762
|
let { cellW: r, cellH: i, offX: a, offY: o } = t.get("cellMetrics"), { cursor: c, cursorColor: l, selectionMode: u } = n.processRect(e);
|
|
2237
|
-
l &&
|
|
2763
|
+
l && Rt(l) !== Rt(t.get("cursorColor")) && t.set("cursorColor", l);
|
|
2238
2764
|
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");
|
|
2239
2765
|
if (u) {
|
|
2240
2766
|
let e = t.get("cursorRect");
|
|
@@ -2290,7 +2816,7 @@ function it(e, t) {
|
|
|
2290
2816
|
}
|
|
2291
2817
|
//#endregion
|
|
2292
2818
|
//#region src/engine/host/hostState.ts
|
|
2293
|
-
var
|
|
2819
|
+
var Bt = (e) => {
|
|
2294
2820
|
let t = e.get("macroStatus");
|
|
2295
2821
|
return {
|
|
2296
2822
|
viewName: e.get("viewName"),
|
|
@@ -2312,13 +2838,13 @@ var at = (e) => {
|
|
|
2312
2838
|
macroCurrentStep: t.currentStep,
|
|
2313
2839
|
macroSequenceLength: t.sequenceLength
|
|
2314
2840
|
};
|
|
2315
|
-
},
|
|
2841
|
+
}, Vt = (e, t, n) => {
|
|
2316
2842
|
let r = e.get("cellMetrics");
|
|
2317
2843
|
return {
|
|
2318
2844
|
x: t * r.cellW + r.offX + r.cellW / 2,
|
|
2319
2845
|
y: n * r.cellH + r.offY + r.cellH / 2
|
|
2320
2846
|
};
|
|
2321
|
-
},
|
|
2847
|
+
}, Ht = (e) => Fe({
|
|
2322
2848
|
left: e.includes("left"),
|
|
2323
2849
|
right: e.includes("right"),
|
|
2324
2850
|
up: e.includes("up"),
|
|
@@ -2327,13 +2853,11 @@ var at = (e) => {
|
|
|
2327
2853
|
play: e.includes("play"),
|
|
2328
2854
|
opt: e.includes("opt"),
|
|
2329
2855
|
edit: e.includes("edit")
|
|
2330
|
-
}),
|
|
2856
|
+
}), Ut = ({ io: e, editors: t, setValueFloat: n, browseFile: r, navigateToView: i, navigateTo: a, enableMidiTransport: o, getScreenCellColors: s }) => {
|
|
2331
2857
|
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);
|
|
2332
2858
|
return {
|
|
2333
2859
|
navigateToView: m("navigateToView", !1, async (e) => (c("[M8SDK] Executing navigateToView:", e), i(e))),
|
|
2334
|
-
navigateTo: m("navigateTo",
|
|
2335
|
-
await a(ot(f, e, t));
|
|
2336
|
-
}),
|
|
2860
|
+
navigateTo: m("navigateTo", !1, async (e, t) => a(Vt(f, e, t))),
|
|
2337
2861
|
setValueToHex: m("setValueToHex", !1, async (e) => (c("[M8SDK] Executing setValueToHex:", e), t.setValueToHex(e))),
|
|
2338
2862
|
setValueToInt: m("setValueToInt", !1, async (e) => (c("[M8SDK] Executing setValueToInt:", e), t.setValueToInt(e))),
|
|
2339
2863
|
setValueFloat: m("setValueFloat", !1, async (e) => (c("[M8SDK] Executing setValueFloat:", e), n(e))),
|
|
@@ -2348,15 +2872,15 @@ var at = (e) => {
|
|
|
2348
2872
|
exact: t
|
|
2349
2873
|
}), r(e, t))),
|
|
2350
2874
|
sendKeyDown: m("sendKeyDown", void 0, async (e) => {
|
|
2351
|
-
c("[M8SDK] Executing sendKeyDown:", e), u(
|
|
2875
|
+
c("[M8SDK] Executing sendKeyDown:", e), u(Ht(e));
|
|
2352
2876
|
}),
|
|
2353
2877
|
sendKeyUp: m("sendKeyUp", void 0, async () => {
|
|
2354
2878
|
c("[M8SDK] Executing sendKeyUp"), u(0);
|
|
2355
2879
|
}),
|
|
2356
2880
|
sendKeyPress: m("sendKeyPress", void 0, async (e) => {
|
|
2357
|
-
c("[M8SDK] Executing sendKeyPress:", e), await d(
|
|
2881
|
+
c("[M8SDK] Executing sendKeyPress:", e), await d(Ht(e));
|
|
2358
2882
|
}),
|
|
2359
|
-
getState: async () =>
|
|
2883
|
+
getState: async () => Bt(f),
|
|
2360
2884
|
getScreen: async () => ({
|
|
2361
2885
|
width: 40,
|
|
2362
2886
|
height: 24,
|
|
@@ -2370,7 +2894,7 @@ var at = (e) => {
|
|
|
2370
2894
|
}),
|
|
2371
2895
|
enableMidiTransport: async () => o()
|
|
2372
2896
|
};
|
|
2373
|
-
},
|
|
2897
|
+
}, Wt = 6, Gt = (e) => {
|
|
2374
2898
|
let t = null, n = 0, r = null, i = null, a = (e) => performance.timeOrigin + e, o = (t) => {
|
|
2375
2899
|
let o = t.data;
|
|
2376
2900
|
if (!o || o.length === 0) return;
|
|
@@ -2381,7 +2905,7 @@ var at = (e) => {
|
|
|
2381
2905
|
let e = s - r;
|
|
2382
2906
|
e > 0 && e < 200 && (i = i == null ? e : i * .9 + e * .1);
|
|
2383
2907
|
}
|
|
2384
|
-
r = s, n += 1, n %
|
|
2908
|
+
r = s, n += 1, n % Wt === 0 && e({
|
|
2385
2909
|
kind: "clock",
|
|
2386
2910
|
ticks: n,
|
|
2387
2911
|
at: s,
|
|
@@ -2424,65 +2948,75 @@ var at = (e) => {
|
|
|
2424
2948
|
return;
|
|
2425
2949
|
}
|
|
2426
2950
|
}
|
|
2427
|
-
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2951
|
+
}, s = null, c = 0, l = async () => {
|
|
2952
|
+
let e = c;
|
|
2953
|
+
if (!("requestMIDIAccess" in navigator)) return {
|
|
2954
|
+
ok: !1,
|
|
2955
|
+
error: "Web MIDI is not available in this browser"
|
|
2956
|
+
};
|
|
2957
|
+
try {
|
|
2958
|
+
let n = [...(await navigator.requestMIDIAccess()).inputs.values()].find((e) => e.name === "M8");
|
|
2959
|
+
return n ? (await n.open(), c === e ? (n.addEventListener("midimessage", o), t = n, { ok: !0 }) : {
|
|
2960
|
+
ok: !1,
|
|
2961
|
+
error: "disabled"
|
|
2962
|
+
}) : {
|
|
2432
2963
|
ok: !1,
|
|
2433
|
-
error: "
|
|
2964
|
+
error: "no MIDI input named M8"
|
|
2434
2965
|
};
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2966
|
+
} catch (e) {
|
|
2967
|
+
return {
|
|
2968
|
+
ok: !1,
|
|
2969
|
+
error: e instanceof Error ? e.message : String(e)
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
};
|
|
2973
|
+
return {
|
|
2974
|
+
enable: () => {
|
|
2975
|
+
if (t) return Promise.resolve({ ok: !0 });
|
|
2976
|
+
if (s) return s;
|
|
2977
|
+
let e = l();
|
|
2978
|
+
return s = e, e.then((t) => {
|
|
2979
|
+
s === e && !t.ok && (s = null);
|
|
2980
|
+
}), e;
|
|
2447
2981
|
},
|
|
2448
2982
|
disable: () => {
|
|
2449
|
-
t?.removeEventListener("midimessage", o), t = null;
|
|
2983
|
+
c += 1, s = null, t?.removeEventListener("midimessage", o), t = null;
|
|
2450
2984
|
}
|
|
2451
2985
|
};
|
|
2452
|
-
},
|
|
2986
|
+
}, Kt = (e, t) => {
|
|
2453
2987
|
let n = [], r = e, i = t;
|
|
2454
2988
|
for (; r !== i;) {
|
|
2455
2989
|
let e = i - r, t = Math.abs(e), a = e > 0 ? 1 : -1;
|
|
2456
2990
|
if (t >= 16) {
|
|
2457
|
-
let e = Math.floor(t / 16), o = a > 0 ?
|
|
2991
|
+
let e = Math.floor(t / 16), o = a > 0 ? R.Up : R.Down, s = R.Edit | o, c = Math.min(e, 10);
|
|
2458
2992
|
for (let e = 0; e < c && (n.push(s), n.push(0), r += a * 16, r !== i); e++);
|
|
2459
2993
|
} else if (t > 0) {
|
|
2460
|
-
let e = a > 0 ?
|
|
2994
|
+
let e = a > 0 ? R.Right : R.Left, t = R.Edit | e;
|
|
2461
2995
|
n.push(t), n.push(0), r += a * 1;
|
|
2462
2996
|
} else break;
|
|
2463
2997
|
}
|
|
2464
2998
|
return n;
|
|
2465
|
-
},
|
|
2999
|
+
}, qt = (e, t) => {
|
|
2466
3000
|
let n = [], r = e;
|
|
2467
3001
|
for (; r !== t;) {
|
|
2468
3002
|
let e = t - r, i = e > 0 ? 1 : -1;
|
|
2469
3003
|
if (Math.abs(e) >= 12) {
|
|
2470
|
-
let e = i > 0 ?
|
|
2471
|
-
n.push(
|
|
3004
|
+
let e = i > 0 ? R.Up : R.Down;
|
|
3005
|
+
n.push(R.Edit | e), n.push(0), r += i * 12;
|
|
2472
3006
|
} else {
|
|
2473
|
-
let e = i > 0 ?
|
|
2474
|
-
n.push(
|
|
3007
|
+
let e = i > 0 ? R.Right : R.Left;
|
|
3008
|
+
n.push(R.Edit | e), n.push(0), r += i;
|
|
2475
3009
|
}
|
|
2476
3010
|
if (n.length > 2048) break;
|
|
2477
3011
|
}
|
|
2478
3012
|
return n;
|
|
2479
|
-
},
|
|
3013
|
+
}, Jt = (e) => {
|
|
2480
3014
|
let { store: t, debugLog: n, hasBus: r, sendKeys: i, pressAndRelease: a, waitForTextUnderCursorChange: o } = e;
|
|
2481
3015
|
return {
|
|
2482
3016
|
setValueToHex: async (e) => {
|
|
2483
3017
|
if (!r()) return !1;
|
|
2484
3018
|
e = Math.max(0, Math.min(255, e));
|
|
2485
|
-
let s = t.get("textUnderCursor"), c =
|
|
3019
|
+
let s = t.get("textUnderCursor"), c = H(s), l = s?.trim() === "--";
|
|
2486
3020
|
if (c === null) return console.warn("[M8SDK] Could not parse current value under cursor:", s), !1;
|
|
2487
3021
|
if (c === e && !l) return n("[M8SDK] Already at target value:", e.toString(16).padStart(2, "0").toUpperCase()), !0;
|
|
2488
3022
|
n(`[M8SDK] Setting value from "${s?.trim() ?? "N/A"}" (${c.toString(16).padStart(2, "0").toUpperCase()}) to ${e.toString(16).padStart(2, "0").toUpperCase()}`);
|
|
@@ -2490,60 +3024,60 @@ var at = (e) => {
|
|
|
2490
3024
|
if (l) {
|
|
2491
3025
|
n("[M8SDK] Initial value is \"--\", pressing edit to recall last value");
|
|
2492
3026
|
let e = s;
|
|
2493
|
-
await a(
|
|
2494
|
-
let r = t.get("textUnderCursor"), i =
|
|
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(
|
|
3027
|
+
await a(R.Edit), await o(e, 250);
|
|
3028
|
+
let r = t.get("textUnderCursor"), i = H(r);
|
|
3029
|
+
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(R.Edit), !1;
|
|
2496
3030
|
u = i;
|
|
2497
3031
|
} else {
|
|
2498
3032
|
let e = s;
|
|
2499
|
-
await a(
|
|
3033
|
+
await a(R.Edit), await o(e, 250);
|
|
2500
3034
|
}
|
|
2501
|
-
let d =
|
|
3035
|
+
let d = Kt(u, e);
|
|
2502
3036
|
n(`[M8SDK] Precalculated ${d.length / 2} key presses from ${u.toString(16).padStart(2, "0").toUpperCase()} to ${e.toString(16).padStart(2, "0").toUpperCase()}`);
|
|
2503
3037
|
for (let e of d) i(e), await B(30);
|
|
2504
3038
|
await o(s, 250);
|
|
2505
|
-
let f = t.get("textUnderCursor"), p =
|
|
3039
|
+
let f = t.get("textUnderCursor"), p = H(f);
|
|
2506
3040
|
if (p !== e) {
|
|
2507
3041
|
n("[M8SDK] Precomputation missed target, falling back to iterative mode");
|
|
2508
3042
|
let r = p ?? u, i = Date.now();
|
|
2509
3043
|
for (; r !== e && Date.now() - i < 5e3;) {
|
|
2510
3044
|
let i = e - r, s = Math.abs(i), c = i > 0 ? 1 : -1, l = t.get("textUnderCursor");
|
|
2511
3045
|
if (s >= 16) {
|
|
2512
|
-
let e = c > 0 ?
|
|
3046
|
+
let e = c > 0 ? R.Up : R.Down, t = R.Edit | e;
|
|
2513
3047
|
await a(t);
|
|
2514
3048
|
} else if (s > 0) {
|
|
2515
|
-
let e = c > 0 ?
|
|
3049
|
+
let e = c > 0 ? R.Right : R.Left, t = R.Edit | e;
|
|
2516
3050
|
await a(t);
|
|
2517
3051
|
}
|
|
2518
3052
|
await o(l, 250);
|
|
2519
|
-
let u =
|
|
3053
|
+
let u = H(t.get("textUnderCursor"));
|
|
2520
3054
|
u !== null && (r = u, n(`[M8SDK] Iterative: current value: ${r.toString(16).padStart(2, "0").toUpperCase()}`));
|
|
2521
3055
|
}
|
|
2522
|
-
p =
|
|
3056
|
+
p = H(t.get("textUnderCursor"));
|
|
2523
3057
|
}
|
|
2524
|
-
await a(
|
|
3058
|
+
await a(R.Edit);
|
|
2525
3059
|
let m = p === e;
|
|
2526
3060
|
return n(`[M8SDK] Value setting ${m ? "succeeded" : "failed"}. Final value: "${f?.trim() ?? "N/A"}" (${p?.toString(16).padStart(2, "0").toUpperCase() ?? "N/A"})`), m;
|
|
2527
3061
|
},
|
|
2528
3062
|
setValueToInt: async (e) => {
|
|
2529
3063
|
if (!r()) return !1;
|
|
2530
3064
|
e = Math.floor(e);
|
|
2531
|
-
let n = t.get("textUnderCursor"), i =
|
|
3065
|
+
let n = t.get("textUnderCursor"), i = yt(n);
|
|
2532
3066
|
if (i === null) return console.warn("[M8SDK] Could not parse current integer value under cursor:", n), !1;
|
|
2533
3067
|
if (i === e) return !0;
|
|
2534
|
-
await a(
|
|
3068
|
+
await a(R.Edit), await o(n, 250);
|
|
2535
3069
|
let s = i, c = i, l = Math.abs(i - e), u = null, d = Date.now();
|
|
2536
3070
|
for (; s !== e && Date.now() - d < 5e3;) {
|
|
2537
3071
|
let n = e - s, r = Math.abs(n), i = n > 0 ? 1 : -1, d = t.get("textUnderCursor"), f = s;
|
|
2538
3072
|
if (r >= 16) {
|
|
2539
|
-
let e = i > 0 ?
|
|
2540
|
-
await a(
|
|
3073
|
+
let e = i > 0 ? R.Up : R.Down;
|
|
3074
|
+
await a(R.Edit | e, 35);
|
|
2541
3075
|
} else {
|
|
2542
|
-
let e = i > 0 ?
|
|
2543
|
-
await a(
|
|
3076
|
+
let e = i > 0 ? R.Right : R.Left;
|
|
3077
|
+
await a(R.Edit | e, 35);
|
|
2544
3078
|
}
|
|
2545
3079
|
await o(d, 250);
|
|
2546
|
-
let p =
|
|
3080
|
+
let p = yt(t.get("textUnderCursor"));
|
|
2547
3081
|
if (p === null || p === s) break;
|
|
2548
3082
|
let m = Math.abs(p - e);
|
|
2549
3083
|
m < l && (l = m, c = p);
|
|
@@ -2556,19 +3090,19 @@ var at = (e) => {
|
|
|
2556
3090
|
for (; s !== c && Date.now() - e < 1500;) {
|
|
2557
3091
|
let e = c - s, n = Math.abs(e), r = e > 0 ? 1 : -1, i = t.get("textUnderCursor");
|
|
2558
3092
|
if (n >= 16) {
|
|
2559
|
-
let e = r > 0 ?
|
|
2560
|
-
await a(
|
|
3093
|
+
let e = r > 0 ? R.Up : R.Down;
|
|
3094
|
+
await a(R.Edit | e, 35);
|
|
2561
3095
|
} else {
|
|
2562
|
-
let e = r > 0 ?
|
|
2563
|
-
await a(
|
|
3096
|
+
let e = r > 0 ? R.Right : R.Left;
|
|
3097
|
+
await a(R.Edit | e, 35);
|
|
2564
3098
|
}
|
|
2565
3099
|
await o(i, 250);
|
|
2566
|
-
let l =
|
|
3100
|
+
let l = yt(t.get("textUnderCursor"));
|
|
2567
3101
|
if (l === null || l === s) break;
|
|
2568
3102
|
s = l;
|
|
2569
3103
|
}
|
|
2570
3104
|
}
|
|
2571
|
-
return await a(
|
|
3105
|
+
return await a(R.Edit), yt(t.get("textUnderCursor")) === e;
|
|
2572
3106
|
},
|
|
2573
3107
|
setNote: async (e) => {
|
|
2574
3108
|
if (!r()) return !1;
|
|
@@ -2576,27 +3110,27 @@ var at = (e) => {
|
|
|
2576
3110
|
if (s === "---" || s === "OFF") {
|
|
2577
3111
|
let e = t.get("textUnderCursor"), r = e?.trim().toUpperCase() ?? "";
|
|
2578
3112
|
if (r === s) return !0;
|
|
2579
|
-
if (await a(
|
|
3113
|
+
if (await a(R.Opt | R.Edit), await o(e, 250), s === "OFF" && r !== "---") {
|
|
2580
3114
|
let e = t.get("textUnderCursor");
|
|
2581
|
-
e?.trim().toUpperCase() === "---" && (await a(
|
|
3115
|
+
e?.trim().toUpperCase() === "---" && (await a(R.Opt | R.Edit), await o(e, 250));
|
|
2582
3116
|
}
|
|
2583
3117
|
let i = t.get("textUnderCursor")?.trim().toUpperCase();
|
|
2584
3118
|
return n("[M8SDK] setNote special value result:", i), i === s;
|
|
2585
3119
|
}
|
|
2586
|
-
let c =
|
|
3120
|
+
let c = W(e);
|
|
2587
3121
|
if (!c) return console.warn("[M8SDK] Invalid note format. Expected like C-1, C#1, D#A, ---, OFF:", e), !1;
|
|
2588
3122
|
let l = t.get("textUnderCursor");
|
|
2589
|
-
(l?.trim() === "---" || l?.trim().toUpperCase() === "OFF") && (n("[M8SDK] Initial note is \"---\" or \"OFF\", priming with edit key"), await a(
|
|
2590
|
-
let u =
|
|
3123
|
+
(l?.trim() === "---" || l?.trim().toUpperCase() === "OFF") && (n("[M8SDK] Initial note is \"---\" or \"OFF\", priming with edit key"), await a(R.Edit), await o(l, 250), l = t.get("textUnderCursor"));
|
|
3124
|
+
let u = W(l);
|
|
2591
3125
|
if (!u) return console.warn("[M8SDK] Could not parse current note under cursor:", l), !1;
|
|
2592
3126
|
if (u.semitoneIndex === c.semitoneIndex) return !0;
|
|
2593
|
-
await a(
|
|
2594
|
-
let d =
|
|
3127
|
+
await a(R.Edit), await o(l, 250);
|
|
3128
|
+
let d = qt(u.semitoneIndex, c.semitoneIndex);
|
|
2595
3129
|
if (d.length > 0) {
|
|
2596
3130
|
for (let e of d) i(e), await B(25);
|
|
2597
3131
|
await o(l, 280);
|
|
2598
|
-
let e =
|
|
2599
|
-
if (e?.semitoneIndex === c.semitoneIndex) return await a(
|
|
3132
|
+
let e = W(t.get("textUnderCursor"));
|
|
3133
|
+
if (e?.semitoneIndex === c.semitoneIndex) return await a(R.Edit), !0;
|
|
2600
3134
|
u = e ?? u;
|
|
2601
3135
|
}
|
|
2602
3136
|
let f = /* @__PURE__ */ new Set();
|
|
@@ -2605,21 +3139,21 @@ var at = (e) => {
|
|
|
2605
3139
|
for (; Date.now() - p < 7e3;) {
|
|
2606
3140
|
let e = c.semitoneIndex - u.semitoneIndex;
|
|
2607
3141
|
if (e === 0) break;
|
|
2608
|
-
let n = Math.abs(e) >= 12, r = e > 0, i = n ? r ?
|
|
2609
|
-
await a(
|
|
2610
|
-
let m =
|
|
3142
|
+
let n = Math.abs(e) >= 12, r = e > 0, i = n ? r ? R.Up : R.Down : r ? R.Right : R.Left, s = n ? r ? R.Down : R.Up : r ? R.Left : R.Right, l = u, d = Math.abs(l.semitoneIndex - c.semitoneIndex), p = t.get("textUnderCursor");
|
|
3143
|
+
await a(R.Edit | i, 35), await o(p, 250);
|
|
3144
|
+
let m = W(t.get("textUnderCursor"));
|
|
2611
3145
|
if (!m || m.label === l.label) break;
|
|
2612
3146
|
if (u = m, Math.abs(u.semitoneIndex - c.semitoneIndex) > d) {
|
|
2613
3147
|
let e = t.get("textUnderCursor");
|
|
2614
|
-
await a(
|
|
2615
|
-
let n =
|
|
3148
|
+
await a(R.Edit | s, 35), await o(e, 250);
|
|
3149
|
+
let n = W(t.get("textUnderCursor"));
|
|
2616
3150
|
n && (u = n);
|
|
2617
3151
|
break;
|
|
2618
3152
|
}
|
|
2619
3153
|
if (f.has(u.label)) break;
|
|
2620
3154
|
f.add(u.label);
|
|
2621
3155
|
}
|
|
2622
|
-
return await a(
|
|
3156
|
+
return await a(R.Edit), W(t.get("textUnderCursor"))?.semitoneIndex === c.semitoneIndex;
|
|
2623
3157
|
},
|
|
2624
3158
|
setValueToString: async (e, i = !0, s = !1) => {
|
|
2625
3159
|
if (!r()) return !1;
|
|
@@ -2631,9 +3165,9 @@ var at = (e) => {
|
|
|
2631
3165
|
};
|
|
2632
3166
|
if (u(l())) return !0;
|
|
2633
3167
|
let d = t.get("textUnderCursor");
|
|
2634
|
-
d?.trim() === "---" && (n("[M8SDK] Initial string value is \"---\", priming with edit key"), await a(
|
|
3168
|
+
d?.trim() === "---" && (n("[M8SDK] Initial string value is \"---\", priming with edit key"), await a(R.Edit), await o(d, 250));
|
|
2635
3169
|
let f = t.get("textUnderCursor");
|
|
2636
|
-
if (await a(
|
|
3170
|
+
if (await a(R.Edit), await o(f, 250), u(l())) return await a(R.Edit), !0;
|
|
2637
3171
|
let p = t.get("textUnderCursor"), m = 0, h = Date.now(), g = [];
|
|
2638
3172
|
for (let e = 0; e < 512; e++) {
|
|
2639
3173
|
if (Date.now() - h > 2e3) {
|
|
@@ -2649,26 +3183,26 @@ var at = (e) => {
|
|
|
2649
3183
|
}
|
|
2650
3184
|
}
|
|
2651
3185
|
let r = p;
|
|
2652
|
-
await a(
|
|
3186
|
+
await a(R.Edit | R.Down, 35);
|
|
2653
3187
|
let i = await o(r, 250);
|
|
2654
3188
|
if (z(i) === z(p) ? m += 1 : (m = 0, p = i), m > 1) break;
|
|
2655
3189
|
}
|
|
2656
3190
|
let _ = /* @__PURE__ */ new Set();
|
|
2657
3191
|
for (let e = 0; e < 1024; e++) {
|
|
2658
3192
|
let e = l();
|
|
2659
|
-
if (u(e)) return await a(
|
|
3193
|
+
if (u(e)) return await a(R.Edit), !0;
|
|
2660
3194
|
let n = z(e);
|
|
2661
3195
|
if (n) {
|
|
2662
3196
|
if (_.has(n)) break;
|
|
2663
3197
|
_.add(n);
|
|
2664
3198
|
}
|
|
2665
3199
|
let r = t.get("textUnderCursor");
|
|
2666
|
-
if (await a(
|
|
3200
|
+
if (await a(R.Edit | R.Right, 35), z(await o(r, 250)) === n) break;
|
|
2667
3201
|
}
|
|
2668
|
-
return await a(
|
|
3202
|
+
return await a(R.Edit), !1;
|
|
2669
3203
|
}
|
|
2670
3204
|
};
|
|
2671
|
-
},
|
|
3205
|
+
}, Yt = ({ bus: e, store: t, cancelOnKeydown: n = !1 }) => {
|
|
2672
3206
|
let r = [], i = null, a = !1, o = () => {
|
|
2673
3207
|
a && u("preempted by keyboard");
|
|
2674
3208
|
}, s = () => {
|
|
@@ -2716,7 +3250,7 @@ var at = (e) => {
|
|
|
2716
3250
|
r = [], i = null, c();
|
|
2717
3251
|
}
|
|
2718
3252
|
};
|
|
2719
|
-
},
|
|
3253
|
+
}, Xt = () => ({
|
|
2720
3254
|
viewName: null,
|
|
2721
3255
|
viewTitle: null,
|
|
2722
3256
|
minimapKey: null,
|
|
@@ -2739,9 +3273,9 @@ var at = (e) => {
|
|
|
2739
3273
|
},
|
|
2740
3274
|
systemInfo: null,
|
|
2741
3275
|
macroStatus: { running: !1 }
|
|
2742
|
-
}),
|
|
3276
|
+
}), Zt = (e = {}) => {
|
|
2743
3277
|
let t = {
|
|
2744
|
-
...
|
|
3278
|
+
...Xt(),
|
|
2745
3279
|
...e
|
|
2746
3280
|
}, n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set();
|
|
2747
3281
|
return {
|
|
@@ -2764,8 +3298,8 @@ var at = (e) => {
|
|
|
2764
3298
|
}),
|
|
2765
3299
|
snapshot: () => ({ ...t })
|
|
2766
3300
|
};
|
|
2767
|
-
},
|
|
2768
|
-
function
|
|
3301
|
+
}, Qt = () => 0, $t = () => 1;
|
|
3302
|
+
function en(e, t, n, r = Qt, i = $t) {
|
|
2769
3303
|
let a = t.trim(), o = n.trim();
|
|
2770
3304
|
if (!e.has(a) || !e.has(o)) return null;
|
|
2771
3305
|
let s = /* @__PURE__ */ new Set([a]), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
@@ -2810,29 +3344,29 @@ function yt(e, t, n, r = _t, i = vt) {
|
|
|
2810
3344
|
}
|
|
2811
3345
|
//#endregion
|
|
2812
3346
|
//#region src/engine/viewNavigation.ts
|
|
2813
|
-
var
|
|
2814
|
-
let n = !!t?.shift, r =
|
|
2815
|
-
up: (n ? r : 0) |
|
|
2816
|
-
down: (n ? r : 0) |
|
|
2817
|
-
left: (n ? r : 0) |
|
|
2818
|
-
right: (n ? r : 0) |
|
|
3347
|
+
var tn = 20, nn = 700, rn = 80, an = 4e3, on = 120, sn = (e, t) => {
|
|
3348
|
+
let n = !!t?.shift, r = R.Shift, i = {
|
|
3349
|
+
up: (n ? r : 0) | R.Up,
|
|
3350
|
+
down: (n ? r : 0) | R.Down,
|
|
3351
|
+
left: (n ? r : 0) | R.Left,
|
|
3352
|
+
right: (n ? r : 0) | R.Right
|
|
2819
3353
|
}, a = [];
|
|
2820
3354
|
n && a.push(r);
|
|
2821
3355
|
for (let t of e) a.push(i[t], n ? r : 0);
|
|
2822
3356
|
return a.push(0), a;
|
|
2823
|
-
},
|
|
3357
|
+
}, cn = (e) => [{
|
|
2824
3358
|
from: e,
|
|
2825
3359
|
to: "",
|
|
2826
3360
|
kind: "exit",
|
|
2827
|
-
keys: [
|
|
3361
|
+
keys: [R.Opt, 0]
|
|
2828
3362
|
}, {
|
|
2829
3363
|
from: e,
|
|
2830
3364
|
to: "",
|
|
2831
3365
|
kind: "shift",
|
|
2832
3366
|
dir: "up",
|
|
2833
|
-
keys: [
|
|
2834
|
-
}],
|
|
2835
|
-
let i =
|
|
3367
|
+
keys: [R.Shift | R.Up, 0]
|
|
3368
|
+
}], ln = (e) => `${e.kind}:${e.dir ?? ""}:${e.keys.join(",")}:${e.cursor ? `${e.cursor.x},${e.cursor.y}` : ""}`, un = (e) => e[e.length - 1] === 0 ? e.slice() : [...e, 0], dn = "#goal", fn = (e, t) => `${e}<${t}`, pn = (e) => new Promise((t) => setTimeout(t, e)), mn = ({ store: e, runner: t, getScreenLines: n, moveCursor: r }) => {
|
|
3369
|
+
let i = Pt(), a = null, o = 0, s = null, c = 0, l = /* @__PURE__ */ new Map(), u = "", d = e.get("viewName") || "", f = e.sub("viewName", () => {
|
|
2836
3370
|
let t = e.get("viewName") || "";
|
|
2837
3371
|
t && t !== d && (d && (u = d), d = t);
|
|
2838
3372
|
}), p = null, m = null, h = () => {
|
|
@@ -2840,11 +3374,11 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2840
3374
|
}, g = (e) => {
|
|
2841
3375
|
let t = (e || "").toLowerCase().trim();
|
|
2842
3376
|
return i.has(t) || console.warn("Target not in viewlist", { goal: t }), t;
|
|
2843
|
-
}, _ = (e, t) => `${e.from}<${t}|${
|
|
3377
|
+
}, _ = (e, t) => `${e.from}<${t}|${ln(e)}`, v = (e, t) => {
|
|
2844
3378
|
let n = /* @__PURE__ */ new Map();
|
|
2845
|
-
for (let t of
|
|
3379
|
+
for (let t of Nt().graph.get(e) ?? []) {
|
|
2846
3380
|
if (t.kind === "button" && !r) continue;
|
|
2847
|
-
let e =
|
|
3381
|
+
let e = ln(t);
|
|
2848
3382
|
n.set(e, [...n.get(e) ?? [], t]);
|
|
2849
3383
|
}
|
|
2850
3384
|
let i = [];
|
|
@@ -2868,11 +3402,11 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2868
3402
|
}
|
|
2869
3403
|
return i;
|
|
2870
3404
|
}, y = (e, t, n) => {
|
|
2871
|
-
let r = /* @__PURE__ */ new Map([[
|
|
2872
|
-
for (r.set(
|
|
2873
|
-
let [e, t] = i.shift(), a = r.get(
|
|
3405
|
+
let r = /* @__PURE__ */ new Map([[dn, []]]), i = [[e, t]];
|
|
3406
|
+
for (r.set(fn(e, t), []); i.length;) {
|
|
3407
|
+
let [e, t] = i.shift(), a = r.get(fn(e, t));
|
|
2874
3408
|
if (e === n) a.push({
|
|
2875
|
-
to:
|
|
3409
|
+
to: dn,
|
|
2876
3410
|
weight: 0,
|
|
2877
3411
|
nav: {
|
|
2878
3412
|
from: e,
|
|
@@ -2882,7 +3416,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2882
3416
|
}
|
|
2883
3417
|
});
|
|
2884
3418
|
else for (let n of v(e, t)) {
|
|
2885
|
-
let t =
|
|
3419
|
+
let t = fn(n.to, e);
|
|
2886
3420
|
a.push({
|
|
2887
3421
|
to: t,
|
|
2888
3422
|
weight: n.weight,
|
|
@@ -2892,10 +3426,10 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2892
3426
|
}
|
|
2893
3427
|
return r;
|
|
2894
3428
|
}, b = (e, t) => {
|
|
2895
|
-
let n =
|
|
3429
|
+
let n = en(y(e, u, t), fn(e, u), dn);
|
|
2896
3430
|
if (n?.edges.length) return n.edges[0].nav;
|
|
2897
|
-
let r =
|
|
2898
|
-
return r.length && !r.some((e) => e.kind === "exit") ? null :
|
|
3431
|
+
let r = Nt().graph.get(e) ?? [];
|
|
3432
|
+
return r.length && !r.some((e) => e.kind === "exit") ? null : cn(e).find((e) => !l.has(_(e, u)) && !l.has(_(e, "*"))) ?? null;
|
|
2899
3433
|
}, x = (e) => {
|
|
2900
3434
|
let t = e.cursor;
|
|
2901
3435
|
if (!e.label || !n) return t;
|
|
@@ -2940,12 +3474,12 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2940
3474
|
}), E();
|
|
2941
3475
|
}, T = async (n, i) => {
|
|
2942
3476
|
let a = x(n.edge);
|
|
2943
|
-
S(a) || (await Promise.race([r(a.x, a.y),
|
|
3477
|
+
S(a) || (await Promise.race([r(a.x, a.y), pn(an)]), await pn(on)), i === c && p === n && (e.get("viewName") !== n.from || !S(a) ? (console.warn("Navigation: button not reached", {
|
|
2944
3478
|
from: n.from,
|
|
2945
3479
|
to: n.edge.to,
|
|
2946
3480
|
cell: a,
|
|
2947
3481
|
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(
|
|
3482
|
+
}), w(e.get("viewName") === n.from ? n.from : e.get("viewName") || "")) : (n.phase = "keys", t.cancel("navigation button"), t.start(un(n.edge.keys))));
|
|
2949
3483
|
}, E = () => {
|
|
2950
3484
|
let n = a, r = e.get("viewName") || "";
|
|
2951
3485
|
if (!n) return;
|
|
@@ -2960,7 +3494,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2960
3494
|
}), C(!0);
|
|
2961
3495
|
return;
|
|
2962
3496
|
}
|
|
2963
|
-
if (o >=
|
|
3497
|
+
if (o >= tn) {
|
|
2964
3498
|
console.warn("Navigation aborted: max steps reached", {
|
|
2965
3499
|
startTitle: r,
|
|
2966
3500
|
goalTitle: n,
|
|
@@ -2995,14 +3529,14 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
2995
3529
|
T(s, c);
|
|
2996
3530
|
return;
|
|
2997
3531
|
}
|
|
2998
|
-
let l = i.keys.length ?
|
|
3532
|
+
let l = i.keys.length ? un(i.keys) : sn([i.dir], { shift: !0 });
|
|
2999
3533
|
t.cancel("iterative navigation step"), t.start(l);
|
|
3000
3534
|
}, D = () => {
|
|
3001
3535
|
if (p?.phase !== "keys") return;
|
|
3002
3536
|
let n = e.get("macroStatus").running, r = e.get("viewName") || "", i = !!r && r !== p.from;
|
|
3003
3537
|
(!n || i) && (h(), m = n ? setTimeout(() => {
|
|
3004
3538
|
t.cancel("navigation: no release echo"), t.start([0]), w(e.get("viewName") || "");
|
|
3005
|
-
},
|
|
3539
|
+
}, nn) : setTimeout(() => w(e.get("viewName") || ""), i ? rn : nn));
|
|
3006
3540
|
}, O = e.sub("viewName", D), k = e.sub("macroStatus", D);
|
|
3007
3541
|
return {
|
|
3008
3542
|
navigateToView: (t) => new Promise((n) => {
|
|
@@ -3016,7 +3550,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
3016
3550
|
f(), O(), k(), c += 1, C(!1);
|
|
3017
3551
|
}
|
|
3018
3552
|
};
|
|
3019
|
-
},
|
|
3553
|
+
}, hn = /* @__PURE__ */ new Set([
|
|
3020
3554
|
"macroStatus",
|
|
3021
3555
|
"deviceModel",
|
|
3022
3556
|
"fontMode",
|
|
@@ -3024,33 +3558,35 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
3024
3558
|
"titleColor",
|
|
3025
3559
|
"backgroundColor",
|
|
3026
3560
|
"minimapKey"
|
|
3027
|
-
]),
|
|
3028
|
-
let n = t.store ??
|
|
3561
|
+
]), gn = (e, t = {}) => {
|
|
3562
|
+
let n = t.store ?? Zt(), r = (...e) => {
|
|
3029
3563
|
t.debug && console.log(...e);
|
|
3030
3564
|
}, i = /* @__PURE__ */ new Map(), a = (e, t) => {
|
|
3031
3565
|
for (let n of [...i.get(e) ?? []]) n(t);
|
|
3032
|
-
}, o =
|
|
3566
|
+
}, o = zt(e.protocol.eventBus, n), s = Yt({
|
|
3033
3567
|
bus: e,
|
|
3034
3568
|
store: n,
|
|
3035
3569
|
cancelOnKeydown: t.cancelMacroOnKeydown
|
|
3036
|
-
}), c =
|
|
3570
|
+
}), c = Ye({
|
|
3037
3571
|
bus: e,
|
|
3038
|
-
store: n
|
|
3039
|
-
|
|
3572
|
+
store: n,
|
|
3573
|
+
getScreenLines: o.getScreenLines,
|
|
3574
|
+
cursorMap: t.cursorMap ?? oe()
|
|
3575
|
+
}), l = mn({
|
|
3040
3576
|
store: n,
|
|
3041
3577
|
runner: s,
|
|
3042
3578
|
getScreenLines: o.getScreenLines,
|
|
3043
|
-
moveCursor: (e, t) => c.navigateTo(
|
|
3044
|
-
}), u =
|
|
3579
|
+
moveCursor: (e, t) => c.navigateTo(Vt(n, e, t))
|
|
3580
|
+
}), u = Gt((e) => a("transport", e)), d = dt({
|
|
3045
3581
|
getKeySink: () => e.commands,
|
|
3046
3582
|
store: n,
|
|
3047
3583
|
debugLog: r,
|
|
3048
3584
|
getScreenLines: o.getScreenLines
|
|
3049
|
-
}), f =
|
|
3585
|
+
}), f = Ut({
|
|
3050
3586
|
io: d,
|
|
3051
|
-
editors:
|
|
3052
|
-
setValueFloat:
|
|
3053
|
-
browseFile:
|
|
3587
|
+
editors: Jt(d),
|
|
3588
|
+
setValueFloat: Dt(d),
|
|
3589
|
+
browseFile: vt(d),
|
|
3054
3590
|
navigateToView: (e) => l.navigateToView(e),
|
|
3055
3591
|
navigateTo: (e) => c.navigateTo(e),
|
|
3056
3592
|
enableMidiTransport: () => u.enable(),
|
|
@@ -3080,7 +3616,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
3080
3616
|
};
|
|
3081
3617
|
(!_ || _.textUnderCursor !== e.textUnderCursor || _.currentLine !== e.currentLine) && (_ = e, r("[M8SDK] Text updated:", e.textUnderCursor, e.currentLine), a("textUpdated", e));
|
|
3082
3618
|
}
|
|
3083
|
-
[...e].some((e) =>
|
|
3619
|
+
[...e].some((e) => hn.has(e)) && a("stateChanged", Bt(n));
|
|
3084
3620
|
}, y = n.subAll((e) => {
|
|
3085
3621
|
p.add(e), m || (m = !0, queueMicrotask(v));
|
|
3086
3622
|
}), b = (e) => {
|
|
@@ -3093,7 +3629,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
3093
3629
|
macro: s,
|
|
3094
3630
|
navigateTo: (e) => c.navigateTo(e),
|
|
3095
3631
|
navigateToView: (e) => l.navigateToView(e),
|
|
3096
|
-
getState: () =>
|
|
3632
|
+
getState: () => Bt(n),
|
|
3097
3633
|
getScreenLines: o.getScreenLines,
|
|
3098
3634
|
getScreenCellColors: o.getScreenCellColors,
|
|
3099
3635
|
on: (e, t) => {
|
|
@@ -3106,184 +3642,17 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
3106
3642
|
e.protocol.eventBus.off("key", b), y(), l.dispose(), c.dispose(), s.dispose(), u.disable(), o.dispose(), i.clear();
|
|
3107
3643
|
}
|
|
3108
3644
|
};
|
|
3109
|
-
},
|
|
3110
|
-
version: "1.0",
|
|
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.",
|
|
3112
|
-
notes: {
|
|
3113
|
-
fontMode: "All x/width values assume font mode 0 (standard, 12×14 px cells, 40 cols × ~22 rows). Font modes 1 (bold uppercase) and 2 (large) may shift column positions.",
|
|
3114
|
-
playbackIndicator: "The M8 renders a '>' character one column to the left of the first data column when that step/row/track is currently playing. This character is part of the text stream; the editing cursor is a separate rect overlay.",
|
|
3115
|
-
screenGeometry: "The screen is 40 columns x 24 rows. Every row has a one-column left margin (the playback marker is drawn in it), so x positions in this schema count from column 1 of the raw screen and cursorPos.x is the schema x + 1. Titles are on row 3, content starts on row 5. The text stream is lower case ('tempo', 'b-4', 'ff').",
|
|
3116
|
-
emptyCell: "An empty or unused cell displays '--' (2 chars) or '---' (3 chars) or '---00' (command+value). Parsed hexValue will be null for these.",
|
|
3117
|
-
relativeHex: "Transpose and some FX values use relative hex: 00 = no change, 01-7F = +1-127, FF-80 = -1-128. See 'Relative and Absolute FX Commands' in the manual.",
|
|
3118
|
-
rowIndex: "Row/step indices are parsed from the leftmost characters of currentLine. They are hex for all grid views.",
|
|
3119
|
-
titleAsterisk: "A '*' next to the item ID in the screen title (e.g. 'PHRASE 03*') indicates the item is used elsewhere in the chain or song (phrase view); on other views it marks unsaved changes. See 'An asterisk next to the phrase number' in the manual."
|
|
3120
|
-
},
|
|
3121
|
-
fieldTypes: {
|
|
3122
|
-
chainRef: "Reference to a chain number (hex 00-FF). '--' means empty/unused.",
|
|
3123
|
-
phraseRef: "Reference to a phrase number (hex 00-FF). '--' means empty/unused.",
|
|
3124
|
-
note: "Musical note in M8 format: [A-G][#|-][0-9] e.g. 'C-4', 'A#3'. '---' = empty. 'OFF' = note-off.",
|
|
3125
|
-
velocity: "Note velocity 00-7F (hex), from 00 (silence) to 7F (maximum). Default is 64 (hex). '--' = inherit.",
|
|
3126
|
-
instrumentRef: "Instrument number 00-7F (hex). '--' = no retrigger (keep current instrument).",
|
|
3127
|
-
fxCommand: "3-character sequencer or instrument FX command name, e.g. 'KIL', 'ARP', 'DEL'. '---' = none.",
|
|
3128
|
-
fxValue: "2-char hex value 00-FF accompanying an FX command. Interpretation depends on the command.",
|
|
3129
|
-
transpose: "Semitone or tick transpose. Relative: 00=none, 01-7F=+1-+127, FF-80=-1--128.",
|
|
3130
|
-
ticks: "Number of ticks this step consumes in a groove (hex 01-FF). '00' skips the step. '--' = empty (groove loops at this point).",
|
|
3131
|
-
volume: "Volume multiplier 00-FF (hex). Multiplied with the phrase-row velocity. '--' = no override.",
|
|
3132
|
-
ppq: "Pulses Per Quarter note: resolution of the groove clock (decimal). One of 24 (default), 48, 96 or 192. Only present on groove row 0; absent on all other rows.",
|
|
3133
|
-
instrumentName: "Instrument name string (up to 12 characters). Text field, no hex value.",
|
|
3134
|
-
eqSlot: "EQ slot assignment (hex). '--' = no EQ applied to this instrument.",
|
|
3135
|
-
rowIndex: "The positional index of this row in the current view (song row, chain position, phrase step, etc.).",
|
|
3136
|
-
noteInterval: "Scale row label: one of the 12 note intervals C, C#, D, D#, E, F, F#, G, G#, A, A#, B (sharps use '#', naturals may render a dim '-' pad, e.g. 'C-').",
|
|
3137
|
-
onOff: "Toggle field displayed as 'ON' or 'OFF'; the scale view 'EN' column shows '--' for a note out of the scale. Used by the scale view 'EN' column.",
|
|
3138
|
-
semitoneOffset: "Signed semitone offset in decimal notation with two decimals (-24.00 to +24.00), e.g. '00.00', '-00.50', '12.00'. Used by the scale view 'OFFSET' column.",
|
|
3139
|
-
swing: "Groove swing amount as a percentage with one decimal, e.g. '50.0%' = straight timing, higher = swung. Shown on every second groove row (0, 2, 4, …): the swing of that pair of steps (ticks 07,05 → 58.3%).",
|
|
3140
|
-
instrumentType: "Instrument engine chosen with the TYPE field of the Instrument view: WAVSYNTH, MACROSYN, SAMPLER, FMSYNTH, HYPERSYNTH, EXTERNAL INST, MIDI OUT (NONE for an unused slot). Text value; changes which parameters the view shows.",
|
|
3141
|
-
name: "Free-text name field (instrument, song, sample, directory). Up to 12 characters; a row of dashes means unnamed.",
|
|
3142
|
-
parameter: "Editable value of a named parameter (CUTOFF, TEMPO, GAIN, …). Its 'format' says how to read it: hex byte, 'XX:YY' pair, decimal, decimal with a fraction, or free text.",
|
|
3143
|
-
option: "Choice from a list, shown as its name, optionally preceded by its index in hex (e.g. '00CHORUS', 'STEREO', 'BELL'). hexValue holds the index when there is one, text holds the name.",
|
|
3144
|
-
action: "Button-like text that does something when [EDIT] is pressed on it (LOAD, SAVE, RENDER, SETTINGS, VIEW INST. POOL, EQ, …). The text is the caption.",
|
|
3145
|
-
fileEntry: "Row of a file browser: '/name' is a directory, '/..' the parent directory, anything else a file (e.g. 'Bass.m8i')."
|
|
3146
|
-
},
|
|
3147
|
-
views: /* @__PURE__ */ JSON.parse("{\"song\":{\"title\":\"Song View\",\"type\":\"grid\",\"description\":\"Arranges chains across 8 independent tracks to form the song structure. Each row is a song position (up to 256 rows); each column is a track. Each track advances vertically until it reaches an empty column ('--'), then loops back to row 00. Chains containing no notes are grayed out; chains '00' or 'FE' are commonly used as silent spacers. On row '00', [UP][UP] enters track reordering mode.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"songRow\",\"description\":\"Song row 00-FF. Each track loops back to row 00 when it reaches '--'.\"},\"columns\":[{\"x\":3,\"width\":2,\"key\":\"track1\",\"type\":\"chainRef\",\"label\":\"Track 1\",\"meta\":{\"trackIndex\":1}},{\"x\":6,\"width\":2,\"key\":\"track2\",\"type\":\"chainRef\",\"label\":\"Track 2\",\"meta\":{\"trackIndex\":2}},{\"x\":9,\"width\":2,\"key\":\"track3\",\"type\":\"chainRef\",\"label\":\"Track 3\",\"meta\":{\"trackIndex\":3}},{\"x\":12,\"width\":2,\"key\":\"track4\",\"type\":\"chainRef\",\"label\":\"Track 4\",\"meta\":{\"trackIndex\":4}},{\"x\":15,\"width\":2,\"key\":\"track5\",\"type\":\"chainRef\",\"label\":\"Track 5\",\"meta\":{\"trackIndex\":5}},{\"x\":18,\"width\":2,\"key\":\"track6\",\"type\":\"chainRef\",\"label\":\"Track 6\",\"meta\":{\"trackIndex\":6}},{\"x\":21,\"width\":2,\"key\":\"track7\",\"type\":\"chainRef\",\"label\":\"Track 7\",\"meta\":{\"trackIndex\":7}},{\"x\":24,\"width\":2,\"key\":\"track8\",\"type\":\"chainRef\",\"label\":\"Track 8\",\"meta\":{\"trackIndex\":8}}]},\"chain\":{\"title\":\"Chain View\",\"type\":\"grid\",\"description\":\"An ordered list of up to 16 phrases (256 steps). The first '--' phrase ends the chain and the song continues on to the next chain in the song view. The 'TSP' column optionally transposes every note in the phrase in semitones (relative hex).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"chainPos\",\"description\":\"Position within the chain (0-F, max 16 entries).\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"phrase\",\"type\":\"phraseRef\",\"label\":\"Phrase (PH)\",\"description\":\"Phrase number to play at this chain position.\"},{\"x\":5,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (TSP)\",\"description\":\"Optional note transpose in semitones applied to every note in the phrase (relative hex).\"}]},\"phrase\":{\"title\":\"Phrase View\",\"type\":\"grid\",\"description\":\"A 16-step sequence. Each step holds a note, velocity, instrument assignment, and up to 3 FX command+value pairs. Notes cover 11 octaves (the full 128 MIDI note range); velocity defaults to 64 (hex); an empty instrument column plays the note without retriggering the instrument. A '*' next to the phrase number indicates the phrase is used elsewhere in the chain or song.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Step index 0-F (16th note by default; altered by groove).\"},\"columns\":[{\"x\":2,\"width\":3,\"key\":\"note\",\"type\":\"note\",\"label\":\"Note (N)\"},{\"x\":6,\"width\":2,\"key\":\"vel\",\"type\":\"velocity\",\"label\":\"Velocity (V)\"},{\"x\":9,\"width\":2,\"key\":\"inst\",\"type\":\"instrumentRef\",\"label\":\"Instrument (I)\"},{\"x\":12,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\"},{\"x\":15,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\"},{\"x\":18,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\"},{\"x\":21,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\"},{\"x\":24,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\"},{\"x\":27,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\"}]},\"table\":{\"title\":\"Table View\",\"type\":\"grid\",\"description\":\"A 16-step mini-sequencer that runs alongside an instrument. Used for arpeggios, multi-stage envelopes, and automated FX. The 'N' column transposes the instrument for the step (relative hex); 'V' multiplies with the phrase-row velocity. Each instrument has a dedicated table plus 128 assignable tables (TBL command). FX columns can run at independent speeds via TIC; a TIC on the last table row affects all rows. HOP shortens or loops the table.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Table step 0-F. Loops from bottom unless a HOP command redirects.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (N)\",\"description\":\"Transposes the instrument for this step (relative hex). '--' = no transpose.\"},{\"x\":5,\"width\":2,\"key\":\"volume\",\"type\":\"volume\",\"label\":\"Volume (V)\",\"description\":\"Volume multiplier applied on top of the phrase velocity (hex). '--' = no override.\"},{\"x\":8,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\",\"description\":\"3-char command name for table FX slot 1.\"},{\"x\":11,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\",\"description\":\"Value for table FX1.\"},{\"x\":14,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\",\"description\":\"3-char command name for table FX slot 2.\"},{\"x\":17,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\",\"description\":\"Value for table FX2.\"},{\"x\":20,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\",\"description\":\"3-char command name for table FX slot 3.\"},{\"x\":23,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\",\"description\":\"Value for table FX3.\"}]},\"groove\":{\"title\":\"Groove View\",\"type\":\"grid\",\"description\":\"Defines the timing of each phrase step in ticks. Used to create swing, shuffle, triplets, and other rhythmic feels. At the default 24 PPQ a step is 6 ticks and a full phrase totals 96 ticks. The groove loops when a '--' entry is reached; a '00' entry skips the phrase step. Row 0 also holds the PPQ resolution (24/48/96/192) and the SWING percentage; editing the row 0 tick value alters rows 0 and 1 together (a common swing setting is 07,05 or 08,04).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Groove step 0-F. The groove loops when a '--' entry is reached.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"ticks\",\"type\":\"ticks\",\"label\":\"Ticks (TIC)\",\"description\":\"Number of ticks this step takes. Default is 06 (at 24 PPQN = 1 sixteenth note). '00' skips the step.\"},{\"x\":6,\"width\":2,\"key\":\"ppq\",\"type\":\"ppq\",\"label\":\"PPQ\",\"description\":\"Pulses Per Quarter note: 24, 48, 96 or 192. Only present on groove row 0; absent on all other rows.\"},{\"x\":10,\"width\":5,\"key\":\"swing\",\"type\":\"swing\",\"label\":\"Swing\",\"description\":\"Swing amount as a percentage (e.g. '50.0%' = straight). Shown on every second row (0, 2, 4, …) for that pair of steps; empty on the others.\"}]},\"scale\":{\"title\":\"Scale View\",\"type\":\"grid\",\"description\":\"Defines and quantizes note-related events (project/chain transpose, note entry, ARP, PIT, and the MIDIOUT instrument CHD and ADD commands). Up to 16 user-defined scales per project; scale '00' is the default for all 8 tracks and can be overridden per track with SCA/SCG FX commands. Each of the 12 note intervals can be enabled ('EN' = ON) with an optional detuning offset (-24.00 to +24.00 semitones); a note out of the scale reads '-- -- --'. Rows that are not column-mapped (Model:02, fw 6.5): KEY under the title (global root note for the default scale, the same on every scale), then after the intervals TUNE (A4 reference in Hz, e.g. '440.00'), NAME (up to 16 characters, 92 factory scales load from /Scales/Factory), and the LOAD/SAVE actions.\",\"columns\":[{\"x\":0,\"width\":2,\"key\":\"interval\",\"type\":\"noteInterval\",\"label\":\"Note Interval\",\"description\":\"Note interval row label: C, C#, D, D#, E, F, F#, G, G#, A, A#, B.\"},{\"x\":3,\"width\":3,\"key\":\"en\",\"type\":\"onOff\",\"label\":\"Enabled (EN)\",\"description\":\"Enables this note interval in the scale: 'ON', or '--' when the note is out of the scale (the device draws the row '-- -- --').\"},{\"x\":6,\"width\":6,\"key\":\"offset\",\"type\":\"semitoneOffset\",\"label\":\"Offset (OFFSET)\",\"description\":\"Detune offset in semitones, decimal with two decimals (-24.00 to +24.00), e.g. '00.00'.\"}]},\"instrumentpool\":{\"title\":\"Instrument Pool\",\"type\":\"grid\",\"description\":\"Lists all 128 instrument slots with their mix routing. Each row shows the instrument name (up to 12 chars, '------------' = empty slot) and its dry/FX send levels. Mixer-related settings can be adjusted per instrument here; [EDIT] on an empty slot loads an instrument or sample.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"instIndex\",\"description\":\"Instrument slot index 00-7F.\"},\"columns\":[{\"x\":3,\"width\":12,\"key\":\"name\",\"type\":\"instrumentName\",\"label\":\"Instrument Name\",\"description\":\"Name of the instrument in this slot (up to 12 chars). '------------' = empty slot.\"},{\"x\":16,\"width\":2,\"key\":\"dry\",\"type\":\"volume\",\"label\":\"Dry (DRY)\",\"description\":\"Direct output volume for this instrument (hex 00-FF).\"},{\"x\":19,\"width\":2,\"key\":\"mx\",\"type\":\"volume\",\"label\":\"ModFX (MX)\",\"description\":\"ModFX send level (hex 00-FF).\"},{\"x\":22,\"width\":2,\"key\":\"de\",\"type\":\"volume\",\"label\":\"Delay (DE)\",\"description\":\"Delay send level (hex 00-FF).\"},{\"x\":25,\"width\":2,\"key\":\"rv\",\"type\":\"volume\",\"label\":\"Reverb (RV)\",\"description\":\"Reverb send level (hex 00-FF).\"},{\"x\":28,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ slot assignment. '--' = no EQ.\"}]},\"inst\":{\"title\":\"Instrument View\",\"type\":\"parameter\",\"description\":\"Per-instrument parameters for the selected slot: TYPE (Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out), LOAD/SAVE presets, NAME, TRANSP. (enable/disable transposition from scales and transpose columns), TABLE TIC (default table speed), and EQ slot, plus the synth engine parameters. Almost every parameter has an associated FX command; highlighting a parameter and navigating to the phrase or table view sets that parameter's FX command as default.\"},\"instmods\":{\"title\":\"Instrument Modulation View\",\"type\":\"parameter\",\"description\":\"Four configurable modulation slots per instrument: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope, or TRACKING, each with a destination parameter (DEST) and optional source (SRC).\"},\"mixer\":{\"title\":\"Mixer View\",\"type\":\"parameter\",\"description\":\"Mix levels for the 8 tracks, the 3 send effects (MX/DE/RE), the analog input (INPUT, with dual-mono mode) and USB input, the DJ filter (DJF), OTT compression, main EQ, limiter (LIM) and main volume (MIX). All signals are summed through OTT, main EQ, limiter, DJ filter and main volume. Most values are also exposed as FX commands (VMV, VMX, VDE, VRE, VT1-8, DJC/DJR/DJT, IVO/IMX/IDE/IRV, USB, ...).\"},\"project\":{\"title\":\"Project View\",\"type\":\"parameter\",\"description\":\"Song-wide settings such as tempo, project transpose (identical to the TSP FX command), and LIVE QUANTIZE behavior for cueing tracks in live mode. [SHIFT]+[OPTION] stores a song snapshot; [SHIFT]+[EDIT] recalls it.\"},\"effectsettings\":{\"title\":\"Effect Settings View\",\"type\":\"parameter\",\"description\":\"Settings for the 3 send effects: ModFX type/depth/frequency/width, Delay time/feedback/width, and Reverb size/decay/modulation/width/freeze — all addressable via the XMT-XMW, XDT-XDR and XRS-XRZ FX commands.\"},\"mixeq\":{\"title\":\"Mix EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the main mix EQ slot (assign with the EQM FX command). Selecting EQ in the Mixer view and pressing [EDIT] or [SHIFT]+[RIGHT] opens this view.\"},\"modfxeq\":{\"title\":\"ModFX EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the ModFX send path.\"},\"delayeq\":{\"title\":\"Delay EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Delay send path.\"},\"reverbeq\":{\"title\":\"Reverb EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Reverb send path.\"},\"mixscope\":{\"title\":\"Mix Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the main mix activity. Opened from the Mixer view with MIX selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"limiterscope\":{\"title\":\"Limiter Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the limiter's compression activity (the white line on the MIX indicator). Opened from the Mixer view with LIM selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"systemsettings\":{\"title\":\"System Settings View\",\"type\":\"parameter\",\"description\":\"System-level settings for the device.\"},\"loadinstrument\":{\"title\":\"Load Instrument\",\"type\":\"parameter\",\"description\":\"File browser for loading an instrument preset from the SD card ([EDIT] on an empty instrument slot, or LOAD in the Instrument view).\"},\"selectsavedirectory\":{\"title\":\"Select Save Directory\",\"type\":\"parameter\",\"description\":\"File browser directory-selection step when saving a preset or project.\"},\"createdirectory\":{\"title\":\"Create Directory\",\"type\":\"parameter\",\"description\":\"File browser dialog for creating a new directory on the SD card.\"}}"),
|
|
3148
|
-
fxCommands: /* @__PURE__ */ JSON.parse("{\"sequencer\":{\"ARP\":{\"description\":\"Arpeggio: plays 3 notes rapidly. X=first interval semitones, Y=second interval semitones.\"},\"ARC\":{\"description\":\"Arpeggio Config: X=direction and number of octaves, Y=speed in ticks.\"},\"CHA\":{\"description\":\"Chance: X=probability left of command (0=never, F=always), Y=probability right. In a table: probability for everything to the left of CHA from 00 (never) to FF (always).\"},\"DEL\":{\"description\":\"Delay: in phrase delays entire row by XX ticks; in table delays table playhead.\"},\"GRV\":{\"description\":\"Groove: sets groove number XX for the current track.\"},\"GGR\":{\"description\":\"Global Groove: sets groove XX for all tracks.\"},\"HOP\":{\"description\":\"Hop: in phrase jumps to row Y of next phrase; HOPFF stops track. In table loops to row Y X times.\"},\"INS\":{\"description\":\"Instrument: changes active instrument to XX as an FX command.\"},\"KIL\":{\"description\":\"Kill: stops the playing instrument after XX ticks.\"},\"RND\":{\"description\":\"Random: randomises the preceding FX command. X=left range, Y=right range (0=none, F=full).\"},\"RNL\":{\"description\":\"Randomise Left: randomises the FX command to the left (X=left range, Y=right range). In the first column RNL randomises the note and instrument number in phrase, or note and velocity in table.\"},\"RET\":{\"description\":\"Retrig: retriggers current row. Y≠0: retrig every Y ticks with X volume ramp. Y=0: single retrig after X ticks.\"},\"REP\":{\"description\":\"Repeat: repeats and increments the last FX command by XX each step. Active until a new command or REP00 stops it; it does not need to appear on every row. In a table: loops the last row XX times. A double-caret '^^' appears next to an FX lane while a REP from a previous phrase is active.\"},\"RTO\":{\"description\":\"Repeat To: use after REP to hold at the given value (the min/max REP stops at). In a table, used alone: loops until the relative value is reached.\"},\"RMX\":{\"description\":\"Remix: repositions the phrase playhead (Y) on tracks to the left (selected by X).\"},\"NTH\":{\"description\":\"Nth Trigger: conditional trigger on the loop count. X=affects what is left of the command, Y=what is right (in a table: what is left).\"},\"PSL\":{\"description\":\"Pitch Slide (portamento): slides to the given note over XX ticks.\"},\"PBN\":{\"description\":\"Pitch Bend: continuous pitch slide up (00-7F) or down (80-FF).\"},\"PVB\":{\"description\":\"Vibrato: X=speed, Y=depth.\"},\"PVX\":{\"description\":\"Extreme Vibrato: like PVB but with higher range.\"},\"SCA\":{\"description\":\"Track Scale: X=key signature, Y=scale number for this track.\"},\"SCG\":{\"description\":\"Global Scale: X=key, Y=scale number for the whole song.\"},\"SNG\":{\"description\":\"Song Hop: moves this track's song position by XX rows relative to current. Ignored if the resulting song position is not playable.\"},\"SED\":{\"description\":\"Random Seed: sets the random seed XX for this track.\"},\"TBL\":{\"description\":\"Table: assigns table number XX to the current instrument.\"},\"THO\":{\"description\":\"Table Hop: jumps all table columns to position 0X.\"},\"TIC\":{\"description\":\"Table Tick: sets the table tick rate. 00 = increment row on each instrument trigger; 01-FB = ticks per row; FC = Octave Map (playing octave maps to table row); FD = Velocity Map; FE = Note Map (pair with HOP00 on row 0C to limit to 12 notes/octave); FF = increment at 200 Hz. In a table each FX column can run independently; a TIC on the last table row affects all rows.\"},\"TBX\":{\"description\":\"Aux Table: runs table XX in parallel with the instrument table. 00=stop aux table. A TIC in the same row as a TBX affects the auxiliary table's tick rate.\"},\"TPO\":{\"description\":\"Tempo: sets song BPM to XX. In a table the tempo is relative.\"},\"TSP\":{\"description\":\"Global Song Transpose: transposes the entire song by XX semitones. Identical to the transpose value in the Project View.\"},\"NXT\":{\"description\":\"Next Track Trigger: plays instrument XX on the track to the right using the current note/velocity. Does not work on track 8.\"},\"OFF\":{\"description\":\"Note Off: stops the playing instrument after XX ticks. Triggers ADSR release stage.\"},\"MTT\":{\"description\":\"Micro Time: adjusts row playback timing in positive or negative sub-tick (1/8 tick) amounts. Negative amounts do not work on the first phrase row. Currently has no action in tables.\"}},\"instrument\":{\"VOL\":{\"description\":\"Volume (relative).\",\"relative\":true},\"PIT\":{\"description\":\"Pitch in semitones (relative).\",\"relative\":true},\"FIN\":{\"description\":\"Fine tune: pitch shift of up to about one semitone (relative).\",\"relative\":true},\"PLY\":{\"description\":\"Sampler play mode (FWD, REV, loop modes…).\"},\"STA\":{\"description\":\"Sampler start position.\"},\"LOP\":{\"description\":\"Sampler loop start (relative).\",\"relative\":true},\"LEN\":{\"description\":\"Sampler length (relative).\",\"relative\":true},\"DEG\":{\"description\":\"Degrade amount (relative).\",\"relative\":true},\"SLI\":{\"description\":\"Sampler slice to play.\"},\"ERR\":{\"description\":\"Error: randomizes pitch and parameters by the given amount.\"},\"FLT\":{\"description\":\"Filter type.\"},\"FIL\":{\"description\":\"Filter type.\"},\"ALG\":{\"description\":\"FM algorithm.\"},\"FM1\":{\"description\":\"FM modulation amount 1 (relative).\",\"relative\":true},\"FM2\":{\"description\":\"FM modulation amount 2 (relative).\",\"relative\":true},\"FM3\":{\"description\":\"FM modulation amount 3 (relative).\",\"relative\":true},\"FM4\":{\"description\":\"FM modulation amount 4 (relative).\",\"relative\":true},\"SNC\":{\"description\":\"Sync: on an FM synth the FM sync phase position; on a Hypersynth it offsets the sync of the oscillators by the given amount.\"},\"CRD\":{\"description\":\"Hypersynth chord set.\"},\"CVO\":{\"description\":\"Hypersynth chord voice offset: X=voice, Y=offset.\"},\"SWM\":{\"description\":\"Hypersynth oscillator swarm (relative).\",\"relative\":true},\"WID\":{\"description\":\"Hypersynth stereo width (relative).\",\"relative\":true},\"SUB\":{\"description\":\"Hypersynth sub-oscillator amount (relative).\",\"relative\":true},\"OSC\":{\"description\":\"Macrosynth oscillator shape.\"},\"TBR\":{\"description\":\"Macrosynth timbre (relative).\",\"relative\":true},\"COL\":{\"description\":\"Macrosynth color (relative).\",\"relative\":true},\"RED\":{\"description\":\"Macrosynth bit reduction (relative).\",\"relative\":true},\"TRG\":{\"description\":\"Macrosynth trigger/strike.\"},\"CUT\":{\"description\":\"Filter cutoff (relative).\",\"relative\":true},\"RES\":{\"description\":\"Filter resonance (relative).\",\"relative\":true},\"AMP\":{\"description\":\"Amp level (relative).\",\"relative\":true},\"LIM\":{\"description\":\"Limit mode: how the amp stage clips.\"},\"PAN\":{\"description\":\"Pan (relative).\",\"relative\":true},\"DRY\":{\"description\":\"Dry volume: the instrument's level in the main mix.\"},\"SMX\":{\"description\":\"Send to ModFX.\"},\"SDL\":{\"description\":\"Send to Delay.\"},\"SRV\":{\"description\":\"Send to Reverb.\"},\"CHD\":{\"description\":\"Chord (MIDIOUT instrument): triggers a chord. Quantized by the active scale.\"},\"ADD\":{\"description\":\"Add Note (MIDIOUT instrument): adds a note to the chord. Quantized by the active scale.\"}},\"modulation\":{\"EA\":{\"description\":\"Envelope amount of modulator N (relative).\",\"relative\":true},\"AT\":{\"description\":\"Envelope attack of modulator N.\"},\"HO\":{\"description\":\"Envelope hold of modulator N.\"},\"DE\":{\"description\":\"Envelope decay of modulator N.\"},\"ET\":{\"description\":\"Envelope trigger of modulator N: retriggers the envelope.\"},\"LA\":{\"description\":\"LFO amount of modulator N (relative).\",\"relative\":true},\"LO\":{\"description\":\"LFO oscillator shape of modulator N.\"},\"LS\":{\"description\":\"LFO trigger settings of modulator N.\"},\"LF\":{\"description\":\"LFO frequency of modulator N.\"},\"LT\":{\"description\":\"LFO trigger of modulator N: at the given offset, resets the LFO to that position.\"}},\"mixer\":{\"VMV\":{\"description\":\"Main Volume: sets the main song output volume.\"},\"VMX\":{\"description\":\"ModFX Volume.\"},\"VDE\":{\"description\":\"Delay Volume.\"},\"VRE\":{\"description\":\"Reverb Volume.\"},\"VT1\":{\"description\":\"Track 1 Volume.\"},\"VT2\":{\"description\":\"Track 2 Volume.\"},\"VT3\":{\"description\":\"Track 3 Volume.\"},\"VT4\":{\"description\":\"Track 4 Volume.\"},\"VT5\":{\"description\":\"Track 5 Volume.\"},\"VT6\":{\"description\":\"Track 6 Volume.\"},\"VT7\":{\"description\":\"Track 7 Volume.\"},\"VT8\":{\"description\":\"Track 8 Volume.\"},\"DJC\":{\"description\":\"DJ Filter Cutoff.\"},\"DJR\":{\"description\":\"DJ Filter Resonance.\"},\"DJT\":{\"description\":\"DJ Filter Type.\"},\"EQM\":{\"description\":\"Assign main mix EQ slot.\"},\"EQI\":{\"description\":\"Assign current instrument EQ slot.\"},\"XMT\":{\"description\":\"Send Effect: ModFX mod type and phase position (Effect Settings view).\"},\"XMM\":{\"description\":\"Send Effect: ModFX mod depth (Effect Settings view).\"},\"XMF\":{\"description\":\"Send Effect: ModFX mod frequency (Effect Settings view).\"},\"XMW\":{\"description\":\"Send Effect: ModFX stereo width (Effect Settings view).\"},\"XMR\":{\"description\":\"Send Effect: ModFX to reverb mix (Effect Settings view).\"},\"XDT\":{\"description\":\"Send Effect: Delay time — X = left, Y = right, in large increments (Effect Settings view).\"},\"XDF\":{\"description\":\"Send Effect: Delay feedback (Effect Settings view).\"},\"XDW\":{\"description\":\"Send Effect: Delay stereo width (Effect Settings view).\"},\"XDR\":{\"description\":\"Send Effect: Delay to reverb mix (Effect Settings view).\"},\"XRS\":{\"description\":\"Send Effect: Reverb room size (Effect Settings view).\"},\"XRD\":{\"description\":\"Send Effect: Reverb decay (Effect Settings view).\"},\"XRM\":{\"description\":\"Send Effect: Reverb mod depth (Effect Settings view).\"},\"XRF\":{\"description\":\"Send Effect: Reverb mod frequency (Effect Settings view).\"},\"XRW\":{\"description\":\"Send Effect: Reverb stereo width (Effect Settings view).\"},\"XRZ\":{\"description\":\"Send Effect: Reverb freeze — enabled with a value greater than 00, disabled at 00.\"},\"VIN\":{\"description\":\"Line Input Mixer: analog input volume (Mixer view).\"},\"XRH\":{\"description\":\"Send Effect: Reverb shimmer (Effect Settings view).\"},\"OTT\":{\"description\":\"OTT mix amount (Mixer OTT settings; OTC and OTI default to 80).\"},\"OTC\":{\"description\":\"OTT color (default 80).\"},\"OTI\":{\"description\":\"OTT time (default 80).\"},\"IVO\":{\"description\":\"Alias of VIN as named in the manual: analog input volume (Mixer view).\"},\"IMX\":{\"description\":\"Line Input Mixer: analog input ModFX send (Mixer view).\"},\"IDE\":{\"description\":\"Line Input Mixer: analog input delay send (Mixer view).\"},\"IRV\":{\"description\":\"Line Input Mixer: analog input reverb send (Mixer view). Also referenced as 'IRE' in the manual text.\"},\"IRE\":{\"description\":\"Alias of IRV: analog input reverb send (Mixer view).\"},\"IV2\":{\"description\":\"Line Input Mixer: volume for the 2nd input (right channel in dual-mono input mode, set in the Mixer view).\"},\"IM2\":{\"description\":\"Line Input Mixer: ModFX send for the 2nd input (right channel in dual-mono input mode).\"},\"ID2\":{\"description\":\"Line Input Mixer: delay send for the 2nd input (right channel in dual-mono input mode).\"},\"IR2\":{\"description\":\"Line Input Mixer: reverb send for the 2nd input (right channel in dual-mono input mode).\"},\"USB\":{\"description\":\"USB input volume (Mixer view).\"}}}"),
|
|
3149
|
-
instrumentFx: {
|
|
3150
|
-
_note: "Instrument commands offered by [EDIT]+[UP/DOWN] (Effect Command Help view) in the Current Instrument section depend on the instrument type; the Instrument Mods section depends on the modulators of that instrument, and Sequencer FX and Mixer/Effects are the same for every type. Lists marked verified were read from a real M8 (Model:02). Wavsynth, External and MIDI Out were not available on that device: their lists are not known here.",
|
|
3151
|
-
types: {
|
|
3152
|
-
sampler: {
|
|
3153
|
-
verified: !0,
|
|
3154
|
-
commands: [
|
|
3155
|
-
"VOL",
|
|
3156
|
-
"PIT",
|
|
3157
|
-
"FIN",
|
|
3158
|
-
"PLY",
|
|
3159
|
-
"STA",
|
|
3160
|
-
"LOP",
|
|
3161
|
-
"LEN",
|
|
3162
|
-
"DEG",
|
|
3163
|
-
"FLT",
|
|
3164
|
-
"CUT",
|
|
3165
|
-
"RES",
|
|
3166
|
-
"AMP",
|
|
3167
|
-
"LIM",
|
|
3168
|
-
"PAN",
|
|
3169
|
-
"SLI",
|
|
3170
|
-
"ERR",
|
|
3171
|
-
"DRY",
|
|
3172
|
-
"SMX",
|
|
3173
|
-
"SDL",
|
|
3174
|
-
"SRV"
|
|
3175
|
-
]
|
|
3176
|
-
},
|
|
3177
|
-
macrosynth: {
|
|
3178
|
-
verified: !0,
|
|
3179
|
-
commands: [
|
|
3180
|
-
"VOL",
|
|
3181
|
-
"PIT",
|
|
3182
|
-
"FIN",
|
|
3183
|
-
"OSC",
|
|
3184
|
-
"TBR",
|
|
3185
|
-
"COL",
|
|
3186
|
-
"DEG",
|
|
3187
|
-
"RED",
|
|
3188
|
-
"FIL",
|
|
3189
|
-
"CUT",
|
|
3190
|
-
"RES",
|
|
3191
|
-
"AMP",
|
|
3192
|
-
"LIM",
|
|
3193
|
-
"PAN",
|
|
3194
|
-
"TRG",
|
|
3195
|
-
"ERR",
|
|
3196
|
-
"DRY",
|
|
3197
|
-
"SMX",
|
|
3198
|
-
"SDL",
|
|
3199
|
-
"SRV"
|
|
3200
|
-
]
|
|
3201
|
-
},
|
|
3202
|
-
fmsynth: {
|
|
3203
|
-
verified: !0,
|
|
3204
|
-
commands: [
|
|
3205
|
-
"VOL",
|
|
3206
|
-
"PIT",
|
|
3207
|
-
"FIN",
|
|
3208
|
-
"ALG",
|
|
3209
|
-
"FM1",
|
|
3210
|
-
"FM2",
|
|
3211
|
-
"FM3",
|
|
3212
|
-
"FM4",
|
|
3213
|
-
"FIL",
|
|
3214
|
-
"CUT",
|
|
3215
|
-
"RES",
|
|
3216
|
-
"AMP",
|
|
3217
|
-
"LIM",
|
|
3218
|
-
"PAN",
|
|
3219
|
-
"SNC",
|
|
3220
|
-
"ERR",
|
|
3221
|
-
"DRY",
|
|
3222
|
-
"SMX",
|
|
3223
|
-
"SDL",
|
|
3224
|
-
"SRV"
|
|
3225
|
-
]
|
|
3226
|
-
},
|
|
3227
|
-
hypersynth: {
|
|
3228
|
-
verified: !0,
|
|
3229
|
-
commands: [
|
|
3230
|
-
"VOL",
|
|
3231
|
-
"PIT",
|
|
3232
|
-
"FIN",
|
|
3233
|
-
"CRD",
|
|
3234
|
-
"CVO",
|
|
3235
|
-
"SWM",
|
|
3236
|
-
"WID",
|
|
3237
|
-
"SUB",
|
|
3238
|
-
"FIL",
|
|
3239
|
-
"CUT",
|
|
3240
|
-
"RES",
|
|
3241
|
-
"AMP",
|
|
3242
|
-
"LIM",
|
|
3243
|
-
"PAN",
|
|
3244
|
-
"SNC",
|
|
3245
|
-
"ERR",
|
|
3246
|
-
"DRY",
|
|
3247
|
-
"SMX",
|
|
3248
|
-
"SDL",
|
|
3249
|
-
"SRV"
|
|
3250
|
-
]
|
|
3251
|
-
},
|
|
3252
|
-
midiout: {
|
|
3253
|
-
verified: !1,
|
|
3254
|
-
commands: ["CHD", "ADD"]
|
|
3255
|
-
}
|
|
3256
|
-
},
|
|
3257
|
-
modulation: {
|
|
3258
|
-
_note: "Modulator slot N (1-4) offers the commands of its kind. Seen: an envelope slot (AHD) offers EA AT HO DE ET, an LFO slot offers LA LO LS LF LT. Slot kinds vary per instrument (e.g. Sampler 00: env, env, LFO, LFO; Hypersynth 01: env, LFO, LFO, LFO). Other modulator kinds were not captured.",
|
|
3259
|
-
envelope: [
|
|
3260
|
-
"EA",
|
|
3261
|
-
"AT",
|
|
3262
|
-
"HO",
|
|
3263
|
-
"DE",
|
|
3264
|
-
"ET"
|
|
3265
|
-
],
|
|
3266
|
-
lfo: [
|
|
3267
|
-
"LA",
|
|
3268
|
-
"LO",
|
|
3269
|
-
"LS",
|
|
3270
|
-
"LF",
|
|
3271
|
-
"LT"
|
|
3272
|
-
]
|
|
3273
|
-
},
|
|
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." }
|
|
3275
|
-
}
|
|
3276
|
-
}, Ft = {
|
|
3645
|
+
}, _n = {
|
|
3277
3646
|
version: "1.0",
|
|
3278
3647
|
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.",
|
|
3279
3648
|
notes: {
|
|
3280
3649
|
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.",
|
|
3281
3650
|
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.",
|
|
3282
|
-
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.",
|
|
3651
|
+
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.",
|
|
3283
3652
|
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."
|
|
3284
3653
|
},
|
|
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\":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.\"}}"),
|
|
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.\"},\"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.\"}}}}"),
|
|
3654
|
+
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.\"}}"),
|
|
3655
|
+
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 (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"C\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator C (C)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"D\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator D (D)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"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.\"}}}}"),
|
|
3287
3656
|
instrumentTypeNames: {
|
|
3288
3657
|
WAVS: "wavsynth",
|
|
3289
3658
|
MACRO: "macrosynth",
|
|
@@ -3293,23 +3662,23 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
|
|
|
3293
3662
|
EXTERN: "external",
|
|
3294
3663
|
MIDI: "midiout"
|
|
3295
3664
|
}
|
|
3296
|
-
},
|
|
3297
|
-
for (let [e, t] of Object.entries(
|
|
3665
|
+
}, vn = _n.views, yn = _n.instrumentTypes, bn = _n.instrumentTypeNames, xn = {};
|
|
3666
|
+
for (let [e, t] of Object.entries(vn)) {
|
|
3298
3667
|
let { extends: n, ...r } = t;
|
|
3299
|
-
|
|
3300
|
-
...
|
|
3668
|
+
xn[e] = n ? {
|
|
3669
|
+
...vn[n],
|
|
3301
3670
|
...r
|
|
3302
3671
|
} : r;
|
|
3303
3672
|
}
|
|
3304
|
-
function
|
|
3305
|
-
return
|
|
3673
|
+
function Sn(e) {
|
|
3674
|
+
return xn[e] ?? null;
|
|
3306
3675
|
}
|
|
3307
3676
|
var G = (e, t, n) => t >= e.length ? "" : e.slice(t, t + n);
|
|
3308
|
-
function
|
|
3677
|
+
function Cn(e, t) {
|
|
3309
3678
|
let n = e.width ?? e.text?.length ?? 1, r = G(t, e.x, n);
|
|
3310
3679
|
return e.text == null ? e.regex != null && new RegExp(e.regex, "i").test(r) : r.toUpperCase() === e.text.toUpperCase();
|
|
3311
3680
|
}
|
|
3312
|
-
function
|
|
3681
|
+
function wn(e, t) {
|
|
3313
3682
|
let n = e.sections;
|
|
3314
3683
|
if (!n) return null;
|
|
3315
3684
|
for (let e of t) {
|
|
@@ -3318,7 +3687,7 @@ function Ht(e, t) {
|
|
|
3318
3687
|
}
|
|
3319
3688
|
return null;
|
|
3320
3689
|
}
|
|
3321
|
-
function
|
|
3690
|
+
function Tn(e, t, n) {
|
|
3322
3691
|
let r = e.zones?.find((e) => e.id === t);
|
|
3323
3692
|
if (!e.slotLabel || !r) return null;
|
|
3324
3693
|
let i = RegExp(`^${e.slotLabel}$`, "i");
|
|
@@ -3328,42 +3697,101 @@ function Ut(e, t, n) {
|
|
|
3328
3697
|
}
|
|
3329
3698
|
return null;
|
|
3330
3699
|
}
|
|
3331
|
-
function
|
|
3700
|
+
function En(e) {
|
|
3332
3701
|
let t = e.toUpperCase().replace(/[^A-Z]/g, "");
|
|
3333
3702
|
if (!t) return null;
|
|
3334
|
-
let n = Object.keys(
|
|
3335
|
-
return n ?
|
|
3703
|
+
let n = Object.keys(bn).find((e) => t.startsWith(e));
|
|
3704
|
+
return n ? bn[n] : null;
|
|
3336
3705
|
}
|
|
3337
|
-
function
|
|
3706
|
+
function Dn(e) {
|
|
3338
3707
|
for (let t of e) if (t.toUpperCase().startsWith("TYPE")) {
|
|
3339
3708
|
let e = G(t, 8, 13).trim();
|
|
3340
3709
|
return e ? {
|
|
3341
3710
|
text: e,
|
|
3342
|
-
key:
|
|
3711
|
+
key: En(e)
|
|
3343
3712
|
} : null;
|
|
3344
3713
|
}
|
|
3345
3714
|
return null;
|
|
3346
3715
|
}
|
|
3347
|
-
function
|
|
3348
|
-
let t =
|
|
3716
|
+
function On(e) {
|
|
3717
|
+
let t = yn[e];
|
|
3349
3718
|
return t ? {
|
|
3350
3719
|
name: t.name,
|
|
3351
3720
|
description: t.description
|
|
3352
3721
|
} : null;
|
|
3353
3722
|
}
|
|
3354
|
-
function
|
|
3723
|
+
function kn(e, t, n) {
|
|
3355
3724
|
let r = t.replace(/^MOD\d$/, "MOD#").replace(/^CC[A-J]$/, "CC#");
|
|
3356
3725
|
if (e.vocabulary) return e.vocabulary[r] ?? null;
|
|
3357
3726
|
if (e.vocabularyFrom !== "instrumentTypes") return null;
|
|
3358
|
-
let i = n ? [n, "common"] : ["common", ...Object.keys(
|
|
3727
|
+
let i = n ? [n, "common"] : ["common", ...Object.keys(yn).filter((e) => e !== "common")];
|
|
3359
3728
|
for (let e of i) {
|
|
3360
|
-
let t =
|
|
3729
|
+
let t = yn[e]?.params[r];
|
|
3361
3730
|
if (t) return t;
|
|
3362
3731
|
}
|
|
3363
3732
|
return null;
|
|
3364
3733
|
}
|
|
3365
|
-
var
|
|
3366
|
-
|
|
3734
|
+
var An = (e) => e.toLowerCase().replace(/[^a-z0-9]+(.)?/g, (e, t) => t ? t.toUpperCase() : "").replace(/^[^a-z]+/, ""), jn = /^[A-Z][A-Z0-9.:#/ -]*$/, Mn = {
|
|
3735
|
+
RATIO: "ratio",
|
|
3736
|
+
"LEV/FB": "levFb",
|
|
3737
|
+
MOD: "mod"
|
|
3738
|
+
}, Nn = /^(?:-----|[1-4]>[A-Z]{3})$/i;
|
|
3739
|
+
function Pn(e, t, n) {
|
|
3740
|
+
let r = G(t, 0, e.x).trim().toUpperCase(), i = e.letters.map((t, n) => ({
|
|
3741
|
+
letter: t,
|
|
3742
|
+
x: e.x + n * e.step
|
|
3743
|
+
})), a = (e, t, n, i, a) => ({
|
|
3744
|
+
...i,
|
|
3745
|
+
x: t,
|
|
3746
|
+
width: n,
|
|
3747
|
+
key: `${e}${a}`,
|
|
3748
|
+
label: `${i.label} · operator ${a}`,
|
|
3749
|
+
meta: {
|
|
3750
|
+
paramLabel: r || e,
|
|
3751
|
+
operator: a
|
|
3752
|
+
}
|
|
3753
|
+
}), o = Mn[r];
|
|
3754
|
+
if (o) {
|
|
3755
|
+
let t = n.params[r];
|
|
3756
|
+
return t ? i.map(({ letter: i, x: s }) => a(o, s, e.width, r === "MOD" ? Fn(t, 1, n) : t, i)) : null;
|
|
3757
|
+
}
|
|
3758
|
+
if (r) return null;
|
|
3759
|
+
if (/^\s+[A-D] \S/i.test(t)) {
|
|
3760
|
+
let r = [];
|
|
3761
|
+
for (let i of t.matchAll(/([A-D]) (\S+)/gi)) {
|
|
3762
|
+
let t = i[1].toUpperCase(), a = n.params[t];
|
|
3763
|
+
a && e.letters.includes(t) && r.push({
|
|
3764
|
+
...a,
|
|
3765
|
+
type: "option",
|
|
3766
|
+
format: "text",
|
|
3767
|
+
x: (i.index ?? 0) + 2,
|
|
3768
|
+
width: i[2].length,
|
|
3769
|
+
key: `shape${t}`,
|
|
3770
|
+
label: `Shape · operator ${t}`,
|
|
3771
|
+
meta: {
|
|
3772
|
+
paramLabel: "SHAPE",
|
|
3773
|
+
operator: t
|
|
3774
|
+
}
|
|
3775
|
+
});
|
|
3776
|
+
}
|
|
3777
|
+
return r.length ? r : null;
|
|
3778
|
+
}
|
|
3779
|
+
let s = i.map(({ x: n }) => G(t, n, e.width).trim()), c = n.params.MOD;
|
|
3780
|
+
return c && s.every((e) => Nn.test(e)) && !G(t, 0, e.x).trim() ? i.map(({ letter: t, x: r }) => a("mod2", r, e.width, Fn(c, 2, n), t)) : null;
|
|
3781
|
+
}
|
|
3782
|
+
function Fn(e, t, n) {
|
|
3783
|
+
let r = {};
|
|
3784
|
+
for (let [e, t] of Object.entries(n.operatorDestinations ?? {})) for (let n = 1; n <= 4; n += 1) r[`${n}>${e}`] = `MOD${n} ${e}: ${t}`;
|
|
3785
|
+
return {
|
|
3786
|
+
...e,
|
|
3787
|
+
type: "option",
|
|
3788
|
+
format: "text",
|
|
3789
|
+
label: `Modulation Slot ${t} (MOD)`,
|
|
3790
|
+
options: r
|
|
3791
|
+
};
|
|
3792
|
+
}
|
|
3793
|
+
var In = /^(CC[A-J])(?: CC:VAL)? +(?=\S)/i, Ln = 6;
|
|
3794
|
+
function Rn(e, t, n) {
|
|
3367
3795
|
if (e.kind === "browser") {
|
|
3368
3796
|
let r = n.entryText?.trim() || t.trim();
|
|
3369
3797
|
return r ? {
|
|
@@ -3401,28 +3829,35 @@ function Xt(e, t, n) {
|
|
|
3401
3829
|
}]
|
|
3402
3830
|
};
|
|
3403
3831
|
}
|
|
3404
|
-
for (let r of e.rows ?? []) if (!(r.section && n.section && r.section !== n.section) && r.match.every((e) =>
|
|
3832
|
+
for (let r of e.rows ?? []) if (!(r.section && n.section && r.section !== n.section) && r.match.every((e) => Cn(e, t))) return {
|
|
3405
3833
|
rowId: r.id,
|
|
3406
3834
|
cells: r.cells
|
|
3407
3835
|
};
|
|
3836
|
+
if (e.operators && n.instrumentKey === e.operators.instrument) {
|
|
3837
|
+
let n = Pn(e.operators, t, yn[e.operators.instrument]);
|
|
3838
|
+
if (n) return {
|
|
3839
|
+
rowId: "operators",
|
|
3840
|
+
cells: n
|
|
3841
|
+
};
|
|
3842
|
+
}
|
|
3408
3843
|
let r = e.zones ?? [], i = [];
|
|
3409
3844
|
return r.forEach((a, o) => {
|
|
3410
|
-
let s = G(t, a.labelX, a.labelWidth).trim().toUpperCase();
|
|
3411
|
-
if (!
|
|
3412
|
-
let
|
|
3413
|
-
if (!G(t,
|
|
3414
|
-
let
|
|
3845
|
+
let s = G(t, a.labelX, a.labelWidth).trim().toUpperCase(), c = a.x, l = null, u = o === 0 ? In.exec(t) : null;
|
|
3846
|
+
if (u && (s = u[1].toUpperCase(), c = u[0].length, l = Ln), !jn.test(s)) return;
|
|
3847
|
+
let d = r[o + 1], f = d != null && G(t, d.labelX - 1, 1).trim() === "" && jn.test(G(t, d.labelX, d.labelWidth).trim().toUpperCase()), p = l ?? (d && f ? Math.max(1, d.labelX - 1 - c) : a.width);
|
|
3848
|
+
if (!G(t, c, p).trim()) return;
|
|
3849
|
+
let m = kn(e, s, n.instrumentKey), h = An(s) || a.id, g = i.some((e) => e.key === h) ? `${h}${a.id[0].toUpperCase()}${a.id.slice(1)}` : h;
|
|
3415
3850
|
i.push({
|
|
3416
|
-
x:
|
|
3417
|
-
width:
|
|
3418
|
-
key:
|
|
3419
|
-
type:
|
|
3420
|
-
format:
|
|
3421
|
-
label:
|
|
3422
|
-
description:
|
|
3423
|
-
parts:
|
|
3424
|
-
options:
|
|
3425
|
-
unit:
|
|
3851
|
+
x: c,
|
|
3852
|
+
width: p,
|
|
3853
|
+
key: g,
|
|
3854
|
+
type: m?.type ?? "parameter",
|
|
3855
|
+
format: m?.format ?? "auto",
|
|
3856
|
+
label: m?.label ?? s,
|
|
3857
|
+
description: m?.description,
|
|
3858
|
+
parts: m?.parts,
|
|
3859
|
+
options: m?.options,
|
|
3860
|
+
unit: m?.unit,
|
|
3426
3861
|
meta: {
|
|
3427
3862
|
paramLabel: s,
|
|
3428
3863
|
zone: a.id
|
|
@@ -3439,13 +3874,13 @@ var K = {
|
|
|
3439
3874
|
values: null,
|
|
3440
3875
|
text: null
|
|
3441
3876
|
};
|
|
3442
|
-
function
|
|
3877
|
+
function zn(e) {
|
|
3443
3878
|
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";
|
|
3444
3879
|
}
|
|
3445
|
-
function
|
|
3880
|
+
function Bn(e, t) {
|
|
3446
3881
|
let n = e.trim();
|
|
3447
3882
|
if (!n) return K;
|
|
3448
|
-
switch (t === "auto" ?
|
|
3883
|
+
switch (t === "auto" ? zn(n) : t) {
|
|
3449
3884
|
case "hex": {
|
|
3450
3885
|
let e = /^([0-9A-F]{2})/i.exec(n), t = e ? parseInt(e[1], 16) : null;
|
|
3451
3886
|
return {
|
|
@@ -3470,7 +3905,7 @@ function Qt(e, t) {
|
|
|
3470
3905
|
};
|
|
3471
3906
|
}
|
|
3472
3907
|
case "hexPair": {
|
|
3473
|
-
let e = /^([0-9A-F]{2})
|
|
3908
|
+
let e = /^([0-9A-F]{2})[:/]([0-9A-F]{2})/i.exec(n);
|
|
3474
3909
|
if (!e) return {
|
|
3475
3910
|
...K,
|
|
3476
3911
|
text: n
|
|
@@ -3527,7 +3962,7 @@ function Qt(e, t) {
|
|
|
3527
3962
|
};
|
|
3528
3963
|
}
|
|
3529
3964
|
}
|
|
3530
|
-
function
|
|
3965
|
+
function Vn(e, t) {
|
|
3531
3966
|
if (!e || !t) return null;
|
|
3532
3967
|
let n = t.toUpperCase().trim();
|
|
3533
3968
|
if (e[n]) return e[n];
|
|
@@ -3536,28 +3971,28 @@ function $t(e, t) {
|
|
|
3536
3971
|
}
|
|
3537
3972
|
//#endregion
|
|
3538
3973
|
//#region src/fxCommands.ts
|
|
3539
|
-
var
|
|
3540
|
-
function
|
|
3541
|
-
let t =
|
|
3542
|
-
return t.length === 0 || t.length ===
|
|
3974
|
+
var Hn = I.fxCommands, Un = I.instrumentFx.types, Wn = Object.entries(Un).filter(([, e]) => e.verified).map(([e]) => e);
|
|
3975
|
+
function Gn(e) {
|
|
3976
|
+
let t = Wn.filter((t) => Un[t].commands.includes(e));
|
|
3977
|
+
return t.length === 0 || t.length === Wn.length ? null : t;
|
|
3543
3978
|
}
|
|
3544
|
-
function
|
|
3979
|
+
function Kn(e) {
|
|
3545
3980
|
let t = e.toUpperCase();
|
|
3546
3981
|
for (let e of [
|
|
3547
3982
|
"sequencer",
|
|
3548
3983
|
"instrument",
|
|
3549
3984
|
"mixer"
|
|
3550
3985
|
]) {
|
|
3551
|
-
let n =
|
|
3986
|
+
let n = Hn[e][t];
|
|
3552
3987
|
if (n) return {
|
|
3553
3988
|
description: n.description,
|
|
3554
3989
|
category: e,
|
|
3555
3990
|
relative: n.relative === !0,
|
|
3556
|
-
instrumentTypes: e === "instrument" ?
|
|
3991
|
+
instrumentTypes: e === "instrument" ? Gn(t) : null,
|
|
3557
3992
|
slot: null
|
|
3558
3993
|
};
|
|
3559
3994
|
}
|
|
3560
|
-
let n = /^([A-Z]{2})([1-4])$/.exec(t), r = n ?
|
|
3995
|
+
let n = /^([A-Z]{2})([1-4])$/.exec(t), r = n ? Hn.modulation[n[1]] : void 0;
|
|
3561
3996
|
if (n && r) return {
|
|
3562
3997
|
description: r.description.replace("modulator N", `modulator ${n[2]}`),
|
|
3563
3998
|
category: "modulation",
|
|
@@ -3567,24 +4002,24 @@ function an(e) {
|
|
|
3567
4002
|
};
|
|
3568
4003
|
}
|
|
3569
4004
|
function q(e) {
|
|
3570
|
-
return
|
|
4005
|
+
return Kn(e)?.relative === !0;
|
|
3571
4006
|
}
|
|
3572
|
-
function
|
|
3573
|
-
let t =
|
|
4007
|
+
function qn(e) {
|
|
4008
|
+
let t = Un[e.toLowerCase()];
|
|
3574
4009
|
return t ? {
|
|
3575
4010
|
type: e.toLowerCase(),
|
|
3576
4011
|
verified: t.verified,
|
|
3577
4012
|
commands: [...t.commands]
|
|
3578
4013
|
} : null;
|
|
3579
4014
|
}
|
|
3580
|
-
var
|
|
3581
|
-
function
|
|
4015
|
+
var Jn = I.instrumentFx.relativeCommands.description, J = (e) => e.toString(16).toUpperCase().padStart(2, "0"), Y = (e) => e.toString(16).toUpperCase(), Yn = (e) => e === 1 ? "once" : e === 2 ? "twice" : `${e} times`, X = (e) => e > 127 ? e - 256 : e, Xn = (e) => `${e > 0 ? "+" : e < 0 ? "−" : "±"}${Math.abs(e)}`;
|
|
4016
|
+
function Zn(e, t) {
|
|
3582
4017
|
return q(e) && t > 127 ? `${J(t)} (${X(t)})` : J(t);
|
|
3583
4018
|
}
|
|
3584
|
-
function
|
|
4019
|
+
function Qn(e) {
|
|
3585
4020
|
return e > 127 ? `−${J(256 - e)}` : `+${J(e)}`;
|
|
3586
4021
|
}
|
|
3587
|
-
var
|
|
4022
|
+
var $n = [
|
|
3588
4023
|
"C-",
|
|
3589
4024
|
"C#",
|
|
3590
4025
|
"D-",
|
|
@@ -3598,16 +4033,16 @@ var fn = [
|
|
|
3598
4033
|
"A#",
|
|
3599
4034
|
"B-"
|
|
3600
4035
|
];
|
|
3601
|
-
function
|
|
4036
|
+
function er(e) {
|
|
3602
4037
|
let t = /^([A-G])([#-])(\d)$/.exec((e ?? "").trim().toUpperCase());
|
|
3603
4038
|
if (!t) return null;
|
|
3604
|
-
let n =
|
|
4039
|
+
let n = $n.indexOf(`${t[1]}${t[2]}`);
|
|
3605
4040
|
return n < 0 ? null : Number(t[3]) * 12 + n;
|
|
3606
4041
|
}
|
|
3607
|
-
function
|
|
3608
|
-
return `${
|
|
4042
|
+
function tr(e) {
|
|
4043
|
+
return `${$n[(e % 12 + 12) % 12]}${Math.floor(e / 12)}`;
|
|
3609
4044
|
}
|
|
3610
|
-
var
|
|
4045
|
+
var nr = [
|
|
3611
4046
|
"C",
|
|
3612
4047
|
"C#",
|
|
3613
4048
|
"D",
|
|
@@ -3620,14 +4055,14 @@ var hn = [
|
|
|
3620
4055
|
"A",
|
|
3621
4056
|
"A#",
|
|
3622
4057
|
"B"
|
|
3623
|
-
],
|
|
3624
|
-
function
|
|
3625
|
-
return e ===
|
|
4058
|
+
], rr = 6;
|
|
4059
|
+
function ir(e) {
|
|
4060
|
+
return e === rr ? "one row per step" : rr % e === 0 ? `${rr / e} rows per step` : e % rr === 0 ? `one row every ${e / rr} steps` : "";
|
|
3626
4061
|
}
|
|
3627
|
-
function
|
|
4062
|
+
function ar(e, t) {
|
|
3628
4063
|
if (e === 0) return `${t} moves one row each time the instrument is triggered`;
|
|
3629
4064
|
if (e <= 251) {
|
|
3630
|
-
let n =
|
|
4065
|
+
let n = ir(e);
|
|
3631
4066
|
return `${t} moves one row every ${e} tick${e === 1 ? "" : "s"}${n ? ` (${n})` : ""}`;
|
|
3632
4067
|
}
|
|
3633
4068
|
return [
|
|
@@ -3637,12 +4072,12 @@ function vn(e, t) {
|
|
|
3637
4072
|
`${t} moves one row every 5 ms (200 Hz)`
|
|
3638
4073
|
][e - 252] ?? null;
|
|
3639
4074
|
}
|
|
3640
|
-
function
|
|
4075
|
+
function or(e, t, n = {}) {
|
|
3641
4076
|
let r = e.toUpperCase(), i = t >> 4, a = t & 15, o = (e) => `${e} tick${e === 1 ? "" : "s"}`;
|
|
3642
4077
|
switch (r) {
|
|
3643
4078
|
case "ARP": {
|
|
3644
|
-
let e =
|
|
3645
|
-
return e == null ? `plays the note, then ${t}` : `${t}: ${
|
|
4079
|
+
let e = er(n.note), t = `+${i} and +${a} semitones`;
|
|
4080
|
+
return e == null ? `plays the note, then ${t}` : `${t}: ${tr(e)} ${tr(e + i)} ${tr(e + a)}`;
|
|
3646
4081
|
}
|
|
3647
4082
|
case "ARC": return `arp mode ${i}, speed ${o(a)}`;
|
|
3648
4083
|
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)`;
|
|
@@ -3656,7 +4091,7 @@ function yn(e, t, n = {}) {
|
|
|
3656
4091
|
case "TBL": return `instrument uses table ${J(t)}`;
|
|
3657
4092
|
case "TBX": return t === 0 ? "stops the auxiliary table" : `plays table ${J(t)} alongside the instrument table`;
|
|
3658
4093
|
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)`;
|
|
3659
|
-
case "HOP": return n.inTable ? a === n.row ? i === 0 ? `this column stops on row ${Y(a)}` : `holds this column on row ${Y(a)} ${
|
|
4094
|
+
case "HOP": return n.inTable ? a === n.row ? i === 0 ? `this column stops on row ${Y(a)}` : `holds this column on row ${Y(a)} ${Yn(i)}` : i === 0 ? `always jumps back to row ${Y(a)}: the rows in between loop` : `jumps to row ${Y(a)} ${Yn(i)}, then the column runs on past it` : t === 255 ? "stops this track" : `jumps to row ${Y(a)} of the next phrase`;
|
|
3660
4095
|
case "REP": return !n.inTable || t === 0 ? null : `plays the command above it ${t} more time${t === 1 ? "" : "s"}, one row each`;
|
|
3661
4096
|
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;
|
|
3662
4097
|
case "RMX": return `tracks to its left (selection ${Y(i)}) jump to phrase row ${Y(a)}`;
|
|
@@ -3668,47 +4103,47 @@ function yn(e, t, n = {}) {
|
|
|
3668
4103
|
case "PBN": return t < 128 ? `pitch slides up by ${t}` : `pitch slides down by ${256 - t}`;
|
|
3669
4104
|
case "SCA":
|
|
3670
4105
|
case "SCG": {
|
|
3671
|
-
let e =
|
|
4106
|
+
let e = nr[i];
|
|
3672
4107
|
return `${r === "SCA" ? "track" : "song"} scale ${a.toString(16).toUpperCase()}${e ? `, key ${e}` : ""}`;
|
|
3673
4108
|
}
|
|
3674
|
-
case "SNG": return `moves the song position by ${
|
|
4109
|
+
case "SNG": return `moves the song position by ${Xn(X(t))} row(s)`;
|
|
3675
4110
|
case "SED": return `random seed ${J(t)}`;
|
|
3676
4111
|
case "NXT": return `plays instrument ${J(t)} on the next track`;
|
|
3677
4112
|
case "MTT": {
|
|
3678
4113
|
if (n.inTable) return "no effect in a table";
|
|
3679
4114
|
let e = X(t);
|
|
3680
|
-
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 ${
|
|
4115
|
+
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 ${Xn(e)}/8 tick`;
|
|
3681
4116
|
}
|
|
3682
4117
|
case "TPO": return `${t} BPM`;
|
|
3683
|
-
case "TSP": return `transposes the song by ${
|
|
3684
|
-
case "TIC": return
|
|
4118
|
+
case "TSP": return `transposes the song by ${Xn(X(t))} semitones`;
|
|
4119
|
+
case "TIC": return ar(t, n.inTable ? "this column" : "the instrument's table");
|
|
3685
4120
|
case "XRZ": return t > 0 ? "reverb freeze on" : "reverb freeze off";
|
|
3686
4121
|
}
|
|
3687
4122
|
if (q(r)) {
|
|
3688
4123
|
let e = X(t);
|
|
3689
|
-
return r === "PIT" ? e === 0 ? "no change" : `${
|
|
4124
|
+
return r === "PIT" ? e === 0 ? "no change" : `${Xn(e)} semitones` : e === 0 ? "no change" : Xn(e);
|
|
3690
4125
|
}
|
|
3691
4126
|
return null;
|
|
3692
4127
|
}
|
|
3693
|
-
function
|
|
4128
|
+
function sr(e, t, n) {
|
|
3694
4129
|
let r = e + t & 255;
|
|
3695
4130
|
return n != null && (t < 128 ? r < e || r > n : r > e || r < n) && (r = n), {
|
|
3696
4131
|
value: r,
|
|
3697
4132
|
atLimit: n != null && r === n
|
|
3698
4133
|
};
|
|
3699
4134
|
}
|
|
3700
|
-
function
|
|
4135
|
+
function cr(e, t, n, r = !1) {
|
|
3701
4136
|
let i = null, a = null, o = null, s = null, c = r, l = !1, u = null, d = "none", f = Math.min(n, e.length - 1);
|
|
3702
4137
|
for (let n = 0; n <= f; n += 1) {
|
|
3703
4138
|
let f = e[n].fields[`fx${t}cmd`], p = e[n].fields[`fx${t}val`];
|
|
3704
4139
|
if (d = "none", f && !f.isEmpty) {
|
|
3705
4140
|
let e = f.rawValue.toUpperCase(), t = p && !p.isEmpty ? p.hexValue : null;
|
|
3706
|
-
e === "REP" ? t ? !i && !r ? d = "orphan" : (d = c ? "repChange" : "rep", c || (u = n), c = !0, o = t, a != null && ({value: a, atLimit: l} =
|
|
4141
|
+
e === "REP" ? t ? !i && !r ? d = "orphan" : (d = c ? "repChange" : "rep", c || (u = n), c = !0, o = t, a != null && ({value: a, atLimit: l} = sr(a, o, s))) : (d = "repStop", c = !1, o = null, l = !1) : e === "RTO" ? (d = "rto", s = t) : (d = c ? "replace" : "set", i = {
|
|
3707
4142
|
cmd: e,
|
|
3708
4143
|
value: t,
|
|
3709
4144
|
step: n
|
|
3710
4145
|
}, a = t, c = !1, o = null, s = null, l = !1, u = null);
|
|
3711
|
-
} else c && (d = "continue", a != null && o != null && ({value: a, atLimit: l} =
|
|
4146
|
+
} else c && (d = "continue", a != null && o != null && ({value: a, atLimit: l} = sr(a, o, s)));
|
|
3712
4147
|
}
|
|
3713
4148
|
return {
|
|
3714
4149
|
lane: t,
|
|
@@ -3724,8 +4159,8 @@ function xn(e, t, n, r = !1) {
|
|
|
3724
4159
|
carried: c && !i
|
|
3725
4160
|
};
|
|
3726
4161
|
}
|
|
3727
|
-
function
|
|
3728
|
-
let t = e.source, n = (e) => e == null ? "??" : t ?
|
|
4162
|
+
function lr(e) {
|
|
4163
|
+
let t = e.source, n = (e) => e == null ? "??" : t ? Zn(t.cmd, e) : J(e), r = e.increment == null ? "" : Qn(e.increment), i = e.atLimit && e.limit != null ? `, held at the RTO limit ${J(e.limit)}` : "";
|
|
3729
4164
|
switch (e.event) {
|
|
3730
4165
|
case "rep":
|
|
3731
4166
|
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}`;
|
|
@@ -3737,7 +4172,7 @@ function Sn(e) {
|
|
|
3737
4172
|
default: return null;
|
|
3738
4173
|
}
|
|
3739
4174
|
}
|
|
3740
|
-
function
|
|
4175
|
+
function ur(e) {
|
|
3741
4176
|
let t = e.fields.inst;
|
|
3742
4177
|
return t && !t.isEmpty ? "instrument" : [
|
|
3743
4178
|
1,
|
|
@@ -3748,10 +4183,10 @@ function Cn(e) {
|
|
|
3748
4183
|
return n != null && !n.isEmpty && n.rawValue.toUpperCase() === "RET";
|
|
3749
4184
|
}) ? "ret" : null;
|
|
3750
4185
|
}
|
|
3751
|
-
function
|
|
4186
|
+
function dr(e, t, n = null) {
|
|
3752
4187
|
let r = {}, i = null, a = null, o = Math.min(t, e.length - 1);
|
|
3753
4188
|
for (let t = 0; t <= o; t += 1) {
|
|
3754
|
-
let o =
|
|
4189
|
+
let o = ur(e[t]);
|
|
3755
4190
|
if (o) {
|
|
3756
4191
|
for (let e of Object.keys(r)) delete r[e];
|
|
3757
4192
|
i = t, a = o;
|
|
@@ -3761,7 +4196,7 @@ function wn(e, t, n = null) {
|
|
|
3761
4196
|
2,
|
|
3762
4197
|
3
|
|
3763
4198
|
]) {
|
|
3764
|
-
let s =
|
|
4199
|
+
let s = cr(e, o, t, n === o), c = s.source;
|
|
3765
4200
|
if (!c || s.carried || !q(c.cmd)) continue;
|
|
3766
4201
|
let l = null, u = !1;
|
|
3767
4202
|
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;
|
|
@@ -3778,21 +4213,21 @@ function wn(e, t, n = null) {
|
|
|
3778
4213
|
}
|
|
3779
4214
|
return r;
|
|
3780
4215
|
}
|
|
3781
|
-
var
|
|
3782
|
-
function
|
|
4216
|
+
var fr = (e) => e === 0 ? "±00" : `${e > 0 ? "+" : "−"}${J(Math.abs(e))}`;
|
|
4217
|
+
function pr(e) {
|
|
3783
4218
|
if (e.resetStep == null) return "the start of this phrase (earlier state unknown)";
|
|
3784
4219
|
let t = e.resetStep.toString(16).toUpperCase();
|
|
3785
4220
|
return e.resetBy === "ret" ? `the RET on step ${t}` : `the instrument number on step ${t}`;
|
|
3786
4221
|
}
|
|
3787
|
-
var
|
|
3788
|
-
function
|
|
3789
|
-
if (!e || e.count <= 1) return `relative: ${
|
|
3790
|
-
let t = e.viaRep ? ", including REP repeats" : "", n = e.total === 0 ? "has had no net effect" : `has moved by ${
|
|
3791
|
-
return `relative: ${e.cmd} ${n} over ${e.count} steps since ${
|
|
4222
|
+
var mr = "stays until a note with an instrument number (I) or a RET resets it";
|
|
4223
|
+
function hr(e) {
|
|
4224
|
+
if (!e || e.count <= 1) return `relative: ${mr}`;
|
|
4225
|
+
let t = e.viaRep ? ", including REP repeats" : "", n = e.total === 0 ? "has had no net effect" : `has moved by ${fr(e.total)}`;
|
|
4226
|
+
return `relative: ${e.cmd} ${n} over ${e.count} steps since ${pr(e)}${t}; ${mr}`;
|
|
3792
4227
|
}
|
|
3793
|
-
function
|
|
4228
|
+
function gr(e) {
|
|
3794
4229
|
let t = Object.values(e).filter((e) => e.total !== 0);
|
|
3795
|
-
return t.length ? `triggers the instrument again: resets the relative changes still in force (${t.map((e) => `${e.cmd} ${
|
|
4230
|
+
return t.length ? `triggers the instrument again: resets the relative changes still in force (${t.map((e) => `${e.cmd} ${fr(e.total)}`).join(", ")})` : null;
|
|
3796
4231
|
}
|
|
3797
4232
|
//#endregion
|
|
3798
4233
|
//#region src/scaleInsight.ts
|
|
@@ -3809,7 +4244,7 @@ var Z = [
|
|
|
3809
4244
|
"A",
|
|
3810
4245
|
"A#",
|
|
3811
4246
|
"B"
|
|
3812
|
-
],
|
|
4247
|
+
], _r = [
|
|
3813
4248
|
{
|
|
3814
4249
|
name: "Major (Ionian)",
|
|
3815
4250
|
steps: [
|
|
@@ -4027,7 +4462,7 @@ var Z = [
|
|
|
4027
4462
|
11
|
|
4028
4463
|
]
|
|
4029
4464
|
}
|
|
4030
|
-
],
|
|
4465
|
+
], vr = [
|
|
4031
4466
|
"the root",
|
|
4032
4467
|
"a minor 2nd",
|
|
4033
4468
|
"a major 2nd",
|
|
@@ -4041,26 +4476,26 @@ var Z = [
|
|
|
4041
4476
|
"a minor 7th",
|
|
4042
4477
|
"a major 7th"
|
|
4043
4478
|
];
|
|
4044
|
-
function
|
|
4479
|
+
function yr(e) {
|
|
4045
4480
|
let t = e.replace(/[-\s]/g, "").toUpperCase();
|
|
4046
4481
|
return Z.includes(t) ? t : null;
|
|
4047
4482
|
}
|
|
4048
|
-
function
|
|
4483
|
+
function br(e) {
|
|
4049
4484
|
let t = Z.indexOf(e);
|
|
4050
|
-
return t <= 0 ? t === 0 ? "the first row (the scale root)" : e : `${
|
|
4485
|
+
return t <= 0 ? t === 0 ? "the first row (the scale root)" : e : `${vr[t]} (+${t} semitones above C)`;
|
|
4051
4486
|
}
|
|
4052
|
-
function
|
|
4487
|
+
function xr(e) {
|
|
4053
4488
|
let t = new Set(e);
|
|
4054
4489
|
if (t.size === 0) return [];
|
|
4055
4490
|
let n = [];
|
|
4056
|
-
for (let e of
|
|
4491
|
+
for (let e of _r) 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({
|
|
4057
4492
|
root: r,
|
|
4058
4493
|
name: e.name
|
|
4059
4494
|
});
|
|
4060
4495
|
return n.sort((e, t) => e.root - t.root), n.map((e) => e.root === 0 ? e.name : `${e.name} from ${Z[e.root]}`);
|
|
4061
4496
|
}
|
|
4062
|
-
function
|
|
4063
|
-
let t = e.filter((e) => e.enabled === !0), n = t.map((e) => e.interval), r =
|
|
4497
|
+
function Sr(e) {
|
|
4498
|
+
let t = e.filter((e) => e.enabled === !0), n = t.map((e) => e.interval), r = xr(t.map((e) => e.semitone)), i = e.filter((e) => e.enabled === !0 && e.offset != null && e.offset !== 0).map((e) => ({
|
|
4064
4499
|
interval: e.interval,
|
|
4065
4500
|
offset: e.offset
|
|
4066
4501
|
}));
|
|
@@ -4069,47 +4504,47 @@ function Fn(e) {
|
|
|
4069
4504
|
enabled: n,
|
|
4070
4505
|
shapes: r,
|
|
4071
4506
|
detuned: i,
|
|
4072
|
-
summary:
|
|
4507
|
+
summary: Cr({
|
|
4073
4508
|
enabled: n,
|
|
4074
4509
|
shapes: r,
|
|
4075
4510
|
detuned: i
|
|
4076
4511
|
}).join(" — ")
|
|
4077
4512
|
};
|
|
4078
4513
|
}
|
|
4079
|
-
function
|
|
4514
|
+
function Cr(e) {
|
|
4080
4515
|
let { enabled: t, shapes: n, detuned: r } = e;
|
|
4081
4516
|
if (!t.length) return ["no notes enabled"];
|
|
4082
4517
|
let i = n[0] ?? `${t.length} notes, custom scale`;
|
|
4083
|
-
return r.length && (i += `; detuned: ${r.map((e) => `${e.interval} ${
|
|
4518
|
+
return r.length && (i += `; detuned: ${r.map((e) => `${e.interval} ${wr(e.offset)}`).join(", ")}`), [t.join(" "), i];
|
|
4084
4519
|
}
|
|
4085
|
-
function
|
|
4520
|
+
function wr(e) {
|
|
4086
4521
|
return `${e > 0 ? "+" : e < 0 ? "−" : ""}${Math.abs(e).toFixed(2)} st`;
|
|
4087
4522
|
}
|
|
4088
4523
|
//#endregion
|
|
4089
4524
|
//#region src/viewContext.ts
|
|
4090
|
-
var
|
|
4525
|
+
var Tr = /* @__PURE__ */ new Set([
|
|
4091
4526
|
"--",
|
|
4092
4527
|
"---",
|
|
4093
4528
|
"---00",
|
|
4094
4529
|
"----",
|
|
4095
4530
|
"-"
|
|
4096
4531
|
]);
|
|
4097
|
-
function
|
|
4098
|
-
return !e ||
|
|
4532
|
+
function Er(e) {
|
|
4533
|
+
return !e || Tr.has(e) || /^-+$/.test(e);
|
|
4099
4534
|
}
|
|
4100
|
-
function
|
|
4535
|
+
function Dr(e) {
|
|
4101
4536
|
let t = e.trim();
|
|
4102
|
-
if (!t ||
|
|
4537
|
+
if (!t || Er(t)) return null;
|
|
4103
4538
|
let n = parseInt(t, 16);
|
|
4104
4539
|
return Number.isNaN(n) ? null : n;
|
|
4105
4540
|
}
|
|
4106
|
-
function
|
|
4541
|
+
function Or(e, t, n) {
|
|
4107
4542
|
return t >= e.length ? "" : e.slice(t, Math.min(t + n, e.length));
|
|
4108
4543
|
}
|
|
4109
|
-
function
|
|
4110
|
-
return
|
|
4544
|
+
function kr(e) {
|
|
4545
|
+
return Ar(e);
|
|
4111
4546
|
}
|
|
4112
|
-
function
|
|
4547
|
+
function Ar(e) {
|
|
4113
4548
|
if (!e) return {
|
|
4114
4549
|
viewId: null,
|
|
4115
4550
|
isUnsaved: !1,
|
|
@@ -4122,7 +4557,7 @@ function Un(e) {
|
|
|
4122
4557
|
marker: r
|
|
4123
4558
|
};
|
|
4124
4559
|
}
|
|
4125
|
-
function
|
|
4560
|
+
function jr(e) {
|
|
4126
4561
|
return e.length > 0 && /^[^a-zA-Z0-9 ]/.test(e[0]) ? {
|
|
4127
4562
|
line: e.slice(1),
|
|
4128
4563
|
offset: 1
|
|
@@ -4131,12 +4566,12 @@ function Wn(e) {
|
|
|
4131
4566
|
offset: 0
|
|
4132
4567
|
};
|
|
4133
4568
|
}
|
|
4134
|
-
function
|
|
4135
|
-
let n =
|
|
4569
|
+
function Mr(e, t) {
|
|
4570
|
+
let n = Or(t, e.x, e.width).trim();
|
|
4136
4571
|
n.includes("^") && (e.type === "fxCommand" || e.type === "fxValue") && (n = "");
|
|
4137
|
-
let r =
|
|
4572
|
+
let r = Er(n);
|
|
4138
4573
|
if (e.format) {
|
|
4139
|
-
let t = r ? null :
|
|
4574
|
+
let t = r ? null : Bn(n, e.format), i = e.type === "name" || e.type === "fileEntry", a = t?.text != null && !i ? t.text.toUpperCase() : t?.text ?? null;
|
|
4140
4575
|
return {
|
|
4141
4576
|
key: e.key,
|
|
4142
4577
|
label: e.label,
|
|
@@ -4151,8 +4586,8 @@ function Gn(e, t) {
|
|
|
4151
4586
|
values: t?.values ?? null,
|
|
4152
4587
|
parts: e.parts,
|
|
4153
4588
|
unit: e.unit,
|
|
4154
|
-
valueDescription: !r &&
|
|
4155
|
-
fileKind: e.type === "fileEntry" ?
|
|
4589
|
+
valueDescription: !r && Vn(e.options, a ?? n) || void 0,
|
|
4590
|
+
fileKind: e.type === "fileEntry" ? Nr(n) : void 0
|
|
4156
4591
|
};
|
|
4157
4592
|
}
|
|
4158
4593
|
let i = [
|
|
@@ -4172,7 +4607,7 @@ function Gn(e, t) {
|
|
|
4172
4607
|
if (a.includes(e.type)) {
|
|
4173
4608
|
let e = parseInt(n, 10);
|
|
4174
4609
|
o = Number.isNaN(e) ? null : e;
|
|
4175
|
-
} else o =
|
|
4610
|
+
} else o = Dr(n);
|
|
4176
4611
|
}
|
|
4177
4612
|
return {
|
|
4178
4613
|
key: e.key,
|
|
@@ -4184,20 +4619,20 @@ function Gn(e, t) {
|
|
|
4184
4619
|
meta: e.meta
|
|
4185
4620
|
};
|
|
4186
4621
|
}
|
|
4187
|
-
function
|
|
4622
|
+
function Nr(e) {
|
|
4188
4623
|
if (e === "/..") return "parent";
|
|
4189
4624
|
if (e.startsWith("/")) return "directory";
|
|
4190
4625
|
let t = /\.([a-z0-9]+)$/i.exec(e)?.[1]?.toLowerCase();
|
|
4191
4626
|
return t === "m8i" ? "instrumentPreset" : t === "wav" ? "sample" : t?.startsWith("m8") ? "m8file" : "file";
|
|
4192
4627
|
}
|
|
4193
|
-
function
|
|
4194
|
-
let n =
|
|
4628
|
+
function Pr(e, t) {
|
|
4629
|
+
let n = Or(e, t.x, t.width).trim();
|
|
4195
4630
|
if (!n) return null;
|
|
4196
|
-
if (t.parse === "hex") return
|
|
4631
|
+
if (t.parse === "hex") return Dr(n);
|
|
4197
4632
|
let r = parseInt(n, 10);
|
|
4198
4633
|
return Number.isNaN(r) ? null : r;
|
|
4199
4634
|
}
|
|
4200
|
-
function
|
|
4635
|
+
function Fr(e, t) {
|
|
4201
4636
|
for (let n of e) if (t >= n.x && t < n.x + n.width) return n;
|
|
4202
4637
|
let n = null, r = 2;
|
|
4203
4638
|
for (let i of e) {
|
|
@@ -4206,9 +4641,9 @@ function Jn(e, t) {
|
|
|
4206
4641
|
}
|
|
4207
4642
|
return n;
|
|
4208
4643
|
}
|
|
4209
|
-
function
|
|
4644
|
+
function Ir(e, t) {
|
|
4210
4645
|
let n = {};
|
|
4211
|
-
for (let r of e.columns ?? []) n[r.key] =
|
|
4646
|
+
for (let r of e.columns ?? []) n[r.key] = Mr(r, t);
|
|
4212
4647
|
for (let e of Object.keys(n)) {
|
|
4213
4648
|
let t = /^fx(\d)cmd$/.exec(e)?.[1], r = t ? n[`fx${t}val`] : void 0;
|
|
4214
4649
|
r && n[e].isEmpty && (n[`fx${t}val`] = {
|
|
@@ -4217,27 +4652,27 @@ function Yn(e, t) {
|
|
|
4217
4652
|
});
|
|
4218
4653
|
}
|
|
4219
4654
|
return {
|
|
4220
|
-
rowIndex: e.rowField ?
|
|
4655
|
+
rowIndex: e.rowField ? Pr(t, e.rowField) : null,
|
|
4221
4656
|
rowKey: e.rowField?.key ?? "row",
|
|
4222
4657
|
rowId: null,
|
|
4223
4658
|
fields: n
|
|
4224
4659
|
};
|
|
4225
4660
|
}
|
|
4226
|
-
var
|
|
4227
|
-
function
|
|
4661
|
+
var Lr = (e) => e.replace(/\s+/g, ""), Rr = 1, Q = (e) => e.slice(1);
|
|
4662
|
+
function zr(e, t) {
|
|
4228
4663
|
if (!t || !e.cursorPos || !e.currentLine) return !1;
|
|
4229
4664
|
let n = t.lines[e.cursorPos.y];
|
|
4230
|
-
return n != null &&
|
|
4665
|
+
return n != null && Lr(n) === Lr(e.currentLine);
|
|
4231
4666
|
}
|
|
4232
|
-
var
|
|
4233
|
-
function
|
|
4234
|
-
let n =
|
|
4667
|
+
var Br = 16, Vr = 4, Hr = 21;
|
|
4668
|
+
function Ur(e, t) {
|
|
4669
|
+
let n = I.views[e];
|
|
4235
4670
|
if (n?.type !== "grid" || !n.rowField) return [];
|
|
4236
|
-
let r = [], i = Math.min(
|
|
4237
|
-
for (let e =
|
|
4671
|
+
let r = [], i = Math.min(Hr, t.lines.length - 1);
|
|
4672
|
+
for (let e = Vr; e <= i; e += 1) {
|
|
4238
4673
|
let i = Q(t.lines[e] ?? ""), a = n.rowField;
|
|
4239
|
-
if (i.length <= a.x + a.width || !
|
|
4240
|
-
let o =
|
|
4674
|
+
if (i.length <= a.x + a.width || !Wr(i[a.x + a.width])) continue;
|
|
4675
|
+
let o = Ir(n, i);
|
|
4241
4676
|
o.rowIndex != null && r.push({
|
|
4242
4677
|
...o,
|
|
4243
4678
|
screenRow: e
|
|
@@ -4245,11 +4680,11 @@ function nr(e, t) {
|
|
|
4245
4680
|
}
|
|
4246
4681
|
return r;
|
|
4247
4682
|
}
|
|
4248
|
-
var
|
|
4249
|
-
function
|
|
4250
|
-
let n =
|
|
4683
|
+
var Wr = (e) => e === " " || /^[^a-zA-Z0-9-]$/.test(e);
|
|
4684
|
+
function Gr(e, t) {
|
|
4685
|
+
let n = I.views[e];
|
|
4251
4686
|
if (n?.type !== "grid") return null;
|
|
4252
|
-
let r =
|
|
4687
|
+
let r = Ur(e, t), i = (e) => !!e && e !== " " && Wr(e);
|
|
4253
4688
|
if (e === "song") return {
|
|
4254
4689
|
tracks: (n.columns ?? []).filter((e) => e.type === "chainRef").map((e) => r.find((n) => i(Q(t.lines[n.screenRow] ?? "")[e.x - 1]))?.rowIndex ?? null),
|
|
4255
4690
|
row: null
|
|
@@ -4263,25 +4698,25 @@ function ir(e, t) {
|
|
|
4263
4698
|
row: null
|
|
4264
4699
|
};
|
|
4265
4700
|
}
|
|
4266
|
-
function
|
|
4701
|
+
function Kr(e, t, n, r) {
|
|
4267
4702
|
let i = (t.cursorPos?.y ?? -1) - r;
|
|
4268
4703
|
if (i < 0) return null;
|
|
4269
4704
|
let a = [], o = null;
|
|
4270
|
-
for (let t = 0; t <
|
|
4705
|
+
for (let t = 0; t < Br; t += 1) {
|
|
4271
4706
|
let r = n.lines[i + t];
|
|
4272
4707
|
if (r == null) return null;
|
|
4273
|
-
let s = Q(r), c =
|
|
4708
|
+
let s = Q(r), c = Ir(e, s);
|
|
4274
4709
|
if (c.rowIndex !== t) return null;
|
|
4275
4710
|
a.push(c);
|
|
4276
4711
|
let l = s.indexOf("^^");
|
|
4277
|
-
o == null && l >= 0 && (o =
|
|
4712
|
+
o == null && l >= 0 && (o = qr(e, l));
|
|
4278
4713
|
}
|
|
4279
4714
|
return {
|
|
4280
4715
|
steps: a,
|
|
4281
4716
|
carriedRepLane: o
|
|
4282
4717
|
};
|
|
4283
4718
|
}
|
|
4284
|
-
function
|
|
4719
|
+
function qr(e, t) {
|
|
4285
4720
|
let n = [];
|
|
4286
4721
|
for (let r = 1; r <= 3; r += 1) {
|
|
4287
4722
|
let i = e.columns?.find((e) => e.key === `fx${r}cmd`), a = e.columns?.find((e) => e.key === `fx${r}val`);
|
|
@@ -4295,8 +4730,8 @@ function or(e, t) {
|
|
|
4295
4730
|
let r = Math.min(...n.map((e) => e.dist)), i = n.filter((e) => e.dist === r);
|
|
4296
4731
|
return r <= 1 && i.length === 1 ? i[0].lane : null;
|
|
4297
4732
|
}
|
|
4298
|
-
function
|
|
4299
|
-
let t =
|
|
4733
|
+
function Jr(e) {
|
|
4734
|
+
let t = yr(e.fields.interval?.rawValue ?? "");
|
|
4300
4735
|
if (!t) return null;
|
|
4301
4736
|
let n = e.fields.en?.rawValue.toUpperCase(), r = parseFloat(e.fields.offset?.rawValue ?? "");
|
|
4302
4737
|
return {
|
|
@@ -4306,15 +4741,15 @@ function sr(e) {
|
|
|
4306
4741
|
offset: Number.isNaN(r) ? null : r
|
|
4307
4742
|
};
|
|
4308
4743
|
}
|
|
4309
|
-
function
|
|
4744
|
+
function Yr(e, t) {
|
|
4310
4745
|
let n = /* @__PURE__ */ new Map();
|
|
4311
4746
|
for (let r of t.lines) {
|
|
4312
|
-
let t =
|
|
4747
|
+
let t = Ir(e, Q(r)), i = Jr(t), a = t.fields.en?.rawValue.toUpperCase();
|
|
4313
4748
|
!i || a !== "ON" && a !== "OFF" && a !== "--" || n.has(i.interval) || n.set(i.interval, i);
|
|
4314
4749
|
}
|
|
4315
4750
|
return n.size === Z.length ? Z.map((e) => n.get(e)) : null;
|
|
4316
4751
|
}
|
|
4317
|
-
function
|
|
4752
|
+
function Xr(e, t, n, r) {
|
|
4318
4753
|
let i = {
|
|
4319
4754
|
...r,
|
|
4320
4755
|
isGridView: !1,
|
|
@@ -4331,40 +4766,40 @@ function lr(e, t, n, r) {
|
|
|
4331
4766
|
resetsRelative: null,
|
|
4332
4767
|
scaleNote: null,
|
|
4333
4768
|
scale: null
|
|
4334
|
-
}, a = e.cursorPos, o = a ? a.x - 1 : 0, s = a &&
|
|
4769
|
+
}, a = e.cursorPos, o = a ? a.x - 1 : 0, s = a && zr(e, t) ? t : null, c = s && a ? Q(s.lines[a.y] ?? "") : e.currentLine ?? "";
|
|
4335
4770
|
if (!c.trim()) return i;
|
|
4336
|
-
let l = s && a ? s.lines.slice(0, a.y + 1).reverse().map(Q) : [c], u = r.viewName === "inst" ?
|
|
4771
|
+
let l = s && a ? s.lines.slice(0, a.y + 1).reverse().map(Q) : [c], u = r.viewName === "inst" ? Dn(s ? s.lines.map(Q) : [c]) : null, d = {
|
|
4337
4772
|
instrumentKey: u?.key ?? null,
|
|
4338
4773
|
entryText: e.textUnderCursor
|
|
4339
4774
|
};
|
|
4340
4775
|
if (!s && a && n.kind == null) for (let e = 0; e <= Math.min(o, 24); e += 1) {
|
|
4341
4776
|
let t = " ".repeat(e) + c;
|
|
4342
|
-
if (
|
|
4777
|
+
if (Rn(n, t, {
|
|
4343
4778
|
...d,
|
|
4344
|
-
section:
|
|
4779
|
+
section: wn(n, [t])
|
|
4345
4780
|
}).cells.some((e) => o >= e.x && o < e.x + e.width)) {
|
|
4346
4781
|
c = t;
|
|
4347
4782
|
break;
|
|
4348
4783
|
}
|
|
4349
4784
|
}
|
|
4350
|
-
let f =
|
|
4785
|
+
let f = wn(n, s ? l : [c]), { rowId: p, cells: m } = Rn(n, c, {
|
|
4351
4786
|
...d,
|
|
4352
4787
|
section: f
|
|
4353
4788
|
}), h = {};
|
|
4354
|
-
for (let e of m) h[e.key] =
|
|
4789
|
+
for (let e of m) h[e.key] = Mr(e, c);
|
|
4355
4790
|
let g = {
|
|
4356
4791
|
rowIndex: null,
|
|
4357
4792
|
rowKey: "param",
|
|
4358
4793
|
rowId: p,
|
|
4359
4794
|
fields: h
|
|
4360
|
-
}, _ = null, v = a ?
|
|
4795
|
+
}, _ = null, v = a ? Fr(m, o) : null;
|
|
4361
4796
|
v && (_ = {
|
|
4362
4797
|
...h[v.key],
|
|
4363
4798
|
viewName: r.viewName,
|
|
4364
4799
|
rowIndex: null
|
|
4365
4800
|
});
|
|
4366
4801
|
let y = v?.meta?.zone;
|
|
4367
|
-
return s && typeof y == "string" && (f =
|
|
4802
|
+
return s && typeof y == "string" && (f = Tn(n, y, l) ?? f), {
|
|
4368
4803
|
...i,
|
|
4369
4804
|
section: f,
|
|
4370
4805
|
instrumentType: u,
|
|
@@ -4373,10 +4808,10 @@ function lr(e, t, n, r) {
|
|
|
4373
4808
|
lineText: c.trim()
|
|
4374
4809
|
};
|
|
4375
4810
|
}
|
|
4376
|
-
function
|
|
4811
|
+
function Zr(e, t) {
|
|
4377
4812
|
let n = e.viewName;
|
|
4378
4813
|
if (!n) return null;
|
|
4379
|
-
let r =
|
|
4814
|
+
let r = I.views[n] ?? null, i = r?.type === "grid", a = r?.title ?? null, o = r?.description ?? null, { viewId: s, isUnsaved: c } = Ar(e.viewTitle), l = {
|
|
4380
4815
|
viewName: n,
|
|
4381
4816
|
viewTitle: a,
|
|
4382
4817
|
viewDescription: o,
|
|
@@ -4395,8 +4830,8 @@ function ur(e, t) {
|
|
|
4395
4830
|
resetsRelative: null,
|
|
4396
4831
|
scaleNote: null,
|
|
4397
4832
|
scale: null
|
|
4398
|
-
}, d = r?.type === "parameter" ?
|
|
4399
|
-
if (d) return
|
|
4833
|
+
}, d = r?.type === "parameter" ? Sn(n) : null;
|
|
4834
|
+
if (d) return Xr(e, t, d, l);
|
|
4400
4835
|
if (!i || !r) return {
|
|
4401
4836
|
...u,
|
|
4402
4837
|
isGridView: !1,
|
|
@@ -4410,11 +4845,11 @@ function ur(e, t) {
|
|
|
4410
4845
|
row: null,
|
|
4411
4846
|
activeField: null
|
|
4412
4847
|
};
|
|
4413
|
-
let { line: p } =
|
|
4414
|
-
if (g) return
|
|
4848
|
+
let { line: p } = jr(f), m = e.cursorPos ? e.cursorPos.x - 1 : 0, h = Ir(r, p), g = n === "scale" && !yr(h.fields.interval?.rawValue ?? "") ? Sn(n) : null;
|
|
4849
|
+
if (g) return Xr(e, t, g, l);
|
|
4415
4850
|
let _ = null;
|
|
4416
4851
|
if (e.cursorPos) {
|
|
4417
|
-
let e =
|
|
4852
|
+
let e = Fr(r.columns ?? [], m);
|
|
4418
4853
|
if (e) {
|
|
4419
4854
|
let t = h.fields[e.key];
|
|
4420
4855
|
t && (_ = {
|
|
@@ -4425,20 +4860,20 @@ function ur(e, t) {
|
|
|
4425
4860
|
}
|
|
4426
4861
|
}
|
|
4427
4862
|
let v = null, y = null, b = null, x = null, S = null, C = null;
|
|
4428
|
-
if (
|
|
4863
|
+
if (zr(e, t)) {
|
|
4429
4864
|
if (n === "phrase" && h.rowIndex != null) {
|
|
4430
|
-
let n =
|
|
4865
|
+
let n = Kr(r, e, t, h.rowIndex);
|
|
4431
4866
|
if (n) {
|
|
4432
4867
|
v = n.steps, y = n.carriedRepLane;
|
|
4433
4868
|
let e = _ ? /^fx(\d)(cmd|val)$/.exec(_.key)?.[1] : void 0;
|
|
4434
|
-
e && (b =
|
|
4869
|
+
e && (b = cr(v, Number(e), h.rowIndex, y === Number(e))), x = dr(v, h.rowIndex, y), h.rowIndex > 0 && ur(h) && (S = dr(v, h.rowIndex - 1, y));
|
|
4435
4870
|
}
|
|
4436
4871
|
} else if (n === "scale") {
|
|
4437
|
-
let e =
|
|
4438
|
-
e && (C =
|
|
4872
|
+
let e = Yr(r, t);
|
|
4873
|
+
e && (C = Sr(e));
|
|
4439
4874
|
}
|
|
4440
4875
|
}
|
|
4441
|
-
let w = n === "scale" ?
|
|
4876
|
+
let w = n === "scale" ? Jr(h) : null;
|
|
4442
4877
|
return {
|
|
4443
4878
|
...u,
|
|
4444
4879
|
isGridView: !0,
|
|
@@ -4454,25 +4889,25 @@ function ur(e, t) {
|
|
|
4454
4889
|
scale: C
|
|
4455
4890
|
};
|
|
4456
4891
|
}
|
|
4457
|
-
function
|
|
4458
|
-
if (e.isParameterView) return
|
|
4892
|
+
function Qr(e) {
|
|
4893
|
+
if (e.isParameterView) return si(e);
|
|
4459
4894
|
if (e.viewName === "scale") {
|
|
4460
|
-
let t =
|
|
4895
|
+
let t = ci(e);
|
|
4461
4896
|
if (t) return t;
|
|
4462
4897
|
}
|
|
4463
4898
|
if (!e.activeField) return $(["location", e.viewTitle ?? e.viewName]);
|
|
4464
|
-
let { activeField: t, row: n } = e, r = $(["field", `${t.label}: ${
|
|
4899
|
+
let { activeField: t, row: n } = e, r = $(["field", `${t.label}: ${ui(t)}`]), i = (t.type === "fxCommand" || t.type === "fxValue") && n ? ei(e, t, n) : [...ri(e, t), ...ii(e, t)], a = i.length > 0;
|
|
4465
4900
|
if (t.type === "fxCommand" && !t.isEmpty) {
|
|
4466
|
-
let e = a && /^(REP|RTO)$/i.test(t.rawValue) ? void 0 :
|
|
4901
|
+
let e = a && /^(REP|RTO)$/i.test(t.rawValue) ? void 0 : Kn(t.rawValue);
|
|
4467
4902
|
e && r.push({
|
|
4468
4903
|
kind: "fxInfo",
|
|
4469
|
-
text: `(${
|
|
4904
|
+
text: `(${ti(e)})`
|
|
4470
4905
|
});
|
|
4471
4906
|
}
|
|
4472
4907
|
if (t.type === "fxValue" && n) {
|
|
4473
4908
|
let e = t.key.replace("val", "cmd"), i = n.fields[e];
|
|
4474
4909
|
if (i && !i.isEmpty) {
|
|
4475
|
-
let e = a && /^(REP|RTO)$/i.test(i.rawValue) ? void 0 :
|
|
4910
|
+
let e = a && /^(REP|RTO)$/i.test(i.rawValue) ? void 0 : Kn(i.rawValue), t = e ? `${i.rawValue} (${ti(e)})` : i.rawValue;
|
|
4476
4911
|
r.push({
|
|
4477
4912
|
kind: "fxTarget",
|
|
4478
4913
|
text: `for ${t}`
|
|
@@ -4491,8 +4926,8 @@ function dr(e) {
|
|
|
4491
4926
|
}
|
|
4492
4927
|
return r;
|
|
4493
4928
|
}
|
|
4494
|
-
function
|
|
4495
|
-
return
|
|
4929
|
+
function $r(e) {
|
|
4930
|
+
return Qr(e).map((e) => e.text).join(" — ");
|
|
4496
4931
|
}
|
|
4497
4932
|
function $(...e) {
|
|
4498
4933
|
return e.flatMap(([e, t]) => t ? [{
|
|
@@ -4500,33 +4935,33 @@ function $(...e) {
|
|
|
4500
4935
|
text: t
|
|
4501
4936
|
}] : []);
|
|
4502
4937
|
}
|
|
4503
|
-
function
|
|
4938
|
+
function ei(e, t, n) {
|
|
4504
4939
|
let r = /^fx(\d)/.exec(t.key)?.[1];
|
|
4505
4940
|
if (!r) return [];
|
|
4506
4941
|
let i = n.fields[`fx${r}cmd`], a = n.fields[`fx${r}val`];
|
|
4507
4942
|
if (!i || i.isEmpty) {
|
|
4508
4943
|
if (e.fxLane?.event !== "continue") return [];
|
|
4509
|
-
let t = e.fxLane.source?.cmd, n = t && q(t) ?
|
|
4510
|
-
return $(["fxLane",
|
|
4944
|
+
let t = e.fxLane.source?.cmd, n = t && q(t) ? hr(e.relativeFx?.[t]) : null;
|
|
4945
|
+
return $(["fxLane", lr(e.fxLane)], ["relative", n]);
|
|
4511
4946
|
}
|
|
4512
|
-
let o = i.rawValue.toUpperCase(), s = e.fxLane && e.fxLane.event !== "none" && e.fxLane.event !== "set" ?
|
|
4947
|
+
let o = i.rawValue.toUpperCase(), s = e.fxLane && e.fxLane.event !== "none" && e.fxLane.event !== "set" ? lr(e.fxLane) : null;
|
|
4513
4948
|
if ((o === "REP" || o === "RTO") && e.viewName !== "table") {
|
|
4514
|
-
let t = e.fxLane?.source?.cmd, n = o === "REP" && e.fxLane?.active && t && q(t) ?
|
|
4949
|
+
let t = e.fxLane?.source?.cmd, n = o === "REP" && e.fxLane?.active && t && q(t) ? hr(e.relativeFx?.[t]) : null;
|
|
4515
4950
|
return $(["fxLane", s], ["relative", n]);
|
|
4516
4951
|
}
|
|
4517
4952
|
if (!a || a.isEmpty || a.hexValue == null) return $(["fxLane", s]);
|
|
4518
|
-
let c = t.type === "fxCommand" ? `value ${
|
|
4953
|
+
let c = t.type === "fxCommand" ? `value ${Zn(o, a.hexValue)}: ` : "", l = or(o, a.hexValue, {
|
|
4519
4954
|
note: n.fields.note?.isEmpty ? null : n.fields.note?.rawValue,
|
|
4520
4955
|
inTable: e.viewName === "table",
|
|
4521
4956
|
row: n.rowIndex
|
|
4522
4957
|
}), u = null;
|
|
4523
|
-
return q(o) ? u =
|
|
4958
|
+
return q(o) ? u = hr(e.relativeFx?.[o]) : o === "RET" && e.resetsRelative && (u = gr(e.resetsRelative)), $(["meaning", l ? `${c}${l}` : null], ["fxLane", s], ["relative", u]);
|
|
4524
4959
|
}
|
|
4525
|
-
function
|
|
4960
|
+
function ti(e) {
|
|
4526
4961
|
let t = e.description.replace(/\.$/, "");
|
|
4527
|
-
return e.instrumentTypes ? `${t}; offered by ${e.instrumentTypes.map((e) =>
|
|
4962
|
+
return e.instrumentTypes ? `${t}; offered by ${e.instrumentTypes.map((e) => ni[e] ?? e).join(", ")} instruments` : t;
|
|
4528
4963
|
}
|
|
4529
|
-
var
|
|
4964
|
+
var ni = {
|
|
4530
4965
|
sampler: "Sampler",
|
|
4531
4966
|
macrosynth: "Macrosynth",
|
|
4532
4967
|
fmsynth: "FM Synth",
|
|
@@ -4535,17 +4970,17 @@ var hr = {
|
|
|
4535
4970
|
external: "External",
|
|
4536
4971
|
midiout: "MIDI Out"
|
|
4537
4972
|
};
|
|
4538
|
-
function
|
|
4539
|
-
return t.type !== "instrumentRef" || t.isEmpty || !e.resetsRelative ? [] : $(["relative",
|
|
4973
|
+
function ri(e, t) {
|
|
4974
|
+
return t.type !== "instrumentRef" || t.isEmpty || !e.resetsRelative ? [] : $(["relative", gr(e.resetsRelative)]);
|
|
4540
4975
|
}
|
|
4541
|
-
function
|
|
4976
|
+
function ii(e, t) {
|
|
4542
4977
|
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`]);
|
|
4543
4978
|
}
|
|
4544
|
-
var
|
|
4979
|
+
var ai = {
|
|
4545
4980
|
MODFX: "ModFX",
|
|
4546
4981
|
DELAY: "Delay",
|
|
4547
4982
|
REVERB: "Reverb"
|
|
4548
|
-
},
|
|
4983
|
+
}, oi = {
|
|
4549
4984
|
parent: "goes up to the parent directory",
|
|
4550
4985
|
directory: "directory: [EDIT] opens it",
|
|
4551
4986
|
instrumentPreset: "instrument preset: [EDIT] selects it",
|
|
@@ -4553,38 +4988,38 @@ var vr = {
|
|
|
4553
4988
|
m8file: "M8 file: [EDIT] selects it",
|
|
4554
4989
|
file: "file: [EDIT] selects it"
|
|
4555
4990
|
};
|
|
4556
|
-
function
|
|
4991
|
+
function si(e) {
|
|
4557
4992
|
let t = `${e.viewTitle ?? e.viewName}${e.viewId ? ` ${e.viewId}` : ""}`, n = e.activeField;
|
|
4558
4993
|
if (!n) return $(["location", t], ["line", e.lineText]);
|
|
4559
|
-
let r = e.instrumentType?.key ?
|
|
4560
|
-
return $(["field", `${n.label}: ${
|
|
4994
|
+
let r = e.instrumentType?.key ? On(e.instrumentType.key) : null;
|
|
4995
|
+
return $(["field", `${n.label}: ${ui(n)}`], ["optionInfo", n.valueDescription], ["fieldInfo", n.description], ["fileInfo", n.fileKind && oi[n.fileKind]], ["instrument", e.instrumentType && `${r?.name ?? e.instrumentType.text} instrument`], ["section", e.section && (ai[e.section] ?? e.section)], ["location", t]);
|
|
4561
4996
|
}
|
|
4562
|
-
function
|
|
4997
|
+
function ci(e) {
|
|
4563
4998
|
let t = `${e.viewTitle ?? e.viewName}${e.viewId ? ` ${e.viewId}` : ""}`, n = e.activeField, r = e.scaleNote;
|
|
4564
4999
|
if (!n || !r) return e.lineText ? $(["location", t], ["line", e.lineText]) : null;
|
|
4565
5000
|
let { interval: i, enabled: a, offset: o } = r, s;
|
|
4566
5001
|
if (n.key === "offset") {
|
|
4567
|
-
let e = o == null ? `detune of ${i}` : o === 0 ? `${i} is not detuned` : `${i} is detuned by ${
|
|
5002
|
+
let e = o == null ? `detune of ${i}` : o === 0 ? `${i} is not detuned` : `${i} is detuned by ${wr(o)}`;
|
|
4568
5003
|
s = $(["field", `${n.label}: ${n.rawValue || "(empty)"}`], ["meaning", e], ["meaning", a === !1 ? "(note is OFF)" : null]);
|
|
4569
5004
|
} else {
|
|
4570
5005
|
let e = a === !0 ? "is in" : a === !1 ? "is not in" : "is not yet set in";
|
|
4571
|
-
s = $(["field", `${n.label}: ${n.key === "en" ? n.rawValue.toUpperCase() || "(unset)" : i}`], ["meaning", i], ["meaning",
|
|
5006
|
+
s = $(["field", `${n.label}: ${n.key === "en" ? n.rawValue.toUpperCase() || "(unset)" : i}`], ["meaning", i], ["meaning", br(i)], ["meaning", `${e} the scale`]);
|
|
4572
5007
|
}
|
|
4573
5008
|
if (e.scale) {
|
|
4574
|
-
let [t, n] =
|
|
5009
|
+
let [t, n] = Cr(e.scale);
|
|
4575
5010
|
s.push(...$(["scale", `Scale: ${t}`], ["scale", n]));
|
|
4576
5011
|
}
|
|
4577
5012
|
return [...s, ...$(["location", `${t} row ${i}`])];
|
|
4578
5013
|
}
|
|
4579
|
-
function
|
|
5014
|
+
function li(e) {
|
|
4580
5015
|
let t = e.unit ?? "";
|
|
4581
5016
|
if (e.values && e.parts) {
|
|
4582
5017
|
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(", ");
|
|
4583
|
-
return `${r.join(":")} (${i})`;
|
|
5018
|
+
return `${r.join(e.rawValue.includes("/") ? "/" : ":")} (${i})`;
|
|
4584
5019
|
}
|
|
4585
5020
|
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}` : ""}`;
|
|
4586
5021
|
}
|
|
4587
|
-
function
|
|
5022
|
+
function ui(e) {
|
|
4588
5023
|
if (e.isEmpty) return "(empty)";
|
|
4589
5024
|
let t = e.type === "name" || e.type === "fileEntry" ? e : {
|
|
4590
5025
|
...e,
|
|
@@ -4611,26 +5046,26 @@ function Cr(e) {
|
|
|
4611
5046
|
case "fileEntry": return t.text ?? t.rawValue;
|
|
4612
5047
|
case "name": return `"${t.rawValue}"`;
|
|
4613
5048
|
case "option": return t.hexValue != null && t.text ? `${t.rawValue.slice(0, 2)} ${t.text}` : t.text ?? t.rawValue;
|
|
4614
|
-
case "parameter": return
|
|
4615
|
-
case "noteInterval": return `Interval ${
|
|
5049
|
+
case "parameter": return li(t);
|
|
5050
|
+
case "noteInterval": return `Interval ${yr(t.rawValue) ?? t.rawValue}`;
|
|
4616
5051
|
case "semitoneOffset": return t.isEmpty ? "(empty)" : `${t.rawValue} st`;
|
|
4617
5052
|
default: return t.rawValue;
|
|
4618
5053
|
}
|
|
4619
5054
|
}
|
|
4620
5055
|
//#endregion
|
|
4621
5056
|
//#region src/client.ts
|
|
4622
|
-
var
|
|
5057
|
+
var di = "yam8d-external-app", fi = 3e3, pi = (e) => {
|
|
4623
5058
|
let t = e.remoteHandle();
|
|
4624
5059
|
return {
|
|
4625
5060
|
call: (e, ...n) => t.call(e, ...n),
|
|
4626
5061
|
on: (e, n) => (t.addEventListener(e, n), () => t.removeEventListener(e, n)),
|
|
4627
5062
|
close: () => e.close()
|
|
4628
5063
|
};
|
|
4629
|
-
},
|
|
5064
|
+
}, mi = (e) => ({
|
|
4630
5065
|
call: (t, ...n) => e.methods[t](...n),
|
|
4631
5066
|
on: (t, n) => e.on(t, n),
|
|
4632
5067
|
close: () => e.dispose()
|
|
4633
|
-
}),
|
|
5068
|
+
}), hi = (e) => {
|
|
4634
5069
|
let t = [];
|
|
4635
5070
|
return {
|
|
4636
5071
|
messenger: {
|
|
@@ -4644,21 +5079,21 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4644
5079
|
for (let e of t.splice(0)) e();
|
|
4645
5080
|
}
|
|
4646
5081
|
};
|
|
4647
|
-
},
|
|
5082
|
+
}, gi = () => {
|
|
4648
5083
|
if (typeof window > "u") return !1;
|
|
4649
5084
|
try {
|
|
4650
5085
|
return window.parent !== window;
|
|
4651
5086
|
} catch {
|
|
4652
5087
|
return !0;
|
|
4653
5088
|
}
|
|
4654
|
-
},
|
|
5089
|
+
}, _i = (e) => e instanceof DOMException && (e.name === "NotFoundError" || e.name === "AbortError"), vi = () => {
|
|
4655
5090
|
let e = typeof navigator > "u" ? null : navigator;
|
|
4656
5091
|
return {
|
|
4657
5092
|
serial: !!e && "serial" in e,
|
|
4658
5093
|
usb: !!e && "usb" in e,
|
|
4659
5094
|
midi: !!e && "requestMIDIAccess" in e
|
|
4660
5095
|
};
|
|
4661
|
-
},
|
|
5096
|
+
}, yi = () => ({
|
|
4662
5097
|
viewName: null,
|
|
4663
5098
|
viewTitle: null,
|
|
4664
5099
|
minimapKey: null,
|
|
@@ -4675,9 +5110,9 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4675
5110
|
fontMode: null,
|
|
4676
5111
|
systemInfo: null,
|
|
4677
5112
|
macroRunning: !1
|
|
4678
|
-
}),
|
|
5113
|
+
}), bi = class {
|
|
4679
5114
|
constructor(e = {}) {
|
|
4680
|
-
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 =
|
|
5115
|
+
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 = yi(), this._screen = null, this.screenRefreshing = !1, this.screenDirty = !1, this.config = e;
|
|
4681
5116
|
}
|
|
4682
5117
|
get state() {
|
|
4683
5118
|
return this._state;
|
|
@@ -4699,11 +5134,11 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4699
5134
|
}
|
|
4700
5135
|
async resolveMode() {
|
|
4701
5136
|
let e = this.config.mode ?? "auto";
|
|
4702
|
-
if (e === "standalone" || e === "auto" && !
|
|
5137
|
+
if (e === "standalone" || e === "auto" && !gi()) {
|
|
4703
5138
|
await this.startStandalone();
|
|
4704
5139
|
return;
|
|
4705
5140
|
}
|
|
4706
|
-
let t = typeof window < "u" && window.name === "yam8d-external-app", n = e === "host" || t ? null : this.config.handshakeTimeoutMs ??
|
|
5141
|
+
let t = typeof window < "u" && window.name === "yam8d-external-app", n = e === "host" || t ? null : this.config.handshakeTimeoutMs ?? fi;
|
|
4707
5142
|
await this.handshake(n) || (this.config.debug && console.log("[M8 SDK] No yam8d host answered, switching to standalone mode"), await this.startStandalone());
|
|
4708
5143
|
}
|
|
4709
5144
|
async handshake(r) {
|
|
@@ -4715,10 +5150,10 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4715
5150
|
this.config.debug && (i = t(i, (e, ...t) => {
|
|
4716
5151
|
console.log("[M8 SDK]", e, ...t);
|
|
4717
5152
|
}));
|
|
4718
|
-
let a =
|
|
5153
|
+
let a = hi(i), o = e(a.messenger, { ping: async () => "pong" }), s, c = new Promise((e) => {
|
|
4719
5154
|
r !== null && (s = setTimeout(() => e(null), r));
|
|
4720
5155
|
}), l = await Promise.race([o, c]);
|
|
4721
|
-
return clearTimeout(s), l ? (this._mode = "host", await this.attach(
|
|
5156
|
+
return clearTimeout(s), l ? (this._mode = "host", await this.attach(pi(l)), l.localHandle().emit("ready", void 0), !0) : (a.dispose(), !1);
|
|
4722
5157
|
}
|
|
4723
5158
|
async startStandalone() {
|
|
4724
5159
|
if (this._mode = "standalone", this.config.autoConnect !== !1) try {
|
|
@@ -4728,7 +5163,7 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4728
5163
|
}
|
|
4729
5164
|
}
|
|
4730
5165
|
async openDevice(e, t) {
|
|
4731
|
-
let n =
|
|
5166
|
+
let n = ee();
|
|
4732
5167
|
if (!n.browserSupport) throw Error("This browser has no Web Serial, WebUSB or Web MIDI: the M8 cannot be reached from this page");
|
|
4733
5168
|
let r = e === "auto" && !t ? [
|
|
4734
5169
|
"serial",
|
|
@@ -4744,14 +5179,14 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4744
5179
|
});
|
|
4745
5180
|
break;
|
|
4746
5181
|
} catch (r) {
|
|
4747
|
-
if (a = r, t && !
|
|
5182
|
+
if (a = r, t && !_i(r) && (i = await te(() => n.connect({
|
|
4748
5183
|
transport: e,
|
|
4749
5184
|
prompt: !1
|
|
4750
5185
|
}), { maxAttempts: 2 }), i)) break;
|
|
4751
5186
|
}
|
|
4752
5187
|
if (!i) throw a;
|
|
4753
|
-
let o =
|
|
4754
|
-
this.device = i, await this.attach(
|
|
5188
|
+
let o = gn(i, { debug: this.config.debug });
|
|
5189
|
+
this.device = i, await this.attach(mi(o)), i.commands.resetScreen();
|
|
4755
5190
|
let s = i;
|
|
4756
5191
|
i.closed.then(() => {
|
|
4757
5192
|
this.device === s && (this.config.debug && console.log("[M8 SDK] Device connection ended"), this.device = null, this.detach());
|
|
@@ -4832,7 +5267,7 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4832
5267
|
await this.disconnectAudio(), await this.closeDevice();
|
|
4833
5268
|
}
|
|
4834
5269
|
async connectAudio() {
|
|
4835
|
-
await this.connect(), this._mode !== "host" && (this.audio ??=
|
|
5270
|
+
await this.connect(), this._mode !== "host" && (this.audio ??= P(), await this.audio.connect({ requireM8: !0 }));
|
|
4836
5271
|
}
|
|
4837
5272
|
async disconnectAudio() {
|
|
4838
5273
|
await this.audio?.disconnect();
|
|
@@ -4901,7 +5336,7 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4901
5336
|
}, e;
|
|
4902
5337
|
}
|
|
4903
5338
|
async captureSnapshot() {
|
|
4904
|
-
let [e, t] = await Promise.all([this.fetchState(), this.getScreen()]), n =
|
|
5339
|
+
let [e, t] = await Promise.all([this.fetchState(), this.getScreen()]), n = Zr(e, t);
|
|
4905
5340
|
return {
|
|
4906
5341
|
capturedAt: Date.now(),
|
|
4907
5342
|
state: {
|
|
@@ -4914,8 +5349,8 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4914
5349
|
deviceModel: e.deviceModel
|
|
4915
5350
|
},
|
|
4916
5351
|
semantic: n,
|
|
4917
|
-
description: n ?
|
|
4918
|
-
descriptionParts: n ?
|
|
5352
|
+
description: n ? $r(n) : null,
|
|
5353
|
+
descriptionParts: n ? Qr(n) : null,
|
|
4919
5354
|
screen: t
|
|
4920
5355
|
};
|
|
4921
5356
|
}
|
|
@@ -4944,23 +5379,23 @@ var wr = "yam8d-external-app", Tr = 3e3, Er = (e) => {
|
|
|
4944
5379
|
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();
|
|
4945
5380
|
}
|
|
4946
5381
|
getSemanticContext() {
|
|
4947
|
-
return
|
|
5382
|
+
return Zr(this._state, this._screen);
|
|
4948
5383
|
}
|
|
4949
5384
|
describeContext() {
|
|
4950
|
-
let e =
|
|
4951
|
-
return e ?
|
|
5385
|
+
let e = Zr(this._state, this._screen);
|
|
5386
|
+
return e ? $r(e) : null;
|
|
4952
5387
|
}
|
|
4953
5388
|
describeContextParts() {
|
|
4954
|
-
let e =
|
|
4955
|
-
return e ?
|
|
5389
|
+
let e = Zr(this._state, this._screen);
|
|
5390
|
+
return e ? Qr(e) : null;
|
|
4956
5391
|
}
|
|
4957
5392
|
};
|
|
4958
|
-
async function
|
|
4959
|
-
let t = new
|
|
5393
|
+
async function xi(e = {}) {
|
|
5394
|
+
let t = new bi(e);
|
|
4960
5395
|
return await t.connect(), t;
|
|
4961
5396
|
}
|
|
4962
|
-
function
|
|
4963
|
-
let t = new
|
|
5397
|
+
function Si(e = {}) {
|
|
5398
|
+
let t = new bi(e);
|
|
4964
5399
|
return {
|
|
4965
5400
|
client: t,
|
|
4966
5401
|
connect: async () => {
|
|
@@ -4968,10 +5403,10 @@ function Fr(e = {}) {
|
|
|
4968
5403
|
}
|
|
4969
5404
|
};
|
|
4970
5405
|
}
|
|
4971
|
-
var
|
|
4972
|
-
createM8Client:
|
|
4973
|
-
createM8ClientSync:
|
|
4974
|
-
},
|
|
5406
|
+
var Ci = {
|
|
5407
|
+
createM8Client: xi,
|
|
5408
|
+
createM8ClientSync: Si
|
|
5409
|
+
}, wi = [
|
|
4975
5410
|
{
|
|
4976
5411
|
role: "background",
|
|
4977
5412
|
label: "BACKGROUND"
|
|
@@ -5024,7 +5459,7 @@ var Ir = {
|
|
|
5024
5459
|
role: "meterPeak",
|
|
5025
5460
|
label: "METER PEAK"
|
|
5026
5461
|
}
|
|
5027
|
-
],
|
|
5462
|
+
], Ti = "themesettings", Ei = (e) => e.replace(/\s+/g, "").toUpperCase(), Di = (e) => Math.max(0, Math.min(255, Math.round(e))).toString(16).padStart(2, "0"), Oi = (e, t, n) => `#${Di(e)}${Di(t)}${Di(n)}`, ki = (e, t, n) => {
|
|
5028
5463
|
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] = [
|
|
5029
5464
|
[
|
|
5030
5465
|
a,
|
|
@@ -5057,14 +5492,14 @@ var Ir = {
|
|
|
5057
5492
|
l
|
|
5058
5493
|
]
|
|
5059
5494
|
][o];
|
|
5060
|
-
return
|
|
5061
|
-
},
|
|
5062
|
-
function
|
|
5495
|
+
return Oi(d * 255, f * 255, p * 255);
|
|
5496
|
+
}, Ai = new Map(wi.map(({ role: e, label: t }) => [Ei(t), e]));
|
|
5497
|
+
function ji(e) {
|
|
5063
5498
|
let t = "rgb", n = null, r = {};
|
|
5064
5499
|
for (let i of e.lines) {
|
|
5065
5500
|
let e = i.replace(/[{}]/g, "0"), a = /^\s*MODE\s+([A-Z])\s+([A-Z])\s+([A-Z])\b/i.exec(e);
|
|
5066
5501
|
if (a) {
|
|
5067
|
-
t =
|
|
5502
|
+
t = Ei(a.slice(1).join("")) === "HSV" ? "hsv" : "rgb";
|
|
5068
5503
|
continue;
|
|
5069
5504
|
}
|
|
5070
5505
|
let o = /^\s*THEME\s+NAME\s+(\S.*?)\s*$/i.exec(e);
|
|
@@ -5075,23 +5510,23 @@ function Wr(e) {
|
|
|
5075
5510
|
}
|
|
5076
5511
|
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);
|
|
5077
5512
|
if (!s) continue;
|
|
5078
|
-
let c =
|
|
5513
|
+
let c = Ai.get(Ei(s[1]));
|
|
5079
5514
|
if (!c || r[c]) continue;
|
|
5080
5515
|
let [l, u, d] = s.slice(2, 5).map((e) => parseInt(e, 16));
|
|
5081
|
-
r[c] = t === "hsv" ?
|
|
5516
|
+
r[c] = t === "hsv" ? ki(l, u, d) : Oi(l, u, d);
|
|
5082
5517
|
}
|
|
5083
|
-
return
|
|
5518
|
+
return wi.some(({ role: e }) => !r[e]) ? null : {
|
|
5084
5519
|
colors: r,
|
|
5085
5520
|
mode: t,
|
|
5086
5521
|
name: n
|
|
5087
5522
|
};
|
|
5088
5523
|
}
|
|
5089
|
-
var
|
|
5090
|
-
function
|
|
5091
|
-
let t = {}, n =
|
|
5524
|
+
var Mi = (e) => e ? Oi(e.r, e.g, e.b) : void 0;
|
|
5525
|
+
function Ni(e) {
|
|
5526
|
+
let t = {}, n = Mi(e.backgroundColor), r = Mi(e.titleColor);
|
|
5092
5527
|
return n && (t.background = n), r && (t.textTitles = r), t;
|
|
5093
5528
|
}
|
|
5094
5529
|
//#endregion
|
|
5095
|
-
export {
|
|
5530
|
+
export { R as M8KeyMask, A as M8_AUDIO_CAPTURE_CONSTRAINTS, wi as M8_THEME_ROLES, Ti as M8_THEME_VIEW, Jn as RELATIVE_FX_RULE, Rr as SCREEN_MARGIN, di as YAM8D_FRAME_NAME, P as createM8Audio, xi as createM8Client, Si as createM8ClientSync, ee as createM8Connection, gn as createM8Engine, Ci as default, $r as describeContext, Qr as describeContextParts, ui as formatFieldValue, vi as getDeviceSupport, qn as getInstrumentFxCommands, N as getM8InputStream, Zr as getSemanticContext, Kn as lookupFxCommand, Ni as observeThemeColors, Ur as parseGridScreen, ji as parseThemeScreen, kr as parseViewTitle, Fe as pressKeys, Gr as readPlayPositions };
|
|
5096
5531
|
|
|
5097
5532
|
//# sourceMappingURL=index.js.map
|