midiwire 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/midiwire.es.js +1030 -799
- package/dist/midiwire.umd.js +1 -1
- package/package.json +3 -2
- package/src/index.js +1 -1
- package/src/utils/dx7/DX7Bank.js +352 -0
- package/src/utils/dx7/DX7Bank.test.js +1069 -0
- package/src/utils/{dx7.js → dx7/DX7Voice.js} +405 -296
- package/src/utils/dx7/DX7Voice.test.js +1959 -0
- package/src/utils/dx7/index.js +7 -0
- package/src/utils/dx7/types.js +40 -0
- package/src/utils/midi.test.js +1 -1
- package/src/utils/validators.test.js +1 -1
- package/src/utils/dx7.test.js +0 -1989
package/dist/midiwire.es.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
class
|
|
1
|
+
class w {
|
|
2
2
|
/**
|
|
3
3
|
* @param {import("../core/MIDIController.js").MIDIController} controller
|
|
4
4
|
* @param {string} selector - CSS selector for elements to bind
|
|
5
5
|
*/
|
|
6
|
-
constructor(
|
|
7
|
-
this.controller =
|
|
6
|
+
constructor(E, s = "[data-midi-cc]") {
|
|
7
|
+
this.controller = E, this.selector = s, this.observer = null;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Bind all matching elements in the document
|
|
@@ -12,10 +12,10 @@ class B {
|
|
|
12
12
|
bindAll() {
|
|
13
13
|
document.querySelectorAll(
|
|
14
14
|
this.selector === "[data-midi-cc]" ? "[data-midi-cc], [data-midi-msb][data-midi-lsb]" : this.selector
|
|
15
|
-
).forEach((
|
|
16
|
-
if (
|
|
17
|
-
const
|
|
18
|
-
|
|
15
|
+
).forEach((s) => {
|
|
16
|
+
if (s.hasAttribute("data-midi-bound")) return;
|
|
17
|
+
const n = this._parseAttributes(s);
|
|
18
|
+
n && (this.controller.bind(s, n), s.setAttribute("data-midi-bound", "true"));
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
@@ -23,25 +23,25 @@ class B {
|
|
|
23
23
|
*/
|
|
24
24
|
enableAutoBinding() {
|
|
25
25
|
if (this.observer) return;
|
|
26
|
-
const
|
|
27
|
-
this.observer = new MutationObserver((
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const E = this.selector === "[data-midi-cc]" ? "[data-midi-cc], [data-midi-msb][data-midi-lsb]" : this.selector;
|
|
27
|
+
this.observer = new MutationObserver((s) => {
|
|
28
|
+
s.forEach((n) => {
|
|
29
|
+
n.addedNodes.forEach((_) => {
|
|
30
30
|
if (_.nodeType === Node.ELEMENT_NODE) {
|
|
31
|
-
if (_.matches?.(
|
|
32
|
-
const
|
|
33
|
-
|
|
31
|
+
if (_.matches?.(E)) {
|
|
32
|
+
const r = this._parseAttributes(_);
|
|
33
|
+
r && !_.hasAttribute("data-midi-bound") && (this.controller.bind(_, r), _.setAttribute("data-midi-bound", "true"));
|
|
34
34
|
}
|
|
35
|
-
_.querySelectorAll && _.querySelectorAll(
|
|
36
|
-
if (!
|
|
37
|
-
const
|
|
38
|
-
|
|
35
|
+
_.querySelectorAll && _.querySelectorAll(E).forEach((e) => {
|
|
36
|
+
if (!e.hasAttribute("data-midi-bound")) {
|
|
37
|
+
const a = this._parseAttributes(e);
|
|
38
|
+
a && (this.controller.bind(e, a), e.setAttribute("data-midi-bound", "true"));
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
}),
|
|
43
|
-
_.nodeType === Node.ELEMENT_NODE && (_.hasAttribute?.("data-midi-bound") && this.controller.unbind(_), _.querySelectorAll && _.querySelectorAll("[data-midi-bound]").forEach((
|
|
44
|
-
this.controller.unbind(
|
|
42
|
+
}), n.removedNodes.forEach((_) => {
|
|
43
|
+
_.nodeType === Node.ELEMENT_NODE && (_.hasAttribute?.("data-midi-bound") && this.controller.unbind(_), _.querySelectorAll && _.querySelectorAll("[data-midi-bound]").forEach((e) => {
|
|
44
|
+
this.controller.unbind(e);
|
|
45
45
|
}));
|
|
46
46
|
});
|
|
47
47
|
});
|
|
@@ -62,33 +62,33 @@ class B {
|
|
|
62
62
|
* @returns {Object|null}
|
|
63
63
|
* @private
|
|
64
64
|
*/
|
|
65
|
-
_parseAttributes(
|
|
66
|
-
const
|
|
67
|
-
if (!Number.isNaN(
|
|
68
|
-
const
|
|
69
|
-
return !Number.isNaN(
|
|
70
|
-
`Element has both 7-bit (data-midi-cc="${
|
|
71
|
-
|
|
65
|
+
_parseAttributes(E) {
|
|
66
|
+
const s = parseInt(E.dataset.midiMsb, 10), n = parseInt(E.dataset.midiLsb, 10);
|
|
67
|
+
if (!Number.isNaN(s) && !Number.isNaN(n) && s >= 0 && s <= 127 && n >= 0 && n <= 127) {
|
|
68
|
+
const r = parseInt(E.dataset.midiCc, 10);
|
|
69
|
+
return !Number.isNaN(r) && r >= 0 && r <= 127 && console.warn(
|
|
70
|
+
`Element has both 7-bit (data-midi-cc="${r}") and 14-bit (data-midi-msb="${s}" data-midi-lsb="${n}") CC attributes. 14-bit takes precedence.`,
|
|
71
|
+
E
|
|
72
72
|
), {
|
|
73
|
-
msb:
|
|
74
|
-
lsb:
|
|
73
|
+
msb: s,
|
|
74
|
+
lsb: n,
|
|
75
75
|
is14Bit: !0,
|
|
76
|
-
channel: parseInt(
|
|
77
|
-
min: parseFloat(
|
|
78
|
-
max: parseFloat(
|
|
79
|
-
invert:
|
|
80
|
-
label:
|
|
76
|
+
channel: parseInt(E.dataset.midiChannel, 10) || void 0,
|
|
77
|
+
min: parseFloat(E.getAttribute("min")) || 0,
|
|
78
|
+
max: parseFloat(E.getAttribute("max")) || 127,
|
|
79
|
+
invert: E.dataset.midiInvert === "true",
|
|
80
|
+
label: E.dataset.midiLabel
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
const _ = parseInt(
|
|
83
|
+
const _ = parseInt(E.dataset.midiCc, 10);
|
|
84
84
|
return !Number.isNaN(_) && _ >= 0 && _ <= 127 ? {
|
|
85
85
|
cc: _,
|
|
86
|
-
channel: parseInt(
|
|
87
|
-
min: parseFloat(
|
|
88
|
-
max: parseFloat(
|
|
89
|
-
invert:
|
|
90
|
-
label:
|
|
91
|
-
} : ((
|
|
86
|
+
channel: parseInt(E.dataset.midiChannel, 10) || void 0,
|
|
87
|
+
min: parseFloat(E.getAttribute("min")) || 0,
|
|
88
|
+
max: parseFloat(E.getAttribute("max")) || 127,
|
|
89
|
+
invert: E.dataset.midiInvert === "true",
|
|
90
|
+
label: E.dataset.midiLabel
|
|
91
|
+
} : ((E.dataset.midiCc !== void 0 || E.dataset.midiMsb !== void 0 && E.dataset.midiLsb !== void 0) && console.warn("Invalid MIDI configuration on element:", E), null);
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
94
|
* Clean up
|
|
@@ -97,59 +97,59 @@ class B {
|
|
|
97
97
|
this.disableAutoBinding();
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
class
|
|
101
|
-
constructor(
|
|
102
|
-
super(
|
|
100
|
+
class p extends Error {
|
|
101
|
+
constructor(E, s) {
|
|
102
|
+
super(E), this.name = "MIDIError", this.code = s, Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
class
|
|
106
|
-
constructor(
|
|
107
|
-
super(
|
|
105
|
+
class G extends p {
|
|
106
|
+
constructor(E, s) {
|
|
107
|
+
super(E, "MIDI_ACCESS_ERROR"), this.name = "MIDIAccessError", this.reason = s;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
class
|
|
111
|
-
constructor(
|
|
112
|
-
super(
|
|
110
|
+
class D extends p {
|
|
111
|
+
constructor(E) {
|
|
112
|
+
super(E, "MIDI_CONNECTION_ERROR"), this.name = "MIDIConnectionError";
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
class
|
|
116
|
-
constructor(
|
|
117
|
-
super(
|
|
115
|
+
class c extends p {
|
|
116
|
+
constructor(E, s, n) {
|
|
117
|
+
super(E, "MIDI_DEVICE_ERROR"), this.name = "MIDIDeviceError", this.deviceType = s, this.deviceId = n;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
-
class R extends
|
|
121
|
-
constructor(
|
|
122
|
-
super(
|
|
120
|
+
class R extends p {
|
|
121
|
+
constructor(E, s) {
|
|
122
|
+
super(E, "MIDI_VALIDATION_ERROR"), this.name = "MIDIValidationError", this.validationType = s;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
class H extends Error {
|
|
126
|
-
constructor(
|
|
127
|
-
super(
|
|
126
|
+
constructor(E, s) {
|
|
127
|
+
super(E), this.name = "DX7Error", this.code = s, Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
class
|
|
131
|
-
constructor(
|
|
132
|
-
super(
|
|
130
|
+
class m extends H {
|
|
131
|
+
constructor(E, s, n) {
|
|
132
|
+
super(E, "DX7_PARSE_ERROR"), this.name = "DX7ParseError", this.parseType = s, this.offset = n;
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
class
|
|
136
|
-
constructor(
|
|
137
|
-
super(
|
|
135
|
+
class o extends H {
|
|
136
|
+
constructor(E, s, n) {
|
|
137
|
+
super(E, "DX7_VALIDATION_ERROR"), this.name = "DX7ValidationError", this.validationType = s, this.value = n;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
function
|
|
141
|
-
return Math.max(
|
|
140
|
+
function h(A, E, s) {
|
|
141
|
+
return Math.max(E, Math.min(s, A));
|
|
142
142
|
}
|
|
143
|
-
function
|
|
144
|
-
const _ = (
|
|
145
|
-
return
|
|
143
|
+
function B(A, E, s, n = !1) {
|
|
144
|
+
const _ = (A - E) / (s - E), e = (n ? 1 - _ : _) * 127;
|
|
145
|
+
return h(Math.round(e), 0, 127);
|
|
146
146
|
}
|
|
147
|
-
function Q(
|
|
148
|
-
let _ =
|
|
149
|
-
return
|
|
147
|
+
function Q(A, E, s, n = !1) {
|
|
148
|
+
let _ = h(A, 0, 127) / 127;
|
|
149
|
+
return n && (_ = 1 - _), E + _ * (s - E);
|
|
150
150
|
}
|
|
151
|
-
function
|
|
152
|
-
const
|
|
151
|
+
function J(A) {
|
|
152
|
+
const E = {
|
|
153
153
|
C: 0,
|
|
154
154
|
"C#": 1,
|
|
155
155
|
DB: 1,
|
|
@@ -167,27 +167,27 @@ function j(i) {
|
|
|
167
167
|
"A#": 10,
|
|
168
168
|
BB: 10,
|
|
169
169
|
B: 11
|
|
170
|
-
},
|
|
171
|
-
if (!
|
|
172
|
-
throw new R(`Invalid note name: ${
|
|
173
|
-
const [,
|
|
174
|
-
if (
|
|
175
|
-
throw new R(`Invalid note: ${
|
|
176
|
-
const
|
|
177
|
-
return
|
|
170
|
+
}, s = A.match(/^([A-G][#b]?)(-?\d+)$/i);
|
|
171
|
+
if (!s)
|
|
172
|
+
throw new R(`Invalid note name: ${A}`, "note", A);
|
|
173
|
+
const [, n, _] = s, r = E[n.toUpperCase()];
|
|
174
|
+
if (r === void 0)
|
|
175
|
+
throw new R(`Invalid note: ${n}`, "note", n);
|
|
176
|
+
const e = (parseInt(_, 10) + 1) * 12 + r;
|
|
177
|
+
return h(e, 0, 127);
|
|
178
178
|
}
|
|
179
|
-
function
|
|
180
|
-
const _ =
|
|
181
|
-
return `${_[
|
|
179
|
+
function j(A, E = !1) {
|
|
180
|
+
const _ = E ? ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"] : ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"], r = Math.floor(A / 12) - 1;
|
|
181
|
+
return `${_[A % 12]}${r}`;
|
|
182
182
|
}
|
|
183
|
-
function k(
|
|
184
|
-
const
|
|
185
|
-
return
|
|
183
|
+
function k(A) {
|
|
184
|
+
const E = 69 + 12 * Math.log2(A / 440);
|
|
185
|
+
return h(Math.round(E), 0, 127);
|
|
186
186
|
}
|
|
187
|
-
function V(
|
|
188
|
-
return 440 * 2 ** ((
|
|
187
|
+
function V(A) {
|
|
188
|
+
return 440 * 2 ** ((A - 69) / 12);
|
|
189
189
|
}
|
|
190
|
-
function X(
|
|
190
|
+
function X(A) {
|
|
191
191
|
return {
|
|
192
192
|
0: "Bank Select",
|
|
193
193
|
1: "Modulation",
|
|
@@ -220,27 +220,27 @@ function X(i) {
|
|
|
220
220
|
120: "All Sound Off",
|
|
221
221
|
121: "Reset All Controllers",
|
|
222
222
|
123: "All Notes Off"
|
|
223
|
-
}[
|
|
223
|
+
}[A] || `CC ${A}`;
|
|
224
224
|
}
|
|
225
|
-
function x(
|
|
226
|
-
const
|
|
225
|
+
function x(A) {
|
|
226
|
+
const E = h(Math.round(A), 0, 16383);
|
|
227
227
|
return {
|
|
228
|
-
msb:
|
|
229
|
-
lsb:
|
|
228
|
+
msb: E >> 7 & 127,
|
|
229
|
+
lsb: E & 127
|
|
230
230
|
};
|
|
231
231
|
}
|
|
232
|
-
function Y(
|
|
233
|
-
return
|
|
232
|
+
function Y(A, E) {
|
|
233
|
+
return h(A, 0, 127) << 7 | h(E, 0, 127);
|
|
234
234
|
}
|
|
235
|
-
function
|
|
236
|
-
const _ = (
|
|
237
|
-
return x(
|
|
235
|
+
function $(A, E, s, n = !1) {
|
|
236
|
+
const _ = (A - E) / (s - E), e = (n ? 1 - _ : _) * 16383;
|
|
237
|
+
return x(e);
|
|
238
238
|
}
|
|
239
|
-
function tt(
|
|
240
|
-
let
|
|
241
|
-
return _ && (
|
|
239
|
+
function tt(A, E, s, n, _ = !1) {
|
|
240
|
+
let e = Y(A, E) / 16383;
|
|
241
|
+
return _ && (e = 1 - e), s + e * (n - s);
|
|
242
242
|
}
|
|
243
|
-
class
|
|
243
|
+
class v {
|
|
244
244
|
constructor() {
|
|
245
245
|
this.events = /* @__PURE__ */ new Map();
|
|
246
246
|
}
|
|
@@ -250,42 +250,42 @@ class b {
|
|
|
250
250
|
* @param {Function} handler - Event handler function
|
|
251
251
|
* @returns {Function} Unsubscribe function
|
|
252
252
|
*/
|
|
253
|
-
on(
|
|
254
|
-
return this.events.has(
|
|
253
|
+
on(E, s) {
|
|
254
|
+
return this.events.has(E) || this.events.set(E, []), this.events.get(E).push(s), () => this.off(E, s);
|
|
255
255
|
}
|
|
256
256
|
/**
|
|
257
257
|
* Register a one-time event listener
|
|
258
258
|
* @param {string} event - Event name
|
|
259
259
|
* @param {Function} handler - Event handler function
|
|
260
260
|
*/
|
|
261
|
-
once(
|
|
262
|
-
const
|
|
263
|
-
|
|
261
|
+
once(E, s) {
|
|
262
|
+
const n = (..._) => {
|
|
263
|
+
s(..._), this.off(E, n);
|
|
264
264
|
};
|
|
265
|
-
this.on(
|
|
265
|
+
this.on(E, n);
|
|
266
266
|
}
|
|
267
267
|
/**
|
|
268
268
|
* Remove an event listener
|
|
269
269
|
* @param {string} event - Event name
|
|
270
270
|
* @param {Function} handler - Event handler function
|
|
271
271
|
*/
|
|
272
|
-
off(
|
|
273
|
-
if (!this.events.has(
|
|
274
|
-
const
|
|
275
|
-
_ > -1 &&
|
|
272
|
+
off(E, s) {
|
|
273
|
+
if (!this.events.has(E)) return;
|
|
274
|
+
const n = this.events.get(E), _ = n.indexOf(s);
|
|
275
|
+
_ > -1 && n.splice(_, 1), n.length === 0 && this.events.delete(E);
|
|
276
276
|
}
|
|
277
277
|
/**
|
|
278
278
|
* Emit an event
|
|
279
279
|
* @param {string} event - Event name
|
|
280
280
|
* @param {*} data - Event data
|
|
281
281
|
*/
|
|
282
|
-
emit(
|
|
283
|
-
if (!this.events.has(
|
|
284
|
-
[...this.events.get(
|
|
282
|
+
emit(E, s) {
|
|
283
|
+
if (!this.events.has(E)) return;
|
|
284
|
+
[...this.events.get(E)].forEach((_) => {
|
|
285
285
|
try {
|
|
286
|
-
_(
|
|
287
|
-
} catch (
|
|
288
|
-
console.error(`Error in event handler for "${
|
|
286
|
+
_(s);
|
|
287
|
+
} catch (r) {
|
|
288
|
+
console.error(`Error in event handler for "${E}":`, r);
|
|
289
289
|
}
|
|
290
290
|
});
|
|
291
291
|
}
|
|
@@ -293,26 +293,26 @@ class b {
|
|
|
293
293
|
* Remove all event listeners
|
|
294
294
|
* @param {string} [event] - Optional event name to clear specific event
|
|
295
295
|
*/
|
|
296
|
-
removeAllListeners(
|
|
297
|
-
|
|
296
|
+
removeAllListeners(E) {
|
|
297
|
+
E ? this.events.delete(E) : this.events.clear();
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
-
const
|
|
300
|
+
const f = {
|
|
301
301
|
DEVICE_CHANGE: "device-change",
|
|
302
302
|
INPUT_DEVICE_CONNECTED: "input-device-connected",
|
|
303
303
|
INPUT_DEVICE_DISCONNECTED: "input-device-disconnected",
|
|
304
304
|
OUTPUT_DEVICE_CONNECTED: "output-device-connected",
|
|
305
305
|
OUTPUT_DEVICE_DISCONNECTED: "output-device-disconnected"
|
|
306
306
|
};
|
|
307
|
-
class
|
|
307
|
+
class Z extends v {
|
|
308
308
|
/**
|
|
309
309
|
* @param {Object} options
|
|
310
310
|
* @param {boolean} [options.sysex=false] - Request SysEx access
|
|
311
311
|
*/
|
|
312
|
-
constructor(
|
|
312
|
+
constructor(E = {}) {
|
|
313
313
|
super(), this.options = {
|
|
314
314
|
sysex: !1,
|
|
315
|
-
...
|
|
315
|
+
...E
|
|
316
316
|
}, this.midiAccess = null, this.output = null, this.input = null;
|
|
317
317
|
}
|
|
318
318
|
/**
|
|
@@ -322,25 +322,25 @@ class $ extends b {
|
|
|
322
322
|
*/
|
|
323
323
|
async requestAccess() {
|
|
324
324
|
if (!navigator.requestMIDIAccess)
|
|
325
|
-
throw new
|
|
325
|
+
throw new G("Web MIDI API is not supported in this browser", "unsupported");
|
|
326
326
|
try {
|
|
327
327
|
this.midiAccess = await navigator.requestMIDIAccess({
|
|
328
328
|
sysex: this.options.sysex
|
|
329
|
-
}), this.midiAccess.onstatechange = (
|
|
330
|
-
const
|
|
331
|
-
this.emit(
|
|
332
|
-
port:
|
|
333
|
-
state:
|
|
334
|
-
type:
|
|
329
|
+
}), this.midiAccess.onstatechange = (E) => {
|
|
330
|
+
const s = E.port, n = E.port.state;
|
|
331
|
+
this.emit(f.DEVICE_CHANGE, {
|
|
332
|
+
port: s,
|
|
333
|
+
state: n,
|
|
334
|
+
type: s.type,
|
|
335
335
|
device: {
|
|
336
|
-
id:
|
|
337
|
-
name:
|
|
338
|
-
manufacturer:
|
|
336
|
+
id: s.id,
|
|
337
|
+
name: s.name,
|
|
338
|
+
manufacturer: s.manufacturer || "Unknown"
|
|
339
339
|
}
|
|
340
|
-
}),
|
|
340
|
+
}), n === "disconnected" ? s.type === "input" ? (this.emit(f.INPUT_DEVICE_DISCONNECTED, { device: s }), this.input && this.input.id === s.id && (this.input = null)) : s.type === "output" && (this.emit(f.OUTPUT_DEVICE_DISCONNECTED, { device: s }), this.output && this.output.id === s.id && (this.output = null)) : n === "connected" && (s.type === "input" ? this.emit(f.INPUT_DEVICE_CONNECTED, { device: s }) : s.type === "output" && this.emit(f.OUTPUT_DEVICE_CONNECTED, { device: s }));
|
|
341
341
|
};
|
|
342
|
-
} catch (
|
|
343
|
-
throw
|
|
342
|
+
} catch (E) {
|
|
343
|
+
throw E.name === "SecurityError" ? new G("MIDI access denied. SysEx requires user permission.", "denied") : new G(`Failed to get MIDI access: ${E.message}`, "failed");
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
/**
|
|
@@ -349,14 +349,14 @@ class $ extends b {
|
|
|
349
349
|
*/
|
|
350
350
|
getOutputs() {
|
|
351
351
|
if (!this.midiAccess) return [];
|
|
352
|
-
const
|
|
353
|
-
return this.midiAccess.outputs.forEach((
|
|
354
|
-
|
|
355
|
-
id:
|
|
356
|
-
name:
|
|
357
|
-
manufacturer:
|
|
352
|
+
const E = [];
|
|
353
|
+
return this.midiAccess.outputs.forEach((s) => {
|
|
354
|
+
s.state === "connected" && E.push({
|
|
355
|
+
id: s.id,
|
|
356
|
+
name: s.name,
|
|
357
|
+
manufacturer: s.manufacturer || "Unknown"
|
|
358
358
|
});
|
|
359
|
-
}),
|
|
359
|
+
}), E;
|
|
360
360
|
}
|
|
361
361
|
/**
|
|
362
362
|
* Get all available MIDI inputs
|
|
@@ -364,14 +364,14 @@ class $ extends b {
|
|
|
364
364
|
*/
|
|
365
365
|
getInputs() {
|
|
366
366
|
if (!this.midiAccess) return [];
|
|
367
|
-
const
|
|
368
|
-
return this.midiAccess.inputs.forEach((
|
|
369
|
-
|
|
370
|
-
id:
|
|
371
|
-
name:
|
|
372
|
-
manufacturer:
|
|
367
|
+
const E = [];
|
|
368
|
+
return this.midiAccess.inputs.forEach((s) => {
|
|
369
|
+
s.state === "connected" && E.push({
|
|
370
|
+
id: s.id,
|
|
371
|
+
name: s.name,
|
|
372
|
+
manufacturer: s.manufacturer || "Unknown"
|
|
373
373
|
});
|
|
374
|
-
}),
|
|
374
|
+
}), E;
|
|
375
375
|
}
|
|
376
376
|
/**
|
|
377
377
|
* Connect to a MIDI output device
|
|
@@ -380,25 +380,25 @@ class $ extends b {
|
|
|
380
380
|
* @throws {MIDIConnectionError} If MIDI access not initialized
|
|
381
381
|
* @throws {MIDIDeviceError} If device not found or index out of range
|
|
382
382
|
*/
|
|
383
|
-
async connect(
|
|
383
|
+
async connect(E) {
|
|
384
384
|
if (!this.midiAccess)
|
|
385
|
-
throw new
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
throw new
|
|
389
|
-
if (
|
|
390
|
-
this.output =
|
|
385
|
+
throw new D("MIDI access not initialized. Call requestAccess() first.");
|
|
386
|
+
const s = Array.from(this.midiAccess.outputs.values());
|
|
387
|
+
if (s.length === 0)
|
|
388
|
+
throw new c("No MIDI output devices available", "output");
|
|
389
|
+
if (E === void 0) {
|
|
390
|
+
this.output = s[0];
|
|
391
391
|
return;
|
|
392
392
|
}
|
|
393
|
-
if (typeof
|
|
394
|
-
if (
|
|
395
|
-
throw new
|
|
396
|
-
this.output =
|
|
393
|
+
if (typeof E == "number") {
|
|
394
|
+
if (E < 0 || E >= s.length)
|
|
395
|
+
throw new c(`Output index ${E} out of range (0-${s.length - 1})`, "output", E);
|
|
396
|
+
this.output = s[E];
|
|
397
397
|
return;
|
|
398
398
|
}
|
|
399
|
-
if (this.output =
|
|
400
|
-
const
|
|
401
|
-
throw new
|
|
399
|
+
if (this.output = s.find((n) => n.name === E || n.id === E), !this.output) {
|
|
400
|
+
const n = s.map((_) => _.name).join(", ");
|
|
401
|
+
throw new c(`MIDI output "${E}" not found. Available: ${n}`, "output", E);
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
404
|
/**
|
|
@@ -410,26 +410,26 @@ class $ extends b {
|
|
|
410
410
|
* @throws {MIDIValidationError} If onMessage is not a function
|
|
411
411
|
* @throws {MIDIDeviceError} If device not found or index out of range
|
|
412
412
|
*/
|
|
413
|
-
async connectInput(
|
|
413
|
+
async connectInput(E, s) {
|
|
414
414
|
if (!this.midiAccess)
|
|
415
|
-
throw new
|
|
416
|
-
if (typeof
|
|
415
|
+
throw new D("MIDI access not initialized. Call requestAccess() first.");
|
|
416
|
+
if (typeof s != "function")
|
|
417
417
|
throw new R("onMessage callback must be a function", "callback");
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
420
|
-
throw new
|
|
421
|
-
if (this.input && (this.input.onmidimessage = null),
|
|
422
|
-
this.input =
|
|
423
|
-
else if (typeof
|
|
424
|
-
if (
|
|
425
|
-
throw new
|
|
426
|
-
this.input =
|
|
427
|
-
} else if (this.input =
|
|
428
|
-
const _ =
|
|
429
|
-
throw new
|
|
418
|
+
const n = Array.from(this.midiAccess.inputs.values());
|
|
419
|
+
if (n.length === 0)
|
|
420
|
+
throw new c("No MIDI input devices available", "input");
|
|
421
|
+
if (this.input && (this.input.onmidimessage = null), E === void 0)
|
|
422
|
+
this.input = n[0];
|
|
423
|
+
else if (typeof E == "number") {
|
|
424
|
+
if (E < 0 || E >= n.length)
|
|
425
|
+
throw new c(`Input index ${E} out of range (0-${n.length - 1})`, "input", E);
|
|
426
|
+
this.input = n[E];
|
|
427
|
+
} else if (this.input = n.find((_) => _.name === E || _.id === E), !this.input) {
|
|
428
|
+
const _ = n.map((r) => r.name).join(", ");
|
|
429
|
+
throw new c(`MIDI input "${E}" not found. Available: ${_}`, "input", E);
|
|
430
430
|
}
|
|
431
431
|
this.input.onmidimessage = (_) => {
|
|
432
|
-
|
|
432
|
+
s(_);
|
|
433
433
|
};
|
|
434
434
|
}
|
|
435
435
|
/**
|
|
@@ -437,16 +437,16 @@ class $ extends b {
|
|
|
437
437
|
* @param {Uint8Array|Array<number>} message - MIDI message bytes
|
|
438
438
|
* @param {number} [timestamp=performance.now()] - Optional timestamp
|
|
439
439
|
*/
|
|
440
|
-
send(
|
|
440
|
+
send(E, s = null) {
|
|
441
441
|
if (!this.output) {
|
|
442
442
|
console.warn("No MIDI output connected. Call connect() first.");
|
|
443
443
|
return;
|
|
444
444
|
}
|
|
445
445
|
try {
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
} catch (
|
|
449
|
-
console.error("Failed to send MIDI message:",
|
|
446
|
+
const n = new Uint8Array(E);
|
|
447
|
+
s === null ? this.output.send(n) : this.output.send(n, s);
|
|
448
|
+
} catch (n) {
|
|
449
|
+
console.error("Failed to send MIDI message:", n);
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
/**
|
|
@@ -454,13 +454,13 @@ class $ extends b {
|
|
|
454
454
|
* @param {Array<number>} data - SysEx data bytes (without F0/F7 wrapper)
|
|
455
455
|
* @param {boolean} [includeWrapper=false] - If true, data already includes F0/F7
|
|
456
456
|
*/
|
|
457
|
-
sendSysEx(
|
|
457
|
+
sendSysEx(E, s = !1) {
|
|
458
458
|
if (!this.options.sysex) {
|
|
459
459
|
console.warn("SysEx not enabled. Initialize with sysex: true");
|
|
460
460
|
return;
|
|
461
461
|
}
|
|
462
|
-
let
|
|
463
|
-
|
|
462
|
+
let n;
|
|
463
|
+
s ? n = [240, ...E, 247] : n = E, this.send(n);
|
|
464
464
|
}
|
|
465
465
|
/**
|
|
466
466
|
* Disconnect from current output and input
|
|
@@ -498,7 +498,7 @@ class $ extends b {
|
|
|
498
498
|
} : null;
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
|
-
const
|
|
501
|
+
const u = {
|
|
502
502
|
READY: "ready",
|
|
503
503
|
ERROR: "error",
|
|
504
504
|
CC_SEND: "cc-send",
|
|
@@ -517,7 +517,7 @@ const O = {
|
|
|
517
517
|
PATCH_LOADED: "patch-loaded",
|
|
518
518
|
PATCH_DELETED: "patch-deleted"
|
|
519
519
|
};
|
|
520
|
-
class z extends
|
|
520
|
+
class z extends v {
|
|
521
521
|
/**
|
|
522
522
|
* @param {Object} options
|
|
523
523
|
* @param {number} [options.channel=1] - Default MIDI channel (1-16)
|
|
@@ -528,12 +528,12 @@ class z extends b {
|
|
|
528
528
|
* @param {Function} [options.onReady] - Callback when MIDI is ready
|
|
529
529
|
* @param {Function} [options.onError] - Error handler
|
|
530
530
|
*/
|
|
531
|
-
constructor(
|
|
531
|
+
constructor(E = {}) {
|
|
532
532
|
super(), this.options = {
|
|
533
533
|
channel: 1,
|
|
534
534
|
autoConnect: !0,
|
|
535
535
|
sysex: !1,
|
|
536
|
-
...
|
|
536
|
+
...E
|
|
537
537
|
}, this.connection = null, this.bindings = /* @__PURE__ */ new Map(), this.state = /* @__PURE__ */ new Map(), this.initialized = !1;
|
|
538
538
|
}
|
|
539
539
|
/**
|
|
@@ -546,11 +546,11 @@ class z extends b {
|
|
|
546
546
|
return;
|
|
547
547
|
}
|
|
548
548
|
try {
|
|
549
|
-
this.connection = new
|
|
549
|
+
this.connection = new Z({
|
|
550
550
|
sysex: this.options.sysex
|
|
551
|
-
}), await this.connection.requestAccess(), this.options.autoConnect && await this.connection.connect(this.options.output), this.options.input !== void 0 && await this.connectInput(this.options.input), this.initialized = !0, this.emit(
|
|
552
|
-
} catch (
|
|
553
|
-
throw this.emit(
|
|
551
|
+
}), await this.connection.requestAccess(), this.options.autoConnect && await this.connection.connect(this.options.output), this.options.input !== void 0 && await this.connectInput(this.options.input), this.initialized = !0, this.emit(u.READY, this), this.options.onReady?.(this);
|
|
552
|
+
} catch (E) {
|
|
553
|
+
throw this.emit(u.ERROR, E), this.options.onError?.(E), E;
|
|
554
554
|
}
|
|
555
555
|
}
|
|
556
556
|
/**
|
|
@@ -558,10 +558,10 @@ class z extends b {
|
|
|
558
558
|
* @param {string|number} device - Device name, ID, or index
|
|
559
559
|
* @returns {Promise<void>}
|
|
560
560
|
*/
|
|
561
|
-
async connectInput(
|
|
562
|
-
await this.connection.connectInput(
|
|
563
|
-
this._handleMIDIMessage(
|
|
564
|
-
}), this.emit(
|
|
561
|
+
async connectInput(E) {
|
|
562
|
+
await this.connection.connectInput(E, (s) => {
|
|
563
|
+
this._handleMIDIMessage(s);
|
|
564
|
+
}), this.emit(u.INPUT_CONNECTED, this.connection.getCurrentInput());
|
|
565
565
|
}
|
|
566
566
|
/**
|
|
567
567
|
* Send a control change message
|
|
@@ -569,16 +569,16 @@ class z extends b {
|
|
|
569
569
|
* @param {number} value - CC value (0-127)
|
|
570
570
|
* @param {number} [channel] - MIDI channel (defaults to controller channel)
|
|
571
571
|
*/
|
|
572
|
-
sendCC(
|
|
572
|
+
sendCC(E, s, n = this.options.channel) {
|
|
573
573
|
if (!this.initialized) {
|
|
574
574
|
console.warn("MIDI not initialized. Call initialize() first.");
|
|
575
575
|
return;
|
|
576
576
|
}
|
|
577
|
-
|
|
578
|
-
const _ = 176 + (
|
|
579
|
-
this.connection.send([_,
|
|
580
|
-
const
|
|
581
|
-
this.state.set(
|
|
577
|
+
E = h(Math.round(E), 0, 127), s = h(Math.round(s), 0, 127), n = h(Math.round(n), 1, 16);
|
|
578
|
+
const _ = 176 + (n - 1);
|
|
579
|
+
this.connection.send([_, E, s]);
|
|
580
|
+
const r = `${n}:${E}`;
|
|
581
|
+
this.state.set(r, s), this.emit(u.CC_SEND, { cc: E, value: s, channel: n });
|
|
582
582
|
}
|
|
583
583
|
/**
|
|
584
584
|
* Send a SysEx message
|
|
@@ -589,7 +589,7 @@ class z extends b {
|
|
|
589
589
|
* // Send with wrapper included
|
|
590
590
|
* midi.sendSysEx([0xF0, 0x42, 0x30, 0x00, 0x01, 0x2F, 0x12, 0xF7], true)
|
|
591
591
|
*/
|
|
592
|
-
sendSysEx(
|
|
592
|
+
sendSysEx(E, s = !1) {
|
|
593
593
|
if (!this.initialized) {
|
|
594
594
|
console.warn("MIDI not initialized. Call initialize() first.");
|
|
595
595
|
return;
|
|
@@ -598,7 +598,7 @@ class z extends b {
|
|
|
598
598
|
console.warn("SysEx not enabled. Initialize with sysex: true");
|
|
599
599
|
return;
|
|
600
600
|
}
|
|
601
|
-
this.connection.sendSysEx(
|
|
601
|
+
this.connection.sendSysEx(E, s), this.emit(u.SYSEX_SEND, { data: E, includeWrapper: s });
|
|
602
602
|
}
|
|
603
603
|
/**
|
|
604
604
|
* Send a note on message
|
|
@@ -606,11 +606,11 @@ class z extends b {
|
|
|
606
606
|
* @param {number} [velocity=64] - Note velocity (0-127)
|
|
607
607
|
* @param {number} [channel] - MIDI channel
|
|
608
608
|
*/
|
|
609
|
-
sendNoteOn(
|
|
609
|
+
sendNoteOn(E, s = 64, n = this.options.channel) {
|
|
610
610
|
if (!this.initialized) return;
|
|
611
|
-
|
|
612
|
-
const _ = 144 + (
|
|
613
|
-
this.connection.send([_,
|
|
611
|
+
E = h(Math.round(E), 0, 127), s = h(Math.round(s), 0, 127), n = h(Math.round(n), 1, 16);
|
|
612
|
+
const _ = 144 + (n - 1);
|
|
613
|
+
this.connection.send([_, E, s]), this.emit(u.NOTE_ON_SEND, { note: E, velocity: s, channel: n });
|
|
614
614
|
}
|
|
615
615
|
/**
|
|
616
616
|
* Send a note off message
|
|
@@ -618,11 +618,11 @@ class z extends b {
|
|
|
618
618
|
* @param {number} [channel] - MIDI channel
|
|
619
619
|
* @param {number} [velocity=0] - Release velocity (0-127)
|
|
620
620
|
*/
|
|
621
|
-
sendNoteOff(
|
|
621
|
+
sendNoteOff(E, s = this.options.channel, n = 0) {
|
|
622
622
|
if (!this.initialized) return;
|
|
623
|
-
|
|
624
|
-
const _ = 144 + (
|
|
625
|
-
this.connection.send([_,
|
|
623
|
+
E = h(Math.round(E), 0, 127), n = h(Math.round(n), 0, 127), s = h(Math.round(s), 1, 16);
|
|
624
|
+
const _ = 144 + (s - 1);
|
|
625
|
+
this.connection.send([_, E, n]), this.emit(u.NOTE_OFF_SEND, { note: E, channel: s, velocity: n });
|
|
626
626
|
}
|
|
627
627
|
/**
|
|
628
628
|
* Bind a control programmatically
|
|
@@ -638,20 +638,20 @@ class z extends b {
|
|
|
638
638
|
* @param {number} [options.debounce=0] - Debounce delay in ms for high-frequency updates
|
|
639
639
|
* @returns {Function} Unbind function
|
|
640
640
|
*/
|
|
641
|
-
bind(
|
|
642
|
-
if (!
|
|
641
|
+
bind(E, s, n = {}) {
|
|
642
|
+
if (!E)
|
|
643
643
|
return console.warn("Cannot bind: element is null or undefined"), () => {
|
|
644
644
|
};
|
|
645
|
-
const _ = this._createBinding(
|
|
646
|
-
return this.bindings.set(
|
|
645
|
+
const _ = this._createBinding(E, s, n);
|
|
646
|
+
return this.bindings.set(E, _), this.initialized && this.connection?.isConnected() && _.handler({ target: E }), () => this.unbind(E);
|
|
647
647
|
}
|
|
648
648
|
/**
|
|
649
649
|
* Unbind a control
|
|
650
650
|
* @param {HTMLElement} element
|
|
651
651
|
*/
|
|
652
|
-
unbind(
|
|
653
|
-
const
|
|
654
|
-
|
|
652
|
+
unbind(E) {
|
|
653
|
+
const s = this.bindings.get(E);
|
|
654
|
+
s && (s.destroy(), this.bindings.delete(E));
|
|
655
655
|
}
|
|
656
656
|
/**
|
|
657
657
|
* Get current value of a CC
|
|
@@ -659,9 +659,9 @@ class z extends b {
|
|
|
659
659
|
* @param {number} [channel] - MIDI channel
|
|
660
660
|
* @returns {number|undefined}
|
|
661
661
|
*/
|
|
662
|
-
getCC(
|
|
663
|
-
const
|
|
664
|
-
return this.state.get(
|
|
662
|
+
getCC(E, s = this.options.channel) {
|
|
663
|
+
const n = `${s}:${E}`;
|
|
664
|
+
return this.state.get(n);
|
|
665
665
|
}
|
|
666
666
|
/**
|
|
667
667
|
* Get all available MIDI outputs
|
|
@@ -682,8 +682,8 @@ class z extends b {
|
|
|
682
682
|
* @param {string|number} output - Device name, ID, or index
|
|
683
683
|
* @returns {Promise<void>}
|
|
684
684
|
*/
|
|
685
|
-
async setOutput(
|
|
686
|
-
await this.connection.connect(
|
|
685
|
+
async setOutput(E) {
|
|
686
|
+
await this.connection.connect(E), this.emit(u.OUTPUT_CHANGED, this.connection.getCurrentOutput());
|
|
687
687
|
}
|
|
688
688
|
/**
|
|
689
689
|
* Get current output device
|
|
@@ -703,112 +703,112 @@ class z extends b {
|
|
|
703
703
|
* Clean up resources
|
|
704
704
|
*/
|
|
705
705
|
destroy() {
|
|
706
|
-
for (const
|
|
707
|
-
|
|
708
|
-
this.bindings.clear(), this.state.clear(), this.connection?.disconnect(), this.initialized = !1, this.emit(
|
|
706
|
+
for (const E of this.bindings.values())
|
|
707
|
+
E.destroy();
|
|
708
|
+
this.bindings.clear(), this.state.clear(), this.connection?.disconnect(), this.initialized = !1, this.emit(u.DESTROYED), this.removeAllListeners();
|
|
709
709
|
}
|
|
710
710
|
/**
|
|
711
711
|
* Handle incoming MIDI messages
|
|
712
712
|
* @private
|
|
713
713
|
*/
|
|
714
|
-
_handleMIDIMessage(
|
|
715
|
-
const [
|
|
716
|
-
if (
|
|
717
|
-
this.emit(
|
|
718
|
-
data: Array.from(
|
|
719
|
-
timestamp:
|
|
714
|
+
_handleMIDIMessage(E) {
|
|
715
|
+
const [s, n, _] = E.data, r = s & 240, e = (s & 15) + 1;
|
|
716
|
+
if (s === 240) {
|
|
717
|
+
this.emit(u.SYSEX_RECV, {
|
|
718
|
+
data: Array.from(E.data),
|
|
719
|
+
timestamp: E.midiwire
|
|
720
720
|
});
|
|
721
721
|
return;
|
|
722
722
|
}
|
|
723
|
-
if (
|
|
724
|
-
const
|
|
725
|
-
this.state.set(
|
|
726
|
-
cc:
|
|
723
|
+
if (r === 176) {
|
|
724
|
+
const a = `${e}:${n}`;
|
|
725
|
+
this.state.set(a, _), this.emit(u.CC_RECV, {
|
|
726
|
+
cc: n,
|
|
727
727
|
value: _,
|
|
728
|
-
channel:
|
|
728
|
+
channel: e
|
|
729
729
|
});
|
|
730
730
|
return;
|
|
731
731
|
}
|
|
732
|
-
if (
|
|
733
|
-
this.emit(
|
|
734
|
-
note:
|
|
732
|
+
if (r === 144 && _ > 0) {
|
|
733
|
+
this.emit(u.NOTE_ON_RECV, {
|
|
734
|
+
note: n,
|
|
735
735
|
velocity: _,
|
|
736
|
-
channel:
|
|
736
|
+
channel: e
|
|
737
737
|
});
|
|
738
738
|
return;
|
|
739
739
|
}
|
|
740
|
-
if (
|
|
741
|
-
this.emit(
|
|
742
|
-
note:
|
|
743
|
-
channel:
|
|
740
|
+
if (r === 128 || r === 144 && _ === 0) {
|
|
741
|
+
this.emit(u.NOTE_OFF_RECV, {
|
|
742
|
+
note: n,
|
|
743
|
+
channel: e
|
|
744
744
|
});
|
|
745
745
|
return;
|
|
746
746
|
}
|
|
747
|
-
this.emit(
|
|
748
|
-
status:
|
|
749
|
-
data: [
|
|
750
|
-
channel:
|
|
751
|
-
timestamp:
|
|
747
|
+
this.emit(u.MIDI_MSG, {
|
|
748
|
+
status: s,
|
|
749
|
+
data: [n, _],
|
|
750
|
+
channel: e,
|
|
751
|
+
timestamp: E.midiwire
|
|
752
752
|
});
|
|
753
753
|
}
|
|
754
754
|
/**
|
|
755
755
|
* Create a binding between an element and MIDI CC
|
|
756
756
|
* @private
|
|
757
757
|
*/
|
|
758
|
-
_createBinding(
|
|
758
|
+
_createBinding(E, s, n = {}) {
|
|
759
759
|
const {
|
|
760
|
-
min: _ = parseFloat(
|
|
761
|
-
max:
|
|
762
|
-
channel:
|
|
763
|
-
invert:
|
|
760
|
+
min: _ = parseFloat(E.getAttribute("min")) || 0,
|
|
761
|
+
max: r = parseFloat(E.getAttribute("max")) || 127,
|
|
762
|
+
channel: e,
|
|
763
|
+
invert: a = !1,
|
|
764
764
|
onInput: T = void 0
|
|
765
|
-
} =
|
|
766
|
-
...
|
|
765
|
+
} = s, { debounce: S = 0 } = n, C = {
|
|
766
|
+
...s,
|
|
767
767
|
min: _,
|
|
768
|
-
max:
|
|
769
|
-
invert:
|
|
768
|
+
max: r,
|
|
769
|
+
invert: a,
|
|
770
770
|
onInput: T
|
|
771
771
|
};
|
|
772
|
-
if (
|
|
773
|
-
const { msb:
|
|
774
|
-
const
|
|
775
|
-
if (Number.isNaN(
|
|
776
|
-
const { msb:
|
|
777
|
-
this.sendCC(
|
|
772
|
+
if (e !== void 0 && (C.channel = e), s.is14Bit) {
|
|
773
|
+
const { msb: K, lsb: U } = s, I = (M) => {
|
|
774
|
+
const g = parseFloat(M.target.value);
|
|
775
|
+
if (Number.isNaN(g)) return;
|
|
776
|
+
const { msb: b, lsb: y } = $(g, _, r, a), F = e || this.options.channel;
|
|
777
|
+
this.sendCC(K, b, F), this.sendCC(U, y, F);
|
|
778
778
|
};
|
|
779
|
-
let
|
|
779
|
+
let L = null;
|
|
780
780
|
const d = S > 0 ? (M) => {
|
|
781
|
-
|
|
782
|
-
|
|
781
|
+
L && clearTimeout(L), L = setTimeout(() => {
|
|
782
|
+
I(M), L = null;
|
|
783
783
|
}, S);
|
|
784
|
-
} :
|
|
785
|
-
return
|
|
786
|
-
element:
|
|
787
|
-
config:
|
|
788
|
-
handler:
|
|
784
|
+
} : I;
|
|
785
|
+
return E.addEventListener("input", d), E.addEventListener("change", d), {
|
|
786
|
+
element: E,
|
|
787
|
+
config: C,
|
|
788
|
+
handler: I,
|
|
789
789
|
destroy: () => {
|
|
790
|
-
|
|
790
|
+
L && clearTimeout(L), E.removeEventListener("input", d), E.removeEventListener("change", d);
|
|
791
791
|
}
|
|
792
792
|
};
|
|
793
793
|
}
|
|
794
|
-
const { cc: P } =
|
|
795
|
-
const
|
|
796
|
-
if (Number.isNaN(
|
|
797
|
-
const
|
|
798
|
-
this.sendCC(P,
|
|
794
|
+
const { cc: P } = s, i = (K) => {
|
|
795
|
+
const U = parseFloat(K.target.value);
|
|
796
|
+
if (Number.isNaN(U)) return;
|
|
797
|
+
const I = B(U, _, r, a), L = e === void 0 ? this.options.channel : e;
|
|
798
|
+
this.sendCC(P, I, L);
|
|
799
799
|
};
|
|
800
|
-
let
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
800
|
+
let l = null;
|
|
801
|
+
const O = S > 0 ? (K) => {
|
|
802
|
+
l && clearTimeout(l), l = setTimeout(() => {
|
|
803
|
+
i(K), l = null;
|
|
804
804
|
}, S);
|
|
805
|
-
} :
|
|
806
|
-
return
|
|
807
|
-
element:
|
|
808
|
-
config:
|
|
809
|
-
handler:
|
|
805
|
+
} : i;
|
|
806
|
+
return E.addEventListener("input", O), E.addEventListener("change", O), {
|
|
807
|
+
element: E,
|
|
808
|
+
config: C,
|
|
809
|
+
handler: i,
|
|
810
810
|
destroy: () => {
|
|
811
|
-
|
|
811
|
+
l && clearTimeout(l), E.removeEventListener("input", O), E.removeEventListener("change", O);
|
|
812
812
|
}
|
|
813
813
|
};
|
|
814
814
|
}
|
|
@@ -817,79 +817,79 @@ class z extends b {
|
|
|
817
817
|
* @param {string} [name] - Optional patch name
|
|
818
818
|
* @returns {Object} Patch object
|
|
819
819
|
*/
|
|
820
|
-
getPatch(
|
|
821
|
-
const
|
|
822
|
-
name:
|
|
820
|
+
getPatch(E = "Unnamed Patch") {
|
|
821
|
+
const s = {
|
|
822
|
+
name: E,
|
|
823
823
|
device: this.getCurrentOutput()?.name || null,
|
|
824
824
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
825
825
|
version: "1.0",
|
|
826
826
|
channels: {},
|
|
827
827
|
settings: {}
|
|
828
828
|
};
|
|
829
|
-
for (const [
|
|
830
|
-
const [
|
|
831
|
-
|
|
829
|
+
for (const [n, _] of this.state.entries()) {
|
|
830
|
+
const [r, e] = n.split(":").map(Number);
|
|
831
|
+
s.channels[r] || (s.channels[r] = { ccs: {}, notes: {} }), s.channels[r].ccs[e] = _;
|
|
832
832
|
}
|
|
833
|
-
for (const [
|
|
834
|
-
const { config:
|
|
835
|
-
if (
|
|
836
|
-
const
|
|
837
|
-
|
|
838
|
-
min:
|
|
839
|
-
max:
|
|
840
|
-
invert:
|
|
841
|
-
is14Bit:
|
|
842
|
-
label:
|
|
843
|
-
elementId:
|
|
833
|
+
for (const [n, _] of this.bindings.entries()) {
|
|
834
|
+
const { config: r } = _;
|
|
835
|
+
if (r.cc) {
|
|
836
|
+
const e = `cc${r.cc}`;
|
|
837
|
+
s.settings[e] = {
|
|
838
|
+
min: r.min,
|
|
839
|
+
max: r.max,
|
|
840
|
+
invert: r.invert || !1,
|
|
841
|
+
is14Bit: r.is14Bit || !1,
|
|
842
|
+
label: n.getAttribute?.("data-midi-label") || null,
|
|
843
|
+
elementId: n.id || null
|
|
844
844
|
};
|
|
845
845
|
}
|
|
846
846
|
}
|
|
847
|
-
return
|
|
847
|
+
return s;
|
|
848
848
|
}
|
|
849
849
|
/**
|
|
850
850
|
* Apply a patch to the controller
|
|
851
851
|
* @param {PatchData} patch - Patch object
|
|
852
852
|
* @returns {Promise<void>}
|
|
853
853
|
*/
|
|
854
|
-
async setPatch(
|
|
855
|
-
if (!
|
|
854
|
+
async setPatch(E) {
|
|
855
|
+
if (!E || !E.channels)
|
|
856
856
|
throw new R("Invalid patch format", "patch");
|
|
857
|
-
const
|
|
858
|
-
|
|
857
|
+
const s = E.version || "1.0";
|
|
858
|
+
s === "1.0" ? await this._applyPatchV1(E) : (console.warn(`Unknown patch version: ${s}. Attempting to apply as v1.0`), await this._applyPatchV1(E)), this.emit(u.PATCH_LOADED, { patch: E });
|
|
859
859
|
}
|
|
860
860
|
/**
|
|
861
861
|
* Apply v1.0 patch format
|
|
862
862
|
* @private
|
|
863
863
|
* @param {PatchData} patch
|
|
864
864
|
*/
|
|
865
|
-
async _applyPatchV1(
|
|
866
|
-
for (const [
|
|
867
|
-
const _ = parseInt(
|
|
868
|
-
if (
|
|
869
|
-
for (const [
|
|
870
|
-
const
|
|
871
|
-
this.sendCC(
|
|
865
|
+
async _applyPatchV1(E) {
|
|
866
|
+
for (const [s, n] of Object.entries(E.channels)) {
|
|
867
|
+
const _ = parseInt(s, 10);
|
|
868
|
+
if (n.ccs)
|
|
869
|
+
for (const [r, e] of Object.entries(n.ccs)) {
|
|
870
|
+
const a = parseInt(r, 10);
|
|
871
|
+
this.sendCC(a, e, _);
|
|
872
872
|
}
|
|
873
|
-
if (
|
|
874
|
-
for (const [
|
|
875
|
-
const
|
|
876
|
-
|
|
873
|
+
if (n.notes)
|
|
874
|
+
for (const [r, e] of Object.entries(n.notes)) {
|
|
875
|
+
const a = parseInt(r, 10);
|
|
876
|
+
e > 0 ? this.sendNoteOn(a, e, _) : this.sendNoteOff(a, _);
|
|
877
877
|
}
|
|
878
878
|
}
|
|
879
|
-
if (
|
|
880
|
-
for (const [
|
|
881
|
-
for (const [_,
|
|
882
|
-
|
|
883
|
-
for (const [
|
|
884
|
-
const { config: _ } =
|
|
879
|
+
if (E.settings)
|
|
880
|
+
for (const [s, n] of Object.entries(E.settings))
|
|
881
|
+
for (const [_, r] of this.bindings.entries())
|
|
882
|
+
r.config.cc?.toString() === s.replace("cc", "") && (_.min !== void 0 && n.min !== void 0 && (_.min = String(n.min)), _.max !== void 0 && n.max !== void 0 && (_.max = String(n.max)));
|
|
883
|
+
for (const [s, n] of this.bindings.entries()) {
|
|
884
|
+
const { config: _ } = n;
|
|
885
885
|
if (_.cc !== void 0) {
|
|
886
|
-
const
|
|
887
|
-
if (
|
|
888
|
-
const
|
|
889
|
-
if (
|
|
890
|
-
const T = _.min !== void 0 ? _.min : parseFloat(
|
|
886
|
+
const r = _.channel || this.options.channel, e = E.channels[r];
|
|
887
|
+
if (e?.ccs) {
|
|
888
|
+
const a = e.ccs[_.cc];
|
|
889
|
+
if (a !== void 0) {
|
|
890
|
+
const T = _.min !== void 0 ? _.min : parseFloat(s.getAttribute?.("min")) || 0, S = _.max !== void 0 ? _.max : parseFloat(s.getAttribute?.("max")) || 127, C = _.invert || !1;
|
|
891
891
|
let P;
|
|
892
|
-
|
|
892
|
+
C ? P = S - a / 127 * (S - T) : P = T + a / 127 * (S - T), _.onInput && typeof _.onInput == "function" ? _.onInput(P) : (s.value = P, s.dispatchEvent(new Event("input", { bubbles: !0 })));
|
|
893
893
|
}
|
|
894
894
|
}
|
|
895
895
|
}
|
|
@@ -901,12 +901,12 @@ class z extends b {
|
|
|
901
901
|
* @param {Object} [patch] - Optional patch object (will use getPatch() if not provided)
|
|
902
902
|
* @returns {string} Storage key used
|
|
903
903
|
*/
|
|
904
|
-
savePatch(
|
|
905
|
-
const
|
|
904
|
+
savePatch(E, s = null) {
|
|
905
|
+
const n = s || this.getPatch(E), _ = `midiwire_patch_${E}`;
|
|
906
906
|
try {
|
|
907
|
-
return localStorage.setItem(_, JSON.stringify(
|
|
908
|
-
} catch (
|
|
909
|
-
throw console.error("Failed to save patch:",
|
|
907
|
+
return localStorage.setItem(_, JSON.stringify(n)), this.emit(u.PATCH_SAVED, { name: E, patch: n }), _;
|
|
908
|
+
} catch (r) {
|
|
909
|
+
throw console.error("Failed to save patch:", r), r;
|
|
910
910
|
}
|
|
911
911
|
}
|
|
912
912
|
/**
|
|
@@ -914,16 +914,16 @@ class z extends b {
|
|
|
914
914
|
* @param {string} name - Patch name
|
|
915
915
|
* @returns {Object|null} Patch object or null if not found
|
|
916
916
|
*/
|
|
917
|
-
loadPatch(
|
|
918
|
-
const
|
|
917
|
+
loadPatch(E) {
|
|
918
|
+
const s = `midiwire_patch_${E}`;
|
|
919
919
|
try {
|
|
920
|
-
const
|
|
921
|
-
if (!
|
|
920
|
+
const n = localStorage.getItem(s);
|
|
921
|
+
if (!n)
|
|
922
922
|
return null;
|
|
923
|
-
const _ = JSON.parse(
|
|
924
|
-
return this.emit(
|
|
925
|
-
} catch (
|
|
926
|
-
return console.error("Failed to load patch:",
|
|
923
|
+
const _ = JSON.parse(n);
|
|
924
|
+
return this.emit(u.PATCH_LOADED, { name: E, patch: _ }), _;
|
|
925
|
+
} catch (n) {
|
|
926
|
+
return console.error("Failed to load patch:", n), null;
|
|
927
927
|
}
|
|
928
928
|
}
|
|
929
929
|
/**
|
|
@@ -931,12 +931,12 @@ class z extends b {
|
|
|
931
931
|
* @param {string} name - Patch name
|
|
932
932
|
* @returns {boolean} Success
|
|
933
933
|
*/
|
|
934
|
-
deletePatch(
|
|
935
|
-
const
|
|
934
|
+
deletePatch(E) {
|
|
935
|
+
const s = `midiwire_patch_${E}`;
|
|
936
936
|
try {
|
|
937
|
-
return localStorage.removeItem(
|
|
938
|
-
} catch (
|
|
939
|
-
return console.error("Failed to delete patch:",
|
|
937
|
+
return localStorage.removeItem(s), this.emit(u.PATCH_DELETED, { name: E }), !0;
|
|
938
|
+
} catch (n) {
|
|
939
|
+
return console.error("Failed to delete patch:", n), !1;
|
|
940
940
|
}
|
|
941
941
|
}
|
|
942
942
|
/**
|
|
@@ -944,19 +944,19 @@ class z extends b {
|
|
|
944
944
|
* @returns {Array<Object>} Array of { name, patch }
|
|
945
945
|
*/
|
|
946
946
|
listPatches() {
|
|
947
|
-
const
|
|
947
|
+
const E = [];
|
|
948
948
|
try {
|
|
949
|
-
for (let
|
|
950
|
-
const
|
|
951
|
-
if (
|
|
952
|
-
const _ =
|
|
953
|
-
|
|
949
|
+
for (let s = 0; s < localStorage.length; s++) {
|
|
950
|
+
const n = localStorage.key(s);
|
|
951
|
+
if (n?.startsWith("midiwire_patch_")) {
|
|
952
|
+
const _ = n.replace("midiwire_patch_", ""), r = this.loadPatch(_);
|
|
953
|
+
r && E.push({ name: _, patch: r });
|
|
954
954
|
}
|
|
955
955
|
}
|
|
956
|
-
} catch (
|
|
957
|
-
console.error("Failed to list patches:",
|
|
956
|
+
} catch (s) {
|
|
957
|
+
console.error("Failed to list patches:", s);
|
|
958
958
|
}
|
|
959
|
-
return
|
|
959
|
+
return E.sort((s, n) => s.name.localeCompare(n.name));
|
|
960
960
|
}
|
|
961
961
|
}
|
|
962
962
|
class q {
|
|
@@ -967,27 +967,27 @@ class q {
|
|
|
967
967
|
* @param {Function} options.onConnectionUpdate - Callback when connection status changes
|
|
968
968
|
* @param {number} [options.channel=1] - Default MIDI channel
|
|
969
969
|
*/
|
|
970
|
-
constructor(
|
|
971
|
-
this.midi =
|
|
972
|
-
}), this.onConnectionUpdate =
|
|
973
|
-
}), this.channel =
|
|
970
|
+
constructor(E = {}) {
|
|
971
|
+
this.midi = E.midiController || null, this.onStatusUpdate = E.onStatusUpdate || (() => {
|
|
972
|
+
}), this.onConnectionUpdate = E.onConnectionUpdate || (() => {
|
|
973
|
+
}), this.channel = E.channel || 1, this.currentDevice = null, this.isConnecting = !1;
|
|
974
974
|
}
|
|
975
975
|
/**
|
|
976
976
|
* Initialize the device manager with a MIDIController
|
|
977
977
|
* @param {MIDIController} midi
|
|
978
978
|
*/
|
|
979
|
-
setMIDI(
|
|
980
|
-
this.midi =
|
|
979
|
+
setMIDI(E) {
|
|
980
|
+
this.midi = E;
|
|
981
981
|
}
|
|
982
982
|
/**
|
|
983
983
|
* Set up device change event listeners
|
|
984
984
|
* @param {Function} [onDeviceListChange] - Optional callback when device list should be refreshed
|
|
985
985
|
*/
|
|
986
|
-
setupDeviceListeners(
|
|
987
|
-
this.midi?.connection && (this.midi.connection.on(
|
|
988
|
-
this.updateStatus(`Device connected: ${
|
|
989
|
-
}), this.midi.connection.on(
|
|
990
|
-
this.updateStatus(`Device disconnected: ${
|
|
986
|
+
setupDeviceListeners(E) {
|
|
987
|
+
this.midi?.connection && (this.midi.connection.on(f.OUTPUT_DEVICE_CONNECTED, ({ device: s }) => {
|
|
988
|
+
this.updateStatus(`Device connected: ${s.name}`, "connected"), E && E();
|
|
989
|
+
}), this.midi.connection.on(f.OUTPUT_DEVICE_DISCONNECTED, ({ device: s }) => {
|
|
990
|
+
this.updateStatus(`Device disconnected: ${s.name}`, "error"), this.currentDevice && s.name === this.currentDevice.name && (this.currentDevice = null, this.updateConnectionStatus()), E && E();
|
|
991
991
|
}));
|
|
992
992
|
}
|
|
993
993
|
/**
|
|
@@ -995,8 +995,8 @@ class q {
|
|
|
995
995
|
* @param {string} message
|
|
996
996
|
* @param {string} state
|
|
997
997
|
*/
|
|
998
|
-
updateStatus(
|
|
999
|
-
this.onStatusUpdate(
|
|
998
|
+
updateStatus(E, s = "") {
|
|
999
|
+
this.onStatusUpdate(E, s);
|
|
1000
1000
|
}
|
|
1001
1001
|
/**
|
|
1002
1002
|
* Update connection status
|
|
@@ -1016,17 +1016,17 @@ class q {
|
|
|
1016
1016
|
* @param {string} deviceName
|
|
1017
1017
|
* @returns {boolean}
|
|
1018
1018
|
*/
|
|
1019
|
-
isDeviceConnected(
|
|
1020
|
-
return this.midi?.connection ? this.midi.connection.getOutputs().some((
|
|
1019
|
+
isDeviceConnected(E) {
|
|
1020
|
+
return this.midi?.connection ? this.midi.connection.getOutputs().some((n) => n.name === E) : !1;
|
|
1021
1021
|
}
|
|
1022
1022
|
/**
|
|
1023
1023
|
* Connect device selection events to the device manager
|
|
1024
1024
|
* @param {HTMLSelectElement} deviceSelectElement
|
|
1025
1025
|
* @param {Function} onConnect - Callback when device is connected (midi, device)
|
|
1026
1026
|
*/
|
|
1027
|
-
connectDeviceSelection(
|
|
1028
|
-
!
|
|
1029
|
-
const _ =
|
|
1027
|
+
connectDeviceSelection(E, s) {
|
|
1028
|
+
!E || !this.midi || E.addEventListener("change", async (n) => {
|
|
1029
|
+
const _ = n.target.value;
|
|
1030
1030
|
if (!_) {
|
|
1031
1031
|
this.currentDevice && this.midi && (this.midi.connection.disconnect(), this.currentDevice = null, this.updateStatus("Disconnected"), this.updateConnectionStatus());
|
|
1032
1032
|
return;
|
|
@@ -1034,9 +1034,9 @@ class q {
|
|
|
1034
1034
|
if (!this.isConnecting) {
|
|
1035
1035
|
this.isConnecting = !0;
|
|
1036
1036
|
try {
|
|
1037
|
-
await this.midi.setOutput(parseInt(_, 10)), this.currentDevice = this.midi.getCurrentOutput(), this.updateConnectionStatus(),
|
|
1038
|
-
} catch (
|
|
1039
|
-
this.updateStatus(`Connection failed: ${
|
|
1037
|
+
await this.midi.setOutput(parseInt(_, 10)), this.currentDevice = this.midi.getCurrentOutput(), this.updateConnectionStatus(), s && await s(this.midi, this.currentDevice);
|
|
1038
|
+
} catch (r) {
|
|
1039
|
+
this.updateStatus(`Connection failed: ${r.message}`, "error");
|
|
1040
1040
|
} finally {
|
|
1041
1041
|
this.isConnecting = !1;
|
|
1042
1042
|
}
|
|
@@ -1047,9 +1047,9 @@ class q {
|
|
|
1047
1047
|
* Connect channel selection events
|
|
1048
1048
|
* @param {HTMLSelectElement} channelSelectElement
|
|
1049
1049
|
*/
|
|
1050
|
-
connectChannelSelection(
|
|
1051
|
-
!
|
|
1052
|
-
this.midi && (this.midi.options.channel = parseInt(
|
|
1050
|
+
connectChannelSelection(E) {
|
|
1051
|
+
!E || !this.midi || E.addEventListener("change", (s) => {
|
|
1052
|
+
this.midi && (this.midi.options.channel = parseInt(s.target.value, 10), this.updateConnectionStatus());
|
|
1053
1053
|
});
|
|
1054
1054
|
}
|
|
1055
1055
|
/**
|
|
@@ -1057,22 +1057,22 @@ class q {
|
|
|
1057
1057
|
* @param {HTMLSelectElement} selectElement
|
|
1058
1058
|
* @param {Function} [onChange] - Optional callback when selection should change
|
|
1059
1059
|
*/
|
|
1060
|
-
populateDeviceList(
|
|
1061
|
-
if (!
|
|
1062
|
-
const
|
|
1063
|
-
if (
|
|
1064
|
-
if (
|
|
1065
|
-
const _ =
|
|
1066
|
-
_ !== -1 ?
|
|
1060
|
+
populateDeviceList(E, s) {
|
|
1061
|
+
if (!E) return;
|
|
1062
|
+
const n = this.getOutputDevices();
|
|
1063
|
+
if (n.length > 0) {
|
|
1064
|
+
if (E.innerHTML = '<option value="">Select a device</option>' + n.map((_, r) => `<option value="${r}">${_.name}</option>`).join(""), this.currentDevice) {
|
|
1065
|
+
const _ = n.findIndex((r) => r.name === this.currentDevice.name);
|
|
1066
|
+
_ !== -1 ? E.value = _.toString() : (E.value = "", this.currentDevice = null, this.updateConnectionStatus());
|
|
1067
1067
|
} else
|
|
1068
|
-
|
|
1068
|
+
E.value = "";
|
|
1069
1069
|
this.currentDevice || this.updateStatus("Select a MIDI device");
|
|
1070
1070
|
} else
|
|
1071
|
-
|
|
1072
|
-
|
|
1071
|
+
E.innerHTML = '<option value="">No MIDI devices found</option>', this.updateStatus("No MIDI devices available", "error");
|
|
1072
|
+
s && s();
|
|
1073
1073
|
}
|
|
1074
1074
|
}
|
|
1075
|
-
class
|
|
1075
|
+
class t {
|
|
1076
1076
|
// Packed format (128 bytes)
|
|
1077
1077
|
// See: DX7 Service Manual, Voice Memory Format
|
|
1078
1078
|
static PACKED_SIZE = 128;
|
|
@@ -1261,24 +1261,24 @@ class E {
|
|
|
1261
1261
|
* @param {number} index - Voice index (0-31)
|
|
1262
1262
|
* @throws {DX7ValidationError} If data length is not exactly 128 bytes
|
|
1263
1263
|
*/
|
|
1264
|
-
constructor(
|
|
1265
|
-
if (
|
|
1266
|
-
throw new
|
|
1267
|
-
`Invalid voice data length: expected ${
|
|
1264
|
+
constructor(E, s = 0) {
|
|
1265
|
+
if (E.length !== t.PACKED_SIZE)
|
|
1266
|
+
throw new o(
|
|
1267
|
+
`Invalid voice data length: expected ${t.PACKED_SIZE} bytes, got ${E.length}`,
|
|
1268
1268
|
"length",
|
|
1269
|
-
|
|
1269
|
+
E.length
|
|
1270
1270
|
);
|
|
1271
|
-
this.index =
|
|
1271
|
+
this.index = s, this.data = new Uint8Array(E), this.name = this._extractName(), this._unpackedCache = null;
|
|
1272
1272
|
}
|
|
1273
1273
|
/**
|
|
1274
1274
|
* Extract the voice name from the data (10 characters at offset 118)
|
|
1275
1275
|
* @private
|
|
1276
1276
|
*/
|
|
1277
1277
|
_extractName() {
|
|
1278
|
-
const
|
|
1279
|
-
return Array.from(
|
|
1280
|
-
let _ =
|
|
1281
|
-
return _ ===
|
|
1278
|
+
const E = this.data.subarray(t.PACKED_NAME_START, t.PACKED_NAME_START + t.NAME_LENGTH);
|
|
1279
|
+
return Array.from(E).map((n) => {
|
|
1280
|
+
let _ = n & t.MASK_7BIT;
|
|
1281
|
+
return _ === t.CHAR_YEN && (_ = t.CHAR_REPLACEMENT_Y), _ === t.CHAR_ARROW_RIGHT && (_ = t.CHAR_REPLACEMENT_GT), _ === t.CHAR_ARROW_LEFT && (_ = t.CHAR_REPLACEMENT_LT), (_ < t.CHAR_MIN_PRINTABLE || _ > t.CHAR_MAX_PRINTABLE) && (_ = t.CHAR_SPACE), String.fromCharCode(_);
|
|
1282
1282
|
}).join("").trim();
|
|
1283
1283
|
}
|
|
1284
1284
|
/**
|
|
@@ -1287,14 +1287,14 @@ class E {
|
|
|
1287
1287
|
* @returns {number} Parameter value (0-127)
|
|
1288
1288
|
* @throws {DX7ValidationError} If offset is out of range
|
|
1289
1289
|
*/
|
|
1290
|
-
getParameter(
|
|
1291
|
-
if (
|
|
1292
|
-
throw new
|
|
1293
|
-
`Parameter offset out of range: ${
|
|
1290
|
+
getParameter(E) {
|
|
1291
|
+
if (E < 0 || E >= t.PACKED_SIZE)
|
|
1292
|
+
throw new o(
|
|
1293
|
+
`Parameter offset out of range: ${E} (must be 0-${t.PACKED_SIZE - 1})`,
|
|
1294
1294
|
"offset",
|
|
1295
|
-
|
|
1295
|
+
E
|
|
1296
1296
|
);
|
|
1297
|
-
return this.data[
|
|
1297
|
+
return this.data[E] & t.MASK_7BIT;
|
|
1298
1298
|
}
|
|
1299
1299
|
/**
|
|
1300
1300
|
* Get a parameter value from the unpacked 169-byte format
|
|
@@ -1302,28 +1302,28 @@ class E {
|
|
|
1302
1302
|
* @returns {number} Parameter value (0-127)
|
|
1303
1303
|
* @throws {DX7ValidationError} If offset is out of range
|
|
1304
1304
|
*/
|
|
1305
|
-
getUnpackedParameter(
|
|
1306
|
-
if (
|
|
1307
|
-
throw new
|
|
1308
|
-
`Unpacked parameter offset out of range: ${
|
|
1305
|
+
getUnpackedParameter(E) {
|
|
1306
|
+
if (E < 0 || E >= t.UNPACKED_SIZE)
|
|
1307
|
+
throw new o(
|
|
1308
|
+
`Unpacked parameter offset out of range: ${E} (must be 0-${t.UNPACKED_SIZE - 1})`,
|
|
1309
1309
|
"offset",
|
|
1310
|
-
|
|
1310
|
+
E
|
|
1311
1311
|
);
|
|
1312
|
-
return this._unpackedCache || (this._unpackedCache = this.unpack()), this._unpackedCache[
|
|
1312
|
+
return this._unpackedCache || (this._unpackedCache = this.unpack()), this._unpackedCache[E] & t.MASK_7BIT;
|
|
1313
1313
|
}
|
|
1314
1314
|
/**
|
|
1315
1315
|
* Set a raw parameter value in the packed data
|
|
1316
1316
|
* @param {number} offset - Byte offset in the voice data
|
|
1317
1317
|
* @param {number} value - Parameter value (0-127)
|
|
1318
1318
|
*/
|
|
1319
|
-
setParameter(
|
|
1320
|
-
if (
|
|
1321
|
-
throw new
|
|
1322
|
-
`Parameter offset out of range: ${
|
|
1319
|
+
setParameter(E, s) {
|
|
1320
|
+
if (E < 0 || E >= t.PACKED_SIZE)
|
|
1321
|
+
throw new o(
|
|
1322
|
+
`Parameter offset out of range: ${E} (must be 0-${t.PACKED_SIZE - 1})`,
|
|
1323
1323
|
"offset",
|
|
1324
|
-
|
|
1324
|
+
E
|
|
1325
1325
|
);
|
|
1326
|
-
this.data[
|
|
1326
|
+
this.data[E] = s & t.MASK_7BIT, this._unpackedCache = null, E >= t.PACKED_NAME_START && E < t.PACKED_NAME_START + t.NAME_LENGTH && (this.name = this._extractName());
|
|
1327
1327
|
}
|
|
1328
1328
|
/**
|
|
1329
1329
|
* Unpack the voice data to 169-byte unpacked format
|
|
@@ -1331,199 +1331,199 @@ class E {
|
|
|
1331
1331
|
* @returns {Uint8Array} 169 bytes of unpacked voice data (138 operator + 8 pitch EG + 13 global + 10 name = 169 bytes)
|
|
1332
1332
|
*/
|
|
1333
1333
|
unpack() {
|
|
1334
|
-
const
|
|
1335
|
-
return this._unpackOperators(
|
|
1334
|
+
const E = this.data, s = new Uint8Array(t.UNPACKED_SIZE);
|
|
1335
|
+
return this._unpackOperators(E, s), this._unpackPitchEG(E, s), this._unpackGlobalParams(E, s), this._unpackName(E, s), s;
|
|
1336
1336
|
}
|
|
1337
1337
|
/**
|
|
1338
1338
|
* Unpack all 6 operators from packed to unpacked format
|
|
1339
1339
|
* @private
|
|
1340
1340
|
*/
|
|
1341
|
-
_unpackOperators(
|
|
1342
|
-
for (let
|
|
1343
|
-
const _ = (
|
|
1344
|
-
this._unpackOperator(
|
|
1341
|
+
_unpackOperators(E, s) {
|
|
1342
|
+
for (let n = 0; n < t.NUM_OPERATORS; n++) {
|
|
1343
|
+
const _ = (t.NUM_OPERATORS - 1 - n) * t.PACKED_OP_SIZE, r = n * t.UNPACKED_OP_SIZE;
|
|
1344
|
+
this._unpackOperator(E, s, _, r);
|
|
1345
1345
|
}
|
|
1346
1346
|
}
|
|
1347
1347
|
/**
|
|
1348
1348
|
* Unpack a single operator's parameters
|
|
1349
1349
|
* @private
|
|
1350
1350
|
*/
|
|
1351
|
-
_unpackOperator(
|
|
1352
|
-
this._unpackOperatorEG(
|
|
1351
|
+
_unpackOperator(E, s, n, _) {
|
|
1352
|
+
this._unpackOperatorEG(E, s, n, _), this._unpackOperatorScaling(E, s, n, _), this._unpackOperatorPackedParams(E, s, n, _), this._unpackOperatorFrequency(E, s, n, _);
|
|
1353
1353
|
}
|
|
1354
1354
|
/**
|
|
1355
1355
|
* Unpack operator EG rates and levels
|
|
1356
1356
|
* @private
|
|
1357
1357
|
*/
|
|
1358
|
-
_unpackOperatorEG(
|
|
1359
|
-
|
|
1358
|
+
_unpackOperatorEG(E, s, n, _) {
|
|
1359
|
+
s[_ + t.UNPACKED_OP_EG_RATE_1] = E[n + t.PACKED_OP_EG_RATE_1] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_RATE_2] = E[n + t.PACKED_OP_EG_RATE_2] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_RATE_3] = E[n + t.PACKED_OP_EG_RATE_3] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_RATE_4] = E[n + t.PACKED_OP_EG_RATE_4] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_LEVEL_1] = E[n + t.PACKED_OP_EG_LEVEL_1] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_LEVEL_2] = E[n + t.PACKED_OP_EG_LEVEL_2] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_LEVEL_3] = E[n + t.PACKED_OP_EG_LEVEL_3] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_EG_LEVEL_4] = E[n + t.PACKED_OP_EG_LEVEL_4] & t.MASK_7BIT;
|
|
1360
1360
|
}
|
|
1361
1361
|
/**
|
|
1362
1362
|
* Unpack operator keyboard scaling parameters
|
|
1363
1363
|
* @private
|
|
1364
1364
|
*/
|
|
1365
|
-
_unpackOperatorScaling(
|
|
1366
|
-
|
|
1365
|
+
_unpackOperatorScaling(E, s, n, _) {
|
|
1366
|
+
s[_ + t.UNPACKED_OP_BREAK_POINT] = E[n + t.PACKED_OP_BREAK_POINT] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_L_SCALE_DEPTH] = E[n + t.PACKED_OP_L_SCALE_DEPTH] & t.MASK_7BIT, s[_ + t.UNPACKED_OP_R_SCALE_DEPTH] = E[n + t.PACKED_OP_R_SCALE_DEPTH] & t.MASK_7BIT;
|
|
1367
1367
|
}
|
|
1368
1368
|
/**
|
|
1369
1369
|
* Unpack operator bit-packed parameters (curves, rate scaling, mod sens)
|
|
1370
1370
|
* @private
|
|
1371
1371
|
*/
|
|
1372
|
-
_unpackOperatorPackedParams(
|
|
1373
|
-
const
|
|
1374
|
-
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1372
|
+
_unpackOperatorPackedParams(E, s, n, _) {
|
|
1373
|
+
const r = E[n + t.PACKED_OP_CURVES] & t.MASK_7BIT;
|
|
1374
|
+
s[_ + t.UNPACKED_OP_L_CURVE] = r & t.MASK_2BIT, s[_ + t.UNPACKED_OP_R_CURVE] = r >> 2 & t.MASK_2BIT;
|
|
1375
|
+
const e = E[n + t.PACKED_OP_RATE_SCALING] & t.MASK_7BIT;
|
|
1376
|
+
s[_ + t.UNPACKED_OP_RATE_SCALING] = e & t.MASK_3BIT, s[_ + t.UNPACKED_OP_DETUNE] = e >> 3 & t.MASK_4BIT;
|
|
1377
|
+
const a = E[n + t.PACKED_OP_MOD_SENS] & t.MASK_7BIT;
|
|
1378
|
+
s[_ + t.UNPACKED_OP_AMP_MOD_SENS] = a & t.MASK_2BIT, s[_ + t.UNPACKED_OP_KEY_VEL_SENS] = a >> 2 & t.MASK_3BIT, s[_ + t.UNPACKED_OP_OUTPUT_LEVEL] = E[n + t.PACKED_OP_OUTPUT_LEVEL] & t.MASK_7BIT;
|
|
1379
1379
|
}
|
|
1380
1380
|
/**
|
|
1381
1381
|
* Unpack operator frequency parameters
|
|
1382
1382
|
* @private
|
|
1383
1383
|
*/
|
|
1384
|
-
_unpackOperatorFrequency(
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
const
|
|
1388
|
-
|
|
1384
|
+
_unpackOperatorFrequency(E, s, n, _) {
|
|
1385
|
+
const r = E[n + t.PACKED_OP_MODE_FREQ] & t.MASK_7BIT;
|
|
1386
|
+
s[_ + t.UNPACKED_OP_MODE] = r & t.MASK_1BIT, s[_ + t.UNPACKED_OP_FREQ_COARSE] = r >> 1 & t.MASK_5BIT;
|
|
1387
|
+
const e = E[n + t.PACKED_OP_DETUNE_FINE] & t.MASK_7BIT;
|
|
1388
|
+
s[_ + t.UNPACKED_OP_OSC_DETUNE] = e & t.MASK_3BIT, s[_ + t.UNPACKED_OP_FREQ_FINE] = e >> 3 & t.MASK_4BIT;
|
|
1389
1389
|
}
|
|
1390
1390
|
/**
|
|
1391
1391
|
* Unpack pitch envelope generator parameters
|
|
1392
1392
|
* @private
|
|
1393
1393
|
*/
|
|
1394
|
-
_unpackPitchEG(
|
|
1395
|
-
|
|
1394
|
+
_unpackPitchEG(E, s) {
|
|
1395
|
+
s[t.UNPACKED_PITCH_EG_RATE_1] = E[t.PACKED_PITCH_EG_RATE_1] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_RATE_2] = E[t.PACKED_PITCH_EG_RATE_2] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_RATE_3] = E[t.PACKED_PITCH_EG_RATE_3] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_RATE_4] = E[t.PACKED_PITCH_EG_RATE_4] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_LEVEL_1] = E[t.PACKED_PITCH_EG_LEVEL_1] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_LEVEL_2] = E[t.PACKED_PITCH_EG_LEVEL_2] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_LEVEL_3] = E[t.PACKED_PITCH_EG_LEVEL_3] & t.MASK_7BIT, s[t.UNPACKED_PITCH_EG_LEVEL_4] = E[t.PACKED_PITCH_EG_LEVEL_4] & t.MASK_7BIT;
|
|
1396
1396
|
}
|
|
1397
1397
|
/**
|
|
1398
1398
|
* Unpack global voice parameters (algorithm, feedback, LFO, etc.)
|
|
1399
1399
|
* @private
|
|
1400
1400
|
*/
|
|
1401
|
-
_unpackGlobalParams(
|
|
1402
|
-
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
const _ = t
|
|
1406
|
-
|
|
1401
|
+
_unpackGlobalParams(E, s) {
|
|
1402
|
+
s[t.UNPACKED_ALGORITHM] = E[t.OFFSET_ALGORITHM] & t.MASK_5BIT;
|
|
1403
|
+
const n = E[t.OFFSET_FEEDBACK] & t.MASK_7BIT;
|
|
1404
|
+
s[t.UNPACKED_FEEDBACK] = n & t.MASK_3BIT, s[t.UNPACKED_OSC_SYNC] = n >> 3 & t.MASK_1BIT, s[t.UNPACKED_LFO_SPEED] = E[t.OFFSET_LFO_SPEED] & t.MASK_7BIT, s[t.UNPACKED_LFO_DELAY] = E[t.OFFSET_LFO_DELAY] & t.MASK_7BIT, s[t.UNPACKED_LFO_PM_DEPTH] = E[t.OFFSET_LFO_PM_DEPTH] & t.MASK_7BIT, s[t.UNPACKED_LFO_AM_DEPTH] = E[t.OFFSET_LFO_AM_DEPTH] & t.MASK_7BIT;
|
|
1405
|
+
const _ = E[t.OFFSET_LFO_SYNC_WAVE] & t.MASK_7BIT;
|
|
1406
|
+
s[t.UNPACKED_LFO_KEY_SYNC] = _ & t.MASK_1BIT, s[t.UNPACKED_LFO_WAVE] = _ >> 1 & t.MASK_3BIT, s[t.UNPACKED_LFO_PM_SENS] = _ >> 4 & t.MASK_3BIT, s[t.UNPACKED_AMP_MOD_SENS] = E[t.OFFSET_AMP_MOD_SENS] & t.MASK_7BIT, s[t.UNPACKED_TRANSPOSE] = E[t.OFFSET_TRANSPOSE] & t.MASK_7BIT, s[t.UNPACKED_EG_BIAS_SENS] = E[t.OFFSET_EG_BIAS_SENS] & t.MASK_7BIT;
|
|
1407
1407
|
}
|
|
1408
1408
|
/**
|
|
1409
1409
|
* Copy voice name from packed to unpacked format
|
|
1410
1410
|
* @private
|
|
1411
1411
|
*/
|
|
1412
|
-
_unpackName(
|
|
1413
|
-
for (let
|
|
1414
|
-
|
|
1412
|
+
_unpackName(E, s) {
|
|
1413
|
+
for (let n = 0; n < t.NAME_LENGTH; n++)
|
|
1414
|
+
s[t.UNPACKED_NAME_START + n] = E[t.PACKED_NAME_START + n] & t.MASK_7BIT;
|
|
1415
1415
|
}
|
|
1416
1416
|
/**
|
|
1417
1417
|
* Pack 169-byte unpacked data to 128-byte format
|
|
1418
1418
|
* @param {Array<number>|Uint8Array} unpacked - 169 bytes of unpacked data (159 parameters + 10 name bytes)
|
|
1419
1419
|
* @returns {Uint8Array} 128 bytes of packed data
|
|
1420
1420
|
*/
|
|
1421
|
-
static pack(
|
|
1422
|
-
if (
|
|
1423
|
-
throw new
|
|
1424
|
-
`Invalid unpacked data length: expected ${
|
|
1421
|
+
static pack(E) {
|
|
1422
|
+
if (E.length !== t.UNPACKED_SIZE)
|
|
1423
|
+
throw new o(
|
|
1424
|
+
`Invalid unpacked data length: expected ${t.UNPACKED_SIZE} bytes, got ${E.length}`,
|
|
1425
1425
|
"length",
|
|
1426
|
-
|
|
1426
|
+
E.length
|
|
1427
1427
|
);
|
|
1428
|
-
const
|
|
1429
|
-
return
|
|
1428
|
+
const s = new Uint8Array(t.PACKED_SIZE);
|
|
1429
|
+
return t._packOperators(E, s), t._packPitchEG(E, s), t._packGlobalParams(E, s), t._packName(E, s), s;
|
|
1430
1430
|
}
|
|
1431
1431
|
/**
|
|
1432
1432
|
* Pack all 6 operators from unpacked to packed format
|
|
1433
1433
|
* @private
|
|
1434
1434
|
*/
|
|
1435
|
-
static _packOperators(
|
|
1436
|
-
for (let
|
|
1437
|
-
const _ =
|
|
1438
|
-
|
|
1435
|
+
static _packOperators(E, s) {
|
|
1436
|
+
for (let n = 0; n < t.NUM_OPERATORS; n++) {
|
|
1437
|
+
const _ = n * t.UNPACKED_OP_SIZE, r = (t.NUM_OPERATORS - 1 - n) * t.PACKED_OP_SIZE;
|
|
1438
|
+
t._packOperator(E, s, _, r);
|
|
1439
1439
|
}
|
|
1440
1440
|
}
|
|
1441
1441
|
/**
|
|
1442
1442
|
* Pack a single operator's parameters
|
|
1443
1443
|
* @private
|
|
1444
1444
|
*/
|
|
1445
|
-
static _packOperator(
|
|
1446
|
-
|
|
1445
|
+
static _packOperator(E, s, n, _) {
|
|
1446
|
+
t._packOperatorEG(E, s, n, _), t._packOperatorScaling(E, s, n, _), t._packOperatorPackedParams(E, s, n, _), t._packOperatorFrequency(E, s, n, _);
|
|
1447
1447
|
}
|
|
1448
1448
|
/**
|
|
1449
1449
|
* Pack operator EG rates and levels
|
|
1450
1450
|
* @private
|
|
1451
1451
|
*/
|
|
1452
|
-
static _packOperatorEG(
|
|
1453
|
-
|
|
1452
|
+
static _packOperatorEG(E, s, n, _) {
|
|
1453
|
+
s[_ + t.PACKED_OP_EG_RATE_1] = E[n + t.UNPACKED_OP_EG_RATE_1], s[_ + t.PACKED_OP_EG_RATE_2] = E[n + t.UNPACKED_OP_EG_RATE_2], s[_ + t.PACKED_OP_EG_RATE_3] = E[n + t.UNPACKED_OP_EG_RATE_3], s[_ + t.PACKED_OP_EG_RATE_4] = E[n + t.UNPACKED_OP_EG_RATE_4], s[_ + t.PACKED_OP_EG_LEVEL_1] = E[n + t.UNPACKED_OP_EG_LEVEL_1], s[_ + t.PACKED_OP_EG_LEVEL_2] = E[n + t.UNPACKED_OP_EG_LEVEL_2], s[_ + t.PACKED_OP_EG_LEVEL_3] = E[n + t.UNPACKED_OP_EG_LEVEL_3], s[_ + t.PACKED_OP_EG_LEVEL_4] = E[n + t.UNPACKED_OP_EG_LEVEL_4];
|
|
1454
1454
|
}
|
|
1455
1455
|
/**
|
|
1456
1456
|
* Pack operator keyboard scaling parameters
|
|
1457
1457
|
* @private
|
|
1458
1458
|
*/
|
|
1459
|
-
static _packOperatorScaling(
|
|
1460
|
-
|
|
1459
|
+
static _packOperatorScaling(E, s, n, _) {
|
|
1460
|
+
s[_ + t.PACKED_OP_BREAK_POINT] = E[n + t.UNPACKED_OP_BREAK_POINT], s[_ + t.PACKED_OP_L_SCALE_DEPTH] = E[n + t.UNPACKED_OP_L_SCALE_DEPTH], s[_ + t.PACKED_OP_R_SCALE_DEPTH] = E[n + t.UNPACKED_OP_R_SCALE_DEPTH];
|
|
1461
1461
|
}
|
|
1462
1462
|
/**
|
|
1463
1463
|
* Pack operator bit-packed parameters
|
|
1464
1464
|
* @private
|
|
1465
1465
|
*/
|
|
1466
|
-
static _packOperatorPackedParams(
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1469
|
-
const
|
|
1470
|
-
|
|
1471
|
-
const S =
|
|
1472
|
-
|
|
1466
|
+
static _packOperatorPackedParams(E, s, n, _) {
|
|
1467
|
+
const r = E[n + t.UNPACKED_OP_L_CURVE] & t.MASK_2BIT, e = E[n + t.UNPACKED_OP_R_CURVE] & t.MASK_2BIT;
|
|
1468
|
+
s[_ + t.PACKED_OP_CURVES] = r | e << 2;
|
|
1469
|
+
const a = E[n + t.UNPACKED_OP_RATE_SCALING] & t.MASK_3BIT, T = E[n + t.UNPACKED_OP_DETUNE] & t.MASK_4BIT;
|
|
1470
|
+
s[_ + t.PACKED_OP_RATE_SCALING] = a | T << 3;
|
|
1471
|
+
const S = E[n + t.UNPACKED_OP_AMP_MOD_SENS] & t.MASK_2BIT, C = E[n + t.UNPACKED_OP_KEY_VEL_SENS] & t.MASK_3BIT;
|
|
1472
|
+
s[_ + t.PACKED_OP_MOD_SENS] = S | C << 2, s[_ + t.PACKED_OP_OUTPUT_LEVEL] = E[n + t.UNPACKED_OP_OUTPUT_LEVEL];
|
|
1473
1473
|
}
|
|
1474
1474
|
/**
|
|
1475
1475
|
* Pack operator frequency parameters
|
|
1476
1476
|
* @private
|
|
1477
1477
|
*/
|
|
1478
|
-
static _packOperatorFrequency(
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1478
|
+
static _packOperatorFrequency(E, s, n, _) {
|
|
1479
|
+
const r = E[n + t.UNPACKED_OP_MODE] & t.MASK_1BIT, e = E[n + t.UNPACKED_OP_FREQ_COARSE] & t.MASK_5BIT;
|
|
1480
|
+
s[_ + t.PACKED_OP_MODE_FREQ] = r | e << 1;
|
|
1481
|
+
const a = E[n + t.UNPACKED_OP_OSC_DETUNE] & t.MASK_3BIT, T = E[n + t.UNPACKED_OP_FREQ_FINE] & t.MASK_4BIT;
|
|
1482
|
+
s[_ + t.PACKED_OP_DETUNE_FINE] = a | T << 3;
|
|
1483
1483
|
}
|
|
1484
1484
|
/**
|
|
1485
1485
|
* Pack pitch envelope generator parameters
|
|
1486
1486
|
* @private
|
|
1487
1487
|
*/
|
|
1488
|
-
static _packPitchEG(
|
|
1489
|
-
|
|
1488
|
+
static _packPitchEG(E, s) {
|
|
1489
|
+
s[t.PACKED_PITCH_EG_RATE_1] = E[t.UNPACKED_PITCH_EG_RATE_1], s[t.PACKED_PITCH_EG_RATE_2] = E[t.UNPACKED_PITCH_EG_RATE_2], s[t.PACKED_PITCH_EG_RATE_3] = E[t.UNPACKED_PITCH_EG_RATE_3], s[t.PACKED_PITCH_EG_RATE_4] = E[t.UNPACKED_PITCH_EG_RATE_4], s[t.PACKED_PITCH_EG_LEVEL_1] = E[t.UNPACKED_PITCH_EG_LEVEL_1], s[t.PACKED_PITCH_EG_LEVEL_2] = E[t.UNPACKED_PITCH_EG_LEVEL_2], s[t.PACKED_PITCH_EG_LEVEL_3] = E[t.UNPACKED_PITCH_EG_LEVEL_3], s[t.PACKED_PITCH_EG_LEVEL_4] = E[t.UNPACKED_PITCH_EG_LEVEL_4];
|
|
1490
1490
|
}
|
|
1491
1491
|
/**
|
|
1492
1492
|
* Pack global voice parameters (algorithm, feedback, LFO, etc.)
|
|
1493
1493
|
* @private
|
|
1494
1494
|
*/
|
|
1495
|
-
static _packGlobalParams(
|
|
1496
|
-
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1495
|
+
static _packGlobalParams(E, s) {
|
|
1496
|
+
s[t.OFFSET_ALGORITHM] = E[t.UNPACKED_ALGORITHM];
|
|
1497
|
+
const n = E[t.UNPACKED_FEEDBACK] & t.MASK_3BIT, _ = E[t.UNPACKED_OSC_SYNC] & t.MASK_1BIT;
|
|
1498
|
+
s[t.OFFSET_FEEDBACK] = n | _ << 3, s[t.OFFSET_LFO_SPEED] = E[t.UNPACKED_LFO_SPEED], s[t.OFFSET_LFO_DELAY] = E[t.UNPACKED_LFO_DELAY], s[t.OFFSET_LFO_PM_DEPTH] = E[t.UNPACKED_LFO_PM_DEPTH], s[t.OFFSET_LFO_AM_DEPTH] = E[t.UNPACKED_LFO_AM_DEPTH];
|
|
1499
|
+
const r = E[t.UNPACKED_LFO_KEY_SYNC] & t.MASK_1BIT, e = E[t.UNPACKED_LFO_WAVE] & t.MASK_3BIT, a = E[t.UNPACKED_LFO_PM_SENS] & t.MASK_3BIT;
|
|
1500
|
+
s[t.OFFSET_LFO_SYNC_WAVE] = r | e << 1 | a << 4, s[t.OFFSET_AMP_MOD_SENS] = E[t.UNPACKED_AMP_MOD_SENS], s[t.OFFSET_TRANSPOSE] = E[t.UNPACKED_TRANSPOSE], s[t.OFFSET_EG_BIAS_SENS] = E[t.UNPACKED_EG_BIAS_SENS];
|
|
1501
1501
|
}
|
|
1502
1502
|
/**
|
|
1503
1503
|
* Copy voice name from unpacked to packed format
|
|
1504
1504
|
* @private
|
|
1505
1505
|
*/
|
|
1506
|
-
static _packName(
|
|
1507
|
-
for (let
|
|
1508
|
-
|
|
1506
|
+
static _packName(E, s) {
|
|
1507
|
+
for (let n = 0; n < t.NAME_LENGTH; n++)
|
|
1508
|
+
s[t.PACKED_NAME_START + n] = E[t.UNPACKED_NAME_START + n];
|
|
1509
1509
|
}
|
|
1510
1510
|
/**
|
|
1511
1511
|
* Create a default/empty voice
|
|
1512
1512
|
* @param {number} index - Voice index
|
|
1513
1513
|
* @returns {DX7Voice}
|
|
1514
1514
|
*/
|
|
1515
|
-
static createDefault(
|
|
1516
|
-
const
|
|
1517
|
-
for (let
|
|
1518
|
-
const
|
|
1519
|
-
|
|
1515
|
+
static createDefault(E = 0) {
|
|
1516
|
+
const s = new Uint8Array(t.UNPACKED_SIZE);
|
|
1517
|
+
for (let r = 0; r < t.NUM_OPERATORS; r++) {
|
|
1518
|
+
const e = r * t.UNPACKED_OP_SIZE;
|
|
1519
|
+
s[e + t.UNPACKED_OP_EG_RATE_1] = t.DEFAULT_EG_RATE, s[e + t.UNPACKED_OP_EG_RATE_2] = t.DEFAULT_EG_RATE, s[e + t.UNPACKED_OP_EG_RATE_3] = t.DEFAULT_EG_RATE, s[e + t.UNPACKED_OP_EG_RATE_4] = t.DEFAULT_EG_RATE, s[e + t.UNPACKED_OP_EG_LEVEL_1] = t.DEFAULT_EG_LEVEL_MAX, s[e + t.UNPACKED_OP_EG_LEVEL_2] = t.DEFAULT_EG_LEVEL_MAX, s[e + t.UNPACKED_OP_EG_LEVEL_3] = t.DEFAULT_EG_LEVEL_MAX, s[e + t.UNPACKED_OP_EG_LEVEL_4] = t.DEFAULT_EG_LEVEL_MIN, s[e + t.UNPACKED_OP_BREAK_POINT] = t.DEFAULT_BREAK_POINT, s[e + t.UNPACKED_OP_L_SCALE_DEPTH] = 0, s[e + t.UNPACKED_OP_R_SCALE_DEPTH] = 0, s[e + t.UNPACKED_OP_L_CURVE] = 0, s[e + t.UNPACKED_OP_R_CURVE] = 0, s[e + t.UNPACKED_OP_RATE_SCALING] = 0, s[e + t.UNPACKED_OP_DETUNE] = 7, s[e + t.UNPACKED_OP_AMP_MOD_SENS] = 0, s[e + t.UNPACKED_OP_KEY_VEL_SENS] = 0, s[e + t.UNPACKED_OP_OUTPUT_LEVEL] = t.DEFAULT_OUTPUT_LEVEL, s[e + t.UNPACKED_OP_MODE] = 0, s[e + t.UNPACKED_OP_FREQ_COARSE] = 0, s[e + t.UNPACKED_OP_OSC_DETUNE] = 0, s[e + t.UNPACKED_OP_FREQ_FINE] = 0;
|
|
1520
1520
|
}
|
|
1521
|
-
|
|
1522
|
-
const
|
|
1523
|
-
for (let
|
|
1524
|
-
|
|
1525
|
-
const _ =
|
|
1526
|
-
return new
|
|
1521
|
+
s[t.UNPACKED_PITCH_EG_RATE_1] = t.DEFAULT_EG_RATE, s[t.UNPACKED_PITCH_EG_RATE_2] = t.DEFAULT_EG_RATE, s[t.UNPACKED_PITCH_EG_RATE_3] = t.DEFAULT_EG_RATE, s[t.UNPACKED_PITCH_EG_RATE_4] = t.DEFAULT_EG_RATE, s[t.UNPACKED_PITCH_EG_LEVEL_1] = t.DEFAULT_PITCH_EG_LEVEL, s[t.UNPACKED_PITCH_EG_LEVEL_2] = t.DEFAULT_PITCH_EG_LEVEL, s[t.UNPACKED_PITCH_EG_LEVEL_3] = t.DEFAULT_PITCH_EG_LEVEL, s[t.UNPACKED_PITCH_EG_LEVEL_4] = t.DEFAULT_PITCH_EG_LEVEL, s[t.UNPACKED_ALGORITHM] = t.DEFAULT_ALGORITHM, s[t.UNPACKED_FEEDBACK] = t.DEFAULT_FEEDBACK, s[t.UNPACKED_OSC_SYNC] = 0, s[t.UNPACKED_LFO_SPEED] = t.DEFAULT_LFO_SPEED, s[t.UNPACKED_LFO_DELAY] = 0, s[t.UNPACKED_LFO_PM_DEPTH] = 0, s[t.UNPACKED_LFO_AM_DEPTH] = 0, s[t.UNPACKED_LFO_KEY_SYNC] = 0, s[t.UNPACKED_LFO_WAVE] = 0, s[t.UNPACKED_LFO_PM_SENS] = t.DEFAULT_LFO_PM_SENS, s[t.UNPACKED_AMP_MOD_SENS] = 0, s[t.UNPACKED_TRANSPOSE] = t.TRANSPOSE_CENTER, s[t.UNPACKED_EG_BIAS_SENS] = 0;
|
|
1522
|
+
const n = "Init Voice";
|
|
1523
|
+
for (let r = 0; r < t.NAME_LENGTH; r++)
|
|
1524
|
+
s[t.UNPACKED_NAME_START + r] = r < n.length ? n.charCodeAt(r) : t.CHAR_SPACE;
|
|
1525
|
+
const _ = t.pack(s);
|
|
1526
|
+
return new t(_, E);
|
|
1527
1527
|
}
|
|
1528
1528
|
/**
|
|
1529
1529
|
* Create a voice from unpacked 169-byte data
|
|
@@ -1531,9 +1531,9 @@ class E {
|
|
|
1531
1531
|
* @param {number} index - Voice index
|
|
1532
1532
|
* @returns {DX7Voice}
|
|
1533
1533
|
*/
|
|
1534
|
-
static fromUnpacked(
|
|
1535
|
-
const
|
|
1536
|
-
return new
|
|
1534
|
+
static fromUnpacked(E, s = 0) {
|
|
1535
|
+
const n = t.pack(E);
|
|
1536
|
+
return new t(n, s);
|
|
1537
1537
|
}
|
|
1538
1538
|
/**
|
|
1539
1539
|
* Load a DX7 voice from a single voice SYX file
|
|
@@ -1542,40 +1542,229 @@ class E {
|
|
|
1542
1542
|
* @throws {DX7ParseError} If file has invalid VCED header
|
|
1543
1543
|
* @throws {Error} If file cannot be read (FileReader error)
|
|
1544
1544
|
*/
|
|
1545
|
-
static async fromFile(
|
|
1546
|
-
return new Promise((
|
|
1545
|
+
static async fromFile(E) {
|
|
1546
|
+
return new Promise((s, n) => {
|
|
1547
1547
|
const _ = new FileReader();
|
|
1548
|
-
_.onload = (
|
|
1548
|
+
_.onload = (r) => {
|
|
1549
1549
|
try {
|
|
1550
|
-
const
|
|
1551
|
-
if (
|
|
1552
|
-
throw new
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
), T =
|
|
1550
|
+
const e = new Uint8Array(r.target.result);
|
|
1551
|
+
if (e[0] !== t.VCED_SYSEX_START || e[1] !== t.VCED_YAMAHA_ID || e[2] !== t.VCED_SUB_STATUS || e[3] !== t.VCED_FORMAT_SINGLE || e[4] !== t.VCED_BYTE_COUNT_MSB || e[5] !== t.VCED_BYTE_COUNT_LSB)
|
|
1552
|
+
throw new m("Invalid VCED header", "header", 0);
|
|
1553
|
+
const a = e.subarray(
|
|
1554
|
+
t.VCED_HEADER_SIZE,
|
|
1555
|
+
t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE
|
|
1556
|
+
), T = e[t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE], S = N._calculateChecksum(a, t.VCED_DATA_SIZE);
|
|
1557
1557
|
T !== S && console.warn(
|
|
1558
1558
|
`DX7 VCED checksum mismatch (expected ${S.toString(16)}, got ${T.toString(16)}). This is common with vintage SysEx files.`
|
|
1559
1559
|
);
|
|
1560
|
-
const
|
|
1560
|
+
const C = new Uint8Array(t.UNPACKED_SIZE);
|
|
1561
1561
|
let P = 0;
|
|
1562
|
-
for (let
|
|
1563
|
-
const
|
|
1564
|
-
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1562
|
+
for (let l = 0; l < t.NUM_OPERATORS; l++) {
|
|
1563
|
+
const O = (t.NUM_OPERATORS - 1 - l) * t.UNPACKED_OP_SIZE;
|
|
1564
|
+
C[O + t.UNPACKED_OP_EG_RATE_1] = a[P++], C[O + t.UNPACKED_OP_EG_RATE_2] = a[P++], C[O + t.UNPACKED_OP_EG_RATE_3] = a[P++], C[O + t.UNPACKED_OP_EG_RATE_4] = a[P++], C[O + t.UNPACKED_OP_EG_LEVEL_1] = a[P++], C[O + t.UNPACKED_OP_EG_LEVEL_2] = a[P++], C[O + t.UNPACKED_OP_EG_LEVEL_3] = a[P++], C[O + t.UNPACKED_OP_EG_LEVEL_4] = a[P++], C[O + t.UNPACKED_OP_BREAK_POINT] = a[P++], C[O + t.UNPACKED_OP_L_SCALE_DEPTH] = a[P++], C[O + t.UNPACKED_OP_R_SCALE_DEPTH] = a[P++], C[O + t.UNPACKED_OP_L_CURVE] = a[P++], C[O + t.UNPACKED_OP_R_CURVE] = a[P++], C[O + t.UNPACKED_OP_RATE_SCALING] = a[P++], C[O + t.UNPACKED_OP_DETUNE] = a[P++];
|
|
1565
|
+
const K = a[P++];
|
|
1566
|
+
C[O + t.UNPACKED_OP_AMP_MOD_SENS] = K & t.MASK_2BIT, C[O + t.UNPACKED_OP_KEY_VEL_SENS] = K >> 2 & t.MASK_3BIT, C[O + t.UNPACKED_OP_OUTPUT_LEVEL] = a[P++], C[O + t.UNPACKED_OP_MODE] = a[P++], C[O + t.UNPACKED_OP_FREQ_COARSE] = a[P++], C[O + t.UNPACKED_OP_FREQ_FINE] = a[P++], C[O + t.UNPACKED_OP_OSC_DETUNE] = a[P++];
|
|
1567
1567
|
}
|
|
1568
|
-
|
|
1569
|
-
for (let
|
|
1570
|
-
|
|
1571
|
-
const
|
|
1572
|
-
|
|
1573
|
-
} catch (
|
|
1574
|
-
|
|
1568
|
+
C[t.UNPACKED_PITCH_EG_RATE_1] = a[P++], C[t.UNPACKED_PITCH_EG_RATE_2] = a[P++], C[t.UNPACKED_PITCH_EG_RATE_3] = a[P++], C[t.UNPACKED_PITCH_EG_RATE_4] = a[P++], C[t.UNPACKED_PITCH_EG_LEVEL_1] = a[P++], C[t.UNPACKED_PITCH_EG_LEVEL_2] = a[P++], C[t.UNPACKED_PITCH_EG_LEVEL_3] = a[P++], C[t.UNPACKED_PITCH_EG_LEVEL_4] = a[P++], C[t.UNPACKED_ALGORITHM] = a[P++], C[t.UNPACKED_FEEDBACK] = a[P++], C[t.UNPACKED_OSC_SYNC] = a[P++], C[t.UNPACKED_LFO_SPEED] = a[P++], C[t.UNPACKED_LFO_DELAY] = a[P++], C[t.UNPACKED_LFO_PM_DEPTH] = a[P++], C[t.UNPACKED_LFO_AM_DEPTH] = a[P++], C[t.UNPACKED_LFO_KEY_SYNC] = a[P++], C[t.UNPACKED_LFO_WAVE] = a[P++], C[t.UNPACKED_LFO_PM_SENS] = a[P++], C[t.UNPACKED_TRANSPOSE] = a[P++];
|
|
1569
|
+
for (let l = 0; l < t.NAME_LENGTH; l++)
|
|
1570
|
+
C[t.UNPACKED_NAME_START + l] = a[P++];
|
|
1571
|
+
const i = t.pack(C);
|
|
1572
|
+
s(new t(i, 0));
|
|
1573
|
+
} catch (e) {
|
|
1574
|
+
n(e);
|
|
1575
1575
|
}
|
|
1576
|
-
}, _.onerror = () =>
|
|
1576
|
+
}, _.onerror = () => n(new Error("Failed to read file")), _.readAsArrayBuffer(E);
|
|
1577
1577
|
});
|
|
1578
1578
|
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Create a DX7Voice from SysEx data
|
|
1581
|
+
* @param {Array<number>|ArrayBuffer|Uint8Array} data - Voice data (128 bytes of packed voice data) or VCED SysEx data (163 bytes with header/footer)
|
|
1582
|
+
* @param {number} index - Voice index (optional, defaults to 0)
|
|
1583
|
+
* @returns {DX7Voice}
|
|
1584
|
+
* @throws {DX7ParseError} If VCED header is invalid
|
|
1585
|
+
* @throws {DX7ValidationError} If data length is invalid
|
|
1586
|
+
*/
|
|
1587
|
+
static fromSysEx(E, s = 0) {
|
|
1588
|
+
const n = E instanceof Uint8Array ? E : new Uint8Array(E);
|
|
1589
|
+
let _;
|
|
1590
|
+
if (n[0] === t.VCED_SYSEX_START) {
|
|
1591
|
+
if (n[0] !== t.VCED_SYSEX_START || n[1] !== t.VCED_YAMAHA_ID || n[2] !== t.VCED_SUB_STATUS || n[3] !== t.VCED_FORMAT_SINGLE || n[4] !== t.VCED_BYTE_COUNT_MSB || n[5] !== t.VCED_BYTE_COUNT_LSB)
|
|
1592
|
+
throw new m("Invalid VCED header", "header", 0);
|
|
1593
|
+
_ = n.subarray(t.VCED_HEADER_SIZE, t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE);
|
|
1594
|
+
} else if (n.length === t.PACKED_SIZE)
|
|
1595
|
+
_ = n;
|
|
1596
|
+
else
|
|
1597
|
+
throw new o(
|
|
1598
|
+
`Invalid data length: expected ${t.PACKED_SIZE} or ${t.VCED_SIZE} bytes, got ${n.length}`,
|
|
1599
|
+
"length",
|
|
1600
|
+
n.length
|
|
1601
|
+
);
|
|
1602
|
+
if (_.length !== t.VCED_DATA_SIZE && _.length !== t.PACKED_SIZE)
|
|
1603
|
+
throw new o(
|
|
1604
|
+
`Invalid voice data length: expected ${t.VCED_DATA_SIZE} or ${t.PACKED_SIZE} bytes, got ${_.length}`,
|
|
1605
|
+
"length",
|
|
1606
|
+
_.length
|
|
1607
|
+
);
|
|
1608
|
+
if (_.length === t.VCED_DATA_SIZE) {
|
|
1609
|
+
const r = new Uint8Array(t.UNPACKED_SIZE);
|
|
1610
|
+
let e = 0;
|
|
1611
|
+
for (let T = 0; T < t.NUM_OPERATORS; T++) {
|
|
1612
|
+
const S = (t.NUM_OPERATORS - 1 - T) * t.UNPACKED_OP_SIZE;
|
|
1613
|
+
r[S + t.UNPACKED_OP_EG_RATE_1] = _[e++], r[S + t.UNPACKED_OP_EG_RATE_2] = _[e++], r[S + t.UNPACKED_OP_EG_RATE_3] = _[e++], r[S + t.UNPACKED_OP_EG_RATE_4] = _[e++], r[S + t.UNPACKED_OP_EG_LEVEL_1] = _[e++], r[S + t.UNPACKED_OP_EG_LEVEL_2] = _[e++], r[S + t.UNPACKED_OP_EG_LEVEL_3] = _[e++], r[S + t.UNPACKED_OP_EG_LEVEL_4] = _[e++], r[S + t.UNPACKED_OP_BREAK_POINT] = _[e++], r[S + t.UNPACKED_OP_L_SCALE_DEPTH] = _[e++], r[S + t.UNPACKED_OP_R_SCALE_DEPTH] = _[e++], r[S + t.UNPACKED_OP_L_CURVE] = _[e++], r[S + t.UNPACKED_OP_R_CURVE] = _[e++], r[S + t.UNPACKED_OP_RATE_SCALING] = _[e++], r[S + t.UNPACKED_OP_DETUNE] = _[e++];
|
|
1614
|
+
const C = _[e++];
|
|
1615
|
+
r[S + t.UNPACKED_OP_AMP_MOD_SENS] = C & t.MASK_2BIT, r[S + t.UNPACKED_OP_KEY_VEL_SENS] = C >> 2 & t.MASK_3BIT, r[S + t.UNPACKED_OP_OUTPUT_LEVEL] = _[e++], r[S + t.UNPACKED_OP_MODE] = _[e++], r[S + t.UNPACKED_OP_FREQ_COARSE] = _[e++], r[S + t.UNPACKED_OP_FREQ_FINE] = _[e++], r[S + t.UNPACKED_OP_OSC_DETUNE] = _[e++];
|
|
1616
|
+
}
|
|
1617
|
+
r[t.UNPACKED_PITCH_EG_RATE_1] = _[e++], r[t.UNPACKED_PITCH_EG_RATE_2] = _[e++], r[t.UNPACKED_PITCH_EG_RATE_3] = _[e++], r[t.UNPACKED_PITCH_EG_RATE_4] = _[e++], r[t.UNPACKED_PITCH_EG_LEVEL_1] = _[e++], r[t.UNPACKED_PITCH_EG_LEVEL_2] = _[e++], r[t.UNPACKED_PITCH_EG_LEVEL_3] = _[e++], r[t.UNPACKED_PITCH_EG_LEVEL_4] = _[e++], r[t.UNPACKED_ALGORITHM] = _[e++], r[t.UNPACKED_FEEDBACK] = _[e++], r[t.UNPACKED_OSC_SYNC] = _[e++], r[t.UNPACKED_LFO_SPEED] = _[e++], r[t.UNPACKED_LFO_DELAY] = _[e++], r[t.UNPACKED_LFO_PM_DEPTH] = _[e++], r[t.UNPACKED_LFO_AM_DEPTH] = _[e++], r[t.UNPACKED_LFO_KEY_SYNC] = _[e++], r[t.UNPACKED_LFO_WAVE] = _[e++], r[t.UNPACKED_LFO_PM_SENS] = _[e++], r[t.UNPACKED_TRANSPOSE] = _[e++];
|
|
1618
|
+
for (let T = 0; T < t.NAME_LENGTH; T++)
|
|
1619
|
+
r[t.UNPACKED_NAME_START + T] = _[e++];
|
|
1620
|
+
const a = t.pack(r);
|
|
1621
|
+
return new t(a, s);
|
|
1622
|
+
}
|
|
1623
|
+
return new t(_, s);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Create a DX7Voice from a JSON object
|
|
1627
|
+
* @param {DX7VoiceJSON} json - JSON representation of a DX7 voice
|
|
1628
|
+
* @param {number} index - Voice index (optional, defaults to 0)
|
|
1629
|
+
* @returns {DX7Voice}
|
|
1630
|
+
* @throws {DX7ValidationError} If JSON structure is invalid
|
|
1631
|
+
*/
|
|
1632
|
+
static fromJSON(E, s = 0) {
|
|
1633
|
+
if (!E || typeof E != "object")
|
|
1634
|
+
throw new o("Invalid JSON: expected object", "json", E);
|
|
1635
|
+
const n = new Uint8Array(t.UNPACKED_SIZE), _ = (P, i, l, O = 0, K = 127) => {
|
|
1636
|
+
if (i == null)
|
|
1637
|
+
throw new o(`Missing required parameter: ${l}`, l, i);
|
|
1638
|
+
const U = Number(i);
|
|
1639
|
+
if (Number.isNaN(U))
|
|
1640
|
+
throw new o(`Invalid parameter value for ${l}: ${i}`, l, i);
|
|
1641
|
+
if (U < O || U > K)
|
|
1642
|
+
throw new o(
|
|
1643
|
+
`Parameter ${l} out of range: ${U} (must be ${O}-${K})`,
|
|
1644
|
+
l,
|
|
1645
|
+
U
|
|
1646
|
+
);
|
|
1647
|
+
n[P] = Math.floor(U);
|
|
1648
|
+
}, r = (P) => {
|
|
1649
|
+
const i = { "-LN": 0, "-EX": 1, "+EX": 2, "+LN": 3 };
|
|
1650
|
+
return i[P] !== void 0 ? i[P] : 0;
|
|
1651
|
+
}, e = (P) => {
|
|
1652
|
+
const i = {
|
|
1653
|
+
TRIANGLE: 0,
|
|
1654
|
+
"SAW DOWN": 1,
|
|
1655
|
+
"SAW UP": 2,
|
|
1656
|
+
SQUARE: 3,
|
|
1657
|
+
SINE: 4,
|
|
1658
|
+
"SAMPLE & HOLD": 5
|
|
1659
|
+
};
|
|
1660
|
+
return i[P] !== void 0 ? i[P] : 0;
|
|
1661
|
+
}, a = (P) => {
|
|
1662
|
+
if (!P || typeof P != "string") return 60;
|
|
1663
|
+
const i = P.trim().match(/^([A-G]#?)(-?\d+)$/);
|
|
1664
|
+
if (!i) return 60;
|
|
1665
|
+
const [, l, O] = i, K = parseInt(O, 10), I = { C: 0, "C#": 1, D: 2, "D#": 3, E: 4, F: 5, "F#": 6, G: 7, "G#": 8, A: 9, "A#": 10, B: 11 }[l.toUpperCase()];
|
|
1666
|
+
return I === void 0 ? 60 : (K - t.MIDI_OCTAVE_OFFSET) * 12 + I;
|
|
1667
|
+
};
|
|
1668
|
+
if (!Array.isArray(E.operators))
|
|
1669
|
+
throw new o("Invalid operators array: expected array", "operators", E.operators);
|
|
1670
|
+
for (let P = 0; P < E.operators.length; P++) {
|
|
1671
|
+
const i = E.operators[P];
|
|
1672
|
+
if (!i || typeof i != "object")
|
|
1673
|
+
throw new o(`Invalid operator data at index ${P}`, `operators[${P}]`, i);
|
|
1674
|
+
if (!i.eg || !Array.isArray(i.eg.rates) || i.eg.rates.length !== 4)
|
|
1675
|
+
throw new o(
|
|
1676
|
+
`Invalid EG rates for operator ${P}`,
|
|
1677
|
+
`operators[${P}].eg.rates`,
|
|
1678
|
+
i.eg?.rates
|
|
1679
|
+
);
|
|
1680
|
+
if (!i.eg || !Array.isArray(i.eg.levels) || i.eg.levels.length !== 4)
|
|
1681
|
+
throw new o(
|
|
1682
|
+
`Invalid EG levels for operator ${P}`,
|
|
1683
|
+
`operators[${P}].eg.levels`,
|
|
1684
|
+
i.eg?.levels
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
if (E.operators.length !== t.NUM_OPERATORS)
|
|
1688
|
+
throw new o(
|
|
1689
|
+
`Invalid operators array: expected ${t.NUM_OPERATORS} operators`,
|
|
1690
|
+
"operators",
|
|
1691
|
+
E.operators
|
|
1692
|
+
);
|
|
1693
|
+
for (let P = 0; P < t.NUM_OPERATORS; P++) {
|
|
1694
|
+
const i = E.operators[P], l = P * t.UNPACKED_OP_SIZE;
|
|
1695
|
+
if (!i.eg || !Array.isArray(i.eg.rates) || i.eg.rates.length !== 4)
|
|
1696
|
+
throw new o(
|
|
1697
|
+
`Invalid EG rates for operator ${P}`,
|
|
1698
|
+
`operators[${P}].eg.rates`,
|
|
1699
|
+
i.eg?.rates
|
|
1700
|
+
);
|
|
1701
|
+
if (_(l + t.UNPACKED_OP_EG_RATE_1, i.eg.rates[0], `operators[${P}].eg.rates[0]`, 0, 99), _(l + t.UNPACKED_OP_EG_RATE_2, i.eg.rates[1], `operators[${P}].eg.rates[1]`, 0, 99), _(l + t.UNPACKED_OP_EG_RATE_3, i.eg.rates[2], `operators[${P}].eg.rates[2]`, 0, 99), _(l + t.UNPACKED_OP_EG_RATE_4, i.eg.rates[3], `operators[${P}].eg.rates[3]`, 0, 99), !i.eg || !Array.isArray(i.eg.levels) || i.eg.levels.length !== 4)
|
|
1702
|
+
throw new o(
|
|
1703
|
+
`Invalid EG levels for operator ${P}`,
|
|
1704
|
+
`operators[${P}].eg.levels`,
|
|
1705
|
+
i.eg?.levels
|
|
1706
|
+
);
|
|
1707
|
+
_(l + t.UNPACKED_OP_EG_LEVEL_1, i.eg.levels[0], `operators[${P}].eg.levels[0]`, 0, 99), _(l + t.UNPACKED_OP_EG_LEVEL_2, i.eg.levels[1], `operators[${P}].eg.levels[1]`, 0, 99), _(l + t.UNPACKED_OP_EG_LEVEL_3, i.eg.levels[2], `operators[${P}].eg.levels[2]`, 0, 99), _(l + t.UNPACKED_OP_EG_LEVEL_4, i.eg.levels[3], `operators[${P}].eg.levels[3]`, 0, 99);
|
|
1708
|
+
const O = a(i.key?.breakPoint) - t.MIDI_BREAK_POINT_OFFSET;
|
|
1709
|
+
_(l + t.UNPACKED_OP_BREAK_POINT, O, `operators[${P}].key.breakPoint`, 0, 127), _(
|
|
1710
|
+
l + t.UNPACKED_OP_L_SCALE_DEPTH,
|
|
1711
|
+
i.scale?.left?.depth || 0,
|
|
1712
|
+
`operators[${P}].scale.left.depth`,
|
|
1713
|
+
0,
|
|
1714
|
+
99
|
|
1715
|
+
), _(
|
|
1716
|
+
l + t.UNPACKED_OP_R_SCALE_DEPTH,
|
|
1717
|
+
i.scale?.right?.depth || 0,
|
|
1718
|
+
`operators[${P}].scale.right.depth`,
|
|
1719
|
+
0,
|
|
1720
|
+
99
|
|
1721
|
+
), n[l + t.UNPACKED_OP_L_CURVE] = r(i.scale?.left?.curve || "-LN"), n[l + t.UNPACKED_OP_R_CURVE] = r(i.scale?.right?.curve || "-LN"), _(
|
|
1722
|
+
l + t.UNPACKED_OP_RATE_SCALING,
|
|
1723
|
+
i.key?.scaling || 0,
|
|
1724
|
+
`operators[${P}].key.scaling`,
|
|
1725
|
+
0,
|
|
1726
|
+
7
|
|
1727
|
+
);
|
|
1728
|
+
const K = Number(i.osc?.detune) || 0;
|
|
1729
|
+
_(l + t.UNPACKED_OP_DETUNE, K + 7, `operators[${P}].osc.detune`, 0, 14), _(
|
|
1730
|
+
l + t.UNPACKED_OP_AMP_MOD_SENS,
|
|
1731
|
+
i.output?.ampModSens || 0,
|
|
1732
|
+
`operators[${P}].output.ampModSens`,
|
|
1733
|
+
0,
|
|
1734
|
+
3
|
|
1735
|
+
), _(
|
|
1736
|
+
l + t.UNPACKED_OP_OUTPUT_LEVEL,
|
|
1737
|
+
i.output?.level || 0,
|
|
1738
|
+
`operators[${P}].output.level`,
|
|
1739
|
+
0,
|
|
1740
|
+
99
|
|
1741
|
+
);
|
|
1742
|
+
const U = i.osc?.freq?.mode?.toUpperCase() === "FIXED" ? 1 : 0, I = Number(i.osc?.freq?.coarse) || 0, L = Number(i.osc?.freq?.fine) || 0;
|
|
1743
|
+
n[l + t.UNPACKED_OP_MODE] = U, _(l + t.UNPACKED_OP_FREQ_COARSE, I, `operators[${P}].osc.freq.coarse`, 0, 31), _(l + t.UNPACKED_OP_FREQ_FINE, L, `operators[${P}].osc.freq.fine`, 0, 15), _(
|
|
1744
|
+
l + t.UNPACKED_OP_KEY_VEL_SENS,
|
|
1745
|
+
i.key?.velocity || 0,
|
|
1746
|
+
`operators[${P}].key.velocity`,
|
|
1747
|
+
0,
|
|
1748
|
+
7
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
if (!E.pitchEG || !Array.isArray(E.pitchEG.rates) || E.pitchEG.rates.length !== 4)
|
|
1752
|
+
throw new o("Invalid pitch EG rates", "pitchEG.rates", E.pitchEG?.rates);
|
|
1753
|
+
if (!E.pitchEG || !Array.isArray(E.pitchEG.levels) || E.pitchEG.levels.length !== 4)
|
|
1754
|
+
throw new o("Invalid pitch EG levels", "pitchEG.levels", E.pitchEG?.levels);
|
|
1755
|
+
if (_(t.UNPACKED_PITCH_EG_RATE_1, E.pitchEG.rates[0], "pitchEG.rates[0]", 0, 99), _(t.UNPACKED_PITCH_EG_RATE_2, E.pitchEG.rates[1], "pitchEG.rates[1]", 0, 99), _(t.UNPACKED_PITCH_EG_RATE_3, E.pitchEG.rates[2], "pitchEG.rates[2]", 0, 99), _(t.UNPACKED_PITCH_EG_RATE_4, E.pitchEG.rates[3], "pitchEG.rates[3]", 0, 99), _(t.UNPACKED_PITCH_EG_LEVEL_1, E.pitchEG.levels[0], "pitchEG.levels[0]", 0, 99), _(t.UNPACKED_PITCH_EG_LEVEL_2, E.pitchEG.levels[1], "pitchEG.levels[1]", 0, 99), _(t.UNPACKED_PITCH_EG_LEVEL_3, E.pitchEG.levels[2], "pitchEG.levels[2]", 0, 99), _(t.UNPACKED_PITCH_EG_LEVEL_4, E.pitchEG.levels[3], "pitchEG.levels[3]", 0, 99), !E.lfo || typeof E.lfo != "object")
|
|
1756
|
+
throw new o("Invalid LFO data", "lfo", E.lfo);
|
|
1757
|
+
if (_(t.UNPACKED_LFO_SPEED, E.lfo.speed, "lfo.speed", 0, 99), _(t.UNPACKED_LFO_DELAY, E.lfo.delay, "lfo.delay", 0, 99), _(t.UNPACKED_LFO_PM_DEPTH, E.lfo.pmDepth, "lfo.pmDepth", 0, 99), _(t.UNPACKED_LFO_AM_DEPTH, E.lfo.amDepth, "lfo.amDepth", 0, 99), n[t.UNPACKED_LFO_KEY_SYNC] = E.lfo.keySync ? 1 : 0, n[t.UNPACKED_LFO_WAVE] = e(E.lfo.wave), !E.global || typeof E.global != "object")
|
|
1758
|
+
throw new o("Invalid global data", "global", E.global);
|
|
1759
|
+
const T = Number(E.global.algorithm) || 1;
|
|
1760
|
+
_(t.UNPACKED_ALGORITHM, T - 1, "global.algorithm", 0, 31), _(t.UNPACKED_FEEDBACK, E.global.feedback, "global.feedback", 0, 7), n[t.UNPACKED_OSC_SYNC] = E.global.oscKeySync ? 1 : 0, _(t.UNPACKED_LFO_PM_SENS, E.global.pitchModSens, "global.pitchModSens", 0, 7);
|
|
1761
|
+
const S = Number(E.global.transpose) || 0;
|
|
1762
|
+
_(t.UNPACKED_TRANSPOSE, S + t.TRANSPOSE_CENTER, "global.transpose", 0, 127), _(t.UNPACKED_AMP_MOD_SENS, E.global.ampModSens || 0, "global.ampModSens", 0, 3), _(t.UNPACKED_EG_BIAS_SENS, E.global.egBiasSens || 0, "global.egBiasSens", 0, 7);
|
|
1763
|
+
const C = E.name || "";
|
|
1764
|
+
for (let P = 0; P < t.NAME_LENGTH; P++)
|
|
1765
|
+
n[t.UNPACKED_NAME_START + P] = P < C.length ? C.charCodeAt(P) : t.CHAR_SPACE;
|
|
1766
|
+
return t.fromUnpacked(n, s);
|
|
1767
|
+
}
|
|
1579
1768
|
/**
|
|
1580
1769
|
* Export voice to DX7 single voice SysEx format (VCED format)
|
|
1581
1770
|
* This is useful for synths that only support single voice dumps (e.g., KORG Volca FM)
|
|
@@ -1583,118 +1772,118 @@ class E {
|
|
|
1583
1772
|
* @returns {Uint8Array} Single voice SysEx data (163 bytes)
|
|
1584
1773
|
*/
|
|
1585
1774
|
toSysEx() {
|
|
1586
|
-
const
|
|
1587
|
-
let
|
|
1588
|
-
n
|
|
1589
|
-
for (let
|
|
1590
|
-
const
|
|
1591
|
-
n
|
|
1592
|
-
const
|
|
1593
|
-
n
|
|
1775
|
+
const E = this.unpack(), s = new Uint8Array(t.VCED_SIZE);
|
|
1776
|
+
let n = 0;
|
|
1777
|
+
s[n++] = t.VCED_SYSEX_START, s[n++] = t.VCED_YAMAHA_ID, s[n++] = t.VCED_SUB_STATUS, s[n++] = t.VCED_FORMAT_SINGLE, s[n++] = t.VCED_BYTE_COUNT_MSB, s[n++] = t.VCED_BYTE_COUNT_LSB;
|
|
1778
|
+
for (let r = t.NUM_OPERATORS - 1; r >= 0; r--) {
|
|
1779
|
+
const e = r * t.UNPACKED_OP_SIZE;
|
|
1780
|
+
s[n++] = E[e + t.UNPACKED_OP_EG_RATE_1], s[n++] = E[e + t.UNPACKED_OP_EG_RATE_2], s[n++] = E[e + t.UNPACKED_OP_EG_RATE_3], s[n++] = E[e + t.UNPACKED_OP_EG_RATE_4], s[n++] = E[e + t.UNPACKED_OP_EG_LEVEL_1], s[n++] = E[e + t.UNPACKED_OP_EG_LEVEL_2], s[n++] = E[e + t.UNPACKED_OP_EG_LEVEL_3], s[n++] = E[e + t.UNPACKED_OP_EG_LEVEL_4], s[n++] = E[e + t.UNPACKED_OP_BREAK_POINT], s[n++] = E[e + t.UNPACKED_OP_L_SCALE_DEPTH], s[n++] = E[e + t.UNPACKED_OP_R_SCALE_DEPTH], s[n++] = E[e + t.UNPACKED_OP_L_CURVE], s[n++] = E[e + t.UNPACKED_OP_R_CURVE], s[n++] = E[e + t.UNPACKED_OP_RATE_SCALING], s[n++] = E[e + t.UNPACKED_OP_DETUNE];
|
|
1781
|
+
const a = E[e + t.UNPACKED_OP_AMP_MOD_SENS] & t.MASK_2BIT, T = E[e + t.UNPACKED_OP_KEY_VEL_SENS] & t.MASK_3BIT;
|
|
1782
|
+
s[n++] = a | T << 2, s[n++] = E[e + t.UNPACKED_OP_OUTPUT_LEVEL], s[n++] = E[e + t.UNPACKED_OP_MODE], s[n++] = E[e + t.UNPACKED_OP_FREQ_COARSE], s[n++] = E[e + t.UNPACKED_OP_OSC_DETUNE], s[n++] = E[e + t.UNPACKED_OP_FREQ_FINE];
|
|
1594
1783
|
}
|
|
1595
|
-
n
|
|
1596
|
-
for (let
|
|
1597
|
-
n
|
|
1598
|
-
const _ =
|
|
1599
|
-
|
|
1600
|
-
|
|
1784
|
+
s[n++] = E[t.UNPACKED_PITCH_EG_RATE_1], s[n++] = E[t.UNPACKED_PITCH_EG_RATE_2], s[n++] = E[t.UNPACKED_PITCH_EG_RATE_3], s[n++] = E[t.UNPACKED_PITCH_EG_RATE_4], s[n++] = E[t.UNPACKED_PITCH_EG_LEVEL_1], s[n++] = E[t.UNPACKED_PITCH_EG_LEVEL_2], s[n++] = E[t.UNPACKED_PITCH_EG_LEVEL_3], s[n++] = E[t.UNPACKED_PITCH_EG_LEVEL_4], s[n++] = E[t.UNPACKED_ALGORITHM], s[n++] = E[t.UNPACKED_FEEDBACK], s[n++] = E[t.UNPACKED_OSC_SYNC], s[n++] = E[t.UNPACKED_LFO_SPEED], s[n++] = E[t.UNPACKED_LFO_DELAY], s[n++] = E[t.UNPACKED_LFO_PM_DEPTH], s[n++] = E[t.UNPACKED_LFO_AM_DEPTH], s[n++] = E[t.UNPACKED_LFO_KEY_SYNC], s[n++] = E[t.UNPACKED_LFO_WAVE], s[n++] = E[t.UNPACKED_LFO_PM_SENS], s[n++] = E[t.UNPACKED_TRANSPOSE];
|
|
1785
|
+
for (let r = 0; r < t.NAME_LENGTH; r++)
|
|
1786
|
+
s[n++] = E[t.UNPACKED_NAME_START + r];
|
|
1787
|
+
const _ = s.subarray(
|
|
1788
|
+
t.VCED_HEADER_SIZE,
|
|
1789
|
+
t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE
|
|
1601
1790
|
);
|
|
1602
|
-
return n
|
|
1791
|
+
return s[n++] = N._calculateChecksum(_, t.VCED_DATA_SIZE), s[n++] = t.VCED_SYSEX_END, s;
|
|
1603
1792
|
}
|
|
1604
1793
|
/**
|
|
1605
1794
|
* Convert voice to JSON format
|
|
1606
1795
|
* @returns {object} Voice data in JSON format
|
|
1607
1796
|
*/
|
|
1608
1797
|
toJSON() {
|
|
1609
|
-
const
|
|
1610
|
-
const
|
|
1611
|
-
return `${
|
|
1798
|
+
const E = this.unpack(), s = [], n = (e) => ["-LN", "-EX", "+EX", "+LN"][e] || "UNKNOWN", _ = (e) => ["TRIANGLE", "SAW DOWN", "SAW UP", "SQUARE", "SINE", "SAMPLE & HOLD"][e] || "UNKNOWN", r = (e) => {
|
|
1799
|
+
const a = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"], T = Math.floor(e / 12) + t.MIDI_OCTAVE_OFFSET;
|
|
1800
|
+
return `${a[e % 12]}${T}`;
|
|
1612
1801
|
};
|
|
1613
|
-
for (let
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
id:
|
|
1802
|
+
for (let e = 0; e < t.NUM_OPERATORS; e++) {
|
|
1803
|
+
const a = e * t.UNPACKED_OP_SIZE, T = E[a + t.UNPACKED_OP_MODE] === 0 ? "RATIO" : "FIXED";
|
|
1804
|
+
s.push({
|
|
1805
|
+
id: e + 1,
|
|
1617
1806
|
osc: {
|
|
1618
|
-
detune:
|
|
1807
|
+
detune: E[a + t.UNPACKED_OP_OSC_DETUNE],
|
|
1619
1808
|
freq: {
|
|
1620
|
-
coarse:
|
|
1621
|
-
fine:
|
|
1809
|
+
coarse: E[a + t.UNPACKED_OP_FREQ_COARSE],
|
|
1810
|
+
fine: E[a + t.UNPACKED_OP_FREQ_FINE],
|
|
1622
1811
|
mode: T
|
|
1623
1812
|
}
|
|
1624
1813
|
},
|
|
1625
1814
|
eg: {
|
|
1626
1815
|
rates: [
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1816
|
+
E[a + t.UNPACKED_OP_EG_RATE_1],
|
|
1817
|
+
E[a + t.UNPACKED_OP_EG_RATE_2],
|
|
1818
|
+
E[a + t.UNPACKED_OP_EG_RATE_3],
|
|
1819
|
+
E[a + t.UNPACKED_OP_EG_RATE_4]
|
|
1631
1820
|
],
|
|
1632
1821
|
levels: [
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1822
|
+
E[a + t.UNPACKED_OP_EG_LEVEL_1],
|
|
1823
|
+
E[a + t.UNPACKED_OP_EG_LEVEL_2],
|
|
1824
|
+
E[a + t.UNPACKED_OP_EG_LEVEL_3],
|
|
1825
|
+
E[a + t.UNPACKED_OP_EG_LEVEL_4]
|
|
1637
1826
|
]
|
|
1638
1827
|
},
|
|
1639
1828
|
key: {
|
|
1640
|
-
velocity:
|
|
1641
|
-
scaling:
|
|
1642
|
-
breakPoint:
|
|
1643
|
-
|
|
1829
|
+
velocity: E[a + t.UNPACKED_OP_KEY_VEL_SENS],
|
|
1830
|
+
scaling: E[a + t.UNPACKED_OP_RATE_SCALING],
|
|
1831
|
+
breakPoint: r(
|
|
1832
|
+
E[a + t.UNPACKED_OP_BREAK_POINT] + t.MIDI_BREAK_POINT_OFFSET
|
|
1644
1833
|
)
|
|
1645
1834
|
},
|
|
1646
1835
|
output: {
|
|
1647
|
-
level:
|
|
1648
|
-
ampModSens:
|
|
1836
|
+
level: E[a + t.UNPACKED_OP_OUTPUT_LEVEL],
|
|
1837
|
+
ampModSens: E[a + t.UNPACKED_OP_AMP_MOD_SENS]
|
|
1649
1838
|
},
|
|
1650
1839
|
scale: {
|
|
1651
1840
|
left: {
|
|
1652
|
-
depth:
|
|
1653
|
-
curve:
|
|
1841
|
+
depth: E[a + t.UNPACKED_OP_L_SCALE_DEPTH],
|
|
1842
|
+
curve: n(E[a + t.UNPACKED_OP_L_CURVE])
|
|
1654
1843
|
},
|
|
1655
1844
|
right: {
|
|
1656
|
-
depth:
|
|
1657
|
-
curve:
|
|
1845
|
+
depth: E[a + t.UNPACKED_OP_R_SCALE_DEPTH],
|
|
1846
|
+
curve: n(E[a + t.UNPACKED_OP_R_CURVE])
|
|
1658
1847
|
}
|
|
1659
1848
|
}
|
|
1660
1849
|
});
|
|
1661
1850
|
}
|
|
1662
1851
|
return {
|
|
1663
1852
|
name: this.name || "(Empty)",
|
|
1664
|
-
operators:
|
|
1853
|
+
operators: s,
|
|
1665
1854
|
pitchEG: {
|
|
1666
1855
|
rates: [
|
|
1667
|
-
t
|
|
1668
|
-
t
|
|
1669
|
-
t
|
|
1670
|
-
t
|
|
1856
|
+
E[t.UNPACKED_PITCH_EG_RATE_1],
|
|
1857
|
+
E[t.UNPACKED_PITCH_EG_RATE_2],
|
|
1858
|
+
E[t.UNPACKED_PITCH_EG_RATE_3],
|
|
1859
|
+
E[t.UNPACKED_PITCH_EG_RATE_4]
|
|
1671
1860
|
],
|
|
1672
1861
|
levels: [
|
|
1673
|
-
t
|
|
1674
|
-
t
|
|
1675
|
-
t
|
|
1676
|
-
t
|
|
1862
|
+
E[t.UNPACKED_PITCH_EG_LEVEL_1],
|
|
1863
|
+
E[t.UNPACKED_PITCH_EG_LEVEL_2],
|
|
1864
|
+
E[t.UNPACKED_PITCH_EG_LEVEL_3],
|
|
1865
|
+
E[t.UNPACKED_PITCH_EG_LEVEL_4]
|
|
1677
1866
|
]
|
|
1678
1867
|
},
|
|
1679
1868
|
lfo: {
|
|
1680
|
-
speed: t
|
|
1681
|
-
delay: t
|
|
1682
|
-
pmDepth: t
|
|
1683
|
-
amDepth: t
|
|
1684
|
-
keySync: t
|
|
1685
|
-
wave: _(t
|
|
1869
|
+
speed: E[t.UNPACKED_LFO_SPEED],
|
|
1870
|
+
delay: E[t.UNPACKED_LFO_DELAY],
|
|
1871
|
+
pmDepth: E[t.UNPACKED_LFO_PM_DEPTH],
|
|
1872
|
+
amDepth: E[t.UNPACKED_LFO_AM_DEPTH],
|
|
1873
|
+
keySync: E[t.UNPACKED_LFO_KEY_SYNC] === 1,
|
|
1874
|
+
wave: _(E[t.UNPACKED_LFO_WAVE])
|
|
1686
1875
|
},
|
|
1687
1876
|
global: {
|
|
1688
|
-
algorithm: t
|
|
1689
|
-
feedback: t
|
|
1690
|
-
oscKeySync: t
|
|
1691
|
-
pitchModSens: t
|
|
1692
|
-
transpose: t
|
|
1877
|
+
algorithm: E[t.UNPACKED_ALGORITHM] + 1,
|
|
1878
|
+
feedback: E[t.UNPACKED_FEEDBACK],
|
|
1879
|
+
oscKeySync: E[t.UNPACKED_OSC_SYNC] === 1,
|
|
1880
|
+
pitchModSens: E[t.UNPACKED_LFO_PM_SENS],
|
|
1881
|
+
transpose: E[t.UNPACKED_TRANSPOSE] - t.TRANSPOSE_CENTER
|
|
1693
1882
|
}
|
|
1694
1883
|
};
|
|
1695
1884
|
}
|
|
1696
1885
|
}
|
|
1697
|
-
class
|
|
1886
|
+
class N {
|
|
1698
1887
|
// SysEx header
|
|
1699
1888
|
static SYSEX_START = 240;
|
|
1700
1889
|
static SYSEX_END = 247;
|
|
@@ -1704,12 +1893,12 @@ class C {
|
|
|
1704
1893
|
static SYSEX_BYTE_COUNT_MSB = 32;
|
|
1705
1894
|
static SYSEX_BYTE_COUNT_LSB = 0;
|
|
1706
1895
|
static SYSEX_HEADER = [
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1896
|
+
N.SYSEX_START,
|
|
1897
|
+
N.SYSEX_YAMAHA_ID,
|
|
1898
|
+
N.SYSEX_SUB_STATUS,
|
|
1899
|
+
N.SYSEX_FORMAT_32_VOICES,
|
|
1900
|
+
N.SYSEX_BYTE_COUNT_MSB,
|
|
1901
|
+
N.SYSEX_BYTE_COUNT_LSB
|
|
1713
1902
|
];
|
|
1714
1903
|
static SYSEX_HEADER_SIZE = 6;
|
|
1715
1904
|
// Bank structure
|
|
@@ -1728,12 +1917,12 @@ class C {
|
|
|
1728
1917
|
* @param {Array<number>|ArrayBuffer|Uint8Array} data - Bank SYX data (optional)
|
|
1729
1918
|
* @param {string} name - Optional bank name (e.g., filename)
|
|
1730
1919
|
*/
|
|
1731
|
-
constructor(
|
|
1732
|
-
if (this.voices = new Array(
|
|
1733
|
-
this._load(
|
|
1920
|
+
constructor(E, s = "") {
|
|
1921
|
+
if (this.voices = new Array(N.NUM_VOICES), this.name = s, E)
|
|
1922
|
+
this._load(E);
|
|
1734
1923
|
else
|
|
1735
|
-
for (let
|
|
1736
|
-
this.voices[
|
|
1924
|
+
for (let n = 0; n < N.NUM_VOICES; n++)
|
|
1925
|
+
this.voices[n] = t.createDefault(n);
|
|
1737
1926
|
}
|
|
1738
1927
|
/**
|
|
1739
1928
|
* Calculate DX7 SysEx checksum
|
|
@@ -1742,55 +1931,55 @@ class C {
|
|
|
1742
1931
|
* @param {number} size - Number of bytes
|
|
1743
1932
|
* @returns {number} Checksum byte
|
|
1744
1933
|
*/
|
|
1745
|
-
static _calculateChecksum(
|
|
1746
|
-
let
|
|
1747
|
-
for (let _ = 0; _ <
|
|
1748
|
-
|
|
1749
|
-
return
|
|
1934
|
+
static _calculateChecksum(E, s) {
|
|
1935
|
+
let n = 0;
|
|
1936
|
+
for (let _ = 0; _ < s; _++)
|
|
1937
|
+
n += E[_];
|
|
1938
|
+
return N.CHECKSUM_MODULO - n % N.CHECKSUM_MODULO & N.MASK_7BIT;
|
|
1750
1939
|
}
|
|
1751
1940
|
/**
|
|
1752
1941
|
* Load and validate bank data
|
|
1753
1942
|
* @private
|
|
1754
1943
|
* @param {Array<number>|ArrayBuffer|Uint8Array} data
|
|
1755
1944
|
*/
|
|
1756
|
-
_load(
|
|
1757
|
-
const
|
|
1758
|
-
let
|
|
1759
|
-
if (
|
|
1760
|
-
const
|
|
1761
|
-
for (let T = 0; T <
|
|
1762
|
-
if (
|
|
1763
|
-
throw new
|
|
1764
|
-
`Invalid SysEx header at position ${T}: expected ${
|
|
1945
|
+
_load(E) {
|
|
1946
|
+
const s = E instanceof Uint8Array ? E : new Uint8Array(E);
|
|
1947
|
+
let n, _ = 0;
|
|
1948
|
+
if (s[0] === N.SYSEX_START) {
|
|
1949
|
+
const e = s.subarray(0, N.SYSEX_HEADER_SIZE), a = N.SYSEX_HEADER;
|
|
1950
|
+
for (let T = 0; T < N.SYSEX_HEADER_SIZE; T++)
|
|
1951
|
+
if (e[T] !== a[T])
|
|
1952
|
+
throw new m(
|
|
1953
|
+
`Invalid SysEx header at position ${T}: expected ${a[T].toString(16)}, got ${e[T].toString(16)}`,
|
|
1765
1954
|
"header",
|
|
1766
1955
|
T
|
|
1767
1956
|
);
|
|
1768
|
-
|
|
1769
|
-
} else if (
|
|
1770
|
-
|
|
1957
|
+
n = s.subarray(N.SYSEX_HEADER_SIZE, N.SYSEX_HEADER_SIZE + N.VOICE_DATA_SIZE), _ = N.SYSEX_HEADER_SIZE;
|
|
1958
|
+
} else if (s.length === N.VOICE_DATA_SIZE)
|
|
1959
|
+
n = s;
|
|
1771
1960
|
else
|
|
1772
|
-
throw new
|
|
1773
|
-
`Invalid data length: expected ${
|
|
1961
|
+
throw new o(
|
|
1962
|
+
`Invalid data length: expected ${N.VOICE_DATA_SIZE} or ${N.SYSEX_SIZE} bytes, got ${s.length}`,
|
|
1774
1963
|
"length",
|
|
1775
|
-
|
|
1964
|
+
s.length
|
|
1776
1965
|
);
|
|
1777
|
-
if (
|
|
1778
|
-
throw new
|
|
1779
|
-
`Invalid voice data length: expected ${
|
|
1966
|
+
if (n.length !== N.VOICE_DATA_SIZE)
|
|
1967
|
+
throw new o(
|
|
1968
|
+
`Invalid voice data length: expected ${N.VOICE_DATA_SIZE} bytes, got ${n.length}`,
|
|
1780
1969
|
"length",
|
|
1781
|
-
|
|
1970
|
+
n.length
|
|
1782
1971
|
);
|
|
1783
|
-
const
|
|
1784
|
-
if (_ > 0 &&
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
`DX7 checksum mismatch (expected ${
|
|
1972
|
+
const r = N.SYSEX_HEADER_SIZE + N.VOICE_DATA_SIZE;
|
|
1973
|
+
if (_ > 0 && s.length >= r + 1) {
|
|
1974
|
+
const e = s[r], a = N._calculateChecksum(n, N.VOICE_DATA_SIZE);
|
|
1975
|
+
e !== a && console.warn(
|
|
1976
|
+
`DX7 checksum mismatch (expected ${a.toString(16)}, got ${e.toString(16)}). This is common with vintage SysEx files and the data is likely still valid.`
|
|
1788
1977
|
);
|
|
1789
1978
|
}
|
|
1790
|
-
this.voices = new Array(
|
|
1791
|
-
for (let
|
|
1792
|
-
const
|
|
1793
|
-
this.voices[
|
|
1979
|
+
this.voices = new Array(N.NUM_VOICES);
|
|
1980
|
+
for (let e = 0; e < N.NUM_VOICES; e++) {
|
|
1981
|
+
const a = e * N.VOICE_SIZE, T = n.subarray(a, a + N.VOICE_SIZE);
|
|
1982
|
+
this.voices[e] = new t(T, e);
|
|
1794
1983
|
}
|
|
1795
1984
|
}
|
|
1796
1985
|
/**
|
|
@@ -1799,22 +1988,22 @@ class C {
|
|
|
1799
1988
|
* @param {DX7Voice} voice - Voice to insert
|
|
1800
1989
|
* @throws {DX7ValidationError} If index is out of range
|
|
1801
1990
|
*/
|
|
1802
|
-
replaceVoice(
|
|
1803
|
-
if (
|
|
1804
|
-
throw new
|
|
1805
|
-
const
|
|
1806
|
-
this.voices[
|
|
1991
|
+
replaceVoice(E, s) {
|
|
1992
|
+
if (E < 0 || E >= N.NUM_VOICES)
|
|
1993
|
+
throw new o(`Invalid voice index: ${E}`, "index", E);
|
|
1994
|
+
const n = new Uint8Array(s.data);
|
|
1995
|
+
this.voices[E] = new t(n, E);
|
|
1807
1996
|
}
|
|
1808
1997
|
/**
|
|
1809
1998
|
* Add a voice to the first empty slot
|
|
1810
1999
|
* @param {DX7Voice} voice - Voice to add
|
|
1811
2000
|
* @returns {number} Index where voice was added, or -1 if bank is full
|
|
1812
2001
|
*/
|
|
1813
|
-
addVoice(
|
|
1814
|
-
for (let
|
|
1815
|
-
const
|
|
1816
|
-
if (
|
|
1817
|
-
return this.replaceVoice(
|
|
2002
|
+
addVoice(E) {
|
|
2003
|
+
for (let s = 0; s < this.voices.length; s++) {
|
|
2004
|
+
const n = this.voices[s];
|
|
2005
|
+
if (n.name === "" || n.name === "Init Voice")
|
|
2006
|
+
return this.replaceVoice(s, E), s;
|
|
1818
2007
|
}
|
|
1819
2008
|
return -1;
|
|
1820
2009
|
}
|
|
@@ -1830,24 +2019,24 @@ class C {
|
|
|
1830
2019
|
* @param {number} index - Voice index (0-31)
|
|
1831
2020
|
* @returns {DX7Voice|null}
|
|
1832
2021
|
*/
|
|
1833
|
-
getVoice(
|
|
1834
|
-
return
|
|
2022
|
+
getVoice(E) {
|
|
2023
|
+
return E < 0 || E >= this.voices.length ? null : this.voices[E];
|
|
1835
2024
|
}
|
|
1836
2025
|
/**
|
|
1837
2026
|
* Get all voice names
|
|
1838
2027
|
* @returns {string[]}
|
|
1839
2028
|
*/
|
|
1840
2029
|
getVoiceNames() {
|
|
1841
|
-
return this.voices.map((
|
|
2030
|
+
return this.voices.map((E) => E.name);
|
|
1842
2031
|
}
|
|
1843
2032
|
/**
|
|
1844
2033
|
* Find a voice by name (case-insensitive, partial match)
|
|
1845
2034
|
* @param {string} name - Voice name to search for
|
|
1846
2035
|
* @returns {DX7Voice|null}
|
|
1847
2036
|
*/
|
|
1848
|
-
findVoiceByName(
|
|
1849
|
-
const
|
|
1850
|
-
return this.voices.find((
|
|
2037
|
+
findVoiceByName(E) {
|
|
2038
|
+
const s = E.toLowerCase();
|
|
2039
|
+
return this.voices.find((n) => n.name.toLowerCase().includes(s)) || null;
|
|
1851
2040
|
}
|
|
1852
2041
|
/**
|
|
1853
2042
|
* Load a DX7 bank from a file
|
|
@@ -1857,216 +2046,258 @@ class C {
|
|
|
1857
2046
|
* @throws {DX7ValidationError} If data is not valid DX7 SYX format
|
|
1858
2047
|
* @throws {Error} If file cannot be read (FileReader error)
|
|
1859
2048
|
*/
|
|
1860
|
-
static async fromFile(
|
|
1861
|
-
return new Promise((
|
|
2049
|
+
static async fromFile(E) {
|
|
2050
|
+
return new Promise((s, n) => {
|
|
1862
2051
|
const _ = new FileReader();
|
|
1863
|
-
_.onload = async (
|
|
2052
|
+
_.onload = async (r) => {
|
|
1864
2053
|
try {
|
|
1865
|
-
const
|
|
1866
|
-
if (
|
|
1867
|
-
|
|
2054
|
+
const e = E.name || "", a = new Uint8Array(r.target.result);
|
|
2055
|
+
if (a[0] === N.SYSEX_START && a[3] === t.VCED_FORMAT_SINGLE)
|
|
2056
|
+
n(new m("This is a single voice file. Use DX7Voice.fromFile() instead.", "format", 3));
|
|
1868
2057
|
else {
|
|
1869
|
-
const T =
|
|
1870
|
-
|
|
2058
|
+
const T = e.replace(/\.[^/.]+$/, ""), S = new N(r.target.result, T);
|
|
2059
|
+
s(S);
|
|
1871
2060
|
}
|
|
1872
|
-
} catch (
|
|
1873
|
-
|
|
2061
|
+
} catch (e) {
|
|
2062
|
+
n(e);
|
|
1874
2063
|
}
|
|
1875
|
-
}, _.onerror = () =>
|
|
2064
|
+
}, _.onerror = () => n(new Error("Failed to read file")), _.readAsArrayBuffer(E);
|
|
1876
2065
|
});
|
|
1877
2066
|
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Create a DX7Bank from SysEx data
|
|
2069
|
+
* @param {Array<number>|ArrayBuffer|Uint8Array} data - SysEx data (4104 bytes with header/footer) or raw voice data (4096 bytes)
|
|
2070
|
+
* @param {string} name - Optional bank name
|
|
2071
|
+
* @returns {DX7Bank}
|
|
2072
|
+
* @throws {DX7ParseError} If SysEx header is invalid
|
|
2073
|
+
* @throws {DX7ValidationError} If data length is invalid
|
|
2074
|
+
*/
|
|
2075
|
+
static fromSysEx(E, s = "") {
|
|
2076
|
+
return new N(E, s);
|
|
2077
|
+
}
|
|
2078
|
+
/**
|
|
2079
|
+
* Create a DX7Bank from a JSON object
|
|
2080
|
+
* @param {DX7BankJSON} json - JSON representation of a DX7 bank
|
|
2081
|
+
* @returns {DX7Bank}
|
|
2082
|
+
* @throws {DX7ValidationError} If JSON structure is invalid
|
|
2083
|
+
*/
|
|
2084
|
+
static fromJSON(E) {
|
|
2085
|
+
if (!E || typeof E != "object")
|
|
2086
|
+
throw new o("Invalid JSON: expected object", "json", E);
|
|
2087
|
+
const s = new N();
|
|
2088
|
+
if (s.name = E.name || "", !Array.isArray(E.voices))
|
|
2089
|
+
throw new o("Invalid voices array", "voices", E.voices);
|
|
2090
|
+
E.voices.length !== N.NUM_VOICES && console.warn(
|
|
2091
|
+
`Bank JSON has ${E.voices.length} voices, expected ${N.NUM_VOICES}. Missing voices will be filled with defaults.`
|
|
2092
|
+
);
|
|
2093
|
+
const n = Math.min(E.voices.length, N.NUM_VOICES);
|
|
2094
|
+
for (let _ = 0; _ < n; _++) {
|
|
2095
|
+
const r = E.voices[_];
|
|
2096
|
+
if (!r || typeof r != "object") {
|
|
2097
|
+
console.warn(`Invalid voice data at index ${_}, using default voice`);
|
|
2098
|
+
continue;
|
|
2099
|
+
}
|
|
2100
|
+
try {
|
|
2101
|
+
const { index: e, ...a } = r, T = t.fromJSON(a, _);
|
|
2102
|
+
s.replaceVoice(_, T);
|
|
2103
|
+
} catch (e) {
|
|
2104
|
+
console.warn(`Failed to load voice at index ${_}: ${e.message}, using default voice`);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
return s;
|
|
2108
|
+
}
|
|
1878
2109
|
/**
|
|
1879
2110
|
* Export bank to SysEx format
|
|
1880
2111
|
* @returns {Uint8Array} Full SysEx data (4104 bytes)
|
|
1881
2112
|
*/
|
|
1882
2113
|
toSysEx() {
|
|
1883
|
-
const
|
|
1884
|
-
let
|
|
1885
|
-
|
|
1886
|
-
|
|
2114
|
+
const E = new Uint8Array(N.SYSEX_SIZE);
|
|
2115
|
+
let s = 0;
|
|
2116
|
+
N.SYSEX_HEADER.forEach((_) => {
|
|
2117
|
+
E[s++] = _;
|
|
1887
2118
|
});
|
|
1888
2119
|
for (const _ of this.voices)
|
|
1889
|
-
for (let
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1892
|
-
return
|
|
2120
|
+
for (let r = 0; r < N.VOICE_SIZE; r++)
|
|
2121
|
+
E[s++] = _.data[r];
|
|
2122
|
+
const n = E.subarray(N.SYSEX_HEADER_SIZE, N.SYSEX_HEADER_SIZE + N.VOICE_DATA_SIZE);
|
|
2123
|
+
return E[s++] = N._calculateChecksum(n, N.VOICE_DATA_SIZE), E[s++] = N.SYSEX_END, E;
|
|
1893
2124
|
}
|
|
1894
2125
|
/**
|
|
1895
2126
|
* Convert bank to JSON format
|
|
1896
2127
|
* @returns {object} Bank data in JSON format
|
|
1897
2128
|
*/
|
|
1898
2129
|
toJSON() {
|
|
1899
|
-
const
|
|
1900
|
-
const _ =
|
|
2130
|
+
const E = this.voices.map((s, n) => {
|
|
2131
|
+
const _ = s.toJSON();
|
|
1901
2132
|
return {
|
|
1902
|
-
index:
|
|
2133
|
+
index: n + 1,
|
|
1903
2134
|
..._
|
|
1904
2135
|
};
|
|
1905
2136
|
});
|
|
1906
2137
|
return {
|
|
1907
2138
|
version: "1.0",
|
|
1908
2139
|
name: this.name || "",
|
|
1909
|
-
voices:
|
|
2140
|
+
voices: E
|
|
1910
2141
|
};
|
|
1911
2142
|
}
|
|
1912
2143
|
}
|
|
1913
|
-
function Et(
|
|
1914
|
-
return
|
|
1915
|
-
manufacturerId:
|
|
1916
|
-
payload:
|
|
1917
|
-
raw:
|
|
2144
|
+
function Et(A) {
|
|
2145
|
+
return A[0] !== 240 || A[A.length - 1] !== 247 ? null : {
|
|
2146
|
+
manufacturerId: A[1],
|
|
2147
|
+
payload: A.slice(2, -1),
|
|
2148
|
+
raw: A
|
|
1918
2149
|
};
|
|
1919
2150
|
}
|
|
1920
|
-
function
|
|
1921
|
-
return [240,
|
|
2151
|
+
function st(A, E) {
|
|
2152
|
+
return [240, A, ...E, 247];
|
|
1922
2153
|
}
|
|
1923
|
-
function
|
|
1924
|
-
return
|
|
2154
|
+
function nt(A) {
|
|
2155
|
+
return A.length >= 2 && A[0] === 240 && A[A.length - 1] === 247;
|
|
1925
2156
|
}
|
|
1926
|
-
function _t(
|
|
1927
|
-
const
|
|
1928
|
-
for (let
|
|
1929
|
-
const
|
|
2157
|
+
function _t(A) {
|
|
2158
|
+
const E = [];
|
|
2159
|
+
for (let s = 0; s < A.length; s += 7) {
|
|
2160
|
+
const n = A.slice(s, s + 7);
|
|
1930
2161
|
let _ = 0;
|
|
1931
|
-
const
|
|
1932
|
-
for (let
|
|
1933
|
-
const
|
|
1934
|
-
|
|
2162
|
+
const r = [];
|
|
2163
|
+
for (let e = 0; e < n.length; e++) {
|
|
2164
|
+
const a = n[e];
|
|
2165
|
+
a & 128 && (_ |= 1 << e), r.push(a & 127);
|
|
1935
2166
|
}
|
|
1936
|
-
|
|
2167
|
+
E.push(_, ...r);
|
|
1937
2168
|
}
|
|
1938
|
-
return
|
|
2169
|
+
return E;
|
|
1939
2170
|
}
|
|
1940
|
-
function
|
|
1941
|
-
const
|
|
1942
|
-
for (let
|
|
1943
|
-
const
|
|
1944
|
-
for (let
|
|
1945
|
-
let
|
|
1946
|
-
|
|
2171
|
+
function et(A) {
|
|
2172
|
+
const E = [];
|
|
2173
|
+
for (let s = 0; s < A.length; s += 8) {
|
|
2174
|
+
const n = A[s], _ = Math.min(7, A.length - s - 1);
|
|
2175
|
+
for (let r = 0; r < _; r++) {
|
|
2176
|
+
let e = A[s + 1 + r];
|
|
2177
|
+
n & 1 << r && (e |= 128), E.push(e);
|
|
1947
2178
|
}
|
|
1948
2179
|
}
|
|
1949
|
-
return
|
|
2180
|
+
return E;
|
|
1950
2181
|
}
|
|
1951
|
-
function
|
|
1952
|
-
return Number.isInteger(
|
|
2182
|
+
function rt(A) {
|
|
2183
|
+
return Number.isInteger(A) && A >= 1 && A <= 16;
|
|
1953
2184
|
}
|
|
1954
|
-
function
|
|
1955
|
-
return Number.isInteger(
|
|
2185
|
+
function at(A) {
|
|
2186
|
+
return Number.isInteger(A) && A >= 0 && A <= 127;
|
|
1956
2187
|
}
|
|
1957
|
-
function
|
|
1958
|
-
return Number.isInteger(
|
|
2188
|
+
function At(A) {
|
|
2189
|
+
return Number.isInteger(A) && A >= 0 && A <= 31;
|
|
1959
2190
|
}
|
|
1960
|
-
function
|
|
1961
|
-
return Number.isInteger(
|
|
2191
|
+
function Pt(A) {
|
|
2192
|
+
return Number.isInteger(A) && A >= 0 && A <= 127;
|
|
1962
2193
|
}
|
|
1963
|
-
function
|
|
1964
|
-
return Number.isInteger(
|
|
2194
|
+
function it(A) {
|
|
2195
|
+
return Number.isInteger(A) && A >= 0 && A <= 127;
|
|
1965
2196
|
}
|
|
1966
|
-
function Ct(
|
|
1967
|
-
return Number.isInteger(
|
|
2197
|
+
function Ct(A) {
|
|
2198
|
+
return Number.isInteger(A) && A >= 0 && A <= 127;
|
|
1968
2199
|
}
|
|
1969
|
-
function
|
|
1970
|
-
return Number.isInteger(
|
|
2200
|
+
function Nt(A) {
|
|
2201
|
+
return Number.isInteger(A) && A >= 0 && A <= 127;
|
|
1971
2202
|
}
|
|
1972
|
-
function
|
|
1973
|
-
return Number.isInteger(
|
|
2203
|
+
function lt(A) {
|
|
2204
|
+
return Number.isInteger(A) && A >= 0 && A <= 16383;
|
|
1974
2205
|
}
|
|
1975
|
-
function
|
|
1976
|
-
return Number.isInteger(
|
|
2206
|
+
function Tt(A, E) {
|
|
2207
|
+
return Number.isInteger(A) && A >= 0 && A <= 127 && Number.isInteger(E) && E >= 0 && E <= 127;
|
|
1977
2208
|
}
|
|
1978
|
-
async function W(
|
|
1979
|
-
const
|
|
1980
|
-
await
|
|
1981
|
-
const
|
|
2209
|
+
async function W(A = {}) {
|
|
2210
|
+
const E = new z(A);
|
|
2211
|
+
await E.initialize();
|
|
2212
|
+
const s = A.selector || "[data-midi-cc]";
|
|
1982
2213
|
{
|
|
1983
|
-
const
|
|
1984
|
-
|
|
2214
|
+
const n = new w(E, s);
|
|
2215
|
+
n.bindAll(), A.watchDOM && n.enableAutoBinding(), E._binder = n;
|
|
1985
2216
|
}
|
|
1986
|
-
return
|
|
2217
|
+
return E;
|
|
1987
2218
|
}
|
|
1988
|
-
async function St(
|
|
2219
|
+
async function St(A = {}) {
|
|
1989
2220
|
const {
|
|
1990
|
-
onStatusUpdate:
|
|
1991
|
-
onConnectionUpdate:
|
|
1992
|
-
channel:
|
|
2221
|
+
onStatusUpdate: E,
|
|
2222
|
+
onConnectionUpdate: s,
|
|
2223
|
+
channel: n,
|
|
1993
2224
|
output: _,
|
|
1994
|
-
sysex:
|
|
1995
|
-
onReady:
|
|
1996
|
-
onError:
|
|
2225
|
+
sysex: r,
|
|
2226
|
+
onReady: e,
|
|
2227
|
+
onError: a,
|
|
1997
2228
|
selector: T,
|
|
1998
2229
|
watchDOM: S,
|
|
1999
|
-
...
|
|
2000
|
-
} =
|
|
2230
|
+
...C
|
|
2231
|
+
} = A, i = await W({
|
|
2001
2232
|
autoConnect: !1,
|
|
2002
|
-
sysex:
|
|
2003
|
-
channel:
|
|
2233
|
+
sysex: r,
|
|
2234
|
+
channel: n || 1,
|
|
2004
2235
|
selector: T || "[data-midi-cc]",
|
|
2005
2236
|
watchDOM: S,
|
|
2006
|
-
onError:
|
|
2007
|
-
...
|
|
2008
|
-
}),
|
|
2009
|
-
midiController:
|
|
2010
|
-
onStatusUpdate:
|
|
2237
|
+
onError: a,
|
|
2238
|
+
...C
|
|
2239
|
+
}), l = new q({
|
|
2240
|
+
midiController: i,
|
|
2241
|
+
onStatusUpdate: E || (() => {
|
|
2011
2242
|
}),
|
|
2012
|
-
onConnectionUpdate:
|
|
2243
|
+
onConnectionUpdate: s || (() => {
|
|
2013
2244
|
}),
|
|
2014
|
-
channel:
|
|
2245
|
+
channel: n || 1
|
|
2015
2246
|
});
|
|
2016
2247
|
if (_)
|
|
2017
2248
|
try {
|
|
2018
|
-
await
|
|
2019
|
-
} catch (
|
|
2020
|
-
|
|
2249
|
+
await i.setOutput(_), l.currentDevice = i.getCurrentOutput(), l.updateConnectionStatus();
|
|
2250
|
+
} catch (O) {
|
|
2251
|
+
a ? a(O) : console.error("Failed to connect to MIDI device:", O.message);
|
|
2021
2252
|
}
|
|
2022
|
-
return
|
|
2253
|
+
return e && e(i, l), l;
|
|
2023
2254
|
}
|
|
2024
2255
|
export {
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2256
|
+
f as CONN,
|
|
2257
|
+
f as CONNECTION_EVENTS,
|
|
2258
|
+
u as CONTROLLER_EVENTS,
|
|
2259
|
+
u as CTRL,
|
|
2260
|
+
N as DX7Bank,
|
|
2030
2261
|
H as DX7Error,
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2262
|
+
m as DX7ParseError,
|
|
2263
|
+
o as DX7ValidationError,
|
|
2264
|
+
t as DX7Voice,
|
|
2265
|
+
w as DataAttributeBinder,
|
|
2266
|
+
v as EventEmitter,
|
|
2267
|
+
G as MIDIAccessError,
|
|
2268
|
+
Z as MIDIConnection,
|
|
2269
|
+
D as MIDIConnectionError,
|
|
2039
2270
|
z as MIDIController,
|
|
2040
|
-
|
|
2271
|
+
c as MIDIDeviceError,
|
|
2041
2272
|
q as MIDIDeviceManager,
|
|
2042
|
-
|
|
2273
|
+
p as MIDIError,
|
|
2043
2274
|
R as MIDIValidationError,
|
|
2044
|
-
|
|
2275
|
+
h as clamp,
|
|
2045
2276
|
W as createMIDIController,
|
|
2046
2277
|
St as createMIDIDeviceManager,
|
|
2047
|
-
|
|
2278
|
+
st as createSysEx,
|
|
2048
2279
|
Y as decode14BitValue,
|
|
2049
|
-
|
|
2280
|
+
et as decode7Bit,
|
|
2050
2281
|
tt as denormalize14BitValue,
|
|
2051
2282
|
Q as denormalizeValue,
|
|
2052
2283
|
x as encode14BitValue,
|
|
2053
2284
|
_t as encode7Bit,
|
|
2054
2285
|
k as frequencyToNote,
|
|
2055
2286
|
X as getCCName,
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2287
|
+
nt as isSysEx,
|
|
2288
|
+
At as isValid14BitCC,
|
|
2289
|
+
at as isValidCC,
|
|
2290
|
+
rt as isValidChannel,
|
|
2291
|
+
Pt as isValidMIDIValue,
|
|
2292
|
+
it as isValidNote,
|
|
2293
|
+
lt as isValidPitchBend,
|
|
2294
|
+
Tt as isValidPitchBendBytes,
|
|
2295
|
+
Nt as isValidProgramChange,
|
|
2065
2296
|
Ct as isValidVelocity,
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2297
|
+
$ as normalize14BitValue,
|
|
2298
|
+
B as normalizeValue,
|
|
2299
|
+
J as noteNameToNumber,
|
|
2300
|
+
j as noteNumberToName,
|
|
2070
2301
|
V as noteToFrequency,
|
|
2071
2302
|
Et as parseSysEx
|
|
2072
2303
|
};
|