midiwire 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/midiwire.es.js +1293 -1427
- package/dist/midiwire.umd.js +1 -1
- package/dist/utils/dx7/DX7Voice.d.ts +2 -0
- package/dist/utils/dx7/DX7Voice.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/utils/dx7/DX7Voice.js +4 -2
package/dist/midiwire.es.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
var x = Object.defineProperty;
|
|
2
|
+
var Y = (c, e, i) => e in c ? x(c, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : c[e] = i;
|
|
3
|
+
var _ = (c, e, i) => Y(c, typeof e != "symbol" ? e + "" : e, i);
|
|
4
|
+
class $ {
|
|
2
5
|
/**
|
|
3
6
|
* Create a new DataAttributeBinder instance for declarative MIDI binding
|
|
4
7
|
*
|
|
@@ -22,8 +25,8 @@ class w {
|
|
|
22
25
|
* binder.bindAll();
|
|
23
26
|
* });
|
|
24
27
|
*/
|
|
25
|
-
constructor(
|
|
26
|
-
this.controller =
|
|
28
|
+
constructor(e, i = "[data-midi-cc]") {
|
|
29
|
+
this.controller = e, this.selector = i, this.observer = null;
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
32
|
* Bind all matching elements in the document to MIDI Controller. Searches the DOM for elements
|
|
@@ -51,10 +54,10 @@ class w {
|
|
|
51
54
|
bindAll() {
|
|
52
55
|
document.querySelectorAll(
|
|
53
56
|
this.selector === "[data-midi-cc]" ? "[data-midi-cc], [data-midi-msb][data-midi-lsb]" : this.selector
|
|
54
|
-
).forEach((
|
|
55
|
-
if (
|
|
56
|
-
const
|
|
57
|
-
|
|
57
|
+
).forEach((i) => {
|
|
58
|
+
if (i.hasAttribute("data-midi-bound")) return;
|
|
59
|
+
const n = this._parseAttributes(i);
|
|
60
|
+
n && (this.controller.bind(i, n), i.setAttribute("data-midi-bound", "true"));
|
|
58
61
|
});
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
@@ -80,25 +83,25 @@ class w {
|
|
|
80
83
|
*/
|
|
81
84
|
enableAutoBinding() {
|
|
82
85
|
if (this.observer) return;
|
|
83
|
-
const
|
|
84
|
-
this.observer = new MutationObserver((
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
if (
|
|
89
|
-
const
|
|
90
|
-
|
|
86
|
+
const e = this.selector === "[data-midi-cc]" ? "[data-midi-cc], [data-midi-msb][data-midi-lsb]" : this.selector;
|
|
87
|
+
this.observer = new MutationObserver((i) => {
|
|
88
|
+
i.forEach((n) => {
|
|
89
|
+
n.addedNodes.forEach((s) => {
|
|
90
|
+
if (s.nodeType === Node.ELEMENT_NODE) {
|
|
91
|
+
if (s.matches?.(e)) {
|
|
92
|
+
const r = this._parseAttributes(s);
|
|
93
|
+
r && !s.hasAttribute("data-midi-bound") && (this.controller.bind(s, r), s.setAttribute("data-midi-bound", "true"));
|
|
91
94
|
}
|
|
92
|
-
|
|
93
|
-
if (!
|
|
94
|
-
const
|
|
95
|
-
|
|
95
|
+
s.querySelectorAll && s.querySelectorAll(e).forEach((E) => {
|
|
96
|
+
if (!E.hasAttribute("data-midi-bound")) {
|
|
97
|
+
const o = this._parseAttributes(E);
|
|
98
|
+
o && (this.controller.bind(E, o), E.setAttribute("data-midi-bound", "true"));
|
|
96
99
|
}
|
|
97
100
|
});
|
|
98
101
|
}
|
|
99
|
-
}),
|
|
100
|
-
|
|
101
|
-
this.controller.unbind(
|
|
102
|
+
}), n.removedNodes.forEach((s) => {
|
|
103
|
+
s.nodeType === Node.ELEMENT_NODE && (s.hasAttribute?.("data-midi-bound") && this.controller.unbind(s), s.querySelectorAll && s.querySelectorAll("[data-midi-bound]").forEach((E) => {
|
|
104
|
+
this.controller.unbind(E);
|
|
102
105
|
}));
|
|
103
106
|
});
|
|
104
107
|
});
|
|
@@ -149,33 +152,33 @@ class w {
|
|
|
149
152
|
* // <input type="range" min="0" max="16383" data-midi-msb="74" data-midi-lsb="75">
|
|
150
153
|
* // Returns: { msb: 74, lsb: 75, is14Bit: true, min: 0, max: 16383 }
|
|
151
154
|
*/
|
|
152
|
-
_parseAttributes(
|
|
153
|
-
const
|
|
154
|
-
if (!Number.isNaN(
|
|
155
|
-
const
|
|
156
|
-
return !Number.isNaN(
|
|
157
|
-
`Element has both 7-bit (data-midi-cc="${
|
|
158
|
-
|
|
155
|
+
_parseAttributes(e) {
|
|
156
|
+
const i = parseInt(e.dataset.midiMsb, 10), n = parseInt(e.dataset.midiLsb, 10);
|
|
157
|
+
if (!Number.isNaN(i) && !Number.isNaN(n) && i >= 0 && i <= 127 && n >= 0 && n <= 127) {
|
|
158
|
+
const r = parseInt(e.dataset.midiCc, 10);
|
|
159
|
+
return !Number.isNaN(r) && r >= 0 && r <= 127 && console.warn(
|
|
160
|
+
`Element has both 7-bit (data-midi-cc="${r}") and 14-bit (data-midi-msb="${i}" data-midi-lsb="${n}") CC attributes. 14-bit takes precedence.`,
|
|
161
|
+
e
|
|
159
162
|
), {
|
|
160
|
-
msb:
|
|
161
|
-
lsb:
|
|
163
|
+
msb: i,
|
|
164
|
+
lsb: n,
|
|
162
165
|
is14Bit: !0,
|
|
163
|
-
channel: parseInt(
|
|
164
|
-
min: parseFloat(
|
|
165
|
-
max: parseFloat(
|
|
166
|
-
invert:
|
|
167
|
-
label:
|
|
166
|
+
channel: parseInt(e.dataset.midiChannel, 10) || void 0,
|
|
167
|
+
min: parseFloat(e.getAttribute("min")) || 0,
|
|
168
|
+
max: parseFloat(e.getAttribute("max")) || 127,
|
|
169
|
+
invert: e.dataset.midiInvert === "true",
|
|
170
|
+
label: e.dataset.midiLabel
|
|
168
171
|
};
|
|
169
172
|
}
|
|
170
|
-
const
|
|
171
|
-
return !Number.isNaN(
|
|
172
|
-
cc:
|
|
173
|
-
channel: parseInt(
|
|
174
|
-
min: parseFloat(
|
|
175
|
-
max: parseFloat(
|
|
176
|
-
invert:
|
|
177
|
-
label:
|
|
178
|
-
} : ((
|
|
173
|
+
const s = parseInt(e.dataset.midiCc, 10);
|
|
174
|
+
return !Number.isNaN(s) && s >= 0 && s <= 127 ? {
|
|
175
|
+
cc: s,
|
|
176
|
+
channel: parseInt(e.dataset.midiChannel, 10) || void 0,
|
|
177
|
+
min: parseFloat(e.getAttribute("min")) || 0,
|
|
178
|
+
max: parseFloat(e.getAttribute("max")) || 127,
|
|
179
|
+
invert: e.dataset.midiInvert === "true",
|
|
180
|
+
label: e.dataset.midiLabel
|
|
181
|
+
} : ((e.dataset.midiCc !== void 0 || e.dataset.midiMsb !== void 0 && e.dataset.midiLsb !== void 0) && console.warn("Invalid MIDI configuration on element:", e), null);
|
|
179
182
|
}
|
|
180
183
|
/**
|
|
181
184
|
* Clean up resources and disconnect any active observers. Disables auto-binding if active
|
|
@@ -203,59 +206,59 @@ class w {
|
|
|
203
206
|
this.disableAutoBinding();
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
|
-
class
|
|
207
|
-
constructor(
|
|
208
|
-
super(
|
|
209
|
+
class g extends Error {
|
|
210
|
+
constructor(e, i) {
|
|
211
|
+
super(e), this.name = "MIDIError", this.code = i, Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
|
|
209
212
|
}
|
|
210
213
|
}
|
|
211
|
-
class
|
|
212
|
-
constructor(
|
|
213
|
-
super(
|
|
214
|
+
class F extends g {
|
|
215
|
+
constructor(e, i) {
|
|
216
|
+
super(e, "MIDI_ACCESS_ERROR"), this.name = "MIDIAccessError", this.reason = i;
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
|
-
class
|
|
217
|
-
constructor(
|
|
218
|
-
super(
|
|
219
|
+
class y extends g {
|
|
220
|
+
constructor(e) {
|
|
221
|
+
super(e, "MIDI_CONNECTION_ERROR"), this.name = "MIDIConnectionError";
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
|
-
class
|
|
222
|
-
constructor(
|
|
223
|
-
super(
|
|
224
|
+
class U extends g {
|
|
225
|
+
constructor(e, i, n) {
|
|
226
|
+
super(e, "MIDI_DEVICE_ERROR"), this.name = "MIDIDeviceError", this.deviceType = i, this.deviceId = n;
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
|
-
class
|
|
227
|
-
constructor(
|
|
228
|
-
super(
|
|
229
|
+
class m extends g {
|
|
230
|
+
constructor(e, i) {
|
|
231
|
+
super(e, "MIDI_VALIDATION_ERROR"), this.name = "MIDIValidationError", this.validationType = i;
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
|
-
class
|
|
232
|
-
constructor(
|
|
233
|
-
super(
|
|
234
|
+
class v extends Error {
|
|
235
|
+
constructor(e, i) {
|
|
236
|
+
super(e), this.name = "DX7Error", this.code = i, Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
|
-
class
|
|
237
|
-
constructor(
|
|
238
|
-
super(
|
|
239
|
+
class R extends v {
|
|
240
|
+
constructor(e, i, n) {
|
|
241
|
+
super(e, "DX7_PARSE_ERROR"), this.name = "DX7ParseError", this.parseType = i, this.offset = n;
|
|
239
242
|
}
|
|
240
243
|
}
|
|
241
|
-
class
|
|
242
|
-
constructor(
|
|
243
|
-
super(
|
|
244
|
+
class O extends v {
|
|
245
|
+
constructor(e, i, n) {
|
|
246
|
+
super(e, "DX7_VALIDATION_ERROR"), this.name = "DX7ValidationError", this.validationType = i, this.value = n;
|
|
244
247
|
}
|
|
245
248
|
}
|
|
246
|
-
function
|
|
247
|
-
return Math.max(
|
|
249
|
+
function D(c, e, i) {
|
|
250
|
+
return Math.max(e, Math.min(i, c));
|
|
248
251
|
}
|
|
249
|
-
function
|
|
250
|
-
const
|
|
251
|
-
return
|
|
252
|
+
function Z(c, e, i, n = !1) {
|
|
253
|
+
const s = (c - e) / (i - e), E = (n ? 1 - s : s) * 127;
|
|
254
|
+
return D(Math.round(E), 0, 127);
|
|
252
255
|
}
|
|
253
|
-
function
|
|
254
|
-
let
|
|
255
|
-
return
|
|
256
|
+
function tt(c, e, i, n = !1) {
|
|
257
|
+
let s = D(c, 0, 127) / 127;
|
|
258
|
+
return n && (s = 1 - s), e + s * (i - e);
|
|
256
259
|
}
|
|
257
|
-
function
|
|
258
|
-
const
|
|
260
|
+
function et(c) {
|
|
261
|
+
const e = {
|
|
259
262
|
C: 0,
|
|
260
263
|
"C#": 1,
|
|
261
264
|
DB: 1,
|
|
@@ -273,27 +276,27 @@ function J(A) {
|
|
|
273
276
|
"A#": 10,
|
|
274
277
|
BB: 10,
|
|
275
278
|
B: 11
|
|
276
|
-
},
|
|
277
|
-
if (!
|
|
278
|
-
throw new
|
|
279
|
-
const [,
|
|
280
|
-
if (
|
|
281
|
-
throw new
|
|
282
|
-
const
|
|
283
|
-
return
|
|
279
|
+
}, i = c.match(/^([A-G][#b]?)(-?\d+)$/i);
|
|
280
|
+
if (!i)
|
|
281
|
+
throw new m(`Invalid note name: ${c}`, "note", c);
|
|
282
|
+
const [, n, s] = i, r = e[n.toUpperCase()];
|
|
283
|
+
if (r === void 0)
|
|
284
|
+
throw new m(`Invalid note: ${n}`, "note", n);
|
|
285
|
+
const E = (parseInt(s, 10) + 1) * 12 + r;
|
|
286
|
+
return D(E, 0, 127);
|
|
284
287
|
}
|
|
285
|
-
function
|
|
286
|
-
const
|
|
287
|
-
return `${
|
|
288
|
+
function it(c, e = !1) {
|
|
289
|
+
const s = 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(c / 12) - 1;
|
|
290
|
+
return `${s[c % 12]}${r}`;
|
|
288
291
|
}
|
|
289
|
-
function
|
|
290
|
-
const
|
|
291
|
-
return
|
|
292
|
+
function nt(c) {
|
|
293
|
+
const e = 69 + 12 * Math.log2(c / 440);
|
|
294
|
+
return D(Math.round(e), 0, 127);
|
|
292
295
|
}
|
|
293
|
-
function
|
|
294
|
-
return 440 * 2 ** ((
|
|
296
|
+
function st(c) {
|
|
297
|
+
return 440 * 2 ** ((c - 69) / 12);
|
|
295
298
|
}
|
|
296
|
-
function
|
|
299
|
+
function Et(c) {
|
|
297
300
|
return {
|
|
298
301
|
0: "Bank Select",
|
|
299
302
|
1: "Modulation",
|
|
@@ -326,27 +329,27 @@ function X(A) {
|
|
|
326
329
|
120: "All Sound Off",
|
|
327
330
|
121: "Reset All Controllers",
|
|
328
331
|
123: "All Notes Off"
|
|
329
|
-
}[
|
|
332
|
+
}[c] || `CC ${c}`;
|
|
330
333
|
}
|
|
331
|
-
function
|
|
332
|
-
const
|
|
334
|
+
function z(c) {
|
|
335
|
+
const e = D(Math.round(c), 0, 16383);
|
|
333
336
|
return {
|
|
334
|
-
msb:
|
|
335
|
-
lsb:
|
|
337
|
+
msb: e >> 7 & 127,
|
|
338
|
+
lsb: e & 127
|
|
336
339
|
};
|
|
337
340
|
}
|
|
338
|
-
function
|
|
339
|
-
return
|
|
341
|
+
function q(c, e) {
|
|
342
|
+
return D(c, 0, 127) << 7 | D(e, 0, 127);
|
|
340
343
|
}
|
|
341
|
-
function
|
|
342
|
-
const
|
|
343
|
-
return
|
|
344
|
+
function W(c, e, i, n = !1) {
|
|
345
|
+
const s = (c - e) / (i - e), E = (n ? 1 - s : s) * 16383;
|
|
346
|
+
return z(E);
|
|
344
347
|
}
|
|
345
|
-
function
|
|
346
|
-
let
|
|
347
|
-
return
|
|
348
|
+
function rt(c, e, i, n, s = !1) {
|
|
349
|
+
let E = q(c, e) / 16383;
|
|
350
|
+
return s && (E = 1 - E), i + E * (n - i);
|
|
348
351
|
}
|
|
349
|
-
class
|
|
352
|
+
class w {
|
|
350
353
|
constructor() {
|
|
351
354
|
this.events = /* @__PURE__ */ new Map();
|
|
352
355
|
}
|
|
@@ -356,42 +359,42 @@ class H {
|
|
|
356
359
|
* @param {Function} handler - Event handler function
|
|
357
360
|
* @returns {Function} Unsubscribe function
|
|
358
361
|
*/
|
|
359
|
-
on(
|
|
360
|
-
return this.events.has(
|
|
362
|
+
on(e, i) {
|
|
363
|
+
return this.events.has(e) || this.events.set(e, []), this.events.get(e).push(i), () => this.off(e, i);
|
|
361
364
|
}
|
|
362
365
|
/**
|
|
363
366
|
* Register a one-time event listener
|
|
364
367
|
* @param {string} event - Event name
|
|
365
368
|
* @param {Function} handler - Event handler function
|
|
366
369
|
*/
|
|
367
|
-
once(
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
+
once(e, i) {
|
|
371
|
+
const n = (...s) => {
|
|
372
|
+
i(...s), this.off(e, n);
|
|
370
373
|
};
|
|
371
|
-
this.on(
|
|
374
|
+
this.on(e, n);
|
|
372
375
|
}
|
|
373
376
|
/**
|
|
374
377
|
* Remove an event listener
|
|
375
378
|
* @param {string} event - Event name
|
|
376
379
|
* @param {Function} handler - Event handler function
|
|
377
380
|
*/
|
|
378
|
-
off(
|
|
379
|
-
if (!this.events.has(
|
|
380
|
-
const
|
|
381
|
-
|
|
381
|
+
off(e, i) {
|
|
382
|
+
if (!this.events.has(e)) return;
|
|
383
|
+
const n = this.events.get(e), s = n.indexOf(i);
|
|
384
|
+
s > -1 && n.splice(s, 1), n.length === 0 && this.events.delete(e);
|
|
382
385
|
}
|
|
383
386
|
/**
|
|
384
387
|
* Emit an event
|
|
385
388
|
* @param {string} event - Event name
|
|
386
389
|
* @param {*} data - Event data
|
|
387
390
|
*/
|
|
388
|
-
emit(
|
|
389
|
-
if (!this.events.has(
|
|
390
|
-
[...this.events.get(
|
|
391
|
+
emit(e, i) {
|
|
392
|
+
if (!this.events.has(e)) return;
|
|
393
|
+
[...this.events.get(e)].forEach((s) => {
|
|
391
394
|
try {
|
|
392
|
-
|
|
393
|
-
} catch (
|
|
394
|
-
console.error(`Error in event handler for "${
|
|
395
|
+
s(i);
|
|
396
|
+
} catch (r) {
|
|
397
|
+
console.error(`Error in event handler for "${e}":`, r);
|
|
395
398
|
}
|
|
396
399
|
});
|
|
397
400
|
}
|
|
@@ -399,26 +402,26 @@ class H {
|
|
|
399
402
|
* Remove all event listeners
|
|
400
403
|
* @param {string} [event] - Optional event name to clear specific event
|
|
401
404
|
*/
|
|
402
|
-
removeAllListeners(
|
|
403
|
-
|
|
405
|
+
removeAllListeners(e) {
|
|
406
|
+
e ? this.events.delete(e) : this.events.clear();
|
|
404
407
|
}
|
|
405
408
|
}
|
|
406
|
-
const
|
|
409
|
+
const p = {
|
|
407
410
|
DEVICE_CHANGE: "device-change",
|
|
408
411
|
IN_DEV_CONNECTED: "in-dev-connected",
|
|
409
412
|
IN_DEV_DISCONNECTED: "in-dev-disconnected",
|
|
410
413
|
OUT_DEV_CONNECTED: "out-dev-connected",
|
|
411
414
|
OUT_DEV_DISCONNECTED: "out-dev-disconnected"
|
|
412
415
|
};
|
|
413
|
-
class
|
|
416
|
+
class Q extends w {
|
|
414
417
|
/**
|
|
415
418
|
* @param {Object} options
|
|
416
419
|
* @param {boolean} [options.sysex=false] - Request SysEx access
|
|
417
420
|
*/
|
|
418
|
-
constructor(
|
|
421
|
+
constructor(e = {}) {
|
|
419
422
|
super(), this.options = {
|
|
420
423
|
sysex: !1,
|
|
421
|
-
...
|
|
424
|
+
...e
|
|
422
425
|
}, this.midiAccess = null, this.output = null, this.input = null;
|
|
423
426
|
}
|
|
424
427
|
/**
|
|
@@ -445,25 +448,25 @@ class Z extends H {
|
|
|
445
448
|
*/
|
|
446
449
|
async requestAccess() {
|
|
447
450
|
if (!navigator.requestMIDIAccess)
|
|
448
|
-
throw new
|
|
451
|
+
throw new F("Web MIDI API is not supported in this browser", "unsupported");
|
|
449
452
|
try {
|
|
450
453
|
this.midiAccess = await navigator.requestMIDIAccess({
|
|
451
454
|
sysex: this.options.sysex
|
|
452
|
-
}), this.midiAccess.onstatechange = (
|
|
453
|
-
const
|
|
454
|
-
this.emit(
|
|
455
|
-
port:
|
|
456
|
-
state:
|
|
457
|
-
type:
|
|
455
|
+
}), this.midiAccess.onstatechange = (e) => {
|
|
456
|
+
const i = e.port, n = e.port.state;
|
|
457
|
+
this.emit(p.DEVICE_CHANGE, {
|
|
458
|
+
port: i,
|
|
459
|
+
state: n,
|
|
460
|
+
type: i.type,
|
|
458
461
|
device: {
|
|
459
|
-
id:
|
|
460
|
-
name:
|
|
461
|
-
manufacturer:
|
|
462
|
+
id: i.id,
|
|
463
|
+
name: i.name,
|
|
464
|
+
manufacturer: i.manufacturer || "Unknown"
|
|
462
465
|
}
|
|
463
|
-
}),
|
|
466
|
+
}), n === "disconnected" ? i.type === "input" ? (this.emit(p.IN_DEV_DISCONNECTED, { device: i }), this.input && this.input.id === i.id && (this.input = null)) : i.type === "output" && (this.emit(p.OUT_DEV_DISCONNECTED, { device: i }), this.output && this.output.id === i.id && (this.output = null)) : n === "connected" && (i.type === "input" ? this.emit(p.IN_DEV_CONNECTED, { device: i }) : i.type === "output" && this.emit(p.OUT_DEV_CONNECTED, { device: i }));
|
|
464
467
|
};
|
|
465
|
-
} catch (
|
|
466
|
-
throw
|
|
468
|
+
} catch (e) {
|
|
469
|
+
throw e.name === "SecurityError" ? new F("MIDI access denied. SysEx requires user permission.", "denied") : new F(`Failed to get MIDI access: ${e.message}`, "failed");
|
|
467
470
|
}
|
|
468
471
|
}
|
|
469
472
|
/**
|
|
@@ -490,25 +493,25 @@ class Z extends H {
|
|
|
490
493
|
* // Connect by device ID
|
|
491
494
|
* await connection.connect("input-12345");
|
|
492
495
|
*/
|
|
493
|
-
async connect(
|
|
496
|
+
async connect(e) {
|
|
494
497
|
if (!this.midiAccess)
|
|
495
|
-
throw new
|
|
496
|
-
const
|
|
497
|
-
if (
|
|
498
|
-
throw new
|
|
499
|
-
if (
|
|
500
|
-
this.output =
|
|
498
|
+
throw new y("MIDI access not initialized. Call requestAccess() first.");
|
|
499
|
+
const i = Array.from(this.midiAccess.outputs.values());
|
|
500
|
+
if (i.length === 0)
|
|
501
|
+
throw new U("No MIDI output devices available", "output");
|
|
502
|
+
if (e === void 0) {
|
|
503
|
+
this.output = i[0];
|
|
501
504
|
return;
|
|
502
505
|
}
|
|
503
|
-
if (typeof
|
|
504
|
-
if (
|
|
505
|
-
throw new
|
|
506
|
-
this.output =
|
|
506
|
+
if (typeof e == "number") {
|
|
507
|
+
if (e < 0 || e >= i.length)
|
|
508
|
+
throw new U(`Output index ${e} out of range (0-${i.length - 1})`, "output", e);
|
|
509
|
+
this.output = i[e];
|
|
507
510
|
return;
|
|
508
511
|
}
|
|
509
|
-
if (this.output =
|
|
510
|
-
const
|
|
511
|
-
throw new
|
|
512
|
+
if (this.output = i.find((n) => n.name === e || n.id === e), !this.output) {
|
|
513
|
+
const n = i.map((s) => s.name).join(", ");
|
|
514
|
+
throw new U(`MIDI output "${e}" not found. Available: ${n}`, "output", e);
|
|
512
515
|
}
|
|
513
516
|
}
|
|
514
517
|
/**
|
|
@@ -520,26 +523,26 @@ class Z extends H {
|
|
|
520
523
|
* @throws {MIDIValidationError} If onMessage is not a function
|
|
521
524
|
* @throws {MIDIDeviceError} If device not found or index out of range
|
|
522
525
|
*/
|
|
523
|
-
async connectInput(
|
|
526
|
+
async connectInput(e, i) {
|
|
524
527
|
if (!this.midiAccess)
|
|
525
|
-
throw new
|
|
526
|
-
if (typeof
|
|
527
|
-
throw new
|
|
528
|
-
const
|
|
529
|
-
if (
|
|
530
|
-
throw new
|
|
531
|
-
if (this.input && (this.input.onmidimessage = null),
|
|
532
|
-
this.input =
|
|
533
|
-
else if (typeof
|
|
534
|
-
if (
|
|
535
|
-
throw new
|
|
536
|
-
this.input =
|
|
537
|
-
} else if (this.input =
|
|
538
|
-
const
|
|
539
|
-
throw new
|
|
540
|
-
}
|
|
541
|
-
this.input.onmidimessage = (
|
|
542
|
-
s
|
|
528
|
+
throw new y("MIDI access not initialized. Call requestAccess() first.");
|
|
529
|
+
if (typeof i != "function")
|
|
530
|
+
throw new m("onMessage callback must be a function", "callback");
|
|
531
|
+
const n = Array.from(this.midiAccess.inputs.values());
|
|
532
|
+
if (n.length === 0)
|
|
533
|
+
throw new U("No MIDI input devices available", "input");
|
|
534
|
+
if (this.input && (this.input.onmidimessage = null), e === void 0)
|
|
535
|
+
this.input = n[0];
|
|
536
|
+
else if (typeof e == "number") {
|
|
537
|
+
if (e < 0 || e >= n.length)
|
|
538
|
+
throw new U(`Input index ${e} out of range (0-${n.length - 1})`, "input", e);
|
|
539
|
+
this.input = n[e];
|
|
540
|
+
} else if (this.input = n.find((s) => s.name === e || s.id === e), !this.input) {
|
|
541
|
+
const s = n.map((r) => r.name).join(", ");
|
|
542
|
+
throw new U(`MIDI input "${e}" not found. Available: ${s}`, "input", e);
|
|
543
|
+
}
|
|
544
|
+
this.input.onmidimessage = (s) => {
|
|
545
|
+
i(s);
|
|
543
546
|
};
|
|
544
547
|
}
|
|
545
548
|
/**
|
|
@@ -595,14 +598,14 @@ class Z extends H {
|
|
|
595
598
|
*/
|
|
596
599
|
getOutputs() {
|
|
597
600
|
if (!this.midiAccess) return [];
|
|
598
|
-
const
|
|
599
|
-
return this.midiAccess.outputs.forEach((
|
|
600
|
-
|
|
601
|
-
id:
|
|
602
|
-
name:
|
|
603
|
-
manufacturer:
|
|
601
|
+
const e = [];
|
|
602
|
+
return this.midiAccess.outputs.forEach((i) => {
|
|
603
|
+
i.state === "connected" && e.push({
|
|
604
|
+
id: i.id,
|
|
605
|
+
name: i.name,
|
|
606
|
+
manufacturer: i.manufacturer || "Unknown"
|
|
604
607
|
});
|
|
605
|
-
}),
|
|
608
|
+
}), e;
|
|
606
609
|
}
|
|
607
610
|
/**
|
|
608
611
|
* Get all available MIDI inputs
|
|
@@ -610,14 +613,14 @@ class Z extends H {
|
|
|
610
613
|
*/
|
|
611
614
|
getInputs() {
|
|
612
615
|
if (!this.midiAccess) return [];
|
|
613
|
-
const
|
|
614
|
-
return this.midiAccess.inputs.forEach((
|
|
615
|
-
|
|
616
|
-
id:
|
|
617
|
-
name:
|
|
618
|
-
manufacturer:
|
|
616
|
+
const e = [];
|
|
617
|
+
return this.midiAccess.inputs.forEach((i) => {
|
|
618
|
+
i.state === "connected" && e.push({
|
|
619
|
+
id: i.id,
|
|
620
|
+
name: i.name,
|
|
621
|
+
manufacturer: i.manufacturer || "Unknown"
|
|
619
622
|
});
|
|
620
|
-
}),
|
|
623
|
+
}), e;
|
|
621
624
|
}
|
|
622
625
|
/**
|
|
623
626
|
* Send a MIDI message to the connected output. Automatically converts
|
|
@@ -639,16 +642,16 @@ class Z extends H {
|
|
|
639
642
|
* // Send a control change
|
|
640
643
|
* connection.send([0xB0, 7, 64]); // Volume to 64
|
|
641
644
|
*/
|
|
642
|
-
send(
|
|
645
|
+
send(e, i = null) {
|
|
643
646
|
if (!this.output) {
|
|
644
647
|
console.warn("No MIDI output connected. Call connect() first.");
|
|
645
648
|
return;
|
|
646
649
|
}
|
|
647
650
|
try {
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
} catch (
|
|
651
|
-
console.error("Failed to send MIDI message:",
|
|
651
|
+
const n = new Uint8Array(e);
|
|
652
|
+
i === null ? this.output.send(n) : this.output.send(n, i);
|
|
653
|
+
} catch (n) {
|
|
654
|
+
console.error("Failed to send MIDI message:", n);
|
|
652
655
|
}
|
|
653
656
|
}
|
|
654
657
|
/**
|
|
@@ -666,16 +669,16 @@ class Z extends H {
|
|
|
666
669
|
* // Send pre-wrapped SysEx message
|
|
667
670
|
* connection.sendSysEx([0xF0, 0x41, 0x10, 0x42, 0xF7], false);
|
|
668
671
|
*/
|
|
669
|
-
sendSysEx(
|
|
672
|
+
sendSysEx(e, i = !1) {
|
|
670
673
|
if (!this.options.sysex) {
|
|
671
674
|
console.warn("SysEx not enabled. Initialize with sysex: true");
|
|
672
675
|
return;
|
|
673
676
|
}
|
|
674
|
-
let
|
|
675
|
-
|
|
677
|
+
let n;
|
|
678
|
+
i ? n = [240, ...e, 247] : n = e, this.send(n);
|
|
676
679
|
}
|
|
677
680
|
}
|
|
678
|
-
const
|
|
681
|
+
const P = {
|
|
679
682
|
// Core controller events
|
|
680
683
|
READY: "ready",
|
|
681
684
|
ERROR: "error",
|
|
@@ -728,7 +731,7 @@ const C = {
|
|
|
728
731
|
PATCH_LOADED: "patch-loaded",
|
|
729
732
|
PATCH_DELETED: "patch-deleted"
|
|
730
733
|
};
|
|
731
|
-
class
|
|
734
|
+
class J extends w {
|
|
732
735
|
/**
|
|
733
736
|
* @param {Object} options
|
|
734
737
|
* @param {number} [options.inputChannel=1] - Input MIDI channel (1-16)
|
|
@@ -740,13 +743,13 @@ class z extends H {
|
|
|
740
743
|
* @param {Function} [options.onReady] - Callback when MIDI is ready
|
|
741
744
|
* @param {Function} [options.onError] - Error handler
|
|
742
745
|
*/
|
|
743
|
-
constructor(
|
|
746
|
+
constructor(e = {}) {
|
|
744
747
|
super(), this.options = {
|
|
745
748
|
inputChannel: 1,
|
|
746
749
|
outputChannel: 1,
|
|
747
750
|
autoConnect: !0,
|
|
748
751
|
sysex: !1,
|
|
749
|
-
...
|
|
752
|
+
...e
|
|
750
753
|
}, this.connection = null, this.bindings = /* @__PURE__ */ new Map(), this.state = {
|
|
751
754
|
controlChange: /* @__PURE__ */ new Map(),
|
|
752
755
|
// ${channel}:${cc} -> value
|
|
@@ -801,28 +804,28 @@ class z extends H {
|
|
|
801
804
|
return;
|
|
802
805
|
}
|
|
803
806
|
try {
|
|
804
|
-
this.connection = new
|
|
807
|
+
this.connection = new Q({
|
|
805
808
|
sysex: this.options.sysex
|
|
806
|
-
}), await this.connection.requestAccess(), this.connection.on(
|
|
809
|
+
}), await this.connection.requestAccess(), this.connection.on(p.DEVICE_CHANGE, async ({ state: e, type: i, device: n }) => {
|
|
807
810
|
try {
|
|
808
|
-
if (
|
|
809
|
-
|
|
810
|
-
else if (
|
|
811
|
-
if (
|
|
812
|
-
const
|
|
813
|
-
|
|
811
|
+
if (e === "connected")
|
|
812
|
+
i === "output" ? this.emit(P.DEV_OUT_CONNECTED, n) : i === "input" && this.emit(P.DEV_IN_CONNECTED, n);
|
|
813
|
+
else if (e === "disconnected") {
|
|
814
|
+
if (i === "output" && n) {
|
|
815
|
+
const s = this.connection.getCurrentOutput();
|
|
816
|
+
s && s.id === n.id ? await this._disconnectOutput() : this.emit(P.DEV_OUT_DISCONNECTED, n);
|
|
814
817
|
}
|
|
815
|
-
if (
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
+
if (i === "input" && n) {
|
|
819
|
+
const s = this.connection.getCurrentInput();
|
|
820
|
+
s && s.id === n.id ? await this._disconnectInput() : this.emit(P.DEV_IN_DISCONNECTED, n);
|
|
818
821
|
}
|
|
819
822
|
}
|
|
820
|
-
} catch (
|
|
821
|
-
console.error("Error in device change handler:",
|
|
823
|
+
} catch (s) {
|
|
824
|
+
console.error("Error in device change handler:", s), this.emit(P.ERROR, s);
|
|
822
825
|
}
|
|
823
|
-
}), 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(
|
|
824
|
-
} catch (
|
|
825
|
-
throw this.emit(
|
|
826
|
+
}), 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(P.READY, this), this.options.onReady?.(this);
|
|
827
|
+
} catch (e) {
|
|
828
|
+
throw this.emit(P.ERROR, e), this.options.onError?.(e), e;
|
|
826
829
|
}
|
|
827
830
|
}
|
|
828
831
|
/**
|
|
@@ -1108,9 +1111,9 @@ class z extends H {
|
|
|
1108
1111
|
* // Send data without wrapper (wrapper added automatically)
|
|
1109
1112
|
* midi.system.sendEx([0x42, 0x30, 0x00, 0x7F]);
|
|
1110
1113
|
*/
|
|
1111
|
-
sendEx:
|
|
1112
|
-
return this._sendSysEx(
|
|
1113
|
-
}
|
|
1114
|
+
sendEx: function(e, i = !1) {
|
|
1115
|
+
return this._sendSysEx(e, i);
|
|
1116
|
+
}.bind(this),
|
|
1114
1117
|
/**
|
|
1115
1118
|
* Send a timing clock message (0xF8)
|
|
1116
1119
|
* Typically used for synchronizing tempo-dependent devices
|
|
@@ -1300,12 +1303,12 @@ class z extends H {
|
|
|
1300
1303
|
* }
|
|
1301
1304
|
* });
|
|
1302
1305
|
*/
|
|
1303
|
-
bind(
|
|
1304
|
-
if (!
|
|
1306
|
+
bind(e, i, n = {}) {
|
|
1307
|
+
if (!e)
|
|
1305
1308
|
return console.warn("Cannot bind: element is null or undefined"), () => {
|
|
1306
1309
|
};
|
|
1307
|
-
const
|
|
1308
|
-
return this.bindings.set(
|
|
1310
|
+
const s = this._createBinding(e, i, n);
|
|
1311
|
+
return this.bindings.set(e, s), this.initialized && this.connection?.isConnected() && s.handler({ target: e }), () => this.unbind(e);
|
|
1309
1312
|
}
|
|
1310
1313
|
/**
|
|
1311
1314
|
* Create a binding between an element and MIDI CC
|
|
@@ -1315,60 +1318,60 @@ class z extends H {
|
|
|
1315
1318
|
* @param {Object} options - Additional binding options
|
|
1316
1319
|
* @returns {Object} Binding object with element, config, handler, and destroy function
|
|
1317
1320
|
*/
|
|
1318
|
-
_createBinding(
|
|
1321
|
+
_createBinding(e, i, n = {}) {
|
|
1319
1322
|
const {
|
|
1320
|
-
min:
|
|
1321
|
-
max:
|
|
1322
|
-
channel:
|
|
1323
|
-
invert:
|
|
1324
|
-
onInput:
|
|
1325
|
-
} =
|
|
1326
|
-
...
|
|
1327
|
-
min:
|
|
1328
|
-
max:
|
|
1329
|
-
invert:
|
|
1330
|
-
onInput:
|
|
1323
|
+
min: s = parseFloat(e.getAttribute("min")) || 0,
|
|
1324
|
+
max: r = parseFloat(e.getAttribute("max")) || 127,
|
|
1325
|
+
channel: E,
|
|
1326
|
+
invert: o = !1,
|
|
1327
|
+
onInput: l = void 0
|
|
1328
|
+
} = i, { debounce: S = 0 } = n, u = {
|
|
1329
|
+
...i,
|
|
1330
|
+
min: s,
|
|
1331
|
+
max: r,
|
|
1332
|
+
invert: o,
|
|
1333
|
+
onInput: l
|
|
1331
1334
|
};
|
|
1332
|
-
if (
|
|
1333
|
-
const { msb: d, lsb: T } =
|
|
1334
|
-
const
|
|
1335
|
-
if (Number.isNaN(
|
|
1336
|
-
const { msb:
|
|
1337
|
-
this._sendCC(d,
|
|
1335
|
+
if (E !== void 0 && (u.channel = E), i.is14Bit) {
|
|
1336
|
+
const { msb: d, lsb: T } = i, f = (G) => {
|
|
1337
|
+
const b = parseFloat(G.target.value);
|
|
1338
|
+
if (Number.isNaN(b)) return;
|
|
1339
|
+
const { msb: V, lsb: B } = W(b, s, r, o), H = E || this.options.outputChannel;
|
|
1340
|
+
this._sendCC(d, V, H), this._sendCC(T, B, H);
|
|
1338
1341
|
};
|
|
1339
1342
|
let I = null;
|
|
1340
|
-
const
|
|
1343
|
+
const L = S > 0 ? (G) => {
|
|
1341
1344
|
I && clearTimeout(I), I = setTimeout(() => {
|
|
1342
|
-
f(
|
|
1345
|
+
f(G), I = null;
|
|
1343
1346
|
}, S);
|
|
1344
1347
|
} : f;
|
|
1345
|
-
return
|
|
1346
|
-
element:
|
|
1347
|
-
config:
|
|
1348
|
+
return e.addEventListener("input", L), e.addEventListener("change", L), {
|
|
1349
|
+
element: e,
|
|
1350
|
+
config: u,
|
|
1348
1351
|
handler: f,
|
|
1349
1352
|
destroy: () => {
|
|
1350
|
-
I && clearTimeout(I),
|
|
1353
|
+
I && clearTimeout(I), e.removeEventListener("input", L), e.removeEventListener("change", L);
|
|
1351
1354
|
}
|
|
1352
1355
|
};
|
|
1353
1356
|
}
|
|
1354
|
-
const { cc: a } =
|
|
1357
|
+
const { cc: a } = i, A = (d) => {
|
|
1355
1358
|
const T = parseFloat(d.target.value);
|
|
1356
1359
|
if (Number.isNaN(T)) return;
|
|
1357
|
-
const f =
|
|
1360
|
+
const f = Z(T, s, r, o), I = E === void 0 ? this.options.outputChannel : E;
|
|
1358
1361
|
this._sendCC(a, f, I);
|
|
1359
1362
|
};
|
|
1360
1363
|
let h = null;
|
|
1361
1364
|
const N = S > 0 ? (d) => {
|
|
1362
1365
|
h && clearTimeout(h), h = setTimeout(() => {
|
|
1363
|
-
|
|
1366
|
+
A(d), h = null;
|
|
1364
1367
|
}, S);
|
|
1365
|
-
} :
|
|
1366
|
-
return
|
|
1367
|
-
element:
|
|
1368
|
-
config:
|
|
1369
|
-
handler:
|
|
1368
|
+
} : A;
|
|
1369
|
+
return e.addEventListener("input", N), e.addEventListener("change", N), {
|
|
1370
|
+
element: e,
|
|
1371
|
+
config: u,
|
|
1372
|
+
handler: A,
|
|
1370
1373
|
destroy: () => {
|
|
1371
|
-
h && clearTimeout(h),
|
|
1374
|
+
h && clearTimeout(h), e.removeEventListener("input", N), e.removeEventListener("change", N);
|
|
1372
1375
|
}
|
|
1373
1376
|
};
|
|
1374
1377
|
}
|
|
@@ -1376,18 +1379,18 @@ class z extends H {
|
|
|
1376
1379
|
* Unbind a control
|
|
1377
1380
|
* @param {HTMLElement} element
|
|
1378
1381
|
*/
|
|
1379
|
-
unbind(
|
|
1380
|
-
const
|
|
1381
|
-
|
|
1382
|
+
unbind(e) {
|
|
1383
|
+
const i = this.bindings.get(e);
|
|
1384
|
+
i && (i.destroy(), this.bindings.delete(e));
|
|
1382
1385
|
}
|
|
1383
1386
|
/**
|
|
1384
1387
|
* Clean up resources
|
|
1385
1388
|
* @returns {Promise<void>}
|
|
1386
1389
|
*/
|
|
1387
1390
|
async destroy() {
|
|
1388
|
-
for (const
|
|
1389
|
-
|
|
1390
|
-
this.bindings.clear(), this.state.controlChange.clear(), this.state.programChange.clear(), this.state.pitchBend.clear(), this.state.monoPressure.clear(), this.state.polyPressure.clear(), await this._disconnect(), this.initialized = !1, this.emit(
|
|
1391
|
+
for (const e of this.bindings.values())
|
|
1392
|
+
e.destroy();
|
|
1393
|
+
this.bindings.clear(), this.state.controlChange.clear(), this.state.programChange.clear(), this.state.pitchBend.clear(), this.state.monoPressure.clear(), this.state.polyPressure.clear(), await this._disconnect(), this.initialized = !1, this.emit(P.DESTROYED), this.removeAllListeners();
|
|
1391
1394
|
}
|
|
1392
1395
|
/**
|
|
1393
1396
|
* Connect to MIDI output device
|
|
@@ -1395,8 +1398,8 @@ class z extends H {
|
|
|
1395
1398
|
* @param {string|number|undefined} device - Device identifier (name, ID, index) or undefined for auto-connect
|
|
1396
1399
|
* @returns {Promise<void>}
|
|
1397
1400
|
*/
|
|
1398
|
-
async _connect(
|
|
1399
|
-
|
|
1401
|
+
async _connect(e) {
|
|
1402
|
+
e ? await this.connection.connect(e) : this.options.output !== void 0 ? await this.connection.connect(this.options.output) : this.options.autoConnect && await this.connection.connect();
|
|
1400
1403
|
}
|
|
1401
1404
|
/**
|
|
1402
1405
|
* Disconnect from current MIDI output device
|
|
@@ -1412,10 +1415,10 @@ class z extends H {
|
|
|
1412
1415
|
* @param {string|number} device - Input device identifier (name, ID, index)
|
|
1413
1416
|
* @returns {Promise<void>}
|
|
1414
1417
|
*/
|
|
1415
|
-
async _connectInput(
|
|
1416
|
-
await this.connection.connectInput(
|
|
1417
|
-
this._handleMIDIMessage(
|
|
1418
|
-
}), this.emit(
|
|
1418
|
+
async _connectInput(e) {
|
|
1419
|
+
await this.connection.connectInput(e, (i) => {
|
|
1420
|
+
this._handleMIDIMessage(i);
|
|
1421
|
+
}), this.emit(P.DEV_IN_CONNECTED, this.connection.getCurrentInput());
|
|
1419
1422
|
}
|
|
1420
1423
|
/**
|
|
1421
1424
|
* Disconnect from MIDI input device
|
|
@@ -1423,8 +1426,8 @@ class z extends H {
|
|
|
1423
1426
|
* @returns {Promise<void>}
|
|
1424
1427
|
*/
|
|
1425
1428
|
async _disconnectInput() {
|
|
1426
|
-
const
|
|
1427
|
-
this.connection.disconnectInput(), this.emit(
|
|
1429
|
+
const e = this.connection.getCurrentInput();
|
|
1430
|
+
this.connection.disconnectInput(), this.emit(P.DEV_IN_DISCONNECTED, e);
|
|
1428
1431
|
}
|
|
1429
1432
|
/**
|
|
1430
1433
|
* Connect to MIDI output device
|
|
@@ -1432,8 +1435,8 @@ class z extends H {
|
|
|
1432
1435
|
* @param {string|number} output - Output device identifier (name, ID, index)
|
|
1433
1436
|
* @returns {Promise<void>}
|
|
1434
1437
|
*/
|
|
1435
|
-
async _connectOutput(
|
|
1436
|
-
await this.connection.connect(
|
|
1438
|
+
async _connectOutput(e) {
|
|
1439
|
+
await this.connection.connect(e), this.emit(P.DEV_OUT_CONNECTED, this.connection.getCurrentOutput());
|
|
1437
1440
|
}
|
|
1438
1441
|
/**
|
|
1439
1442
|
* Disconnect from MIDI output device
|
|
@@ -1441,8 +1444,8 @@ class z extends H {
|
|
|
1441
1444
|
* @returns {Promise<void>}
|
|
1442
1445
|
*/
|
|
1443
1446
|
async _disconnectOutput() {
|
|
1444
|
-
const
|
|
1445
|
-
this.connection.disconnectOutput(), this.emit(
|
|
1447
|
+
const e = this.connection.getCurrentOutput();
|
|
1448
|
+
this.connection.disconnectOutput(), this.emit(P.DEV_OUT_DISCONNECTED, e);
|
|
1446
1449
|
}
|
|
1447
1450
|
/**
|
|
1448
1451
|
* Get current output device information
|
|
@@ -1480,12 +1483,12 @@ class z extends H {
|
|
|
1480
1483
|
* Send raw MIDI data
|
|
1481
1484
|
* @param {Array<number>} data - MIDI message bytes
|
|
1482
1485
|
*/
|
|
1483
|
-
send(
|
|
1486
|
+
send(e) {
|
|
1484
1487
|
if (!this.initialized) {
|
|
1485
1488
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1486
1489
|
return;
|
|
1487
1490
|
}
|
|
1488
|
-
this.connection.send(
|
|
1491
|
+
this.connection.send(e);
|
|
1489
1492
|
}
|
|
1490
1493
|
/**
|
|
1491
1494
|
* Send Note On message
|
|
@@ -1495,11 +1498,11 @@ class z extends H {
|
|
|
1495
1498
|
* @param {number} channel - MIDI channel (1-16, default outputChannel)
|
|
1496
1499
|
* @returns {void}
|
|
1497
1500
|
*/
|
|
1498
|
-
_sendNoteOn(
|
|
1501
|
+
_sendNoteOn(e, i = 64, n = this.options.outputChannel) {
|
|
1499
1502
|
if (!this.initialized) return;
|
|
1500
|
-
|
|
1501
|
-
const
|
|
1502
|
-
this.connection.send([
|
|
1503
|
+
e = D(Math.round(e), 0, 127), i = D(Math.round(i), 0, 127), n = D(Math.round(n), 1, 16);
|
|
1504
|
+
const s = 144 + (n - 1);
|
|
1505
|
+
this.connection.send([s, e, i]), this.emit(P.CH_NOTE_ON_SEND, { note: e, velocity: i, channel: n });
|
|
1503
1506
|
}
|
|
1504
1507
|
/**
|
|
1505
1508
|
* Send Note Off message
|
|
@@ -1509,11 +1512,11 @@ class z extends H {
|
|
|
1509
1512
|
* @param {number} velocity - Release velocity (0-127, default 0)
|
|
1510
1513
|
* @returns {void}
|
|
1511
1514
|
*/
|
|
1512
|
-
_sendNoteOff(
|
|
1515
|
+
_sendNoteOff(e, i = this.options.outputChannel, n = 0) {
|
|
1513
1516
|
if (!this.initialized) return;
|
|
1514
|
-
|
|
1515
|
-
const
|
|
1516
|
-
this.connection.send([
|
|
1517
|
+
e = D(Math.round(e), 0, 127), n = D(Math.round(n), 0, 127), i = D(Math.round(i), 1, 16);
|
|
1518
|
+
const s = 144 + (i - 1);
|
|
1519
|
+
this.connection.send([s, e, n]), this.emit(P.CH_NOTE_OFF_SEND, { note: e, channel: i, velocity: n });
|
|
1517
1520
|
}
|
|
1518
1521
|
/**
|
|
1519
1522
|
* Send Control Change message
|
|
@@ -1523,16 +1526,16 @@ class z extends H {
|
|
|
1523
1526
|
* @param {number} channel - MIDI channel (1-16, default outputChannel)
|
|
1524
1527
|
* @returns {void}
|
|
1525
1528
|
*/
|
|
1526
|
-
_sendCC(
|
|
1529
|
+
_sendCC(e, i, n = this.options.outputChannel) {
|
|
1527
1530
|
if (!this.initialized) {
|
|
1528
1531
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1529
1532
|
return;
|
|
1530
1533
|
}
|
|
1531
|
-
|
|
1532
|
-
const
|
|
1533
|
-
this.connection.send([
|
|
1534
|
-
const
|
|
1535
|
-
this.state.controlChange.set(
|
|
1534
|
+
e = D(Math.round(e), 0, 127), i = D(Math.round(i), 0, 127), n = D(Math.round(n), 1, 16);
|
|
1535
|
+
const s = 176 + (n - 1);
|
|
1536
|
+
this.connection.send([s, e, i]);
|
|
1537
|
+
const r = `${n}:${e}`;
|
|
1538
|
+
this.state.controlChange.set(r, i), this.emit(P.CH_CC_SEND, { cc: e, value: i, channel: n });
|
|
1536
1539
|
}
|
|
1537
1540
|
/**
|
|
1538
1541
|
* Send Program Change message
|
|
@@ -1541,11 +1544,11 @@ class z extends H {
|
|
|
1541
1544
|
* @param {number} channel - MIDI channel (1-16, default outputChannel)
|
|
1542
1545
|
* @returns {void}
|
|
1543
1546
|
*/
|
|
1544
|
-
_sendPC(
|
|
1547
|
+
_sendPC(e, i = this.options.outputChannel) {
|
|
1545
1548
|
if (!this.initialized) return;
|
|
1546
|
-
|
|
1547
|
-
const
|
|
1548
|
-
this.connection.send([
|
|
1549
|
+
e = D(Math.round(e), 0, 127), i = D(Math.round(i), 1, 16);
|
|
1550
|
+
const n = 192 + (i - 1);
|
|
1551
|
+
this.connection.send([n, e]), this.state.programChange.set(i.toString(), e), this.emit(P.CH_PC_SEND, { program: e, channel: i });
|
|
1549
1552
|
}
|
|
1550
1553
|
/**
|
|
1551
1554
|
* Get current Program Change value for channel
|
|
@@ -1553,8 +1556,8 @@ class z extends H {
|
|
|
1553
1556
|
* @param {number} channel - MIDI channel (1-16, default inputChannel)
|
|
1554
1557
|
* @returns {number|undefined} Program number or undefined if not set
|
|
1555
1558
|
*/
|
|
1556
|
-
_getPC(
|
|
1557
|
-
return this.state.programChange.get(
|
|
1559
|
+
_getPC(e = this.options.inputChannel) {
|
|
1560
|
+
return this.state.programChange.get(e.toString());
|
|
1558
1561
|
}
|
|
1559
1562
|
/**
|
|
1560
1563
|
* Get current Control Change value
|
|
@@ -1563,9 +1566,9 @@ class z extends H {
|
|
|
1563
1566
|
* @param {number} channel - MIDI channel (1-16, default inputChannel)
|
|
1564
1567
|
* @returns {number|undefined} CC value or undefined if not set
|
|
1565
1568
|
*/
|
|
1566
|
-
_getCC(
|
|
1567
|
-
const
|
|
1568
|
-
return this.state.controlChange.get(
|
|
1569
|
+
_getCC(e, i = this.options.inputChannel) {
|
|
1570
|
+
const n = `${i}:${e}`;
|
|
1571
|
+
return this.state.controlChange.get(n);
|
|
1569
1572
|
}
|
|
1570
1573
|
/**
|
|
1571
1574
|
* Send Pitch Bend message on specified channel
|
|
@@ -1574,11 +1577,11 @@ class z extends H {
|
|
|
1574
1577
|
* @param {number} [channel=this.options.outputChannel] - MIDI channel (1-16)
|
|
1575
1578
|
* @returns {void}
|
|
1576
1579
|
*/
|
|
1577
|
-
_sendPitchBend(
|
|
1580
|
+
_sendPitchBend(e, i = this.options.outputChannel) {
|
|
1578
1581
|
if (!this.initialized) return;
|
|
1579
|
-
|
|
1580
|
-
const
|
|
1581
|
-
this.connection.send([
|
|
1582
|
+
e = D(Math.round(e), 0, 16383), i = D(Math.round(i), 1, 16);
|
|
1583
|
+
const n = 224 + (i - 1), s = e & 127, r = e >> 7 & 127;
|
|
1584
|
+
this.connection.send([n, s, r]), this.state.pitchBend.set(i.toString(), e), this.emit(P.CH_PITCH_BEND_SEND, { value: e, channel: i });
|
|
1582
1585
|
}
|
|
1583
1586
|
/**
|
|
1584
1587
|
* Get current Pitch Bend value for a channel
|
|
@@ -1586,8 +1589,8 @@ class z extends H {
|
|
|
1586
1589
|
* @param {number} channel - MIDI channel (1-16, default inputChannel)
|
|
1587
1590
|
* @returns {number|undefined} Pitch bend value (0-16383) or undefined if not set
|
|
1588
1591
|
*/
|
|
1589
|
-
_getPitchBend(
|
|
1590
|
-
return this.state.pitchBend.get(
|
|
1592
|
+
_getPitchBend(e = this.options.inputChannel) {
|
|
1593
|
+
return this.state.pitchBend.get(e.toString());
|
|
1591
1594
|
}
|
|
1592
1595
|
/**
|
|
1593
1596
|
* Send Channel Pressure (Aftertouch) message
|
|
@@ -1596,11 +1599,11 @@ class z extends H {
|
|
|
1596
1599
|
* @param {number} channel - MIDI channel (1-16, default outputChannel)
|
|
1597
1600
|
* @returns {void}
|
|
1598
1601
|
*/
|
|
1599
|
-
_sendMonoPressure(
|
|
1602
|
+
_sendMonoPressure(e, i = this.options.outputChannel) {
|
|
1600
1603
|
if (!this.initialized) return;
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
this.connection.send([
|
|
1604
|
+
e = D(Math.round(e), 0, 127), i = D(Math.round(i), 1, 16);
|
|
1605
|
+
const n = 208 + (i - 1);
|
|
1606
|
+
this.connection.send([n, e]), this.state.monoPressure.set(i.toString(), e), this.emit(P.CH_MONO_PRESS_SEND, { pressure: e, channel: i });
|
|
1604
1607
|
}
|
|
1605
1608
|
/**
|
|
1606
1609
|
* Get current Channel Pressure (Aftertouch) value for a channel
|
|
@@ -1608,8 +1611,8 @@ class z extends H {
|
|
|
1608
1611
|
* @param {number} channel - MIDI channel (1-16, default inputChannel)
|
|
1609
1612
|
* @returns {number|undefined} Pressure value (0-127) or undefined if not set
|
|
1610
1613
|
*/
|
|
1611
|
-
_getMonoPressure(
|
|
1612
|
-
return this.state.monoPressure.get(
|
|
1614
|
+
_getMonoPressure(e = this.options.inputChannel) {
|
|
1615
|
+
return this.state.monoPressure.get(e.toString());
|
|
1613
1616
|
}
|
|
1614
1617
|
/**
|
|
1615
1618
|
* Send Polyphonic Key Pressure (Polyphonic Aftertouch) message
|
|
@@ -1619,13 +1622,13 @@ class z extends H {
|
|
|
1619
1622
|
* @param {number} channel - MIDI channel (1-16, default outputChannel)
|
|
1620
1623
|
* @returns {void}
|
|
1621
1624
|
*/
|
|
1622
|
-
_sendPolyPressure(
|
|
1625
|
+
_sendPolyPressure(e, i, n = this.options.outputChannel) {
|
|
1623
1626
|
if (!this.initialized) return;
|
|
1624
|
-
|
|
1625
|
-
const
|
|
1626
|
-
this.connection.send([
|
|
1627
|
-
const
|
|
1628
|
-
this.state.polyPressure.set(
|
|
1627
|
+
e = D(Math.round(e), 0, 127), i = D(Math.round(i), 0, 127), n = D(Math.round(n), 1, 16);
|
|
1628
|
+
const s = 160 + (n - 1);
|
|
1629
|
+
this.connection.send([s, e, i]);
|
|
1630
|
+
const r = `${n}:${e}`;
|
|
1631
|
+
this.state.polyPressure.set(r, i), this.emit(P.CH_POLY_PRESS_SEND, { note: e, pressure: i, channel: n });
|
|
1629
1632
|
}
|
|
1630
1633
|
/**
|
|
1631
1634
|
* Get current Polyphonic Pressure (Aftertouch) value for a specific note on a channel
|
|
@@ -1634,169 +1637,169 @@ class z extends H {
|
|
|
1634
1637
|
* @param {number} [channel=this.options.inputChannel] - MIDI channel (1-16)
|
|
1635
1638
|
* @returns {number|undefined} Pressure value (0-127) or undefined if not set
|
|
1636
1639
|
*/
|
|
1637
|
-
_getPolyPressure(
|
|
1638
|
-
const
|
|
1639
|
-
return this.state.polyPressure.get(
|
|
1640
|
+
_getPolyPressure(e, i = this.options.inputChannel) {
|
|
1641
|
+
const n = `${i}:${e}`;
|
|
1642
|
+
return this.state.polyPressure.get(n);
|
|
1640
1643
|
}
|
|
1641
1644
|
/**
|
|
1642
1645
|
* @private
|
|
1643
1646
|
*/
|
|
1644
|
-
_allSoundsOff(
|
|
1647
|
+
_allSoundsOff(e) {
|
|
1645
1648
|
if (!this.initialized) {
|
|
1646
1649
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1647
1650
|
return;
|
|
1648
1651
|
}
|
|
1649
|
-
if (
|
|
1650
|
-
|
|
1651
|
-
const
|
|
1652
|
-
this.connection.send([
|
|
1652
|
+
if (e !== void 0) {
|
|
1653
|
+
e = D(Math.round(e), 1, 16);
|
|
1654
|
+
const i = 176 + (e - 1);
|
|
1655
|
+
this.connection.send([i, 120, 0]), this.emit(P.CH_ALL_SOUNDS_OFF_SEND, { channel: e });
|
|
1653
1656
|
} else {
|
|
1654
|
-
for (let
|
|
1655
|
-
const
|
|
1656
|
-
this.connection.send([
|
|
1657
|
+
for (let i = 1; i <= 16; i++) {
|
|
1658
|
+
const n = 176 + (i - 1);
|
|
1659
|
+
this.connection.send([n, 120, 0]);
|
|
1657
1660
|
}
|
|
1658
|
-
this.emit(
|
|
1661
|
+
this.emit(P.CH_ALL_SOUNDS_OFF_SEND, { channel: null });
|
|
1659
1662
|
}
|
|
1660
1663
|
}
|
|
1661
1664
|
/**
|
|
1662
1665
|
* @private
|
|
1663
1666
|
*/
|
|
1664
|
-
_resetControllers(
|
|
1667
|
+
_resetControllers(e) {
|
|
1665
1668
|
if (!this.initialized) {
|
|
1666
1669
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1667
1670
|
return;
|
|
1668
1671
|
}
|
|
1669
|
-
if (
|
|
1670
|
-
|
|
1671
|
-
const
|
|
1672
|
-
this.connection.send([
|
|
1672
|
+
if (e !== void 0) {
|
|
1673
|
+
e = D(Math.round(e), 1, 16);
|
|
1674
|
+
const i = 176 + (e - 1);
|
|
1675
|
+
this.connection.send([i, 121, 0]), this.emit(P.CH_RESET_CONTROLLERS_SEND, { channel: e });
|
|
1673
1676
|
} else {
|
|
1674
|
-
for (let
|
|
1675
|
-
const
|
|
1676
|
-
this.connection.send([
|
|
1677
|
+
for (let i = 1; i <= 16; i++) {
|
|
1678
|
+
const n = 176 + (i - 1);
|
|
1679
|
+
this.connection.send([n, 121, 0]);
|
|
1677
1680
|
}
|
|
1678
|
-
this.emit(
|
|
1681
|
+
this.emit(P.CH_RESET_CONTROLLERS_SEND, { channel: null });
|
|
1679
1682
|
}
|
|
1680
1683
|
}
|
|
1681
1684
|
/**
|
|
1682
1685
|
* @private
|
|
1683
1686
|
*/
|
|
1684
|
-
_localControl(
|
|
1687
|
+
_localControl(e, i) {
|
|
1685
1688
|
if (!this.initialized) {
|
|
1686
1689
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1687
1690
|
return;
|
|
1688
1691
|
}
|
|
1689
|
-
const
|
|
1690
|
-
if (
|
|
1691
|
-
|
|
1692
|
-
const
|
|
1693
|
-
this.connection.send([
|
|
1692
|
+
const n = e ? 127 : 0;
|
|
1693
|
+
if (i !== void 0) {
|
|
1694
|
+
i = D(Math.round(i), 1, 16);
|
|
1695
|
+
const s = 176 + (i - 1);
|
|
1696
|
+
this.connection.send([s, 122, n]), this.emit(P.CH_LOCAL_CONTROL_SEND, { enabled: e, channel: i });
|
|
1694
1697
|
} else {
|
|
1695
|
-
for (let
|
|
1696
|
-
const
|
|
1697
|
-
this.connection.send([
|
|
1698
|
+
for (let s = 1; s <= 16; s++) {
|
|
1699
|
+
const r = 176 + (s - 1);
|
|
1700
|
+
this.connection.send([r, 122, n]);
|
|
1698
1701
|
}
|
|
1699
|
-
this.emit(
|
|
1702
|
+
this.emit(P.CH_LOCAL_CONTROL_SEND, { enabled: e, channel: null });
|
|
1700
1703
|
}
|
|
1701
1704
|
}
|
|
1702
1705
|
/**
|
|
1703
1706
|
* @private
|
|
1704
1707
|
*/
|
|
1705
|
-
_allNotesOff(
|
|
1708
|
+
_allNotesOff(e) {
|
|
1706
1709
|
if (!this.initialized) {
|
|
1707
1710
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1708
1711
|
return;
|
|
1709
1712
|
}
|
|
1710
|
-
if (
|
|
1711
|
-
|
|
1712
|
-
const
|
|
1713
|
-
this.connection.send([
|
|
1713
|
+
if (e !== void 0) {
|
|
1714
|
+
e = D(Math.round(e), 1, 16);
|
|
1715
|
+
const i = 176 + (e - 1);
|
|
1716
|
+
this.connection.send([i, 123, 0]), this.emit(P.CH_ALL_NOTES_OFF_SEND, { channel: e });
|
|
1714
1717
|
} else {
|
|
1715
|
-
for (let
|
|
1716
|
-
const
|
|
1717
|
-
this.connection.send([
|
|
1718
|
+
for (let i = 1; i <= 16; i++) {
|
|
1719
|
+
const n = 176 + (i - 1);
|
|
1720
|
+
this.connection.send([n, 123, 0]);
|
|
1718
1721
|
}
|
|
1719
|
-
this.emit(
|
|
1722
|
+
this.emit(P.CH_ALL_NOTES_OFF_SEND, { channel: null });
|
|
1720
1723
|
}
|
|
1721
1724
|
}
|
|
1722
1725
|
/**
|
|
1723
1726
|
* @private
|
|
1724
1727
|
*/
|
|
1725
|
-
_omniOff(
|
|
1728
|
+
_omniOff(e) {
|
|
1726
1729
|
if (!this.initialized) {
|
|
1727
1730
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1728
1731
|
return;
|
|
1729
1732
|
}
|
|
1730
|
-
if (
|
|
1731
|
-
|
|
1732
|
-
const
|
|
1733
|
-
this.connection.send([
|
|
1733
|
+
if (e !== void 0) {
|
|
1734
|
+
e = D(Math.round(e), 1, 16);
|
|
1735
|
+
const i = 176 + (e - 1);
|
|
1736
|
+
this.connection.send([i, 124, 0]), this.emit(P.CH_OMNI_OFF_SEND, { channel: e });
|
|
1734
1737
|
} else {
|
|
1735
|
-
for (let
|
|
1736
|
-
const
|
|
1737
|
-
this.connection.send([
|
|
1738
|
+
for (let i = 1; i <= 16; i++) {
|
|
1739
|
+
const n = 176 + (i - 1);
|
|
1740
|
+
this.connection.send([n, 124, 0]);
|
|
1738
1741
|
}
|
|
1739
|
-
this.emit(
|
|
1742
|
+
this.emit(P.CH_OMNI_OFF_SEND, { channel: null });
|
|
1740
1743
|
}
|
|
1741
1744
|
}
|
|
1742
1745
|
/**
|
|
1743
1746
|
* @private
|
|
1744
1747
|
*/
|
|
1745
|
-
_omniOn(
|
|
1748
|
+
_omniOn(e) {
|
|
1746
1749
|
if (!this.initialized) {
|
|
1747
1750
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1748
1751
|
return;
|
|
1749
1752
|
}
|
|
1750
|
-
if (
|
|
1751
|
-
|
|
1752
|
-
const
|
|
1753
|
-
this.connection.send([
|
|
1753
|
+
if (e !== void 0) {
|
|
1754
|
+
e = D(Math.round(e), 1, 16);
|
|
1755
|
+
const i = 176 + (e - 1);
|
|
1756
|
+
this.connection.send([i, 125, 0]), this.emit(P.CH_OMNI_ON_SEND, { channel: e });
|
|
1754
1757
|
} else {
|
|
1755
|
-
for (let
|
|
1756
|
-
const
|
|
1757
|
-
this.connection.send([
|
|
1758
|
+
for (let i = 1; i <= 16; i++) {
|
|
1759
|
+
const n = 176 + (i - 1);
|
|
1760
|
+
this.connection.send([n, 125, 0]);
|
|
1758
1761
|
}
|
|
1759
|
-
this.emit(
|
|
1762
|
+
this.emit(P.CH_OMNI_ON_SEND, { channel: null });
|
|
1760
1763
|
}
|
|
1761
1764
|
}
|
|
1762
1765
|
/**
|
|
1763
1766
|
* @private
|
|
1764
1767
|
*/
|
|
1765
|
-
_monoOn(
|
|
1768
|
+
_monoOn(e = 1, i) {
|
|
1766
1769
|
if (!this.initialized) {
|
|
1767
1770
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1768
1771
|
return;
|
|
1769
1772
|
}
|
|
1770
|
-
if (
|
|
1771
|
-
|
|
1772
|
-
const
|
|
1773
|
-
this.connection.send([
|
|
1773
|
+
if (e = Math.max(0, Math.min(16, Math.round(e))), i !== void 0) {
|
|
1774
|
+
i = D(Math.round(i), 1, 16);
|
|
1775
|
+
const n = 176 + (i - 1);
|
|
1776
|
+
this.connection.send([n, 126, e]), this.emit(P.CH_MONO_ON_SEND, { channels: e, channel: i });
|
|
1774
1777
|
} else {
|
|
1775
|
-
for (let
|
|
1776
|
-
const
|
|
1777
|
-
this.connection.send([
|
|
1778
|
+
for (let n = 1; n <= 16; n++) {
|
|
1779
|
+
const s = 176 + (n - 1);
|
|
1780
|
+
this.connection.send([s, 126, e]);
|
|
1778
1781
|
}
|
|
1779
|
-
this.emit(
|
|
1782
|
+
this.emit(P.CH_MONO_ON_SEND, { channels: e, channel: null });
|
|
1780
1783
|
}
|
|
1781
1784
|
}
|
|
1782
1785
|
/**
|
|
1783
1786
|
* @private
|
|
1784
1787
|
*/
|
|
1785
|
-
_polyOn(
|
|
1788
|
+
_polyOn(e) {
|
|
1786
1789
|
if (!this.initialized) {
|
|
1787
1790
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1788
1791
|
return;
|
|
1789
1792
|
}
|
|
1790
|
-
if (
|
|
1791
|
-
|
|
1792
|
-
const
|
|
1793
|
-
this.connection.send([
|
|
1793
|
+
if (e !== void 0) {
|
|
1794
|
+
e = D(Math.round(e), 1, 16);
|
|
1795
|
+
const i = 176 + (e - 1);
|
|
1796
|
+
this.connection.send([i, 127, 0]), this.emit(P.CH_POLY_ON_SEND, { channel: e });
|
|
1794
1797
|
} else {
|
|
1795
|
-
for (let
|
|
1796
|
-
const
|
|
1797
|
-
this.connection.send([
|
|
1798
|
+
for (let i = 1; i <= 16; i++) {
|
|
1799
|
+
const n = 176 + (i - 1);
|
|
1800
|
+
this.connection.send([n, 127, 0]);
|
|
1798
1801
|
}
|
|
1799
|
-
this.emit(
|
|
1802
|
+
this.emit(P.CH_POLY_ON_SEND, { channel: null });
|
|
1800
1803
|
}
|
|
1801
1804
|
}
|
|
1802
1805
|
/**
|
|
@@ -1809,7 +1812,7 @@ class z extends H {
|
|
|
1809
1812
|
* this._sendSysEx([0x42, 0x30, 0x00, 0x7F]) // Send without wrapper
|
|
1810
1813
|
* this._sendSysEx([0xF0, 0x42, 0x30, 0xF7], true) // Send with wrapper
|
|
1811
1814
|
*/
|
|
1812
|
-
_sendSysEx(
|
|
1815
|
+
_sendSysEx(e, i = !1) {
|
|
1813
1816
|
if (!this.initialized) {
|
|
1814
1817
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1815
1818
|
return;
|
|
@@ -1818,7 +1821,7 @@ class z extends H {
|
|
|
1818
1821
|
console.warn("SysEx not enabled. Initialize with sysex: true");
|
|
1819
1822
|
return;
|
|
1820
1823
|
}
|
|
1821
|
-
this.connection.sendSysEx(
|
|
1824
|
+
this.connection.sendSysEx(e, i), this.emit(P.SYS_EX_SEND, { data: e, includeWrapper: i });
|
|
1822
1825
|
}
|
|
1823
1826
|
/**
|
|
1824
1827
|
* Send MIDI Timing Clock message (System Real-Time)
|
|
@@ -1874,12 +1877,12 @@ class z extends H {
|
|
|
1874
1877
|
* @param {number} data - MTC quarter frame data (0-127)
|
|
1875
1878
|
* @returns {void}
|
|
1876
1879
|
*/
|
|
1877
|
-
_sendMTC(
|
|
1880
|
+
_sendMTC(e) {
|
|
1878
1881
|
if (!this.initialized) {
|
|
1879
1882
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1880
1883
|
return;
|
|
1881
1884
|
}
|
|
1882
|
-
|
|
1885
|
+
e = D(Math.round(e), 0, 127), this.connection.send([241, e]);
|
|
1883
1886
|
}
|
|
1884
1887
|
/**
|
|
1885
1888
|
* Send Song Position Pointer message (System Common)
|
|
@@ -1887,14 +1890,14 @@ class z extends H {
|
|
|
1887
1890
|
* @param {number} position - Song position in beats (0-16383)
|
|
1888
1891
|
* @returns {void}
|
|
1889
1892
|
*/
|
|
1890
|
-
_sendSongPosition(
|
|
1893
|
+
_sendSongPosition(e) {
|
|
1891
1894
|
if (!this.initialized) {
|
|
1892
1895
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1893
1896
|
return;
|
|
1894
1897
|
}
|
|
1895
|
-
|
|
1896
|
-
const
|
|
1897
|
-
this.connection.send([242,
|
|
1898
|
+
e = D(Math.round(e), 0, 16383);
|
|
1899
|
+
const i = e & 127, n = e >> 7 & 127;
|
|
1900
|
+
this.connection.send([242, i, n]);
|
|
1898
1901
|
}
|
|
1899
1902
|
/**
|
|
1900
1903
|
* Send Song Select message (System Common)
|
|
@@ -1902,12 +1905,12 @@ class z extends H {
|
|
|
1902
1905
|
* @param {number} song - Song/sequence number (0-127)
|
|
1903
1906
|
* @returns {void}
|
|
1904
1907
|
*/
|
|
1905
|
-
_sendSongSelect(
|
|
1908
|
+
_sendSongSelect(e) {
|
|
1906
1909
|
if (!this.initialized) {
|
|
1907
1910
|
console.warn("MIDI not initialized. Call init() first.");
|
|
1908
1911
|
return;
|
|
1909
1912
|
}
|
|
1910
|
-
|
|
1913
|
+
e = D(Math.round(e), 0, 127), this.connection.send([243, e]);
|
|
1911
1914
|
}
|
|
1912
1915
|
/**
|
|
1913
1916
|
* Send Tune Request message (System Common)
|
|
@@ -1953,148 +1956,148 @@ class z extends H {
|
|
|
1953
1956
|
* @param {number} event.midiwire - Timestamp
|
|
1954
1957
|
* @returns {void}
|
|
1955
1958
|
*/
|
|
1956
|
-
_handleMIDIMessage(
|
|
1957
|
-
const [
|
|
1958
|
-
if (
|
|
1959
|
-
this.emit(
|
|
1960
|
-
timestamp:
|
|
1959
|
+
_handleMIDIMessage(e) {
|
|
1960
|
+
const [i, n, s] = e.data, r = i & 240, E = (i & 15) + 1;
|
|
1961
|
+
if (i === 248) {
|
|
1962
|
+
this.emit(P.SYS_CLOCK_RECV, {
|
|
1963
|
+
timestamp: e.midiwire
|
|
1961
1964
|
});
|
|
1962
1965
|
return;
|
|
1963
1966
|
}
|
|
1964
|
-
if (
|
|
1965
|
-
this.emit(
|
|
1966
|
-
timestamp:
|
|
1967
|
+
if (i === 250) {
|
|
1968
|
+
this.emit(P.SYS_START_RECV, {
|
|
1969
|
+
timestamp: e.midiwire
|
|
1967
1970
|
});
|
|
1968
1971
|
return;
|
|
1969
1972
|
}
|
|
1970
|
-
if (
|
|
1971
|
-
this.emit(
|
|
1972
|
-
timestamp:
|
|
1973
|
+
if (i === 251) {
|
|
1974
|
+
this.emit(P.SYS_CONTINUE_RECV, {
|
|
1975
|
+
timestamp: e.midiwire
|
|
1973
1976
|
});
|
|
1974
1977
|
return;
|
|
1975
1978
|
}
|
|
1976
|
-
if (
|
|
1977
|
-
this.emit(
|
|
1978
|
-
timestamp:
|
|
1979
|
+
if (i === 252) {
|
|
1980
|
+
this.emit(P.SYS_STOP_RECV, {
|
|
1981
|
+
timestamp: e.midiwire
|
|
1979
1982
|
});
|
|
1980
1983
|
return;
|
|
1981
1984
|
}
|
|
1982
|
-
if (
|
|
1983
|
-
this.emit(
|
|
1984
|
-
timestamp:
|
|
1985
|
+
if (i === 254) {
|
|
1986
|
+
this.emit(P.SYS_ACT_SENSE_RECV, {
|
|
1987
|
+
timestamp: e.midiwire
|
|
1985
1988
|
});
|
|
1986
1989
|
return;
|
|
1987
1990
|
}
|
|
1988
|
-
if (
|
|
1989
|
-
this.emit(
|
|
1990
|
-
timestamp:
|
|
1991
|
+
if (i === 255) {
|
|
1992
|
+
this.emit(P.SYS_RESET_RECV, {
|
|
1993
|
+
timestamp: e.midiwire
|
|
1991
1994
|
});
|
|
1992
1995
|
return;
|
|
1993
1996
|
}
|
|
1994
|
-
if (
|
|
1995
|
-
this.emit(
|
|
1996
|
-
data: Array.from(
|
|
1997
|
-
timestamp:
|
|
1997
|
+
if (i === 240) {
|
|
1998
|
+
this.emit(P.SYS_EX_RECV, {
|
|
1999
|
+
data: Array.from(e.data),
|
|
2000
|
+
timestamp: e.midiwire
|
|
1998
2001
|
});
|
|
1999
2002
|
return;
|
|
2000
2003
|
}
|
|
2001
|
-
if (
|
|
2002
|
-
this.emit(
|
|
2003
|
-
data:
|
|
2004
|
-
timestamp:
|
|
2004
|
+
if (i === 241) {
|
|
2005
|
+
this.emit(P.SYS_MTC_RECV, {
|
|
2006
|
+
data: n,
|
|
2007
|
+
timestamp: e.midiwire
|
|
2005
2008
|
});
|
|
2006
2009
|
return;
|
|
2007
2010
|
}
|
|
2008
|
-
if (
|
|
2009
|
-
const
|
|
2010
|
-
this.emit(
|
|
2011
|
-
position:
|
|
2012
|
-
timestamp:
|
|
2011
|
+
if (i === 242) {
|
|
2012
|
+
const o = n + (s << 7);
|
|
2013
|
+
this.emit(P.SYS_SONG_POS_RECV, {
|
|
2014
|
+
position: o,
|
|
2015
|
+
timestamp: e.midiwire
|
|
2013
2016
|
});
|
|
2014
2017
|
return;
|
|
2015
2018
|
}
|
|
2016
|
-
if (
|
|
2017
|
-
this.emit(
|
|
2018
|
-
song:
|
|
2019
|
-
timestamp:
|
|
2019
|
+
if (i === 243) {
|
|
2020
|
+
this.emit(P.SYS_SONG_SEL_RECV, {
|
|
2021
|
+
song: n,
|
|
2022
|
+
timestamp: e.midiwire
|
|
2020
2023
|
});
|
|
2021
2024
|
return;
|
|
2022
2025
|
}
|
|
2023
|
-
if (
|
|
2024
|
-
this.emit(
|
|
2025
|
-
timestamp:
|
|
2026
|
+
if (i === 246) {
|
|
2027
|
+
this.emit(P.SYS_TUNE_REQ_RECV, {
|
|
2028
|
+
timestamp: e.midiwire
|
|
2026
2029
|
});
|
|
2027
2030
|
return;
|
|
2028
2031
|
}
|
|
2029
|
-
if (
|
|
2030
|
-
this.emit(
|
|
2031
|
-
status:
|
|
2032
|
-
data: [
|
|
2033
|
-
channel:
|
|
2034
|
-
timestamp:
|
|
2032
|
+
if (i === 247) {
|
|
2033
|
+
this.emit(P.MIDI_RAW, {
|
|
2034
|
+
status: i,
|
|
2035
|
+
data: [n, s],
|
|
2036
|
+
channel: E,
|
|
2037
|
+
timestamp: e.midiwire
|
|
2035
2038
|
});
|
|
2036
2039
|
return;
|
|
2037
2040
|
}
|
|
2038
|
-
if (
|
|
2039
|
-
const
|
|
2040
|
-
this.state.controlChange.set(
|
|
2041
|
-
cc:
|
|
2042
|
-
value:
|
|
2043
|
-
channel:
|
|
2041
|
+
if (r === 176) {
|
|
2042
|
+
const o = `${E}:${n}`;
|
|
2043
|
+
this.state.controlChange.set(o, s), this.emit(P.CH_CC_RECV, {
|
|
2044
|
+
cc: n,
|
|
2045
|
+
value: s,
|
|
2046
|
+
channel: E
|
|
2044
2047
|
});
|
|
2045
2048
|
return;
|
|
2046
2049
|
}
|
|
2047
|
-
if (
|
|
2048
|
-
this.state.programChange.set(
|
|
2049
|
-
program:
|
|
2050
|
-
channel:
|
|
2050
|
+
if (r === 192) {
|
|
2051
|
+
this.state.programChange.set(E.toString(), n), this.emit(P.CH_PC_RECV, {
|
|
2052
|
+
program: n,
|
|
2053
|
+
channel: E
|
|
2051
2054
|
});
|
|
2052
2055
|
return;
|
|
2053
2056
|
}
|
|
2054
|
-
if (
|
|
2055
|
-
const
|
|
2056
|
-
this.state.pitchBend.set(
|
|
2057
|
-
value:
|
|
2058
|
-
channel:
|
|
2057
|
+
if (r === 224) {
|
|
2058
|
+
const o = n + (s << 7);
|
|
2059
|
+
this.state.pitchBend.set(E.toString(), o), this.emit(P.CH_PITCH_BEND_RECV, {
|
|
2060
|
+
value: o,
|
|
2061
|
+
channel: E
|
|
2059
2062
|
});
|
|
2060
2063
|
return;
|
|
2061
2064
|
}
|
|
2062
|
-
if (
|
|
2063
|
-
this.state.monoPressure.set(
|
|
2064
|
-
pressure:
|
|
2065
|
-
channel:
|
|
2065
|
+
if (r === 208) {
|
|
2066
|
+
this.state.monoPressure.set(E.toString(), n), this.emit(P.CH_MONO_PRESS_RECV, {
|
|
2067
|
+
pressure: n,
|
|
2068
|
+
channel: E
|
|
2066
2069
|
});
|
|
2067
2070
|
return;
|
|
2068
2071
|
}
|
|
2069
|
-
if (
|
|
2070
|
-
const
|
|
2071
|
-
this.state.polyPressure.set(
|
|
2072
|
-
note:
|
|
2073
|
-
pressure:
|
|
2074
|
-
channel:
|
|
2072
|
+
if (r === 160) {
|
|
2073
|
+
const o = `${E}:${n}`;
|
|
2074
|
+
this.state.polyPressure.set(o, s), this.emit(P.CH_POLY_PRESS_RECV, {
|
|
2075
|
+
note: n,
|
|
2076
|
+
pressure: s,
|
|
2077
|
+
channel: E
|
|
2075
2078
|
});
|
|
2076
2079
|
return;
|
|
2077
2080
|
}
|
|
2078
|
-
if (
|
|
2079
|
-
this.emit(
|
|
2080
|
-
note:
|
|
2081
|
-
velocity:
|
|
2082
|
-
channel:
|
|
2081
|
+
if (r === 144 && s > 0) {
|
|
2082
|
+
this.emit(P.CH_NOTE_ON_RECV, {
|
|
2083
|
+
note: n,
|
|
2084
|
+
velocity: s,
|
|
2085
|
+
channel: E
|
|
2083
2086
|
});
|
|
2084
2087
|
return;
|
|
2085
2088
|
}
|
|
2086
|
-
if (
|
|
2087
|
-
this.emit(
|
|
2088
|
-
note:
|
|
2089
|
-
channel:
|
|
2089
|
+
if (r === 128 || r === 144 && s === 0) {
|
|
2090
|
+
this.emit(P.CH_NOTE_OFF_RECV, {
|
|
2091
|
+
note: n,
|
|
2092
|
+
channel: E
|
|
2090
2093
|
});
|
|
2091
2094
|
return;
|
|
2092
2095
|
}
|
|
2093
|
-
this.emit(
|
|
2094
|
-
status:
|
|
2095
|
-
data: [
|
|
2096
|
-
channel:
|
|
2097
|
-
timestamp:
|
|
2096
|
+
this.emit(P.MIDI_RAW, {
|
|
2097
|
+
status: i,
|
|
2098
|
+
data: [n, s],
|
|
2099
|
+
channel: E,
|
|
2100
|
+
timestamp: e.midiwire
|
|
2098
2101
|
});
|
|
2099
2102
|
}
|
|
2100
2103
|
/**
|
|
@@ -2103,50 +2106,50 @@ class z extends H {
|
|
|
2103
2106
|
* @param {string} [name="Unnamed Patch"] - Patch name
|
|
2104
2107
|
* @returns {PatchData} Patch data object with channels, settings, and metadata
|
|
2105
2108
|
*/
|
|
2106
|
-
_getPatch(
|
|
2107
|
-
const
|
|
2108
|
-
name:
|
|
2109
|
+
_getPatch(e = "Unnamed Patch") {
|
|
2110
|
+
const i = {
|
|
2111
|
+
name: e,
|
|
2109
2112
|
device: this._getCurrentOutput()?.name || null,
|
|
2110
2113
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2111
2114
|
version: "1.0",
|
|
2112
2115
|
channels: {},
|
|
2113
2116
|
settings: {}
|
|
2114
2117
|
};
|
|
2115
|
-
for (const [
|
|
2116
|
-
const [
|
|
2117
|
-
|
|
2118
|
-
}
|
|
2119
|
-
for (const [
|
|
2120
|
-
const
|
|
2121
|
-
|
|
2122
|
-
}
|
|
2123
|
-
for (const [
|
|
2124
|
-
const
|
|
2125
|
-
|
|
2126
|
-
}
|
|
2127
|
-
for (const [
|
|
2128
|
-
const
|
|
2129
|
-
|
|
2130
|
-
}
|
|
2131
|
-
for (const [
|
|
2132
|
-
const [
|
|
2133
|
-
|
|
2134
|
-
}
|
|
2135
|
-
for (const [
|
|
2136
|
-
const { config:
|
|
2137
|
-
if (
|
|
2138
|
-
const
|
|
2139
|
-
|
|
2140
|
-
min:
|
|
2141
|
-
max:
|
|
2142
|
-
invert:
|
|
2143
|
-
is14Bit:
|
|
2144
|
-
label:
|
|
2145
|
-
elementId:
|
|
2118
|
+
for (const [n, s] of this.state.controlChange.entries()) {
|
|
2119
|
+
const [r, E] = n.split(":").map(Number);
|
|
2120
|
+
i.channels[r] || (i.channels[r] = { ccs: {}, notes: {} }), i.channels[r].ccs[E] = s;
|
|
2121
|
+
}
|
|
2122
|
+
for (const [n, s] of this.state.programChange.entries()) {
|
|
2123
|
+
const r = parseInt(n, 10);
|
|
2124
|
+
i.channels[r] || (i.channels[r] = { ccs: {}, notes: {} }), i.channels[r].program = s;
|
|
2125
|
+
}
|
|
2126
|
+
for (const [n, s] of this.state.pitchBend.entries()) {
|
|
2127
|
+
const r = parseInt(n, 10);
|
|
2128
|
+
i.channels[r] || (i.channels[r] = { ccs: {}, notes: {} }), i.channels[r].pitchBend = s;
|
|
2129
|
+
}
|
|
2130
|
+
for (const [n, s] of this.state.monoPressure.entries()) {
|
|
2131
|
+
const r = parseInt(n, 10);
|
|
2132
|
+
i.channels[r] || (i.channels[r] = { ccs: {}, notes: {} }), i.channels[r].monoPressure = s;
|
|
2133
|
+
}
|
|
2134
|
+
for (const [n, s] of this.state.polyPressure.entries()) {
|
|
2135
|
+
const [r, E] = n.split(":").map(Number), o = parseInt(r, 10);
|
|
2136
|
+
i.channels[o] || (i.channels[o] = { ccs: {}, notes: {} }), i.channels[o].polyPressure || (i.channels[o].polyPressure = {}), i.channels[o].polyPressure[E] = s;
|
|
2137
|
+
}
|
|
2138
|
+
for (const [n, s] of this.bindings.entries()) {
|
|
2139
|
+
const { config: r } = s;
|
|
2140
|
+
if (r.cc) {
|
|
2141
|
+
const E = `cc${r.cc}`;
|
|
2142
|
+
i.settings[E] = {
|
|
2143
|
+
min: r.min,
|
|
2144
|
+
max: r.max,
|
|
2145
|
+
invert: r.invert || !1,
|
|
2146
|
+
is14Bit: r.is14Bit || !1,
|
|
2147
|
+
label: n.getAttribute?.("data-midi-label") || null,
|
|
2148
|
+
elementId: n.id || null
|
|
2146
2149
|
};
|
|
2147
2150
|
}
|
|
2148
2151
|
}
|
|
2149
|
-
return
|
|
2152
|
+
return i;
|
|
2150
2153
|
}
|
|
2151
2154
|
/**
|
|
2152
2155
|
* Apply a patch to the controller
|
|
@@ -2155,11 +2158,11 @@ class z extends H {
|
|
|
2155
2158
|
* @returns {Promise<void>}
|
|
2156
2159
|
* @throws {MIDIValidationError} If patch format is invalid
|
|
2157
2160
|
*/
|
|
2158
|
-
async _setPatch(
|
|
2159
|
-
if (!
|
|
2160
|
-
throw new
|
|
2161
|
-
const
|
|
2162
|
-
|
|
2161
|
+
async _setPatch(e) {
|
|
2162
|
+
if (!e || !e.channels)
|
|
2163
|
+
throw new m("Invalid patch format", "patch");
|
|
2164
|
+
const i = e.version || "1.0";
|
|
2165
|
+
i === "1.0" ? await this._applyPatchV1(e) : (console.warn(`Unknown patch version: ${i}. Attempting to apply as v1.0`), await this._applyPatchV1(e)), this.emit(P.PATCH_LOADED, { patch: e });
|
|
2163
2166
|
}
|
|
2164
2167
|
/**
|
|
2165
2168
|
* Apply patch data for version 1.0 format
|
|
@@ -2167,39 +2170,39 @@ class z extends H {
|
|
|
2167
2170
|
* @param {PatchData} patch - Patch object to apply
|
|
2168
2171
|
* @returns {Promise<void>}
|
|
2169
2172
|
*/
|
|
2170
|
-
async _applyPatchV1(
|
|
2171
|
-
for (const [
|
|
2172
|
-
const
|
|
2173
|
-
if (
|
|
2174
|
-
for (const [
|
|
2175
|
-
const
|
|
2176
|
-
this._sendCC(
|
|
2173
|
+
async _applyPatchV1(e) {
|
|
2174
|
+
for (const [i, n] of Object.entries(e.channels)) {
|
|
2175
|
+
const s = parseInt(i, 10);
|
|
2176
|
+
if (n.ccs)
|
|
2177
|
+
for (const [r, E] of Object.entries(n.ccs)) {
|
|
2178
|
+
const o = parseInt(r, 10);
|
|
2179
|
+
this._sendCC(o, E, s);
|
|
2177
2180
|
}
|
|
2178
|
-
if (
|
|
2179
|
-
for (const [
|
|
2180
|
-
const
|
|
2181
|
-
this._sendPolyPressure(
|
|
2181
|
+
if (n.program !== void 0 && this._sendPC(n.program, s), n.pitchBend !== void 0 && this._sendPitchBend(n.pitchBend, s), n.monoPressure !== void 0 && this._sendMonoPressure(n.monoPressure, s), n.polyPressure)
|
|
2182
|
+
for (const [r, E] of Object.entries(n.polyPressure)) {
|
|
2183
|
+
const o = parseInt(r, 10);
|
|
2184
|
+
this._sendPolyPressure(o, E, s);
|
|
2182
2185
|
}
|
|
2183
|
-
if (
|
|
2184
|
-
for (const [
|
|
2185
|
-
const
|
|
2186
|
-
|
|
2186
|
+
if (n.notes)
|
|
2187
|
+
for (const [r, E] of Object.entries(n.notes)) {
|
|
2188
|
+
const o = parseInt(r, 10);
|
|
2189
|
+
E > 0 ? this._sendNoteOn(o, E, s) : this._sendNoteOff(o, s);
|
|
2187
2190
|
}
|
|
2188
2191
|
}
|
|
2189
|
-
if (
|
|
2190
|
-
for (const [
|
|
2191
|
-
for (const [
|
|
2192
|
-
|
|
2193
|
-
for (const [
|
|
2194
|
-
const { config:
|
|
2195
|
-
if (
|
|
2196
|
-
const
|
|
2197
|
-
if (
|
|
2198
|
-
const
|
|
2199
|
-
if (
|
|
2200
|
-
const
|
|
2192
|
+
if (e.settings)
|
|
2193
|
+
for (const [i, n] of Object.entries(e.settings))
|
|
2194
|
+
for (const [s, r] of this.bindings.entries())
|
|
2195
|
+
r.config.cc?.toString() === i.replace("cc", "") && (s.min !== void 0 && n.min !== void 0 && (s.min = String(n.min)), s.max !== void 0 && n.max !== void 0 && (s.max = String(n.max)));
|
|
2196
|
+
for (const [i, n] of this.bindings.entries()) {
|
|
2197
|
+
const { config: s } = n;
|
|
2198
|
+
if (s.cc !== void 0) {
|
|
2199
|
+
const r = s.channel || this.options.inputChannel, E = e.channels[r];
|
|
2200
|
+
if (E?.ccs) {
|
|
2201
|
+
const o = E.ccs[s.cc];
|
|
2202
|
+
if (o !== void 0) {
|
|
2203
|
+
const l = s.min !== void 0 ? s.min : parseFloat(i.getAttribute?.("min")) || 0, S = s.max !== void 0 ? s.max : parseFloat(i.getAttribute?.("max")) || 127, u = s.invert || !1;
|
|
2201
2204
|
let a;
|
|
2202
|
-
|
|
2205
|
+
u ? a = S - o / 127 * (S - l) : a = l + o / 127 * (S - l), s.onInput && typeof s.onInput == "function" ? s.onInput(a) : (i.value = a, i.dispatchEvent(new Event("input", { bubbles: !0 })));
|
|
2203
2206
|
}
|
|
2204
2207
|
}
|
|
2205
2208
|
}
|
|
@@ -2212,12 +2215,12 @@ class z extends H {
|
|
|
2212
2215
|
* @param {PatchData} [patch] - Optional patch object (will use get() if not provided)
|
|
2213
2216
|
* @returns {string} Storage key used
|
|
2214
2217
|
*/
|
|
2215
|
-
_savePatch(
|
|
2216
|
-
const
|
|
2218
|
+
_savePatch(e, i = null) {
|
|
2219
|
+
const n = i || this._getPatch(e), s = `midiwire_patch_${e}`;
|
|
2217
2220
|
try {
|
|
2218
|
-
return localStorage.setItem(
|
|
2219
|
-
} catch (
|
|
2220
|
-
throw console.error("Failed to save patch:",
|
|
2221
|
+
return localStorage.setItem(s, JSON.stringify(n)), this.emit(P.PATCH_SAVED, { name: e, patch: n }), s;
|
|
2222
|
+
} catch (r) {
|
|
2223
|
+
throw console.error("Failed to save patch:", r), r;
|
|
2221
2224
|
}
|
|
2222
2225
|
}
|
|
2223
2226
|
/**
|
|
@@ -2226,16 +2229,16 @@ class z extends H {
|
|
|
2226
2229
|
* @param {string} name - Patch name
|
|
2227
2230
|
* @returns {PatchData|null} Patch object or null if not found
|
|
2228
2231
|
*/
|
|
2229
|
-
_loadPatch(
|
|
2230
|
-
const
|
|
2232
|
+
_loadPatch(e) {
|
|
2233
|
+
const i = `midiwire_patch_${e}`;
|
|
2231
2234
|
try {
|
|
2232
|
-
const
|
|
2233
|
-
if (!
|
|
2235
|
+
const n = localStorage.getItem(i);
|
|
2236
|
+
if (!n)
|
|
2234
2237
|
return null;
|
|
2235
|
-
const
|
|
2236
|
-
return this.emit(
|
|
2237
|
-
} catch (
|
|
2238
|
-
return console.error("Failed to load patch:",
|
|
2238
|
+
const s = JSON.parse(n);
|
|
2239
|
+
return this.emit(P.PATCH_LOADED, { name: e, patch: s }), s;
|
|
2240
|
+
} catch (n) {
|
|
2241
|
+
return console.error("Failed to load patch:", n), null;
|
|
2239
2242
|
}
|
|
2240
2243
|
}
|
|
2241
2244
|
/**
|
|
@@ -2244,12 +2247,12 @@ class z extends H {
|
|
|
2244
2247
|
* @param {string} name - Patch name
|
|
2245
2248
|
* @returns {boolean} Success status
|
|
2246
2249
|
*/
|
|
2247
|
-
_deletePatch(
|
|
2248
|
-
const
|
|
2250
|
+
_deletePatch(e) {
|
|
2251
|
+
const i = `midiwire_patch_${e}`;
|
|
2249
2252
|
try {
|
|
2250
|
-
return localStorage.removeItem(
|
|
2251
|
-
} catch (
|
|
2252
|
-
return console.error("Failed to delete patch:",
|
|
2253
|
+
return localStorage.removeItem(i), this.emit(P.PATCH_DELETED, { name: e }), !0;
|
|
2254
|
+
} catch (n) {
|
|
2255
|
+
return console.error("Failed to delete patch:", n), !1;
|
|
2253
2256
|
}
|
|
2254
2257
|
}
|
|
2255
2258
|
/**
|
|
@@ -2258,22 +2261,22 @@ class z extends H {
|
|
|
2258
2261
|
* @returns {Array<{name: string, patch: PatchData}>} Sorted array of patch objects
|
|
2259
2262
|
*/
|
|
2260
2263
|
_listPatches() {
|
|
2261
|
-
const
|
|
2264
|
+
const e = [];
|
|
2262
2265
|
try {
|
|
2263
|
-
for (let
|
|
2264
|
-
const
|
|
2265
|
-
if (
|
|
2266
|
-
const
|
|
2267
|
-
|
|
2266
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
2267
|
+
const n = localStorage.key(i);
|
|
2268
|
+
if (n?.startsWith("midiwire_patch_")) {
|
|
2269
|
+
const s = n.replace("midiwire_patch_", ""), r = this._loadPatch(s);
|
|
2270
|
+
r && e.push({ name: s, patch: r });
|
|
2268
2271
|
}
|
|
2269
2272
|
}
|
|
2270
|
-
} catch (
|
|
2271
|
-
console.error("Failed to list patches:",
|
|
2273
|
+
} catch (i) {
|
|
2274
|
+
console.error("Failed to list patches:", i);
|
|
2272
2275
|
}
|
|
2273
|
-
return
|
|
2276
|
+
return e.sort((i, n) => i.name.localeCompare(n.name));
|
|
2274
2277
|
}
|
|
2275
2278
|
}
|
|
2276
|
-
class
|
|
2279
|
+
class k {
|
|
2277
2280
|
/**
|
|
2278
2281
|
* Create a new MIDIDeviceManager instance
|
|
2279
2282
|
* @param {Object} options - Configuration options
|
|
@@ -2282,10 +2285,10 @@ class q {
|
|
|
2282
2285
|
* @param {Function} [options.onConnectionUpdate] - Callback when connection status changes (outputDevice: Object, inputDevice: Object, midi: MIDIController)
|
|
2283
2286
|
* @param {number} [options.channel=1] - Default MIDI channel
|
|
2284
2287
|
*/
|
|
2285
|
-
constructor(
|
|
2286
|
-
this.midi =
|
|
2287
|
-
}), this.onConnectionUpdate =
|
|
2288
|
-
}), this.channel =
|
|
2288
|
+
constructor(e = {}) {
|
|
2289
|
+
this.midi = e.midiController || null, this.onStatusUpdate = e.onStatusUpdate || (() => {
|
|
2290
|
+
}), this.onConnectionUpdate = e.onConnectionUpdate || (() => {
|
|
2291
|
+
}), this.channel = e.channel || 1, this.currentOutput = null, this.currentInput = null, this.isConnecting = !1;
|
|
2289
2292
|
}
|
|
2290
2293
|
/**
|
|
2291
2294
|
* Set up all MIDI device selectors in one call. Handles population, connection
|
|
@@ -2301,21 +2304,21 @@ class q {
|
|
|
2301
2304
|
* @param {Function} [options.onDeviceListChange] - Called when device list changes (for custom UI updates)
|
|
2302
2305
|
* @returns {Promise<MIDIController>} The MIDI controller instance for chaining
|
|
2303
2306
|
*/
|
|
2304
|
-
async setupSelectors(
|
|
2307
|
+
async setupSelectors(e = {}, i = {}) {
|
|
2305
2308
|
if (!this.midi)
|
|
2306
2309
|
throw new Error("MIDI controller not initialized. Pass midiController in constructor options.");
|
|
2307
|
-
const { output:
|
|
2308
|
-
if (this._setupDeviceChangeListeners({ output:
|
|
2309
|
-
await this._populateOutputDeviceList(
|
|
2310
|
-
const S =
|
|
2311
|
-
this._connectOutputDeviceSelection(
|
|
2310
|
+
const { output: n, input: s, channel: r } = e, E = this._resolveSelector(n), o = this._resolveSelector(s), l = this._resolveSelector(r);
|
|
2311
|
+
if (this._setupDeviceChangeListeners({ output: E, input: o }, i.onDeviceListChange), E) {
|
|
2312
|
+
await this._populateOutputDeviceList(E);
|
|
2313
|
+
const S = i.onConnect ? async (a, A) => i.onConnect({ midi: a, device: A, type: "output" }) : void 0, u = i.onDisconnect ? async (a) => i.onDisconnect({ midi: a, type: "output" }) : void 0;
|
|
2314
|
+
this._connectOutputDeviceSelection(E, S, u);
|
|
2312
2315
|
}
|
|
2313
|
-
if (
|
|
2314
|
-
await this._populateInputDeviceList(
|
|
2315
|
-
const S =
|
|
2316
|
-
this._connectInputDeviceSelection(
|
|
2316
|
+
if (o) {
|
|
2317
|
+
await this._populateInputDeviceList(o);
|
|
2318
|
+
const S = i.onConnect ? async (a, A) => i.onConnect({ midi: a, device: A, type: "input" }) : void 0, u = i.onDisconnect ? async (a) => i.onDisconnect({ midi: a, type: "input" }) : void 0;
|
|
2319
|
+
this._connectInputDeviceSelection(o, S, u);
|
|
2317
2320
|
}
|
|
2318
|
-
return
|
|
2321
|
+
return l && this._connectChannelSelection(l, "output"), this.midi;
|
|
2319
2322
|
}
|
|
2320
2323
|
/**
|
|
2321
2324
|
* Update status message and trigger status callback
|
|
@@ -2330,8 +2333,8 @@ class q {
|
|
|
2330
2333
|
* @example
|
|
2331
2334
|
* manager.updateStatus("Connection failed", "error");
|
|
2332
2335
|
*/
|
|
2333
|
-
updateStatus(
|
|
2334
|
-
this.onStatusUpdate(
|
|
2336
|
+
updateStatus(e, i = "") {
|
|
2337
|
+
this.onStatusUpdate(e, i);
|
|
2335
2338
|
}
|
|
2336
2339
|
/**
|
|
2337
2340
|
* Update connection status
|
|
@@ -2348,15 +2351,15 @@ class q {
|
|
|
2348
2351
|
* @param {HTMLSelectElement} [selectElements.input] - Input device dropdown
|
|
2349
2352
|
* @param {Function} [onDeviceListChange] - Callback when device list changes
|
|
2350
2353
|
*/
|
|
2351
|
-
_setupDeviceChangeListeners(
|
|
2352
|
-
!this.midi || this._listenersInitialized || (this._listenersInitialized = !0, this.midi.on(
|
|
2353
|
-
this.updateStatus(`Output device connected: ${
|
|
2354
|
-
}), this.midi.on(
|
|
2355
|
-
this.updateStatus(`Output device disconnected: ${
|
|
2356
|
-
}), this.midi.on(
|
|
2357
|
-
this.updateStatus(`Input device connected: ${
|
|
2358
|
-
}), this.midi.on(
|
|
2359
|
-
this.updateStatus(`Input device disconnected: ${
|
|
2354
|
+
_setupDeviceChangeListeners(e = {}, i) {
|
|
2355
|
+
!this.midi || this._listenersInitialized || (this._listenersInitialized = !0, this.midi.on(P.DEV_OUT_CONNECTED, async (n) => {
|
|
2356
|
+
this.updateStatus(`Output device connected: ${n?.name || "Unknown"}`, "connected"), e.output && await this._populateOutputDeviceList(e.output), i && i();
|
|
2357
|
+
}), this.midi.on(P.DEV_OUT_DISCONNECTED, async (n) => {
|
|
2358
|
+
this.updateStatus(`Output device disconnected: ${n?.name || "Unknown"}`, "error"), this.currentOutput && n?.name === this.currentOutput.name && (this.currentOutput = null, this.updateConnectionStatus(), e.output && (e.output.value = "")), e.output && await this._populateOutputDeviceList(e.output), i && i();
|
|
2359
|
+
}), this.midi.on(P.DEV_IN_CONNECTED, async (n) => {
|
|
2360
|
+
this.updateStatus(`Input device connected: ${n?.name || "Unknown"}`, "connected"), e.input && await this._populateInputDeviceList(e.input), i && i();
|
|
2361
|
+
}), this.midi.on(P.DEV_IN_DISCONNECTED, async (n) => {
|
|
2362
|
+
this.updateStatus(`Input device disconnected: ${n?.name || "Unknown"}`, "error"), e.input && (e.input.value = "", await this._populateInputDeviceList(e.input)), i && i();
|
|
2360
2363
|
}));
|
|
2361
2364
|
}
|
|
2362
2365
|
/**
|
|
@@ -2365,12 +2368,12 @@ class q {
|
|
|
2365
2368
|
* @param {string|HTMLElement} selector - CSS selector string or DOM element
|
|
2366
2369
|
* @returns {HTMLElement|null} The resolved element or null
|
|
2367
2370
|
*/
|
|
2368
|
-
_resolveSelector(
|
|
2369
|
-
if (typeof
|
|
2370
|
-
const
|
|
2371
|
-
return
|
|
2371
|
+
_resolveSelector(e) {
|
|
2372
|
+
if (typeof e == "string") {
|
|
2373
|
+
const i = document.querySelector(e);
|
|
2374
|
+
return i || console.warn(`MIDIDeviceManager: Selector "${e}" not found`), i;
|
|
2372
2375
|
}
|
|
2373
|
-
return
|
|
2376
|
+
return e || null;
|
|
2374
2377
|
}
|
|
2375
2378
|
/**
|
|
2376
2379
|
* Get the current list of MIDI output devices
|
|
@@ -2397,23 +2400,23 @@ class q {
|
|
|
2397
2400
|
* @param {Function} [onDisconnect] - Callback when device is disconnected (midi: MIDIController)
|
|
2398
2401
|
* @returns {void}
|
|
2399
2402
|
*/
|
|
2400
|
-
_connectOutputDeviceSelection(
|
|
2401
|
-
!
|
|
2403
|
+
_connectOutputDeviceSelection(e, i, n) {
|
|
2404
|
+
!e || !this.midi || e.addEventListener("change", async (s) => {
|
|
2402
2405
|
if (this.isConnecting) return;
|
|
2403
2406
|
this.isConnecting = !0;
|
|
2404
|
-
const
|
|
2405
|
-
if (!
|
|
2406
|
-
this.currentOutput && this.midi && (await this.midi.device.disconnectOutput(), this.currentOutput = null, this.updateStatus("Output device disconnected", ""), this.updateConnectionStatus()), this.isConnecting = !1,
|
|
2407
|
+
const r = s.target.value;
|
|
2408
|
+
if (!r) {
|
|
2409
|
+
this.currentOutput && this.midi && (await this.midi.device.disconnectOutput(), this.currentOutput = null, this.updateStatus("Output device disconnected", ""), this.updateConnectionStatus()), this.isConnecting = !1, n && await n(this.midi);
|
|
2407
2410
|
return;
|
|
2408
2411
|
}
|
|
2409
2412
|
try {
|
|
2410
|
-
if (await this.midi.device.connectOutput(parseInt(
|
|
2411
|
-
const
|
|
2412
|
-
|
|
2413
|
+
if (await this.midi.device.connectOutput(parseInt(r, 10)), this.currentOutput = this.midi.device.getCurrentOutput(), this.currentOutput) {
|
|
2414
|
+
const o = this.midi.device.getOutputs().findIndex((l) => l.id === this.currentOutput.id);
|
|
2415
|
+
o !== -1 && (e.value = o.toString());
|
|
2413
2416
|
}
|
|
2414
|
-
this.updateConnectionStatus(),
|
|
2415
|
-
} catch (
|
|
2416
|
-
this.updateStatus(`Output connection failed: ${
|
|
2417
|
+
this.updateConnectionStatus(), i && await i(this.midi, this.currentOutput);
|
|
2418
|
+
} catch (E) {
|
|
2419
|
+
this.updateStatus(`Output connection failed: ${E.message}`, "error");
|
|
2417
2420
|
} finally {
|
|
2418
2421
|
this.isConnecting = !1;
|
|
2419
2422
|
}
|
|
@@ -2428,21 +2431,21 @@ class q {
|
|
|
2428
2431
|
* @param {Function} [onDisconnect] - Callback when device is disconnected (midi: MIDIController)
|
|
2429
2432
|
* @returns {void}
|
|
2430
2433
|
*/
|
|
2431
|
-
_connectInputDeviceSelection(
|
|
2432
|
-
!
|
|
2433
|
-
const
|
|
2434
|
-
if (!
|
|
2435
|
-
this.midi && (await this.midi.device.disconnectInput(), this.updateStatus("Input device disconnected", ""), this.updateConnectionStatus()),
|
|
2434
|
+
_connectInputDeviceSelection(e, i, n) {
|
|
2435
|
+
!e || !this.midi || e.addEventListener("change", async (s) => {
|
|
2436
|
+
const r = s.target.value;
|
|
2437
|
+
if (!r) {
|
|
2438
|
+
this.midi && (await this.midi.device.disconnectInput(), this.updateStatus("Input device disconnected", ""), this.updateConnectionStatus()), n && await n(this.midi);
|
|
2436
2439
|
return;
|
|
2437
2440
|
}
|
|
2438
2441
|
if (!this.isConnecting) {
|
|
2439
2442
|
this.isConnecting = !0;
|
|
2440
2443
|
try {
|
|
2441
|
-
await this.midi.device.connectInput(parseInt(
|
|
2442
|
-
const
|
|
2443
|
-
this.updateConnectionStatus(),
|
|
2444
|
-
} catch (
|
|
2445
|
-
this.updateStatus(`Input connection failed: ${
|
|
2444
|
+
await this.midi.device.connectInput(parseInt(r, 10));
|
|
2445
|
+
const E = this.midi.device.getCurrentInput();
|
|
2446
|
+
this.updateConnectionStatus(), i && await i(this.midi, E);
|
|
2447
|
+
} catch (E) {
|
|
2448
|
+
this.updateStatus(`Input connection failed: ${E.message}`, "error");
|
|
2446
2449
|
} finally {
|
|
2447
2450
|
this.isConnecting = !1;
|
|
2448
2451
|
}
|
|
@@ -2459,17 +2462,17 @@ class q {
|
|
|
2459
2462
|
* @param {boolean} isOutput - Whether these are output devices
|
|
2460
2463
|
* @returns {void}
|
|
2461
2464
|
*/
|
|
2462
|
-
_populateDeviceList(
|
|
2463
|
-
if (
|
|
2464
|
-
if (
|
|
2465
|
-
const
|
|
2466
|
-
|
|
2465
|
+
_populateDeviceList(e, i, n, s, r) {
|
|
2466
|
+
if (i.length > 0) {
|
|
2467
|
+
if (e.innerHTML = '<option value="">Select a device</option>' + i.map((E, o) => `<option value="${o}">${E.name}</option>`).join(""), n) {
|
|
2468
|
+
const E = i.findIndex((o) => o.name === n.name);
|
|
2469
|
+
E !== -1 ? e.value = E.toString() : (e.value = "", r && (this.currentOutput = null, this.updateConnectionStatus()));
|
|
2467
2470
|
} else
|
|
2468
|
-
|
|
2469
|
-
|
|
2471
|
+
e.value = "";
|
|
2472
|
+
e.disabled = !1, r && !this.currentOutput && this.updateStatus("Select a device");
|
|
2470
2473
|
} else
|
|
2471
|
-
|
|
2472
|
-
|
|
2474
|
+
e.innerHTML = '<option value="">No devices connected</option>', e.disabled = !0, r && this.updateStatus("No devices connected", "error");
|
|
2475
|
+
s && s();
|
|
2473
2476
|
}
|
|
2474
2477
|
/**
|
|
2475
2478
|
* Populate a select element with available MIDI output devices. Automatically
|
|
@@ -2480,10 +2483,10 @@ class q {
|
|
|
2480
2483
|
* @param {Function} [onChange] - Optional callback invoked after populating the list
|
|
2481
2484
|
* @returns {Promise<void>}
|
|
2482
2485
|
*/
|
|
2483
|
-
async _populateOutputDeviceList(
|
|
2484
|
-
if (!
|
|
2485
|
-
const
|
|
2486
|
-
this._populateDeviceList(
|
|
2486
|
+
async _populateOutputDeviceList(e, i) {
|
|
2487
|
+
if (!e || !this.midi) return;
|
|
2488
|
+
const n = this._getOutputDevices();
|
|
2489
|
+
this._populateDeviceList(e, n, this.currentOutput, i, !0);
|
|
2487
2490
|
}
|
|
2488
2491
|
/**
|
|
2489
2492
|
* Populate a select element with available MIDI input devices. Automatically
|
|
@@ -2494,10 +2497,10 @@ class q {
|
|
|
2494
2497
|
* @param {Function} [onChange] - Optional callback invoked after populating the list
|
|
2495
2498
|
* @returns {Promise<void>}
|
|
2496
2499
|
*/
|
|
2497
|
-
async _populateInputDeviceList(
|
|
2498
|
-
if (!
|
|
2499
|
-
const
|
|
2500
|
-
this._populateDeviceList(
|
|
2500
|
+
async _populateInputDeviceList(e, i) {
|
|
2501
|
+
if (!e || !this.midi) return;
|
|
2502
|
+
const n = this._getInputDevices(), s = this.midi.device.getCurrentInput();
|
|
2503
|
+
this._populateDeviceList(e, n, s, i, !1);
|
|
2501
2504
|
}
|
|
2502
2505
|
/**
|
|
2503
2506
|
* Connect channel selection events to automatically update the MIDI channel when
|
|
@@ -2519,199 +2522,15 @@ class q {
|
|
|
2519
2522
|
* const inputChannelSelect = document.getElementById("input-channel-select");
|
|
2520
2523
|
* manager.input.connectChannelSelection(inputChannelSelect);
|
|
2521
2524
|
*/
|
|
2522
|
-
_connectChannelSelection(
|
|
2523
|
-
if (!
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2526
|
-
this.midi && (this.midi.options[
|
|
2525
|
+
_connectChannelSelection(e, i) {
|
|
2526
|
+
if (!e || !this.midi) return;
|
|
2527
|
+
const n = i === "input" ? "inputChannel" : "outputChannel";
|
|
2528
|
+
e.addEventListener("change", (s) => {
|
|
2529
|
+
this.midi && (this.midi.options[n] = parseInt(s.target.value, 10), this.updateConnectionStatus());
|
|
2527
2530
|
});
|
|
2528
2531
|
}
|
|
2529
2532
|
}
|
|
2530
|
-
class
|
|
2531
|
-
// Packed format (128 bytes)
|
|
2532
|
-
// See: DX7 Service Manual, Voice Memory Format
|
|
2533
|
-
static PACKED_SIZE = 128;
|
|
2534
|
-
static PACKED_OP_SIZE = 17;
|
|
2535
|
-
// 17 bytes per operator in packed format
|
|
2536
|
-
static NUM_OPERATORS = 6;
|
|
2537
|
-
// Packed operator parameter offsets (within each 17-byte operator block)
|
|
2538
|
-
static PACKED_OP_EG_RATE_1 = 0;
|
|
2539
|
-
static PACKED_OP_EG_RATE_2 = 1;
|
|
2540
|
-
static PACKED_OP_EG_RATE_3 = 2;
|
|
2541
|
-
static PACKED_OP_EG_RATE_4 = 3;
|
|
2542
|
-
static PACKED_OP_EG_LEVEL_1 = 4;
|
|
2543
|
-
static PACKED_OP_EG_LEVEL_2 = 5;
|
|
2544
|
-
static PACKED_OP_EG_LEVEL_3 = 6;
|
|
2545
|
-
static PACKED_OP_EG_LEVEL_4 = 7;
|
|
2546
|
-
static PACKED_OP_BREAK_POINT = 8;
|
|
2547
|
-
static PACKED_OP_L_SCALE_DEPTH = 9;
|
|
2548
|
-
static PACKED_OP_R_SCALE_DEPTH = 10;
|
|
2549
|
-
static PACKED_OP_CURVES = 11;
|
|
2550
|
-
// LC and RC packed
|
|
2551
|
-
static PACKED_OP_RATE_SCALING = 12;
|
|
2552
|
-
// RS and DET packed
|
|
2553
|
-
static PACKED_OP_MOD_SENS = 13;
|
|
2554
|
-
// AMS and KVS packed
|
|
2555
|
-
static PACKED_OP_OUTPUT_LEVEL = 14;
|
|
2556
|
-
static PACKED_OP_MODE_FREQ = 15;
|
|
2557
|
-
// Mode and Freq Coarse packed
|
|
2558
|
-
static PACKED_OP_DETUNE_FINE = 16;
|
|
2559
|
-
// OSC Detune and Freq Fine packed
|
|
2560
|
-
// Packed voice offsets (after 6 operators = bytes 102+)
|
|
2561
|
-
static PACKED_PITCH_EG_RATE_1 = 102;
|
|
2562
|
-
static PACKED_PITCH_EG_RATE_2 = 103;
|
|
2563
|
-
static PACKED_PITCH_EG_RATE_3 = 104;
|
|
2564
|
-
static PACKED_PITCH_EG_RATE_4 = 105;
|
|
2565
|
-
static PACKED_PITCH_EG_LEVEL_1 = 106;
|
|
2566
|
-
static PACKED_PITCH_EG_LEVEL_2 = 107;
|
|
2567
|
-
static PACKED_PITCH_EG_LEVEL_3 = 108;
|
|
2568
|
-
static PACKED_PITCH_EG_LEVEL_4 = 109;
|
|
2569
|
-
static OFFSET_ALGORITHM = 110;
|
|
2570
|
-
static OFFSET_FEEDBACK = 111;
|
|
2571
|
-
// Also contains OSC Sync
|
|
2572
|
-
static OFFSET_LFO_SPEED = 112;
|
|
2573
|
-
static OFFSET_LFO_DELAY = 113;
|
|
2574
|
-
static OFFSET_LFO_PM_DEPTH = 114;
|
|
2575
|
-
static OFFSET_LFO_AM_DEPTH = 115;
|
|
2576
|
-
static OFFSET_LFO_SYNC_WAVE = 116;
|
|
2577
|
-
// LFO sync, wave, and PM sensitivity packed
|
|
2578
|
-
static OFFSET_TRANSPOSE = 117;
|
|
2579
|
-
static OFFSET_AMP_MOD_SENS = 118;
|
|
2580
|
-
static OFFSET_EG_BIAS_SENS = 119;
|
|
2581
|
-
// Voice name (bytes 118-127)
|
|
2582
|
-
// IMPORTANT: Byte 118 serves dual-purpose in DX7 hardware:
|
|
2583
|
-
// 1. First character of voice name (as ASCII)
|
|
2584
|
-
// 2. Amp Mod Sensitivity parameter (as numeric value 0-127)
|
|
2585
|
-
// Both interpretations are used when converting to unpacked format
|
|
2586
|
-
static PACKED_NAME_START = 118;
|
|
2587
|
-
static NAME_LENGTH = 10;
|
|
2588
|
-
// Unpacked format (169 bytes)
|
|
2589
|
-
static UNPACKED_SIZE = 169;
|
|
2590
|
-
// Total unpacked size (159 params + 10 name)
|
|
2591
|
-
static UNPACKED_OP_SIZE = 23;
|
|
2592
|
-
// 23 bytes per operator in unpacked format
|
|
2593
|
-
// Unpacked operator parameter offsets (within each 23-byte operator block)
|
|
2594
|
-
static UNPACKED_OP_EG_RATE_1 = 0;
|
|
2595
|
-
static UNPACKED_OP_EG_RATE_2 = 1;
|
|
2596
|
-
static UNPACKED_OP_EG_RATE_3 = 2;
|
|
2597
|
-
static UNPACKED_OP_EG_RATE_4 = 3;
|
|
2598
|
-
static UNPACKED_OP_EG_LEVEL_1 = 4;
|
|
2599
|
-
static UNPACKED_OP_EG_LEVEL_2 = 5;
|
|
2600
|
-
static UNPACKED_OP_EG_LEVEL_3 = 6;
|
|
2601
|
-
static UNPACKED_OP_EG_LEVEL_4 = 7;
|
|
2602
|
-
static UNPACKED_OP_BREAK_POINT = 8;
|
|
2603
|
-
static UNPACKED_OP_L_SCALE_DEPTH = 9;
|
|
2604
|
-
static UNPACKED_OP_R_SCALE_DEPTH = 10;
|
|
2605
|
-
static UNPACKED_OP_L_CURVE = 11;
|
|
2606
|
-
static UNPACKED_OP_R_CURVE = 12;
|
|
2607
|
-
static UNPACKED_OP_RATE_SCALING = 13;
|
|
2608
|
-
static UNPACKED_OP_DETUNE = 14;
|
|
2609
|
-
static UNPACKED_OP_AMP_MOD_SENS = 15;
|
|
2610
|
-
static UNPACKED_OP_OUTPUT_LEVEL = 16;
|
|
2611
|
-
static UNPACKED_OP_MODE = 17;
|
|
2612
|
-
// Mode (0=ratio, 1=fixed)
|
|
2613
|
-
static UNPACKED_OP_KEY_VEL_SENS = 18;
|
|
2614
|
-
static UNPACKED_OP_FREQ_COARSE = 19;
|
|
2615
|
-
static UNPACKED_OP_OSC_DETUNE = 20;
|
|
2616
|
-
static UNPACKED_OP_FREQ_FINE = 21;
|
|
2617
|
-
// Unpacked pitch EG offsets (after 6 operators = index 138+)
|
|
2618
|
-
static UNPACKED_PITCH_EG_RATE_1 = 138;
|
|
2619
|
-
static UNPACKED_PITCH_EG_RATE_2 = 139;
|
|
2620
|
-
static UNPACKED_PITCH_EG_RATE_3 = 140;
|
|
2621
|
-
static UNPACKED_PITCH_EG_RATE_4 = 141;
|
|
2622
|
-
static UNPACKED_PITCH_EG_LEVEL_1 = 142;
|
|
2623
|
-
static UNPACKED_PITCH_EG_LEVEL_2 = 143;
|
|
2624
|
-
static UNPACKED_PITCH_EG_LEVEL_3 = 144;
|
|
2625
|
-
static UNPACKED_PITCH_EG_LEVEL_4 = 145;
|
|
2626
|
-
// Unpacked global parameters (after pitch EG = index 146+)
|
|
2627
|
-
static UNPACKED_ALGORITHM = 146;
|
|
2628
|
-
static UNPACKED_FEEDBACK = 147;
|
|
2629
|
-
static UNPACKED_OSC_SYNC = 148;
|
|
2630
|
-
static UNPACKED_LFO_SPEED = 149;
|
|
2631
|
-
static UNPACKED_LFO_DELAY = 150;
|
|
2632
|
-
static UNPACKED_LFO_PM_DEPTH = 151;
|
|
2633
|
-
static UNPACKED_LFO_AM_DEPTH = 152;
|
|
2634
|
-
static UNPACKED_LFO_KEY_SYNC = 153;
|
|
2635
|
-
static UNPACKED_LFO_WAVE = 154;
|
|
2636
|
-
static UNPACKED_LFO_PM_SENS = 155;
|
|
2637
|
-
static UNPACKED_AMP_MOD_SENS = 156;
|
|
2638
|
-
static UNPACKED_TRANSPOSE = 157;
|
|
2639
|
-
static UNPACKED_EG_BIAS_SENS = 158;
|
|
2640
|
-
static UNPACKED_NAME_START = 159;
|
|
2641
|
-
// VCED (single voice SysEx) format - for DX7 single patch dumps
|
|
2642
|
-
static VCED_SIZE = 163;
|
|
2643
|
-
// Total VCED sysex size (6 header + 155 data + 1 checksum + 1 end)
|
|
2644
|
-
static VCED_HEADER_SIZE = 6;
|
|
2645
|
-
static VCED_DATA_SIZE = 155;
|
|
2646
|
-
// Voice data bytes (6 operators × 21 bytes + 8 pitch EG + 11 global + 10 name)
|
|
2647
|
-
// VCED header bytes - DX7 single voice dump format
|
|
2648
|
-
static VCED_SYSEX_START = 240;
|
|
2649
|
-
// SysEx Message Start
|
|
2650
|
-
static VCED_YAMAHA_ID = 67;
|
|
2651
|
-
// Yamaha manufacturer ID
|
|
2652
|
-
static VCED_SUB_STATUS = 0;
|
|
2653
|
-
static VCED_FORMAT_SINGLE = 0;
|
|
2654
|
-
// Single voice format identifier
|
|
2655
|
-
static VCED_BYTE_COUNT_MSB = 1;
|
|
2656
|
-
// High byte of data length (1)
|
|
2657
|
-
static VCED_BYTE_COUNT_LSB = 27;
|
|
2658
|
-
// Low byte of data length (27 in decimal = 155 bytes)
|
|
2659
|
-
static VCED_SYSEX_END = 247;
|
|
2660
|
-
// SysEx Message End
|
|
2661
|
-
// Bit masks
|
|
2662
|
-
static MASK_7BIT = 127;
|
|
2663
|
-
// Standard 7-bit MIDI data mask
|
|
2664
|
-
static MASK_2BIT = 3;
|
|
2665
|
-
// For 2-bit values (curves)
|
|
2666
|
-
static MASK_3BIT = 7;
|
|
2667
|
-
// For 3-bit values (RS, detune)
|
|
2668
|
-
static MASK_4BIT = 15;
|
|
2669
|
-
// For 4-bit values (detune, fine freq)
|
|
2670
|
-
static MASK_5BIT = 31;
|
|
2671
|
-
// For 5-bit values (algorithm, freq coarse)
|
|
2672
|
-
static MASK_1BIT = 1;
|
|
2673
|
-
// For 1-bit values (mode, sync)
|
|
2674
|
-
// Parameter value ranges
|
|
2675
|
-
static TRANSPOSE_CENTER = 24;
|
|
2676
|
-
// MIDI note 24 = C0 (center of DX7 transpose range: -24 to +24 semitones)
|
|
2677
|
-
// Special character mappings - for Japanese DX7 character set compatibility
|
|
2678
|
-
static CHAR_YEN = 92;
|
|
2679
|
-
// Japanese Yen symbol (¥) maps to ASCII backslash
|
|
2680
|
-
static CHAR_ARROW_RIGHT = 126;
|
|
2681
|
-
// Right arrow (→) maps to ASCII tilde
|
|
2682
|
-
static CHAR_ARROW_LEFT = 127;
|
|
2683
|
-
// Left arrow (←) maps to ASCII DEL
|
|
2684
|
-
static CHAR_REPLACEMENT_Y = 89;
|
|
2685
|
-
// Replace Yen symbol with "Y"
|
|
2686
|
-
static CHAR_REPLACEMENT_GT = 62;
|
|
2687
|
-
// Right arrow with ">"
|
|
2688
|
-
static CHAR_REPLACEMENT_LT = 60;
|
|
2689
|
-
// Left arrow with "<"
|
|
2690
|
-
static CHAR_SPACE = 32;
|
|
2691
|
-
// Standard space character
|
|
2692
|
-
static CHAR_MIN_PRINTABLE = 32;
|
|
2693
|
-
// Minimum ASCII printable character
|
|
2694
|
-
static CHAR_MAX_PRINTABLE = 126;
|
|
2695
|
-
// Maximum ASCII printable character
|
|
2696
|
-
// Default voice values
|
|
2697
|
-
static DEFAULT_EG_RATE = 99;
|
|
2698
|
-
static DEFAULT_EG_LEVEL_MAX = 99;
|
|
2699
|
-
static DEFAULT_EG_LEVEL_MIN = 0;
|
|
2700
|
-
static DEFAULT_BREAK_POINT = 0;
|
|
2701
|
-
// DX7 breakpoint 0 = A-1 (MIDI note 9)
|
|
2702
|
-
static DEFAULT_OUTPUT_LEVEL = 99;
|
|
2703
|
-
static DEFAULT_PITCH_EG_LEVEL = 50;
|
|
2704
|
-
static DEFAULT_LFO_SPEED = 35;
|
|
2705
|
-
static DEFAULT_LFO_PM_SENS = 3;
|
|
2706
|
-
static DEFAULT_DETUNE = 7;
|
|
2707
|
-
// Detune center (actual detune = value - 7)
|
|
2708
|
-
static DEFAULT_FREQ_COARSE = 1;
|
|
2709
|
-
static DEFAULT_ALGORITHM = 0;
|
|
2710
|
-
static DEFAULT_FEEDBACK = 0;
|
|
2711
|
-
// MIDI notes
|
|
2712
|
-
static MIDI_OCTAVE_OFFSET = -2;
|
|
2713
|
-
// For displaying MIDI notes (MIDI 0 = C-2)
|
|
2714
|
-
static MIDI_BREAK_POINT_OFFSET = 21;
|
|
2533
|
+
const t = class t {
|
|
2715
2534
|
// Offset for breakpoint display
|
|
2716
2535
|
/**
|
|
2717
2536
|
* Create a DX7Voice from raw 128-byte data
|
|
@@ -2719,24 +2538,24 @@ class n {
|
|
|
2719
2538
|
* @param {number} index - Voice index (0-31)
|
|
2720
2539
|
* @throws {DX7ValidationError} If data length is not exactly 128 bytes
|
|
2721
2540
|
*/
|
|
2722
|
-
constructor(
|
|
2723
|
-
if (
|
|
2724
|
-
throw new
|
|
2725
|
-
`Invalid voice data length: expected ${
|
|
2541
|
+
constructor(e, i = 0) {
|
|
2542
|
+
if (e.length !== t.PACKED_SIZE)
|
|
2543
|
+
throw new O(
|
|
2544
|
+
`Invalid voice data length: expected ${t.PACKED_SIZE} bytes, got ${e.length}`,
|
|
2726
2545
|
"length",
|
|
2727
|
-
|
|
2546
|
+
e.length
|
|
2728
2547
|
);
|
|
2729
|
-
this.index =
|
|
2548
|
+
this.index = i, this.data = new Uint8Array(e), this.name = this._extractName(), this._unpackedCache = null;
|
|
2730
2549
|
}
|
|
2731
2550
|
/**
|
|
2732
2551
|
* Extract the voice name from the data (10 characters at offset 118)
|
|
2733
2552
|
* @private
|
|
2734
2553
|
*/
|
|
2735
2554
|
_extractName() {
|
|
2736
|
-
const
|
|
2737
|
-
return Array.from(
|
|
2738
|
-
let
|
|
2739
|
-
return
|
|
2555
|
+
const e = this.data.subarray(t.PACKED_NAME_START, t.PACKED_NAME_START + t.NAME_LENGTH);
|
|
2556
|
+
return Array.from(e).map((n) => {
|
|
2557
|
+
let s = n & t.MASK_7BIT;
|
|
2558
|
+
return s === t.CHAR_YEN && (s = t.CHAR_REPLACEMENT_Y), s === t.CHAR_ARROW_RIGHT && (s = t.CHAR_REPLACEMENT_GT), s === t.CHAR_ARROW_LEFT && (s = t.CHAR_REPLACEMENT_LT), (s < t.CHAR_MIN_PRINTABLE || s > t.CHAR_MAX_PRINTABLE) && (s = t.CHAR_SPACE), String.fromCharCode(s);
|
|
2740
2559
|
}).join("").trim();
|
|
2741
2560
|
}
|
|
2742
2561
|
/**
|
|
@@ -2745,14 +2564,14 @@ class n {
|
|
|
2745
2564
|
* @returns {number} Parameter value (0-127)
|
|
2746
2565
|
* @throws {DX7ValidationError} If offset is out of range
|
|
2747
2566
|
*/
|
|
2748
|
-
getParameter(
|
|
2749
|
-
if (
|
|
2750
|
-
throw new
|
|
2751
|
-
`Parameter offset out of range: ${
|
|
2567
|
+
getParameter(e) {
|
|
2568
|
+
if (e < 0 || e >= t.PACKED_SIZE)
|
|
2569
|
+
throw new O(
|
|
2570
|
+
`Parameter offset out of range: ${e} (must be 0-${t.PACKED_SIZE - 1})`,
|
|
2752
2571
|
"offset",
|
|
2753
|
-
|
|
2572
|
+
e
|
|
2754
2573
|
);
|
|
2755
|
-
return this.data[
|
|
2574
|
+
return this.data[e] & t.MASK_7BIT;
|
|
2756
2575
|
}
|
|
2757
2576
|
/**
|
|
2758
2577
|
* Get a parameter value from the unpacked 169-byte format
|
|
@@ -2760,28 +2579,28 @@ class n {
|
|
|
2760
2579
|
* @returns {number} Parameter value (0-127)
|
|
2761
2580
|
* @throws {DX7ValidationError} If offset is out of range
|
|
2762
2581
|
*/
|
|
2763
|
-
getUnpackedParameter(
|
|
2764
|
-
if (
|
|
2765
|
-
throw new
|
|
2766
|
-
`Unpacked parameter offset out of range: ${
|
|
2582
|
+
getUnpackedParameter(e) {
|
|
2583
|
+
if (e < 0 || e >= t.UNPACKED_SIZE)
|
|
2584
|
+
throw new O(
|
|
2585
|
+
`Unpacked parameter offset out of range: ${e} (must be 0-${t.UNPACKED_SIZE - 1})`,
|
|
2767
2586
|
"offset",
|
|
2768
|
-
|
|
2587
|
+
e
|
|
2769
2588
|
);
|
|
2770
|
-
return this._unpackedCache || (this._unpackedCache = this.unpack()), this._unpackedCache[
|
|
2589
|
+
return this._unpackedCache || (this._unpackedCache = this.unpack()), this._unpackedCache[e] & t.MASK_7BIT;
|
|
2771
2590
|
}
|
|
2772
2591
|
/**
|
|
2773
2592
|
* Set a raw parameter value in the packed data
|
|
2774
2593
|
* @param {number} offset - Byte offset in the voice data
|
|
2775
2594
|
* @param {number} value - Parameter value (0-127)
|
|
2776
2595
|
*/
|
|
2777
|
-
setParameter(
|
|
2778
|
-
if (
|
|
2779
|
-
throw new
|
|
2780
|
-
`Parameter offset out of range: ${
|
|
2596
|
+
setParameter(e, i) {
|
|
2597
|
+
if (e < 0 || e >= t.PACKED_SIZE)
|
|
2598
|
+
throw new O(
|
|
2599
|
+
`Parameter offset out of range: ${e} (must be 0-${t.PACKED_SIZE - 1})`,
|
|
2781
2600
|
"offset",
|
|
2782
|
-
|
|
2601
|
+
e
|
|
2783
2602
|
);
|
|
2784
|
-
this.data[
|
|
2603
|
+
this.data[e] = i & t.MASK_7BIT, this._unpackedCache = null, e >= t.PACKED_NAME_START && e < t.PACKED_NAME_START + t.NAME_LENGTH && (this.name = this._extractName());
|
|
2785
2604
|
}
|
|
2786
2605
|
/**
|
|
2787
2606
|
* Unpack the voice data to 169-byte unpacked format
|
|
@@ -2789,199 +2608,199 @@ class n {
|
|
|
2789
2608
|
* @returns {Uint8Array} 169 bytes of unpacked voice data (138 operator + 8 pitch EG + 13 global + 10 name = 169 bytes)
|
|
2790
2609
|
*/
|
|
2791
2610
|
unpack() {
|
|
2792
|
-
const
|
|
2793
|
-
return this._unpackOperators(
|
|
2611
|
+
const e = this.data, i = new Uint8Array(t.UNPACKED_SIZE);
|
|
2612
|
+
return this._unpackOperators(e, i), this._unpackPitchEG(e, i), this._unpackGlobalParams(e, i), this._unpackName(e, i), i;
|
|
2794
2613
|
}
|
|
2795
2614
|
/**
|
|
2796
2615
|
* Unpack all 6 operators from packed to unpacked format
|
|
2797
2616
|
* @private
|
|
2798
2617
|
*/
|
|
2799
|
-
_unpackOperators(
|
|
2800
|
-
for (let
|
|
2801
|
-
const
|
|
2802
|
-
this._unpackOperator(
|
|
2618
|
+
_unpackOperators(e, i) {
|
|
2619
|
+
for (let n = 0; n < t.NUM_OPERATORS; n++) {
|
|
2620
|
+
const s = (t.NUM_OPERATORS - 1 - n) * t.PACKED_OP_SIZE, r = n * t.UNPACKED_OP_SIZE;
|
|
2621
|
+
this._unpackOperator(e, i, s, r);
|
|
2803
2622
|
}
|
|
2804
2623
|
}
|
|
2805
2624
|
/**
|
|
2806
2625
|
* Unpack a single operator's parameters
|
|
2807
2626
|
* @private
|
|
2808
2627
|
*/
|
|
2809
|
-
_unpackOperator(
|
|
2810
|
-
this._unpackOperatorEG(
|
|
2628
|
+
_unpackOperator(e, i, n, s) {
|
|
2629
|
+
this._unpackOperatorEG(e, i, n, s), this._unpackOperatorScaling(e, i, n, s), this._unpackOperatorPackedParams(e, i, n, s), this._unpackOperatorFrequency(e, i, n, s);
|
|
2811
2630
|
}
|
|
2812
2631
|
/**
|
|
2813
2632
|
* Unpack operator EG rates and levels
|
|
2814
2633
|
* @private
|
|
2815
2634
|
*/
|
|
2816
|
-
_unpackOperatorEG(
|
|
2817
|
-
s
|
|
2635
|
+
_unpackOperatorEG(e, i, n, s) {
|
|
2636
|
+
i[s + t.UNPACKED_OP_EG_RATE_1] = e[n + t.PACKED_OP_EG_RATE_1] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_RATE_2] = e[n + t.PACKED_OP_EG_RATE_2] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_RATE_3] = e[n + t.PACKED_OP_EG_RATE_3] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_RATE_4] = e[n + t.PACKED_OP_EG_RATE_4] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_LEVEL_1] = e[n + t.PACKED_OP_EG_LEVEL_1] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_LEVEL_2] = e[n + t.PACKED_OP_EG_LEVEL_2] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_LEVEL_3] = e[n + t.PACKED_OP_EG_LEVEL_3] & t.MASK_7BIT, i[s + t.UNPACKED_OP_EG_LEVEL_4] = e[n + t.PACKED_OP_EG_LEVEL_4] & t.MASK_7BIT;
|
|
2818
2637
|
}
|
|
2819
2638
|
/**
|
|
2820
2639
|
* Unpack operator keyboard scaling parameters
|
|
2821
2640
|
* @private
|
|
2822
2641
|
*/
|
|
2823
|
-
_unpackOperatorScaling(
|
|
2824
|
-
s
|
|
2642
|
+
_unpackOperatorScaling(e, i, n, s) {
|
|
2643
|
+
i[s + t.UNPACKED_OP_BREAK_POINT] = e[n + t.PACKED_OP_BREAK_POINT] & t.MASK_7BIT, i[s + t.UNPACKED_OP_L_SCALE_DEPTH] = e[n + t.PACKED_OP_L_SCALE_DEPTH] & t.MASK_7BIT, i[s + t.UNPACKED_OP_R_SCALE_DEPTH] = e[n + t.PACKED_OP_R_SCALE_DEPTH] & t.MASK_7BIT;
|
|
2825
2644
|
}
|
|
2826
2645
|
/**
|
|
2827
2646
|
* Unpack operator bit-packed parameters (curves, rate scaling, mod sens)
|
|
2828
2647
|
* @private
|
|
2829
2648
|
*/
|
|
2830
|
-
_unpackOperatorPackedParams(
|
|
2831
|
-
const
|
|
2832
|
-
s
|
|
2833
|
-
const
|
|
2834
|
-
s
|
|
2835
|
-
const
|
|
2836
|
-
s
|
|
2649
|
+
_unpackOperatorPackedParams(e, i, n, s) {
|
|
2650
|
+
const r = e[n + t.PACKED_OP_CURVES] & t.MASK_7BIT;
|
|
2651
|
+
i[s + t.UNPACKED_OP_L_CURVE] = r & t.MASK_2BIT, i[s + t.UNPACKED_OP_R_CURVE] = r >> 2 & t.MASK_2BIT;
|
|
2652
|
+
const E = e[n + t.PACKED_OP_RATE_SCALING] & t.MASK_7BIT;
|
|
2653
|
+
i[s + t.UNPACKED_OP_RATE_SCALING] = E & t.MASK_3BIT, i[s + t.UNPACKED_OP_DETUNE] = E >> 3 & t.MASK_4BIT;
|
|
2654
|
+
const o = e[n + t.PACKED_OP_MOD_SENS] & t.MASK_7BIT;
|
|
2655
|
+
i[s + t.UNPACKED_OP_AMP_MOD_SENS] = o & t.MASK_2BIT, i[s + t.UNPACKED_OP_KEY_VEL_SENS] = o >> 2 & t.MASK_3BIT, i[s + t.UNPACKED_OP_OUTPUT_LEVEL] = e[n + t.PACKED_OP_OUTPUT_LEVEL] & t.MASK_7BIT;
|
|
2837
2656
|
}
|
|
2838
2657
|
/**
|
|
2839
2658
|
* Unpack operator frequency parameters
|
|
2840
2659
|
* @private
|
|
2841
2660
|
*/
|
|
2842
|
-
_unpackOperatorFrequency(
|
|
2843
|
-
const
|
|
2844
|
-
s
|
|
2845
|
-
const
|
|
2846
|
-
s
|
|
2661
|
+
_unpackOperatorFrequency(e, i, n, s) {
|
|
2662
|
+
const r = e[n + t.PACKED_OP_MODE_FREQ] & t.MASK_7BIT;
|
|
2663
|
+
i[s + t.UNPACKED_OP_MODE] = r & t.MASK_1BIT, i[s + t.UNPACKED_OP_FREQ_COARSE] = r >> 1 & t.MASK_5BIT;
|
|
2664
|
+
const E = e[n + t.PACKED_OP_DETUNE_FINE] & t.MASK_7BIT;
|
|
2665
|
+
i[s + t.UNPACKED_OP_OSC_DETUNE] = E & t.MASK_3BIT, i[s + t.UNPACKED_OP_FREQ_FINE] = E >> 3 & t.MASK_4BIT;
|
|
2847
2666
|
}
|
|
2848
2667
|
/**
|
|
2849
2668
|
* Unpack pitch envelope generator parameters
|
|
2850
2669
|
* @private
|
|
2851
2670
|
*/
|
|
2852
|
-
_unpackPitchEG(
|
|
2853
|
-
|
|
2671
|
+
_unpackPitchEG(e, i) {
|
|
2672
|
+
i[t.UNPACKED_PITCH_EG_RATE_1] = e[t.PACKED_PITCH_EG_RATE_1] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_RATE_2] = e[t.PACKED_PITCH_EG_RATE_2] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_RATE_3] = e[t.PACKED_PITCH_EG_RATE_3] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_RATE_4] = e[t.PACKED_PITCH_EG_RATE_4] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_LEVEL_1] = e[t.PACKED_PITCH_EG_LEVEL_1] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_LEVEL_2] = e[t.PACKED_PITCH_EG_LEVEL_2] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_LEVEL_3] = e[t.PACKED_PITCH_EG_LEVEL_3] & t.MASK_7BIT, i[t.UNPACKED_PITCH_EG_LEVEL_4] = e[t.PACKED_PITCH_EG_LEVEL_4] & t.MASK_7BIT;
|
|
2854
2673
|
}
|
|
2855
2674
|
/**
|
|
2856
2675
|
* Unpack global voice parameters (algorithm, feedback, LFO, etc.)
|
|
2857
2676
|
* @private
|
|
2858
2677
|
*/
|
|
2859
|
-
_unpackGlobalParams(
|
|
2860
|
-
|
|
2861
|
-
const
|
|
2862
|
-
|
|
2863
|
-
const
|
|
2864
|
-
|
|
2678
|
+
_unpackGlobalParams(e, i) {
|
|
2679
|
+
i[t.UNPACKED_ALGORITHM] = e[t.OFFSET_ALGORITHM] & t.MASK_5BIT;
|
|
2680
|
+
const n = e[t.OFFSET_FEEDBACK] & t.MASK_7BIT;
|
|
2681
|
+
i[t.UNPACKED_FEEDBACK] = n & t.MASK_3BIT, i[t.UNPACKED_OSC_SYNC] = n >> 3 & t.MASK_1BIT, i[t.UNPACKED_LFO_SPEED] = e[t.OFFSET_LFO_SPEED] & t.MASK_7BIT, i[t.UNPACKED_LFO_DELAY] = e[t.OFFSET_LFO_DELAY] & t.MASK_7BIT, i[t.UNPACKED_LFO_PM_DEPTH] = e[t.OFFSET_LFO_PM_DEPTH] & t.MASK_7BIT, i[t.UNPACKED_LFO_AM_DEPTH] = e[t.OFFSET_LFO_AM_DEPTH] & t.MASK_7BIT;
|
|
2682
|
+
const s = e[t.OFFSET_LFO_SYNC_WAVE] & t.MASK_7BIT;
|
|
2683
|
+
i[t.UNPACKED_LFO_KEY_SYNC] = s & t.MASK_1BIT, i[t.UNPACKED_LFO_WAVE] = s >> 1 & t.MASK_3BIT, i[t.UNPACKED_LFO_PM_SENS] = s >> 4 & t.MASK_3BIT, i[t.UNPACKED_AMP_MOD_SENS] = e[t.OFFSET_AMP_MOD_SENS] & t.MASK_7BIT, i[t.UNPACKED_TRANSPOSE] = e[t.OFFSET_TRANSPOSE] & t.MASK_7BIT, i[t.UNPACKED_EG_BIAS_SENS] = e[t.OFFSET_EG_BIAS_SENS] & t.MASK_7BIT;
|
|
2865
2684
|
}
|
|
2866
2685
|
/**
|
|
2867
2686
|
* Copy voice name from packed to unpacked format
|
|
2868
2687
|
* @private
|
|
2869
2688
|
*/
|
|
2870
|
-
_unpackName(
|
|
2871
|
-
for (let
|
|
2872
|
-
|
|
2689
|
+
_unpackName(e, i) {
|
|
2690
|
+
for (let n = 0; n < t.NAME_LENGTH; n++)
|
|
2691
|
+
i[t.UNPACKED_NAME_START + n] = e[t.PACKED_NAME_START + n] & t.MASK_7BIT;
|
|
2873
2692
|
}
|
|
2874
2693
|
/**
|
|
2875
2694
|
* Pack 169-byte unpacked data to 128-byte format
|
|
2876
2695
|
* @param {Array<number>|Uint8Array} unpacked - 169 bytes of unpacked data (159 parameters + 10 name bytes)
|
|
2877
2696
|
* @returns {Uint8Array} 128 bytes of packed data
|
|
2878
2697
|
*/
|
|
2879
|
-
static pack(
|
|
2880
|
-
if (
|
|
2881
|
-
throw new
|
|
2882
|
-
`Invalid unpacked data length: expected ${
|
|
2698
|
+
static pack(e) {
|
|
2699
|
+
if (e.length !== t.UNPACKED_SIZE)
|
|
2700
|
+
throw new O(
|
|
2701
|
+
`Invalid unpacked data length: expected ${t.UNPACKED_SIZE} bytes, got ${e.length}`,
|
|
2883
2702
|
"length",
|
|
2884
|
-
|
|
2703
|
+
e.length
|
|
2885
2704
|
);
|
|
2886
|
-
const
|
|
2887
|
-
return
|
|
2705
|
+
const i = new Uint8Array(t.PACKED_SIZE);
|
|
2706
|
+
return t._packOperators(e, i), t._packPitchEG(e, i), t._packGlobalParams(e, i), t._packName(e, i), i;
|
|
2888
2707
|
}
|
|
2889
2708
|
/**
|
|
2890
2709
|
* Pack all 6 operators from unpacked to packed format
|
|
2891
2710
|
* @private
|
|
2892
2711
|
*/
|
|
2893
|
-
static _packOperators(
|
|
2894
|
-
for (let
|
|
2895
|
-
const
|
|
2896
|
-
|
|
2712
|
+
static _packOperators(e, i) {
|
|
2713
|
+
for (let n = 0; n < t.NUM_OPERATORS; n++) {
|
|
2714
|
+
const s = n * t.UNPACKED_OP_SIZE, r = (t.NUM_OPERATORS - 1 - n) * t.PACKED_OP_SIZE;
|
|
2715
|
+
t._packOperator(e, i, s, r);
|
|
2897
2716
|
}
|
|
2898
2717
|
}
|
|
2899
2718
|
/**
|
|
2900
2719
|
* Pack a single operator's parameters
|
|
2901
2720
|
* @private
|
|
2902
2721
|
*/
|
|
2903
|
-
static _packOperator(
|
|
2904
|
-
|
|
2722
|
+
static _packOperator(e, i, n, s) {
|
|
2723
|
+
t._packOperatorEG(e, i, n, s), t._packOperatorScaling(e, i, n, s), t._packOperatorPackedParams(e, i, n, s), t._packOperatorFrequency(e, i, n, s);
|
|
2905
2724
|
}
|
|
2906
2725
|
/**
|
|
2907
2726
|
* Pack operator EG rates and levels
|
|
2908
2727
|
* @private
|
|
2909
2728
|
*/
|
|
2910
|
-
static _packOperatorEG(
|
|
2911
|
-
s
|
|
2729
|
+
static _packOperatorEG(e, i, n, s) {
|
|
2730
|
+
i[s + t.PACKED_OP_EG_RATE_1] = e[n + t.UNPACKED_OP_EG_RATE_1], i[s + t.PACKED_OP_EG_RATE_2] = e[n + t.UNPACKED_OP_EG_RATE_2], i[s + t.PACKED_OP_EG_RATE_3] = e[n + t.UNPACKED_OP_EG_RATE_3], i[s + t.PACKED_OP_EG_RATE_4] = e[n + t.UNPACKED_OP_EG_RATE_4], i[s + t.PACKED_OP_EG_LEVEL_1] = e[n + t.UNPACKED_OP_EG_LEVEL_1], i[s + t.PACKED_OP_EG_LEVEL_2] = e[n + t.UNPACKED_OP_EG_LEVEL_2], i[s + t.PACKED_OP_EG_LEVEL_3] = e[n + t.UNPACKED_OP_EG_LEVEL_3], i[s + t.PACKED_OP_EG_LEVEL_4] = e[n + t.UNPACKED_OP_EG_LEVEL_4];
|
|
2912
2731
|
}
|
|
2913
2732
|
/**
|
|
2914
2733
|
* Pack operator keyboard scaling parameters
|
|
2915
2734
|
* @private
|
|
2916
2735
|
*/
|
|
2917
|
-
static _packOperatorScaling(
|
|
2918
|
-
s
|
|
2736
|
+
static _packOperatorScaling(e, i, n, s) {
|
|
2737
|
+
i[s + t.PACKED_OP_BREAK_POINT] = e[n + t.UNPACKED_OP_BREAK_POINT], i[s + t.PACKED_OP_L_SCALE_DEPTH] = e[n + t.UNPACKED_OP_L_SCALE_DEPTH], i[s + t.PACKED_OP_R_SCALE_DEPTH] = e[n + t.UNPACKED_OP_R_SCALE_DEPTH];
|
|
2919
2738
|
}
|
|
2920
2739
|
/**
|
|
2921
2740
|
* Pack operator bit-packed parameters
|
|
2922
2741
|
* @private
|
|
2923
2742
|
*/
|
|
2924
|
-
static _packOperatorPackedParams(
|
|
2925
|
-
const
|
|
2926
|
-
s
|
|
2927
|
-
const
|
|
2928
|
-
s
|
|
2929
|
-
const S =
|
|
2930
|
-
s
|
|
2743
|
+
static _packOperatorPackedParams(e, i, n, s) {
|
|
2744
|
+
const r = e[n + t.UNPACKED_OP_L_CURVE] & t.MASK_2BIT, E = e[n + t.UNPACKED_OP_R_CURVE] & t.MASK_2BIT;
|
|
2745
|
+
i[s + t.PACKED_OP_CURVES] = r | E << 2;
|
|
2746
|
+
const o = e[n + t.UNPACKED_OP_RATE_SCALING] & t.MASK_3BIT, l = e[n + t.UNPACKED_OP_DETUNE] & t.MASK_4BIT;
|
|
2747
|
+
i[s + t.PACKED_OP_RATE_SCALING] = o | l << 3;
|
|
2748
|
+
const S = e[n + t.UNPACKED_OP_AMP_MOD_SENS] & t.MASK_2BIT, u = e[n + t.UNPACKED_OP_KEY_VEL_SENS] & t.MASK_3BIT;
|
|
2749
|
+
i[s + t.PACKED_OP_MOD_SENS] = S | u << 2, i[s + t.PACKED_OP_OUTPUT_LEVEL] = e[n + t.UNPACKED_OP_OUTPUT_LEVEL];
|
|
2931
2750
|
}
|
|
2932
2751
|
/**
|
|
2933
2752
|
* Pack operator frequency parameters
|
|
2934
2753
|
* @private
|
|
2935
2754
|
*/
|
|
2936
|
-
static _packOperatorFrequency(
|
|
2937
|
-
const
|
|
2938
|
-
s
|
|
2939
|
-
const
|
|
2940
|
-
s
|
|
2755
|
+
static _packOperatorFrequency(e, i, n, s) {
|
|
2756
|
+
const r = e[n + t.UNPACKED_OP_MODE] & t.MASK_1BIT, E = e[n + t.UNPACKED_OP_FREQ_COARSE] & t.MASK_5BIT;
|
|
2757
|
+
i[s + t.PACKED_OP_MODE_FREQ] = r | E << 1;
|
|
2758
|
+
const o = e[n + t.UNPACKED_OP_OSC_DETUNE] & t.MASK_3BIT, l = e[n + t.UNPACKED_OP_FREQ_FINE] & t.MASK_4BIT;
|
|
2759
|
+
i[s + t.PACKED_OP_DETUNE_FINE] = o | l << 3;
|
|
2941
2760
|
}
|
|
2942
2761
|
/**
|
|
2943
2762
|
* Pack pitch envelope generator parameters
|
|
2944
2763
|
* @private
|
|
2945
2764
|
*/
|
|
2946
|
-
static _packPitchEG(
|
|
2947
|
-
|
|
2765
|
+
static _packPitchEG(e, i) {
|
|
2766
|
+
i[t.PACKED_PITCH_EG_RATE_1] = e[t.UNPACKED_PITCH_EG_RATE_1], i[t.PACKED_PITCH_EG_RATE_2] = e[t.UNPACKED_PITCH_EG_RATE_2], i[t.PACKED_PITCH_EG_RATE_3] = e[t.UNPACKED_PITCH_EG_RATE_3], i[t.PACKED_PITCH_EG_RATE_4] = e[t.UNPACKED_PITCH_EG_RATE_4], i[t.PACKED_PITCH_EG_LEVEL_1] = e[t.UNPACKED_PITCH_EG_LEVEL_1], i[t.PACKED_PITCH_EG_LEVEL_2] = e[t.UNPACKED_PITCH_EG_LEVEL_2], i[t.PACKED_PITCH_EG_LEVEL_3] = e[t.UNPACKED_PITCH_EG_LEVEL_3], i[t.PACKED_PITCH_EG_LEVEL_4] = e[t.UNPACKED_PITCH_EG_LEVEL_4];
|
|
2948
2767
|
}
|
|
2949
2768
|
/**
|
|
2950
2769
|
* Pack global voice parameters (algorithm, feedback, LFO, etc.)
|
|
2951
2770
|
* @private
|
|
2952
2771
|
*/
|
|
2953
|
-
static _packGlobalParams(
|
|
2954
|
-
|
|
2955
|
-
const
|
|
2956
|
-
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2772
|
+
static _packGlobalParams(e, i) {
|
|
2773
|
+
i[t.OFFSET_ALGORITHM] = e[t.UNPACKED_ALGORITHM];
|
|
2774
|
+
const n = e[t.UNPACKED_FEEDBACK] & t.MASK_3BIT, s = e[t.UNPACKED_OSC_SYNC] & t.MASK_1BIT;
|
|
2775
|
+
i[t.OFFSET_FEEDBACK] = n | s << 3, i[t.OFFSET_LFO_SPEED] = e[t.UNPACKED_LFO_SPEED], i[t.OFFSET_LFO_DELAY] = e[t.UNPACKED_LFO_DELAY], i[t.OFFSET_LFO_PM_DEPTH] = e[t.UNPACKED_LFO_PM_DEPTH], i[t.OFFSET_LFO_AM_DEPTH] = e[t.UNPACKED_LFO_AM_DEPTH];
|
|
2776
|
+
const r = e[t.UNPACKED_LFO_KEY_SYNC] & t.MASK_1BIT, E = e[t.UNPACKED_LFO_WAVE] & t.MASK_3BIT, o = e[t.UNPACKED_LFO_PM_SENS] & t.MASK_3BIT;
|
|
2777
|
+
i[t.OFFSET_LFO_SYNC_WAVE] = r | E << 1 | o << 4, i[t.OFFSET_AMP_MOD_SENS] = e[t.UNPACKED_AMP_MOD_SENS], i[t.OFFSET_TRANSPOSE] = e[t.UNPACKED_TRANSPOSE], i[t.OFFSET_EG_BIAS_SENS] = e[t.UNPACKED_EG_BIAS_SENS];
|
|
2959
2778
|
}
|
|
2960
2779
|
/**
|
|
2961
2780
|
* Copy voice name from unpacked to packed format
|
|
2962
2781
|
* @private
|
|
2963
2782
|
*/
|
|
2964
|
-
static _packName(
|
|
2965
|
-
for (let
|
|
2966
|
-
|
|
2783
|
+
static _packName(e, i) {
|
|
2784
|
+
for (let n = 0; n < t.NAME_LENGTH; n++)
|
|
2785
|
+
i[t.PACKED_NAME_START + n] = e[t.UNPACKED_NAME_START + n];
|
|
2967
2786
|
}
|
|
2968
2787
|
/**
|
|
2969
2788
|
* Create a default/empty voice
|
|
2970
2789
|
* @param {number} index - Voice index
|
|
2971
2790
|
* @returns {DX7Voice}
|
|
2972
2791
|
*/
|
|
2973
|
-
static createDefault(
|
|
2974
|
-
const
|
|
2975
|
-
for (let
|
|
2976
|
-
const
|
|
2977
|
-
|
|
2792
|
+
static createDefault(e = 0) {
|
|
2793
|
+
const i = new Uint8Array(t.UNPACKED_SIZE);
|
|
2794
|
+
for (let r = 0; r < t.NUM_OPERATORS; r++) {
|
|
2795
|
+
const E = r * t.UNPACKED_OP_SIZE;
|
|
2796
|
+
i[E + t.UNPACKED_OP_EG_RATE_1] = t.DEFAULT_EG_RATE, i[E + t.UNPACKED_OP_EG_RATE_2] = t.DEFAULT_EG_RATE, i[E + t.UNPACKED_OP_EG_RATE_3] = t.DEFAULT_EG_RATE, i[E + t.UNPACKED_OP_EG_RATE_4] = t.DEFAULT_EG_RATE, i[E + t.UNPACKED_OP_EG_LEVEL_1] = t.DEFAULT_EG_LEVEL_MAX, i[E + t.UNPACKED_OP_EG_LEVEL_2] = t.DEFAULT_EG_LEVEL_MAX, i[E + t.UNPACKED_OP_EG_LEVEL_3] = t.DEFAULT_EG_LEVEL_MAX, i[E + t.UNPACKED_OP_EG_LEVEL_4] = t.DEFAULT_EG_LEVEL_MIN, i[E + t.UNPACKED_OP_BREAK_POINT] = t.DEFAULT_BREAK_POINT, i[E + t.UNPACKED_OP_L_SCALE_DEPTH] = 0, i[E + t.UNPACKED_OP_R_SCALE_DEPTH] = 0, i[E + t.UNPACKED_OP_L_CURVE] = 0, i[E + t.UNPACKED_OP_R_CURVE] = 0, i[E + t.UNPACKED_OP_RATE_SCALING] = 0, i[E + t.UNPACKED_OP_DETUNE] = t.DEFAULT_DETUNE, i[E + t.UNPACKED_OP_AMP_MOD_SENS] = 0, i[E + t.UNPACKED_OP_KEY_VEL_SENS] = 0, i[E + t.UNPACKED_OP_OUTPUT_LEVEL] = t.DEFAULT_OUTPUT_LEVEL, i[E + t.UNPACKED_OP_MODE] = 0, i[E + t.UNPACKED_OP_FREQ_COARSE] = t.DEFAULT_FREQ_COARSE, i[E + t.UNPACKED_OP_OSC_DETUNE] = 0, i[E + t.UNPACKED_OP_FREQ_FINE] = 0;
|
|
2978
2797
|
}
|
|
2979
|
-
|
|
2980
|
-
const
|
|
2981
|
-
for (let
|
|
2982
|
-
|
|
2983
|
-
const
|
|
2984
|
-
return new
|
|
2798
|
+
i[t.UNPACKED_PITCH_EG_RATE_1] = t.DEFAULT_EG_RATE, i[t.UNPACKED_PITCH_EG_RATE_2] = t.DEFAULT_EG_RATE, i[t.UNPACKED_PITCH_EG_RATE_3] = t.DEFAULT_EG_RATE, i[t.UNPACKED_PITCH_EG_RATE_4] = t.DEFAULT_EG_RATE, i[t.UNPACKED_PITCH_EG_LEVEL_1] = t.DEFAULT_PITCH_EG_LEVEL, i[t.UNPACKED_PITCH_EG_LEVEL_2] = t.DEFAULT_PITCH_EG_LEVEL, i[t.UNPACKED_PITCH_EG_LEVEL_3] = t.DEFAULT_PITCH_EG_LEVEL, i[t.UNPACKED_PITCH_EG_LEVEL_4] = t.DEFAULT_PITCH_EG_LEVEL, i[t.UNPACKED_ALGORITHM] = t.DEFAULT_ALGORITHM, i[t.UNPACKED_FEEDBACK] = t.DEFAULT_FEEDBACK, i[t.UNPACKED_OSC_SYNC] = t.DEFAULT_OSC_SYNC, i[t.UNPACKED_LFO_SPEED] = t.DEFAULT_LFO_SPEED, i[t.UNPACKED_LFO_DELAY] = 0, i[t.UNPACKED_LFO_PM_DEPTH] = 0, i[t.UNPACKED_LFO_AM_DEPTH] = 0, i[t.UNPACKED_LFO_KEY_SYNC] = t.DEFAULT_LFO_KEY_SYNC, i[t.UNPACKED_LFO_WAVE] = 0, i[t.UNPACKED_LFO_PM_SENS] = t.DEFAULT_LFO_PM_SENS, i[t.UNPACKED_AMP_MOD_SENS] = 0, i[t.UNPACKED_TRANSPOSE] = t.TRANSPOSE_CENTER, i[t.UNPACKED_EG_BIAS_SENS] = 0;
|
|
2799
|
+
const n = "Init Voice";
|
|
2800
|
+
for (let r = 0; r < t.NAME_LENGTH; r++)
|
|
2801
|
+
i[t.UNPACKED_NAME_START + r] = r < n.length ? n.charCodeAt(r) : t.CHAR_SPACE;
|
|
2802
|
+
const s = t.pack(i);
|
|
2803
|
+
return new t(s, e);
|
|
2985
2804
|
}
|
|
2986
2805
|
/**
|
|
2987
2806
|
* Create a voice from unpacked 169-byte data
|
|
@@ -2989,9 +2808,9 @@ class n {
|
|
|
2989
2808
|
* @param {number} index - Voice index
|
|
2990
2809
|
* @returns {DX7Voice}
|
|
2991
2810
|
*/
|
|
2992
|
-
static fromUnpacked(
|
|
2993
|
-
const
|
|
2994
|
-
return new n
|
|
2811
|
+
static fromUnpacked(e, i = 0) {
|
|
2812
|
+
const n = t.pack(e);
|
|
2813
|
+
return new t(n, i);
|
|
2995
2814
|
}
|
|
2996
2815
|
/**
|
|
2997
2816
|
* Load a DX7 voice from a single voice SYX file
|
|
@@ -3000,38 +2819,38 @@ class n {
|
|
|
3000
2819
|
* @throws {DX7ParseError} If file has invalid VCED header
|
|
3001
2820
|
* @throws {Error} If file cannot be read (FileReader error)
|
|
3002
2821
|
*/
|
|
3003
|
-
static async fromFile(
|
|
3004
|
-
return new Promise((
|
|
3005
|
-
const
|
|
3006
|
-
|
|
2822
|
+
static async fromFile(e) {
|
|
2823
|
+
return new Promise((i, n) => {
|
|
2824
|
+
const s = new FileReader();
|
|
2825
|
+
s.onload = (r) => {
|
|
3007
2826
|
try {
|
|
3008
|
-
const
|
|
3009
|
-
if (
|
|
3010
|
-
throw new
|
|
3011
|
-
const
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
),
|
|
3015
|
-
|
|
3016
|
-
`DX7 VCED checksum mismatch (expected ${S.toString(16)}, got ${
|
|
2827
|
+
const E = new Uint8Array(r.target.result);
|
|
2828
|
+
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)
|
|
2829
|
+
throw new R("Invalid VCED header", "header", 0);
|
|
2830
|
+
const o = E.subarray(
|
|
2831
|
+
t.VCED_HEADER_SIZE,
|
|
2832
|
+
t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE
|
|
2833
|
+
), l = E[t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE], S = M._calculateChecksum(o, t.VCED_DATA_SIZE);
|
|
2834
|
+
l !== S && console.warn(
|
|
2835
|
+
`DX7 VCED checksum mismatch (expected ${S.toString(16)}, got ${l.toString(16)}). This is common with vintage SysEx files.`
|
|
3017
2836
|
);
|
|
3018
|
-
const
|
|
2837
|
+
const u = new Uint8Array(t.UNPACKED_SIZE);
|
|
3019
2838
|
let a = 0;
|
|
3020
|
-
for (let h = 0; h <
|
|
3021
|
-
const N = (
|
|
3022
|
-
|
|
3023
|
-
const d =
|
|
3024
|
-
|
|
2839
|
+
for (let h = 0; h < t.NUM_OPERATORS; h++) {
|
|
2840
|
+
const N = (t.NUM_OPERATORS - 1 - h) * t.UNPACKED_OP_SIZE;
|
|
2841
|
+
u[N + t.UNPACKED_OP_EG_RATE_1] = o[a++], u[N + t.UNPACKED_OP_EG_RATE_2] = o[a++], u[N + t.UNPACKED_OP_EG_RATE_3] = o[a++], u[N + t.UNPACKED_OP_EG_RATE_4] = o[a++], u[N + t.UNPACKED_OP_EG_LEVEL_1] = o[a++], u[N + t.UNPACKED_OP_EG_LEVEL_2] = o[a++], u[N + t.UNPACKED_OP_EG_LEVEL_3] = o[a++], u[N + t.UNPACKED_OP_EG_LEVEL_4] = o[a++], u[N + t.UNPACKED_OP_BREAK_POINT] = o[a++], u[N + t.UNPACKED_OP_L_SCALE_DEPTH] = o[a++], u[N + t.UNPACKED_OP_R_SCALE_DEPTH] = o[a++], u[N + t.UNPACKED_OP_L_CURVE] = o[a++], u[N + t.UNPACKED_OP_R_CURVE] = o[a++], u[N + t.UNPACKED_OP_RATE_SCALING] = o[a++], u[N + t.UNPACKED_OP_DETUNE] = o[a++];
|
|
2842
|
+
const d = o[a++];
|
|
2843
|
+
u[N + t.UNPACKED_OP_AMP_MOD_SENS] = d & t.MASK_2BIT, u[N + t.UNPACKED_OP_KEY_VEL_SENS] = d >> 2 & t.MASK_3BIT, u[N + t.UNPACKED_OP_OUTPUT_LEVEL] = o[a++], u[N + t.UNPACKED_OP_MODE] = o[a++], u[N + t.UNPACKED_OP_FREQ_COARSE] = o[a++], u[N + t.UNPACKED_OP_FREQ_FINE] = o[a++], u[N + t.UNPACKED_OP_OSC_DETUNE] = o[a++];
|
|
3025
2844
|
}
|
|
3026
|
-
|
|
3027
|
-
for (let h = 0; h <
|
|
3028
|
-
|
|
3029
|
-
const
|
|
3030
|
-
|
|
3031
|
-
} catch (
|
|
3032
|
-
E
|
|
2845
|
+
u[t.UNPACKED_PITCH_EG_RATE_1] = o[a++], u[t.UNPACKED_PITCH_EG_RATE_2] = o[a++], u[t.UNPACKED_PITCH_EG_RATE_3] = o[a++], u[t.UNPACKED_PITCH_EG_RATE_4] = o[a++], u[t.UNPACKED_PITCH_EG_LEVEL_1] = o[a++], u[t.UNPACKED_PITCH_EG_LEVEL_2] = o[a++], u[t.UNPACKED_PITCH_EG_LEVEL_3] = o[a++], u[t.UNPACKED_PITCH_EG_LEVEL_4] = o[a++], u[t.UNPACKED_ALGORITHM] = o[a++], u[t.UNPACKED_FEEDBACK] = o[a++], u[t.UNPACKED_OSC_SYNC] = o[a++], u[t.UNPACKED_LFO_SPEED] = o[a++], u[t.UNPACKED_LFO_DELAY] = o[a++], u[t.UNPACKED_LFO_PM_DEPTH] = o[a++], u[t.UNPACKED_LFO_AM_DEPTH] = o[a++], u[t.UNPACKED_LFO_KEY_SYNC] = o[a++], u[t.UNPACKED_LFO_WAVE] = o[a++], u[t.UNPACKED_LFO_PM_SENS] = o[a++], u[t.UNPACKED_TRANSPOSE] = o[a++];
|
|
2846
|
+
for (let h = 0; h < t.NAME_LENGTH; h++)
|
|
2847
|
+
u[t.UNPACKED_NAME_START + h] = o[a++];
|
|
2848
|
+
const A = t.pack(u);
|
|
2849
|
+
i(new t(A, 0));
|
|
2850
|
+
} catch (E) {
|
|
2851
|
+
n(E);
|
|
3033
2852
|
}
|
|
3034
|
-
},
|
|
2853
|
+
}, s.onerror = () => n(new Error("Failed to read file")), s.readAsArrayBuffer(e);
|
|
3035
2854
|
});
|
|
3036
2855
|
}
|
|
3037
2856
|
/**
|
|
@@ -3042,43 +2861,43 @@ class n {
|
|
|
3042
2861
|
* @throws {DX7ParseError} If VCED header is invalid
|
|
3043
2862
|
* @throws {DX7ValidationError} If data length is invalid
|
|
3044
2863
|
*/
|
|
3045
|
-
static fromSysEx(
|
|
3046
|
-
const
|
|
3047
|
-
let
|
|
3048
|
-
if (
|
|
3049
|
-
if (
|
|
3050
|
-
throw new
|
|
3051
|
-
|
|
3052
|
-
} else if (
|
|
3053
|
-
|
|
2864
|
+
static fromSysEx(e, i = 0) {
|
|
2865
|
+
const n = e instanceof Uint8Array ? e : new Uint8Array(e);
|
|
2866
|
+
let s;
|
|
2867
|
+
if (n[0] === t.VCED_SYSEX_START) {
|
|
2868
|
+
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)
|
|
2869
|
+
throw new R("Invalid VCED header", "header", 0);
|
|
2870
|
+
s = n.subarray(t.VCED_HEADER_SIZE, t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE);
|
|
2871
|
+
} else if (n.length === t.PACKED_SIZE)
|
|
2872
|
+
s = n;
|
|
3054
2873
|
else
|
|
3055
|
-
throw new
|
|
3056
|
-
`Invalid data length: expected ${
|
|
2874
|
+
throw new O(
|
|
2875
|
+
`Invalid data length: expected ${t.PACKED_SIZE} or ${t.VCED_SIZE} bytes, got ${n.length}`,
|
|
3057
2876
|
"length",
|
|
3058
|
-
|
|
2877
|
+
n.length
|
|
3059
2878
|
);
|
|
3060
|
-
if (
|
|
3061
|
-
throw new
|
|
3062
|
-
`Invalid voice data length: expected ${
|
|
2879
|
+
if (s.length !== t.VCED_DATA_SIZE && s.length !== t.PACKED_SIZE)
|
|
2880
|
+
throw new O(
|
|
2881
|
+
`Invalid voice data length: expected ${t.VCED_DATA_SIZE} or ${t.PACKED_SIZE} bytes, got ${s.length}`,
|
|
3063
2882
|
"length",
|
|
3064
|
-
|
|
2883
|
+
s.length
|
|
3065
2884
|
);
|
|
3066
|
-
if (
|
|
3067
|
-
const
|
|
3068
|
-
let
|
|
3069
|
-
for (let
|
|
3070
|
-
const S = (
|
|
3071
|
-
|
|
3072
|
-
const
|
|
3073
|
-
|
|
2885
|
+
if (s.length === t.VCED_DATA_SIZE) {
|
|
2886
|
+
const r = new Uint8Array(t.UNPACKED_SIZE);
|
|
2887
|
+
let E = 0;
|
|
2888
|
+
for (let l = 0; l < t.NUM_OPERATORS; l++) {
|
|
2889
|
+
const S = (t.NUM_OPERATORS - 1 - l) * t.UNPACKED_OP_SIZE;
|
|
2890
|
+
r[S + t.UNPACKED_OP_EG_RATE_1] = s[E++], r[S + t.UNPACKED_OP_EG_RATE_2] = s[E++], r[S + t.UNPACKED_OP_EG_RATE_3] = s[E++], r[S + t.UNPACKED_OP_EG_RATE_4] = s[E++], r[S + t.UNPACKED_OP_EG_LEVEL_1] = s[E++], r[S + t.UNPACKED_OP_EG_LEVEL_2] = s[E++], r[S + t.UNPACKED_OP_EG_LEVEL_3] = s[E++], r[S + t.UNPACKED_OP_EG_LEVEL_4] = s[E++], r[S + t.UNPACKED_OP_BREAK_POINT] = s[E++], r[S + t.UNPACKED_OP_L_SCALE_DEPTH] = s[E++], r[S + t.UNPACKED_OP_R_SCALE_DEPTH] = s[E++], r[S + t.UNPACKED_OP_L_CURVE] = s[E++], r[S + t.UNPACKED_OP_R_CURVE] = s[E++], r[S + t.UNPACKED_OP_RATE_SCALING] = s[E++], r[S + t.UNPACKED_OP_DETUNE] = s[E++];
|
|
2891
|
+
const u = s[E++];
|
|
2892
|
+
r[S + t.UNPACKED_OP_AMP_MOD_SENS] = u & t.MASK_2BIT, r[S + t.UNPACKED_OP_KEY_VEL_SENS] = u >> 2 & t.MASK_3BIT, r[S + t.UNPACKED_OP_OUTPUT_LEVEL] = s[E++], r[S + t.UNPACKED_OP_MODE] = s[E++], r[S + t.UNPACKED_OP_FREQ_COARSE] = s[E++], r[S + t.UNPACKED_OP_FREQ_FINE] = s[E++], r[S + t.UNPACKED_OP_OSC_DETUNE] = s[E++];
|
|
3074
2893
|
}
|
|
3075
|
-
|
|
3076
|
-
for (let
|
|
3077
|
-
|
|
3078
|
-
const
|
|
3079
|
-
return new
|
|
2894
|
+
r[t.UNPACKED_PITCH_EG_RATE_1] = s[E++], r[t.UNPACKED_PITCH_EG_RATE_2] = s[E++], r[t.UNPACKED_PITCH_EG_RATE_3] = s[E++], r[t.UNPACKED_PITCH_EG_RATE_4] = s[E++], r[t.UNPACKED_PITCH_EG_LEVEL_1] = s[E++], r[t.UNPACKED_PITCH_EG_LEVEL_2] = s[E++], r[t.UNPACKED_PITCH_EG_LEVEL_3] = s[E++], r[t.UNPACKED_PITCH_EG_LEVEL_4] = s[E++], r[t.UNPACKED_ALGORITHM] = s[E++], r[t.UNPACKED_FEEDBACK] = s[E++], r[t.UNPACKED_OSC_SYNC] = s[E++], r[t.UNPACKED_LFO_SPEED] = s[E++], r[t.UNPACKED_LFO_DELAY] = s[E++], r[t.UNPACKED_LFO_PM_DEPTH] = s[E++], r[t.UNPACKED_LFO_AM_DEPTH] = s[E++], r[t.UNPACKED_LFO_KEY_SYNC] = s[E++], r[t.UNPACKED_LFO_WAVE] = s[E++], r[t.UNPACKED_LFO_PM_SENS] = s[E++], r[t.UNPACKED_TRANSPOSE] = s[E++];
|
|
2895
|
+
for (let l = 0; l < t.NAME_LENGTH; l++)
|
|
2896
|
+
r[t.UNPACKED_NAME_START + l] = s[E++];
|
|
2897
|
+
const o = t.pack(r);
|
|
2898
|
+
return new t(o, i);
|
|
3080
2899
|
}
|
|
3081
|
-
return new
|
|
2900
|
+
return new t(s, i);
|
|
3082
2901
|
}
|
|
3083
2902
|
/**
|
|
3084
2903
|
* Create a DX7Voice from a JSON object
|
|
@@ -3087,27 +2906,27 @@ class n {
|
|
|
3087
2906
|
* @returns {DX7Voice}
|
|
3088
2907
|
* @throws {DX7ValidationError} If JSON structure is invalid
|
|
3089
2908
|
*/
|
|
3090
|
-
static fromJSON(
|
|
3091
|
-
if (!
|
|
3092
|
-
throw new
|
|
3093
|
-
const
|
|
3094
|
-
if (
|
|
3095
|
-
throw new
|
|
3096
|
-
const T = Number(
|
|
2909
|
+
static fromJSON(e, i = 0) {
|
|
2910
|
+
if (!e || typeof e != "object")
|
|
2911
|
+
throw new O("Invalid JSON: expected object", "json", e);
|
|
2912
|
+
const n = new Uint8Array(t.UNPACKED_SIZE), s = (a, A, h, N = 0, d = 127) => {
|
|
2913
|
+
if (A == null)
|
|
2914
|
+
throw new O(`Missing required parameter: ${h}`, h, A);
|
|
2915
|
+
const T = Number(A);
|
|
3097
2916
|
if (Number.isNaN(T))
|
|
3098
|
-
throw new
|
|
2917
|
+
throw new O(`Invalid parameter value for ${h}: ${A}`, h, A);
|
|
3099
2918
|
if (T < N || T > d)
|
|
3100
|
-
throw new
|
|
2919
|
+
throw new O(
|
|
3101
2920
|
`Parameter ${h} out of range: ${T} (must be ${N}-${d})`,
|
|
3102
2921
|
h,
|
|
3103
2922
|
T
|
|
3104
2923
|
);
|
|
3105
|
-
|
|
3106
|
-
},
|
|
3107
|
-
const
|
|
3108
|
-
return
|
|
3109
|
-
},
|
|
3110
|
-
const
|
|
2924
|
+
n[a] = Math.floor(T);
|
|
2925
|
+
}, r = (a) => {
|
|
2926
|
+
const A = { "-LN": 0, "-EX": 1, "+EX": 2, "+LN": 3 };
|
|
2927
|
+
return A[a] !== void 0 ? A[a] : 0;
|
|
2928
|
+
}, E = (a) => {
|
|
2929
|
+
const A = {
|
|
3111
2930
|
TRIANGLE: 0,
|
|
3112
2931
|
"SAW DOWN": 1,
|
|
3113
2932
|
"SAW UP": 2,
|
|
@@ -3115,113 +2934,113 @@ class n {
|
|
|
3115
2934
|
SINE: 4,
|
|
3116
2935
|
"SAMPLE & HOLD": 5
|
|
3117
2936
|
};
|
|
3118
|
-
return
|
|
3119
|
-
},
|
|
2937
|
+
return A[a] !== void 0 ? A[a] : 0;
|
|
2938
|
+
}, o = (a) => {
|
|
3120
2939
|
if (!a || typeof a != "string") return 60;
|
|
3121
|
-
const
|
|
3122
|
-
if (!
|
|
3123
|
-
const [, h, N] =
|
|
3124
|
-
return f === void 0 ? 60 : (d -
|
|
2940
|
+
const A = a.trim().match(/^([A-G]#?)(-?\d+)$/);
|
|
2941
|
+
if (!A) return 60;
|
|
2942
|
+
const [, h, N] = A, d = parseInt(N, 10), f = { C: 0, "C#": 1, D: 2, "D#": 3, E: 4, F: 5, "F#": 6, G: 7, "G#": 8, A: 9, "A#": 10, B: 11 }[h.toUpperCase()];
|
|
2943
|
+
return f === void 0 ? 60 : (d - t.MIDI_OCTAVE_OFFSET) * 12 + f;
|
|
3125
2944
|
};
|
|
3126
|
-
if (!Array.isArray(
|
|
3127
|
-
throw new
|
|
3128
|
-
for (let a = 0; a <
|
|
3129
|
-
const
|
|
3130
|
-
if (!
|
|
3131
|
-
throw new
|
|
3132
|
-
if (!
|
|
3133
|
-
throw new
|
|
2945
|
+
if (!Array.isArray(e.operators))
|
|
2946
|
+
throw new O("Invalid operators array: expected array", "operators", e.operators);
|
|
2947
|
+
for (let a = 0; a < e.operators.length; a++) {
|
|
2948
|
+
const A = e.operators[a];
|
|
2949
|
+
if (!A || typeof A != "object")
|
|
2950
|
+
throw new O(`Invalid operator data at index ${a}`, `operators[${a}]`, A);
|
|
2951
|
+
if (!A.eg || !Array.isArray(A.eg.rates) || A.eg.rates.length !== 4)
|
|
2952
|
+
throw new O(
|
|
3134
2953
|
`Invalid EG rates for operator ${a}`,
|
|
3135
2954
|
`operators[${a}].eg.rates`,
|
|
3136
|
-
|
|
2955
|
+
A.eg?.rates
|
|
3137
2956
|
);
|
|
3138
|
-
if (!
|
|
3139
|
-
throw new
|
|
2957
|
+
if (!A.eg || !Array.isArray(A.eg.levels) || A.eg.levels.length !== 4)
|
|
2958
|
+
throw new O(
|
|
3140
2959
|
`Invalid EG levels for operator ${a}`,
|
|
3141
2960
|
`operators[${a}].eg.levels`,
|
|
3142
|
-
|
|
2961
|
+
A.eg?.levels
|
|
3143
2962
|
);
|
|
3144
2963
|
}
|
|
3145
|
-
if (
|
|
3146
|
-
throw new
|
|
3147
|
-
`Invalid operators array: expected ${
|
|
2964
|
+
if (e.operators.length !== t.NUM_OPERATORS)
|
|
2965
|
+
throw new O(
|
|
2966
|
+
`Invalid operators array: expected ${t.NUM_OPERATORS} operators`,
|
|
3148
2967
|
"operators",
|
|
3149
|
-
|
|
2968
|
+
e.operators
|
|
3150
2969
|
);
|
|
3151
|
-
for (let a = 0; a <
|
|
3152
|
-
const
|
|
3153
|
-
if (!
|
|
3154
|
-
throw new
|
|
2970
|
+
for (let a = 0; a < t.NUM_OPERATORS; a++) {
|
|
2971
|
+
const A = e.operators[a], h = a * t.UNPACKED_OP_SIZE;
|
|
2972
|
+
if (!A.eg || !Array.isArray(A.eg.rates) || A.eg.rates.length !== 4)
|
|
2973
|
+
throw new O(
|
|
3155
2974
|
`Invalid EG rates for operator ${a}`,
|
|
3156
2975
|
`operators[${a}].eg.rates`,
|
|
3157
|
-
|
|
2976
|
+
A.eg?.rates
|
|
3158
2977
|
);
|
|
3159
|
-
if (
|
|
3160
|
-
throw new
|
|
2978
|
+
if (s(h + t.UNPACKED_OP_EG_RATE_1, A.eg.rates[0], `operators[${a}].eg.rates[0]`, 0, 99), s(h + t.UNPACKED_OP_EG_RATE_2, A.eg.rates[1], `operators[${a}].eg.rates[1]`, 0, 99), s(h + t.UNPACKED_OP_EG_RATE_3, A.eg.rates[2], `operators[${a}].eg.rates[2]`, 0, 99), s(h + t.UNPACKED_OP_EG_RATE_4, A.eg.rates[3], `operators[${a}].eg.rates[3]`, 0, 99), !A.eg || !Array.isArray(A.eg.levels) || A.eg.levels.length !== 4)
|
|
2979
|
+
throw new O(
|
|
3161
2980
|
`Invalid EG levels for operator ${a}`,
|
|
3162
2981
|
`operators[${a}].eg.levels`,
|
|
3163
|
-
|
|
2982
|
+
A.eg?.levels
|
|
3164
2983
|
);
|
|
3165
|
-
|
|
3166
|
-
const N =
|
|
3167
|
-
|
|
3168
|
-
h +
|
|
3169
|
-
|
|
2984
|
+
s(h + t.UNPACKED_OP_EG_LEVEL_1, A.eg.levels[0], `operators[${a}].eg.levels[0]`, 0, 99), s(h + t.UNPACKED_OP_EG_LEVEL_2, A.eg.levels[1], `operators[${a}].eg.levels[1]`, 0, 99), s(h + t.UNPACKED_OP_EG_LEVEL_3, A.eg.levels[2], `operators[${a}].eg.levels[2]`, 0, 99), s(h + t.UNPACKED_OP_EG_LEVEL_4, A.eg.levels[3], `operators[${a}].eg.levels[3]`, 0, 99);
|
|
2985
|
+
const N = o(A.key?.breakPoint) - t.MIDI_BREAK_POINT_OFFSET;
|
|
2986
|
+
s(h + t.UNPACKED_OP_BREAK_POINT, N, `operators[${a}].key.breakPoint`, 0, 127), s(
|
|
2987
|
+
h + t.UNPACKED_OP_L_SCALE_DEPTH,
|
|
2988
|
+
A.scale?.left?.depth || 0,
|
|
3170
2989
|
`operators[${a}].scale.left.depth`,
|
|
3171
2990
|
0,
|
|
3172
2991
|
99
|
|
3173
|
-
),
|
|
3174
|
-
h +
|
|
3175
|
-
|
|
2992
|
+
), s(
|
|
2993
|
+
h + t.UNPACKED_OP_R_SCALE_DEPTH,
|
|
2994
|
+
A.scale?.right?.depth || 0,
|
|
3176
2995
|
`operators[${a}].scale.right.depth`,
|
|
3177
2996
|
0,
|
|
3178
2997
|
99
|
|
3179
|
-
),
|
|
3180
|
-
h +
|
|
3181
|
-
|
|
2998
|
+
), n[h + t.UNPACKED_OP_L_CURVE] = r(A.scale?.left?.curve || "-LN"), n[h + t.UNPACKED_OP_R_CURVE] = r(A.scale?.right?.curve || "-LN"), s(
|
|
2999
|
+
h + t.UNPACKED_OP_RATE_SCALING,
|
|
3000
|
+
A.key?.scaling || 0,
|
|
3182
3001
|
`operators[${a}].key.scaling`,
|
|
3183
3002
|
0,
|
|
3184
3003
|
7
|
|
3185
3004
|
);
|
|
3186
|
-
const d = Number(
|
|
3187
|
-
|
|
3188
|
-
h +
|
|
3189
|
-
|
|
3005
|
+
const d = Number(A.osc?.detune) || 0;
|
|
3006
|
+
s(h + t.UNPACKED_OP_DETUNE, d + 7, `operators[${a}].osc.detune`, 0, 14), s(
|
|
3007
|
+
h + t.UNPACKED_OP_AMP_MOD_SENS,
|
|
3008
|
+
A.output?.ampModSens || 0,
|
|
3190
3009
|
`operators[${a}].output.ampModSens`,
|
|
3191
3010
|
0,
|
|
3192
3011
|
3
|
|
3193
|
-
),
|
|
3194
|
-
h +
|
|
3195
|
-
|
|
3012
|
+
), s(
|
|
3013
|
+
h + t.UNPACKED_OP_OUTPUT_LEVEL,
|
|
3014
|
+
A.output?.level || 0,
|
|
3196
3015
|
`operators[${a}].output.level`,
|
|
3197
3016
|
0,
|
|
3198
3017
|
99
|
|
3199
3018
|
);
|
|
3200
|
-
const T =
|
|
3201
|
-
|
|
3202
|
-
h +
|
|
3203
|
-
|
|
3019
|
+
const T = A.osc?.freq?.mode?.toUpperCase() === "FIXED" ? 1 : 0, f = Number(A.osc?.freq?.coarse) || 0, I = Number(A.osc?.freq?.fine) || 0;
|
|
3020
|
+
n[h + t.UNPACKED_OP_MODE] = T, s(h + t.UNPACKED_OP_FREQ_COARSE, f, `operators[${a}].osc.freq.coarse`, 0, 31), s(h + t.UNPACKED_OP_FREQ_FINE, I, `operators[${a}].osc.freq.fine`, 0, 15), s(
|
|
3021
|
+
h + t.UNPACKED_OP_KEY_VEL_SENS,
|
|
3022
|
+
A.key?.velocity || 0,
|
|
3204
3023
|
`operators[${a}].key.velocity`,
|
|
3205
3024
|
0,
|
|
3206
3025
|
7
|
|
3207
3026
|
);
|
|
3208
3027
|
}
|
|
3209
|
-
if (!
|
|
3210
|
-
throw new
|
|
3211
|
-
if (!
|
|
3212
|
-
throw new
|
|
3213
|
-
if (
|
|
3214
|
-
throw new
|
|
3215
|
-
if (
|
|
3216
|
-
throw new
|
|
3217
|
-
const
|
|
3218
|
-
|
|
3219
|
-
const S = Number(
|
|
3220
|
-
|
|
3221
|
-
const
|
|
3222
|
-
for (let a = 0; a <
|
|
3223
|
-
|
|
3224
|
-
return
|
|
3028
|
+
if (!e.pitchEG || !Array.isArray(e.pitchEG.rates) || e.pitchEG.rates.length !== 4)
|
|
3029
|
+
throw new O("Invalid pitch EG rates", "pitchEG.rates", e.pitchEG?.rates);
|
|
3030
|
+
if (!e.pitchEG || !Array.isArray(e.pitchEG.levels) || e.pitchEG.levels.length !== 4)
|
|
3031
|
+
throw new O("Invalid pitch EG levels", "pitchEG.levels", e.pitchEG?.levels);
|
|
3032
|
+
if (s(t.UNPACKED_PITCH_EG_RATE_1, e.pitchEG.rates[0], "pitchEG.rates[0]", 0, 99), s(t.UNPACKED_PITCH_EG_RATE_2, e.pitchEG.rates[1], "pitchEG.rates[1]", 0, 99), s(t.UNPACKED_PITCH_EG_RATE_3, e.pitchEG.rates[2], "pitchEG.rates[2]", 0, 99), s(t.UNPACKED_PITCH_EG_RATE_4, e.pitchEG.rates[3], "pitchEG.rates[3]", 0, 99), s(t.UNPACKED_PITCH_EG_LEVEL_1, e.pitchEG.levels[0], "pitchEG.levels[0]", 0, 99), s(t.UNPACKED_PITCH_EG_LEVEL_2, e.pitchEG.levels[1], "pitchEG.levels[1]", 0, 99), s(t.UNPACKED_PITCH_EG_LEVEL_3, e.pitchEG.levels[2], "pitchEG.levels[2]", 0, 99), s(t.UNPACKED_PITCH_EG_LEVEL_4, e.pitchEG.levels[3], "pitchEG.levels[3]", 0, 99), !e.lfo || typeof e.lfo != "object")
|
|
3033
|
+
throw new O("Invalid LFO data", "lfo", e.lfo);
|
|
3034
|
+
if (s(t.UNPACKED_LFO_SPEED, e.lfo.speed, "lfo.speed", 0, 99), s(t.UNPACKED_LFO_DELAY, e.lfo.delay, "lfo.delay", 0, 99), s(t.UNPACKED_LFO_PM_DEPTH, e.lfo.pmDepth, "lfo.pmDepth", 0, 99), s(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")
|
|
3035
|
+
throw new O("Invalid global data", "global", e.global);
|
|
3036
|
+
const l = Number(e.global.algorithm) || 1;
|
|
3037
|
+
s(t.UNPACKED_ALGORITHM, l - 1, "global.algorithm", 0, 31), s(t.UNPACKED_FEEDBACK, e.global.feedback, "global.feedback", 0, 7), n[t.UNPACKED_OSC_SYNC] = e.global.oscKeySync ? 1 : 0, s(t.UNPACKED_LFO_PM_SENS, e.global.pitchModSens, "global.pitchModSens", 0, 7);
|
|
3038
|
+
const S = Number(e.global.transpose) || 0;
|
|
3039
|
+
s(t.UNPACKED_TRANSPOSE, S + t.TRANSPOSE_CENTER, "global.transpose", 0, 127), s(t.UNPACKED_AMP_MOD_SENS, e.global.ampModSens || 0, "global.ampModSens", 0, 3), s(t.UNPACKED_EG_BIAS_SENS, e.global.egBiasSens || 0, "global.egBiasSens", 0, 7);
|
|
3040
|
+
const u = e.name || "";
|
|
3041
|
+
for (let a = 0; a < t.NAME_LENGTH; a++)
|
|
3042
|
+
n[t.UNPACKED_NAME_START + a] = a < u.length ? u.charCodeAt(a) : t.CHAR_SPACE;
|
|
3043
|
+
return t.fromUnpacked(n, i);
|
|
3225
3044
|
}
|
|
3226
3045
|
/**
|
|
3227
3046
|
* Export voice to DX7 single voice SysEx format (VCED format)
|
|
@@ -3230,146 +3049,178 @@ class n {
|
|
|
3230
3049
|
* @returns {Uint8Array} Single voice SysEx data (163 bytes)
|
|
3231
3050
|
*/
|
|
3232
3051
|
toSysEx() {
|
|
3233
|
-
const
|
|
3234
|
-
let
|
|
3235
|
-
|
|
3236
|
-
for (let
|
|
3237
|
-
const
|
|
3238
|
-
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
for (let
|
|
3244
|
-
|
|
3245
|
-
const
|
|
3246
|
-
|
|
3247
|
-
|
|
3052
|
+
const e = this.unpack(), i = new Uint8Array(t.VCED_SIZE);
|
|
3053
|
+
let n = 0;
|
|
3054
|
+
i[n++] = t.VCED_SYSEX_START, i[n++] = t.VCED_YAMAHA_ID, i[n++] = t.VCED_SUB_STATUS, i[n++] = t.VCED_FORMAT_SINGLE, i[n++] = t.VCED_BYTE_COUNT_MSB, i[n++] = t.VCED_BYTE_COUNT_LSB;
|
|
3055
|
+
for (let r = t.NUM_OPERATORS - 1; r >= 0; r--) {
|
|
3056
|
+
const E = r * t.UNPACKED_OP_SIZE;
|
|
3057
|
+
i[n++] = e[E + t.UNPACKED_OP_EG_RATE_1], i[n++] = e[E + t.UNPACKED_OP_EG_RATE_2], i[n++] = e[E + t.UNPACKED_OP_EG_RATE_3], i[n++] = e[E + t.UNPACKED_OP_EG_RATE_4], i[n++] = e[E + t.UNPACKED_OP_EG_LEVEL_1], i[n++] = e[E + t.UNPACKED_OP_EG_LEVEL_2], i[n++] = e[E + t.UNPACKED_OP_EG_LEVEL_3], i[n++] = e[E + t.UNPACKED_OP_EG_LEVEL_4], i[n++] = e[E + t.UNPACKED_OP_BREAK_POINT], i[n++] = e[E + t.UNPACKED_OP_L_SCALE_DEPTH], i[n++] = e[E + t.UNPACKED_OP_R_SCALE_DEPTH], i[n++] = e[E + t.UNPACKED_OP_L_CURVE], i[n++] = e[E + t.UNPACKED_OP_R_CURVE], i[n++] = e[E + t.UNPACKED_OP_RATE_SCALING], i[n++] = e[E + t.UNPACKED_OP_DETUNE];
|
|
3058
|
+
const o = e[E + t.UNPACKED_OP_AMP_MOD_SENS] & t.MASK_2BIT, l = e[E + t.UNPACKED_OP_KEY_VEL_SENS] & t.MASK_3BIT;
|
|
3059
|
+
i[n++] = o | l << 2, i[n++] = e[E + t.UNPACKED_OP_OUTPUT_LEVEL], i[n++] = e[E + t.UNPACKED_OP_MODE], i[n++] = e[E + t.UNPACKED_OP_FREQ_COARSE], i[n++] = e[E + t.UNPACKED_OP_OSC_DETUNE], i[n++] = e[E + t.UNPACKED_OP_FREQ_FINE];
|
|
3060
|
+
}
|
|
3061
|
+
i[n++] = e[t.UNPACKED_PITCH_EG_RATE_1], i[n++] = e[t.UNPACKED_PITCH_EG_RATE_2], i[n++] = e[t.UNPACKED_PITCH_EG_RATE_3], i[n++] = e[t.UNPACKED_PITCH_EG_RATE_4], i[n++] = e[t.UNPACKED_PITCH_EG_LEVEL_1], i[n++] = e[t.UNPACKED_PITCH_EG_LEVEL_2], i[n++] = e[t.UNPACKED_PITCH_EG_LEVEL_3], i[n++] = e[t.UNPACKED_PITCH_EG_LEVEL_4], i[n++] = e[t.UNPACKED_ALGORITHM], i[n++] = e[t.UNPACKED_FEEDBACK], i[n++] = e[t.UNPACKED_OSC_SYNC], i[n++] = e[t.UNPACKED_LFO_SPEED], i[n++] = e[t.UNPACKED_LFO_DELAY], i[n++] = e[t.UNPACKED_LFO_PM_DEPTH], i[n++] = e[t.UNPACKED_LFO_AM_DEPTH], i[n++] = e[t.UNPACKED_LFO_KEY_SYNC], i[n++] = e[t.UNPACKED_LFO_WAVE], i[n++] = e[t.UNPACKED_LFO_PM_SENS], i[n++] = e[t.UNPACKED_TRANSPOSE];
|
|
3062
|
+
for (let r = 0; r < t.NAME_LENGTH; r++)
|
|
3063
|
+
i[n++] = e[t.UNPACKED_NAME_START + r];
|
|
3064
|
+
const s = i.subarray(
|
|
3065
|
+
t.VCED_HEADER_SIZE,
|
|
3066
|
+
t.VCED_HEADER_SIZE + t.VCED_DATA_SIZE
|
|
3248
3067
|
);
|
|
3249
|
-
return
|
|
3068
|
+
return i[n++] = M._calculateChecksum(s, t.VCED_DATA_SIZE), i[n++] = t.VCED_SYSEX_END, i;
|
|
3250
3069
|
}
|
|
3251
3070
|
/**
|
|
3252
3071
|
* Convert voice to JSON format
|
|
3253
3072
|
* @returns {object} Voice data in JSON format
|
|
3254
3073
|
*/
|
|
3255
3074
|
toJSON() {
|
|
3256
|
-
const
|
|
3257
|
-
const
|
|
3258
|
-
return `${
|
|
3075
|
+
const e = this.unpack(), i = [], n = (E) => ["-LN", "-EX", "+EX", "+LN"][E] || "UNKNOWN", s = (E) => ["TRIANGLE", "SAW DOWN", "SAW UP", "SQUARE", "SINE", "SAMPLE & HOLD"][E] || "UNKNOWN", r = (E) => {
|
|
3076
|
+
const o = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"], l = Math.floor(E / 12) + t.MIDI_OCTAVE_OFFSET;
|
|
3077
|
+
return `${o[E % 12]}${l}`;
|
|
3259
3078
|
};
|
|
3260
|
-
for (let
|
|
3261
|
-
const
|
|
3262
|
-
|
|
3263
|
-
id:
|
|
3079
|
+
for (let E = 0; E < t.NUM_OPERATORS; E++) {
|
|
3080
|
+
const o = E * t.UNPACKED_OP_SIZE, l = e[o + t.UNPACKED_OP_MODE] === 0 ? "RATIO" : "FIXED";
|
|
3081
|
+
i.push({
|
|
3082
|
+
id: E + 1,
|
|
3264
3083
|
osc: {
|
|
3265
|
-
detune:
|
|
3084
|
+
detune: e[o + t.UNPACKED_OP_OSC_DETUNE],
|
|
3266
3085
|
freq: {
|
|
3267
|
-
coarse:
|
|
3268
|
-
fine:
|
|
3269
|
-
mode:
|
|
3086
|
+
coarse: e[o + t.UNPACKED_OP_FREQ_COARSE],
|
|
3087
|
+
fine: e[o + t.UNPACKED_OP_FREQ_FINE],
|
|
3088
|
+
mode: l
|
|
3270
3089
|
}
|
|
3271
3090
|
},
|
|
3272
3091
|
eg: {
|
|
3273
3092
|
rates: [
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3093
|
+
e[o + t.UNPACKED_OP_EG_RATE_1],
|
|
3094
|
+
e[o + t.UNPACKED_OP_EG_RATE_2],
|
|
3095
|
+
e[o + t.UNPACKED_OP_EG_RATE_3],
|
|
3096
|
+
e[o + t.UNPACKED_OP_EG_RATE_4]
|
|
3278
3097
|
],
|
|
3279
3098
|
levels: [
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3099
|
+
e[o + t.UNPACKED_OP_EG_LEVEL_1],
|
|
3100
|
+
e[o + t.UNPACKED_OP_EG_LEVEL_2],
|
|
3101
|
+
e[o + t.UNPACKED_OP_EG_LEVEL_3],
|
|
3102
|
+
e[o + t.UNPACKED_OP_EG_LEVEL_4]
|
|
3284
3103
|
]
|
|
3285
3104
|
},
|
|
3286
3105
|
key: {
|
|
3287
|
-
velocity:
|
|
3288
|
-
scaling:
|
|
3289
|
-
breakPoint:
|
|
3290
|
-
|
|
3106
|
+
velocity: e[o + t.UNPACKED_OP_KEY_VEL_SENS],
|
|
3107
|
+
scaling: e[o + t.UNPACKED_OP_RATE_SCALING],
|
|
3108
|
+
breakPoint: r(
|
|
3109
|
+
e[o + t.UNPACKED_OP_BREAK_POINT] + t.MIDI_BREAK_POINT_OFFSET
|
|
3291
3110
|
)
|
|
3292
3111
|
},
|
|
3293
3112
|
output: {
|
|
3294
|
-
level:
|
|
3295
|
-
ampModSens:
|
|
3113
|
+
level: e[o + t.UNPACKED_OP_OUTPUT_LEVEL],
|
|
3114
|
+
ampModSens: e[o + t.UNPACKED_OP_AMP_MOD_SENS]
|
|
3296
3115
|
},
|
|
3297
3116
|
scale: {
|
|
3298
3117
|
left: {
|
|
3299
|
-
depth:
|
|
3300
|
-
curve:
|
|
3118
|
+
depth: e[o + t.UNPACKED_OP_L_SCALE_DEPTH],
|
|
3119
|
+
curve: n(e[o + t.UNPACKED_OP_L_CURVE])
|
|
3301
3120
|
},
|
|
3302
3121
|
right: {
|
|
3303
|
-
depth:
|
|
3304
|
-
curve:
|
|
3122
|
+
depth: e[o + t.UNPACKED_OP_R_SCALE_DEPTH],
|
|
3123
|
+
curve: n(e[o + t.UNPACKED_OP_R_CURVE])
|
|
3305
3124
|
}
|
|
3306
3125
|
}
|
|
3307
3126
|
});
|
|
3308
3127
|
}
|
|
3309
3128
|
return {
|
|
3310
3129
|
name: this.name || "(Empty)",
|
|
3311
|
-
operators:
|
|
3130
|
+
operators: i,
|
|
3312
3131
|
pitchEG: {
|
|
3313
3132
|
rates: [
|
|
3314
|
-
t
|
|
3315
|
-
t
|
|
3316
|
-
t
|
|
3317
|
-
t
|
|
3133
|
+
e[t.UNPACKED_PITCH_EG_RATE_1],
|
|
3134
|
+
e[t.UNPACKED_PITCH_EG_RATE_2],
|
|
3135
|
+
e[t.UNPACKED_PITCH_EG_RATE_3],
|
|
3136
|
+
e[t.UNPACKED_PITCH_EG_RATE_4]
|
|
3318
3137
|
],
|
|
3319
3138
|
levels: [
|
|
3320
|
-
t
|
|
3321
|
-
t
|
|
3322
|
-
t
|
|
3323
|
-
t
|
|
3139
|
+
e[t.UNPACKED_PITCH_EG_LEVEL_1],
|
|
3140
|
+
e[t.UNPACKED_PITCH_EG_LEVEL_2],
|
|
3141
|
+
e[t.UNPACKED_PITCH_EG_LEVEL_3],
|
|
3142
|
+
e[t.UNPACKED_PITCH_EG_LEVEL_4]
|
|
3324
3143
|
]
|
|
3325
3144
|
},
|
|
3326
3145
|
lfo: {
|
|
3327
|
-
speed: t
|
|
3328
|
-
delay: t
|
|
3329
|
-
pmDepth: t
|
|
3330
|
-
amDepth: t
|
|
3331
|
-
keySync: t
|
|
3332
|
-
wave: e
|
|
3146
|
+
speed: e[t.UNPACKED_LFO_SPEED],
|
|
3147
|
+
delay: e[t.UNPACKED_LFO_DELAY],
|
|
3148
|
+
pmDepth: e[t.UNPACKED_LFO_PM_DEPTH],
|
|
3149
|
+
amDepth: e[t.UNPACKED_LFO_AM_DEPTH],
|
|
3150
|
+
keySync: e[t.UNPACKED_LFO_KEY_SYNC] === 1,
|
|
3151
|
+
wave: s(e[t.UNPACKED_LFO_WAVE])
|
|
3333
3152
|
},
|
|
3334
3153
|
global: {
|
|
3335
|
-
algorithm: t
|
|
3336
|
-
feedback: t
|
|
3337
|
-
oscKeySync: t
|
|
3338
|
-
pitchModSens: t
|
|
3339
|
-
transpose: t
|
|
3154
|
+
algorithm: e[t.UNPACKED_ALGORITHM] + 1,
|
|
3155
|
+
feedback: e[t.UNPACKED_FEEDBACK],
|
|
3156
|
+
oscKeySync: e[t.UNPACKED_OSC_SYNC] === 1,
|
|
3157
|
+
pitchModSens: e[t.UNPACKED_LFO_PM_SENS],
|
|
3158
|
+
transpose: e[t.UNPACKED_TRANSPOSE] - t.TRANSPOSE_CENTER
|
|
3340
3159
|
}
|
|
3341
3160
|
};
|
|
3342
3161
|
}
|
|
3343
|
-
}
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3162
|
+
};
|
|
3163
|
+
// Packed format (128 bytes)
|
|
3164
|
+
// See: DX7 Service Manual, Voice Memory Format
|
|
3165
|
+
_(t, "PACKED_SIZE", 128), _(t, "PACKED_OP_SIZE", 17), // 17 bytes per operator in packed format
|
|
3166
|
+
_(t, "NUM_OPERATORS", 6), // Packed operator parameter offsets (within each 17-byte operator block)
|
|
3167
|
+
_(t, "PACKED_OP_EG_RATE_1", 0), _(t, "PACKED_OP_EG_RATE_2", 1), _(t, "PACKED_OP_EG_RATE_3", 2), _(t, "PACKED_OP_EG_RATE_4", 3), _(t, "PACKED_OP_EG_LEVEL_1", 4), _(t, "PACKED_OP_EG_LEVEL_2", 5), _(t, "PACKED_OP_EG_LEVEL_3", 6), _(t, "PACKED_OP_EG_LEVEL_4", 7), _(t, "PACKED_OP_BREAK_POINT", 8), _(t, "PACKED_OP_L_SCALE_DEPTH", 9), _(t, "PACKED_OP_R_SCALE_DEPTH", 10), _(t, "PACKED_OP_CURVES", 11), // LC and RC packed
|
|
3168
|
+
_(t, "PACKED_OP_RATE_SCALING", 12), // RS and DET packed
|
|
3169
|
+
_(t, "PACKED_OP_MOD_SENS", 13), // AMS and KVS packed
|
|
3170
|
+
_(t, "PACKED_OP_OUTPUT_LEVEL", 14), _(t, "PACKED_OP_MODE_FREQ", 15), // Mode and Freq Coarse packed
|
|
3171
|
+
_(t, "PACKED_OP_DETUNE_FINE", 16), // OSC Detune and Freq Fine packed
|
|
3172
|
+
// Packed voice offsets (after 6 operators = bytes 102+)
|
|
3173
|
+
_(t, "PACKED_PITCH_EG_RATE_1", 102), _(t, "PACKED_PITCH_EG_RATE_2", 103), _(t, "PACKED_PITCH_EG_RATE_3", 104), _(t, "PACKED_PITCH_EG_RATE_4", 105), _(t, "PACKED_PITCH_EG_LEVEL_1", 106), _(t, "PACKED_PITCH_EG_LEVEL_2", 107), _(t, "PACKED_PITCH_EG_LEVEL_3", 108), _(t, "PACKED_PITCH_EG_LEVEL_4", 109), _(t, "OFFSET_ALGORITHM", 110), _(t, "OFFSET_FEEDBACK", 111), // Also contains OSC Sync
|
|
3174
|
+
_(t, "OFFSET_LFO_SPEED", 112), _(t, "OFFSET_LFO_DELAY", 113), _(t, "OFFSET_LFO_PM_DEPTH", 114), _(t, "OFFSET_LFO_AM_DEPTH", 115), _(t, "OFFSET_LFO_SYNC_WAVE", 116), // LFO sync, wave, and PM sensitivity packed
|
|
3175
|
+
_(t, "OFFSET_TRANSPOSE", 117), _(t, "OFFSET_AMP_MOD_SENS", 118), _(t, "OFFSET_EG_BIAS_SENS", 119), // Voice name (bytes 118-127)
|
|
3176
|
+
// IMPORTANT: Byte 118 serves dual-purpose in DX7 hardware:
|
|
3177
|
+
// 1. First character of voice name (as ASCII)
|
|
3178
|
+
// 2. Amp Mod Sensitivity parameter (as numeric value 0-127)
|
|
3179
|
+
// Both interpretations are used when converting to unpacked format
|
|
3180
|
+
_(t, "PACKED_NAME_START", 118), _(t, "NAME_LENGTH", 10), // Unpacked format (169 bytes)
|
|
3181
|
+
_(t, "UNPACKED_SIZE", 169), // Total unpacked size (159 params + 10 name)
|
|
3182
|
+
_(t, "UNPACKED_OP_SIZE", 23), // 23 bytes per operator in unpacked format
|
|
3183
|
+
// Unpacked operator parameter offsets (within each 23-byte operator block)
|
|
3184
|
+
_(t, "UNPACKED_OP_EG_RATE_1", 0), _(t, "UNPACKED_OP_EG_RATE_2", 1), _(t, "UNPACKED_OP_EG_RATE_3", 2), _(t, "UNPACKED_OP_EG_RATE_4", 3), _(t, "UNPACKED_OP_EG_LEVEL_1", 4), _(t, "UNPACKED_OP_EG_LEVEL_2", 5), _(t, "UNPACKED_OP_EG_LEVEL_3", 6), _(t, "UNPACKED_OP_EG_LEVEL_4", 7), _(t, "UNPACKED_OP_BREAK_POINT", 8), _(t, "UNPACKED_OP_L_SCALE_DEPTH", 9), _(t, "UNPACKED_OP_R_SCALE_DEPTH", 10), _(t, "UNPACKED_OP_L_CURVE", 11), _(t, "UNPACKED_OP_R_CURVE", 12), _(t, "UNPACKED_OP_RATE_SCALING", 13), _(t, "UNPACKED_OP_DETUNE", 14), _(t, "UNPACKED_OP_AMP_MOD_SENS", 15), _(t, "UNPACKED_OP_OUTPUT_LEVEL", 16), _(t, "UNPACKED_OP_MODE", 17), // Mode (0=ratio, 1=fixed)
|
|
3185
|
+
_(t, "UNPACKED_OP_KEY_VEL_SENS", 18), _(t, "UNPACKED_OP_FREQ_COARSE", 19), _(t, "UNPACKED_OP_OSC_DETUNE", 20), _(t, "UNPACKED_OP_FREQ_FINE", 21), // Unpacked pitch EG offsets (after 6 operators = index 138+)
|
|
3186
|
+
_(t, "UNPACKED_PITCH_EG_RATE_1", 138), _(t, "UNPACKED_PITCH_EG_RATE_2", 139), _(t, "UNPACKED_PITCH_EG_RATE_3", 140), _(t, "UNPACKED_PITCH_EG_RATE_4", 141), _(t, "UNPACKED_PITCH_EG_LEVEL_1", 142), _(t, "UNPACKED_PITCH_EG_LEVEL_2", 143), _(t, "UNPACKED_PITCH_EG_LEVEL_3", 144), _(t, "UNPACKED_PITCH_EG_LEVEL_4", 145), // Unpacked global parameters (after pitch EG = index 146+)
|
|
3187
|
+
_(t, "UNPACKED_ALGORITHM", 146), _(t, "UNPACKED_FEEDBACK", 147), _(t, "UNPACKED_OSC_SYNC", 148), _(t, "UNPACKED_LFO_SPEED", 149), _(t, "UNPACKED_LFO_DELAY", 150), _(t, "UNPACKED_LFO_PM_DEPTH", 151), _(t, "UNPACKED_LFO_AM_DEPTH", 152), _(t, "UNPACKED_LFO_KEY_SYNC", 153), _(t, "UNPACKED_LFO_WAVE", 154), _(t, "UNPACKED_LFO_PM_SENS", 155), _(t, "UNPACKED_AMP_MOD_SENS", 156), _(t, "UNPACKED_TRANSPOSE", 157), _(t, "UNPACKED_EG_BIAS_SENS", 158), _(t, "UNPACKED_NAME_START", 159), // VCED (single voice SysEx) format - for DX7 single patch dumps
|
|
3188
|
+
_(t, "VCED_SIZE", 163), // Total VCED sysex size (6 header + 155 data + 1 checksum + 1 end)
|
|
3189
|
+
_(t, "VCED_HEADER_SIZE", 6), _(t, "VCED_DATA_SIZE", 155), // Voice data bytes (6 operators × 21 bytes + 8 pitch EG + 11 global + 10 name)
|
|
3190
|
+
// VCED header bytes - DX7 single voice dump format
|
|
3191
|
+
_(t, "VCED_SYSEX_START", 240), // SysEx Message Start
|
|
3192
|
+
_(t, "VCED_YAMAHA_ID", 67), // Yamaha manufacturer ID
|
|
3193
|
+
_(t, "VCED_SUB_STATUS", 0), _(t, "VCED_FORMAT_SINGLE", 0), // Single voice format identifier
|
|
3194
|
+
_(t, "VCED_BYTE_COUNT_MSB", 1), // High byte of data length (1)
|
|
3195
|
+
_(t, "VCED_BYTE_COUNT_LSB", 27), // Low byte of data length (27 in decimal = 155 bytes)
|
|
3196
|
+
_(t, "VCED_SYSEX_END", 247), // SysEx Message End
|
|
3197
|
+
// Bit masks
|
|
3198
|
+
_(t, "MASK_7BIT", 127), // Standard 7-bit MIDI data mask
|
|
3199
|
+
_(t, "MASK_2BIT", 3), // For 2-bit values (curves)
|
|
3200
|
+
_(t, "MASK_3BIT", 7), // For 3-bit values (RS, detune)
|
|
3201
|
+
_(t, "MASK_4BIT", 15), // For 4-bit values (detune, fine freq)
|
|
3202
|
+
_(t, "MASK_5BIT", 31), // For 5-bit values (algorithm, freq coarse)
|
|
3203
|
+
_(t, "MASK_1BIT", 1), // For 1-bit values (mode, sync)
|
|
3204
|
+
// Parameter value ranges
|
|
3205
|
+
_(t, "TRANSPOSE_CENTER", 24), // MIDI note 24 = C0 (center of DX7 transpose range: -24 to +24 semitones)
|
|
3206
|
+
// Special character mappings - for Japanese DX7 character set compatibility
|
|
3207
|
+
_(t, "CHAR_YEN", 92), // Japanese Yen symbol (¥) maps to ASCII backslash
|
|
3208
|
+
_(t, "CHAR_ARROW_RIGHT", 126), // Right arrow (→) maps to ASCII tilde
|
|
3209
|
+
_(t, "CHAR_ARROW_LEFT", 127), // Left arrow (←) maps to ASCII DEL
|
|
3210
|
+
_(t, "CHAR_REPLACEMENT_Y", 89), // Replace Yen symbol with "Y"
|
|
3211
|
+
_(t, "CHAR_REPLACEMENT_GT", 62), // Right arrow with ">"
|
|
3212
|
+
_(t, "CHAR_REPLACEMENT_LT", 60), // Left arrow with "<"
|
|
3213
|
+
_(t, "CHAR_SPACE", 32), // Standard space character
|
|
3214
|
+
_(t, "CHAR_MIN_PRINTABLE", 32), // Minimum ASCII printable character
|
|
3215
|
+
_(t, "CHAR_MAX_PRINTABLE", 126), // Maximum ASCII printable character
|
|
3216
|
+
// Default voice values
|
|
3217
|
+
_(t, "DEFAULT_EG_RATE", 99), _(t, "DEFAULT_EG_LEVEL_MAX", 99), _(t, "DEFAULT_EG_LEVEL_MIN", 0), _(t, "DEFAULT_BREAK_POINT", 0), // DX7 breakpoint 0 = A-1 (MIDI note 9)
|
|
3218
|
+
_(t, "DEFAULT_OUTPUT_LEVEL", 99), _(t, "DEFAULT_PITCH_EG_LEVEL", 50), _(t, "DEFAULT_LFO_SPEED", 35), _(t, "DEFAULT_LFO_PM_SENS", 3), _(t, "DEFAULT_DETUNE", 7), // Detune center (actual detune = value - 7)
|
|
3219
|
+
_(t, "DEFAULT_FREQ_COARSE", 1), _(t, "DEFAULT_ALGORITHM", 0), _(t, "DEFAULT_FEEDBACK", 0), _(t, "DEFAULT_OSC_SYNC", 1), _(t, "DEFAULT_LFO_KEY_SYNC", 1), // MIDI notes
|
|
3220
|
+
_(t, "MIDI_OCTAVE_OFFSET", -2), // For displaying MIDI notes (MIDI 0 = C-2)
|
|
3221
|
+
_(t, "MIDI_BREAK_POINT_OFFSET", 21);
|
|
3222
|
+
let K = t;
|
|
3223
|
+
const C = class C {
|
|
3373
3224
|
/**
|
|
3374
3225
|
* Create a DX7Bank instance. Can be initialized with SYX data or created empty.
|
|
3375
3226
|
* When data is provided, it's validated and parsed. When no data is provided,
|
|
@@ -3394,12 +3245,12 @@ class l {
|
|
|
3394
3245
|
* const voiceData = new Uint8Array(4096); // 32 voices × 128 bytes
|
|
3395
3246
|
* const bank = new DX7Bank(voiceData, "Raw Bank");
|
|
3396
3247
|
*/
|
|
3397
|
-
constructor(
|
|
3398
|
-
if (this.voices = new Array(
|
|
3399
|
-
this._load(
|
|
3248
|
+
constructor(e, i = "") {
|
|
3249
|
+
if (this.voices = new Array(C.NUM_VOICES), this.name = i, e)
|
|
3250
|
+
this._load(e);
|
|
3400
3251
|
else
|
|
3401
|
-
for (let
|
|
3402
|
-
this.voices[
|
|
3252
|
+
for (let n = 0; n < C.NUM_VOICES; n++)
|
|
3253
|
+
this.voices[n] = K.createDefault(n);
|
|
3403
3254
|
}
|
|
3404
3255
|
/**
|
|
3405
3256
|
* Calculate DX7 SysEx checksum
|
|
@@ -3408,55 +3259,55 @@ class l {
|
|
|
3408
3259
|
* @param {number} size - Number of bytes
|
|
3409
3260
|
* @returns {number} Checksum byte
|
|
3410
3261
|
*/
|
|
3411
|
-
static _calculateChecksum(
|
|
3412
|
-
let
|
|
3413
|
-
for (let
|
|
3414
|
-
|
|
3415
|
-
return
|
|
3262
|
+
static _calculateChecksum(e, i) {
|
|
3263
|
+
let n = 0;
|
|
3264
|
+
for (let s = 0; s < i; s++)
|
|
3265
|
+
n += e[s];
|
|
3266
|
+
return C.CHECKSUM_MODULO - n % C.CHECKSUM_MODULO & C.MASK_7BIT;
|
|
3416
3267
|
}
|
|
3417
3268
|
/**
|
|
3418
3269
|
* Load and validate bank data
|
|
3419
3270
|
* @private
|
|
3420
3271
|
* @param {Array<number>|ArrayBuffer|Uint8Array} data
|
|
3421
3272
|
*/
|
|
3422
|
-
_load(
|
|
3423
|
-
const
|
|
3424
|
-
let
|
|
3425
|
-
if (
|
|
3426
|
-
const
|
|
3427
|
-
for (let
|
|
3428
|
-
if (
|
|
3429
|
-
throw new
|
|
3430
|
-
`Invalid SysEx header at position ${
|
|
3273
|
+
_load(e) {
|
|
3274
|
+
const i = e instanceof Uint8Array ? e : new Uint8Array(e);
|
|
3275
|
+
let n, s = 0;
|
|
3276
|
+
if (i[0] === C.SYSEX_START) {
|
|
3277
|
+
const E = i.subarray(0, C.SYSEX_HEADER_SIZE), o = C.SYSEX_HEADER;
|
|
3278
|
+
for (let l = 0; l < C.SYSEX_HEADER_SIZE; l++)
|
|
3279
|
+
if (E[l] !== o[l])
|
|
3280
|
+
throw new R(
|
|
3281
|
+
`Invalid SysEx header at position ${l}: expected ${o[l].toString(16)}, got ${E[l].toString(16)}`,
|
|
3431
3282
|
"header",
|
|
3432
|
-
|
|
3283
|
+
l
|
|
3433
3284
|
);
|
|
3434
|
-
|
|
3435
|
-
} else if (
|
|
3436
|
-
|
|
3285
|
+
n = i.subarray(C.SYSEX_HEADER_SIZE, C.SYSEX_HEADER_SIZE + C.VOICE_DATA_SIZE), s = C.SYSEX_HEADER_SIZE;
|
|
3286
|
+
} else if (i.length === C.VOICE_DATA_SIZE)
|
|
3287
|
+
n = i;
|
|
3437
3288
|
else
|
|
3438
|
-
throw new
|
|
3439
|
-
`Invalid data length: expected ${
|
|
3289
|
+
throw new O(
|
|
3290
|
+
`Invalid data length: expected ${C.VOICE_DATA_SIZE} or ${C.SYSEX_SIZE} bytes, got ${i.length}`,
|
|
3440
3291
|
"length",
|
|
3441
|
-
|
|
3292
|
+
i.length
|
|
3442
3293
|
);
|
|
3443
|
-
if (
|
|
3444
|
-
throw new
|
|
3445
|
-
`Invalid voice data length: expected ${
|
|
3294
|
+
if (n.length !== C.VOICE_DATA_SIZE)
|
|
3295
|
+
throw new O(
|
|
3296
|
+
`Invalid voice data length: expected ${C.VOICE_DATA_SIZE} bytes, got ${n.length}`,
|
|
3446
3297
|
"length",
|
|
3447
|
-
|
|
3298
|
+
n.length
|
|
3448
3299
|
);
|
|
3449
|
-
const
|
|
3450
|
-
if (
|
|
3451
|
-
const
|
|
3452
|
-
|
|
3453
|
-
`DX7 checksum mismatch (expected ${
|
|
3300
|
+
const r = C.SYSEX_HEADER_SIZE + C.VOICE_DATA_SIZE;
|
|
3301
|
+
if (s > 0 && i.length >= r + 1) {
|
|
3302
|
+
const E = i[r], o = C._calculateChecksum(n, C.VOICE_DATA_SIZE);
|
|
3303
|
+
E !== o && console.warn(
|
|
3304
|
+
`DX7 checksum mismatch (expected ${o.toString(16)}, got ${E.toString(16)}). This is common with vintage SysEx files and the data is likely still valid.`
|
|
3454
3305
|
);
|
|
3455
3306
|
}
|
|
3456
|
-
this.voices = new Array(
|
|
3457
|
-
for (let
|
|
3458
|
-
const
|
|
3459
|
-
this.voices[
|
|
3307
|
+
this.voices = new Array(C.NUM_VOICES);
|
|
3308
|
+
for (let E = 0; E < C.NUM_VOICES; E++) {
|
|
3309
|
+
const o = E * C.VOICE_SIZE, l = n.subarray(o, o + C.VOICE_SIZE);
|
|
3310
|
+
this.voices[E] = new K(l, E);
|
|
3460
3311
|
}
|
|
3461
3312
|
}
|
|
3462
3313
|
/**
|
|
@@ -3482,22 +3333,22 @@ class l {
|
|
|
3482
3333
|
* const voiceFromBank2 = bank2.getVoice(5);
|
|
3483
3334
|
* bank1.replaceVoice(0, voiceFromBank2); // Copy voice from bank2 to bank1
|
|
3484
3335
|
*/
|
|
3485
|
-
replaceVoice(
|
|
3486
|
-
if (
|
|
3487
|
-
throw new
|
|
3488
|
-
const
|
|
3489
|
-
this.voices[
|
|
3336
|
+
replaceVoice(e, i) {
|
|
3337
|
+
if (e < 0 || e >= C.NUM_VOICES)
|
|
3338
|
+
throw new O(`Invalid voice index: ${e}`, "index", e);
|
|
3339
|
+
const n = new Uint8Array(i.data);
|
|
3340
|
+
this.voices[e] = new K(n, e);
|
|
3490
3341
|
}
|
|
3491
3342
|
/**
|
|
3492
3343
|
* Add a voice to the first empty slot
|
|
3493
3344
|
* @param {DX7Voice} voice - Voice to add
|
|
3494
3345
|
* @returns {number} Index where voice was added, or -1 if bank is full
|
|
3495
3346
|
*/
|
|
3496
|
-
addVoice(
|
|
3497
|
-
for (let
|
|
3498
|
-
const
|
|
3499
|
-
if (
|
|
3500
|
-
return this.replaceVoice(
|
|
3347
|
+
addVoice(e) {
|
|
3348
|
+
for (let i = 0; i < this.voices.length; i++) {
|
|
3349
|
+
const n = this.voices[i];
|
|
3350
|
+
if (n.name === "" || n.name === "Init Voice")
|
|
3351
|
+
return this.replaceVoice(i, e), i;
|
|
3501
3352
|
}
|
|
3502
3353
|
return -1;
|
|
3503
3354
|
}
|
|
@@ -3513,24 +3364,24 @@ class l {
|
|
|
3513
3364
|
* @param {number} index - Voice index (0-31)
|
|
3514
3365
|
* @returns {DX7Voice|null}
|
|
3515
3366
|
*/
|
|
3516
|
-
getVoice(
|
|
3517
|
-
return
|
|
3367
|
+
getVoice(e) {
|
|
3368
|
+
return e < 0 || e >= this.voices.length ? null : this.voices[e];
|
|
3518
3369
|
}
|
|
3519
3370
|
/**
|
|
3520
3371
|
* Get all voice names
|
|
3521
3372
|
* @returns {string[]}
|
|
3522
3373
|
*/
|
|
3523
3374
|
getVoiceNames() {
|
|
3524
|
-
return this.voices.map((
|
|
3375
|
+
return this.voices.map((e) => e.name);
|
|
3525
3376
|
}
|
|
3526
3377
|
/**
|
|
3527
3378
|
* Find a voice by name (case-insensitive, partial match)
|
|
3528
3379
|
* @param {string} name - Voice name to search for
|
|
3529
3380
|
* @returns {DX7Voice|null}
|
|
3530
3381
|
*/
|
|
3531
|
-
findVoiceByName(
|
|
3532
|
-
const
|
|
3533
|
-
return this.voices.find((
|
|
3382
|
+
findVoiceByName(e) {
|
|
3383
|
+
const i = e.toLowerCase();
|
|
3384
|
+
return this.voices.find((n) => n.name.toLowerCase().includes(i)) || null;
|
|
3534
3385
|
}
|
|
3535
3386
|
/**
|
|
3536
3387
|
* Load a DX7 bank from a file
|
|
@@ -3540,22 +3391,22 @@ class l {
|
|
|
3540
3391
|
* @throws {DX7ValidationError} If data is not valid DX7 SYX format
|
|
3541
3392
|
* @throws {Error} If file cannot be read (FileReader error)
|
|
3542
3393
|
*/
|
|
3543
|
-
static async fromFile(
|
|
3544
|
-
return new Promise((
|
|
3545
|
-
const
|
|
3546
|
-
|
|
3394
|
+
static async fromFile(e) {
|
|
3395
|
+
return new Promise((i, n) => {
|
|
3396
|
+
const s = new FileReader();
|
|
3397
|
+
s.onload = async (r) => {
|
|
3547
3398
|
try {
|
|
3548
|
-
const
|
|
3549
|
-
if (
|
|
3550
|
-
|
|
3399
|
+
const E = e.name || "", o = new Uint8Array(r.target.result);
|
|
3400
|
+
if (o[0] === C.SYSEX_START && o[3] === K.VCED_FORMAT_SINGLE)
|
|
3401
|
+
n(new R("This is a single voice file. Use DX7Voice.fromFile() instead.", "format", 3));
|
|
3551
3402
|
else {
|
|
3552
|
-
const
|
|
3553
|
-
|
|
3403
|
+
const l = E.replace(/\.[^/.]+$/, ""), S = new C(r.target.result, l);
|
|
3404
|
+
i(S);
|
|
3554
3405
|
}
|
|
3555
|
-
} catch (
|
|
3556
|
-
E
|
|
3406
|
+
} catch (E) {
|
|
3407
|
+
n(E);
|
|
3557
3408
|
}
|
|
3558
|
-
},
|
|
3409
|
+
}, s.onerror = () => n(new Error("Failed to read file")), s.readAsArrayBuffer(e);
|
|
3559
3410
|
});
|
|
3560
3411
|
}
|
|
3561
3412
|
/**
|
|
@@ -3566,8 +3417,8 @@ class l {
|
|
|
3566
3417
|
* @throws {DX7ParseError} If SysEx header is invalid
|
|
3567
3418
|
* @throws {DX7ValidationError} If data length is invalid
|
|
3568
3419
|
*/
|
|
3569
|
-
static fromSysEx(
|
|
3570
|
-
return new
|
|
3420
|
+
static fromSysEx(e, i = "") {
|
|
3421
|
+
return new C(e, i);
|
|
3571
3422
|
}
|
|
3572
3423
|
/**
|
|
3573
3424
|
* Create a DX7Bank from a JSON object
|
|
@@ -3575,225 +3426,240 @@ class l {
|
|
|
3575
3426
|
* @returns {DX7Bank}
|
|
3576
3427
|
* @throws {DX7ValidationError} If JSON structure is invalid
|
|
3577
3428
|
*/
|
|
3578
|
-
static fromJSON(
|
|
3579
|
-
if (!
|
|
3580
|
-
throw new
|
|
3581
|
-
const
|
|
3582
|
-
if (
|
|
3583
|
-
throw new
|
|
3584
|
-
|
|
3585
|
-
`Bank JSON has ${
|
|
3429
|
+
static fromJSON(e) {
|
|
3430
|
+
if (!e || typeof e != "object")
|
|
3431
|
+
throw new O("Invalid JSON: expected object", "json", e);
|
|
3432
|
+
const i = new C();
|
|
3433
|
+
if (i.name = e.name || "", !Array.isArray(e.voices))
|
|
3434
|
+
throw new O("Invalid voices array", "voices", e.voices);
|
|
3435
|
+
e.voices.length !== C.NUM_VOICES && console.warn(
|
|
3436
|
+
`Bank JSON has ${e.voices.length} voices, expected ${C.NUM_VOICES}. Missing voices will be filled with defaults.`
|
|
3586
3437
|
);
|
|
3587
|
-
const
|
|
3588
|
-
for (let
|
|
3589
|
-
const
|
|
3590
|
-
if (!
|
|
3591
|
-
console.warn(`Invalid voice data at index ${
|
|
3438
|
+
const n = Math.min(e.voices.length, C.NUM_VOICES);
|
|
3439
|
+
for (let s = 0; s < n; s++) {
|
|
3440
|
+
const r = e.voices[s];
|
|
3441
|
+
if (!r || typeof r != "object") {
|
|
3442
|
+
console.warn(`Invalid voice data at index ${s}, using default voice`);
|
|
3592
3443
|
continue;
|
|
3593
3444
|
}
|
|
3594
3445
|
try {
|
|
3595
|
-
const { index:
|
|
3596
|
-
|
|
3597
|
-
} catch (
|
|
3598
|
-
console.warn(`Failed to load voice at index ${
|
|
3446
|
+
const { index: E, ...o } = r, l = K.fromJSON(o, s);
|
|
3447
|
+
i.replaceVoice(s, l);
|
|
3448
|
+
} catch (E) {
|
|
3449
|
+
console.warn(`Failed to load voice at index ${s}: ${E.message}, using default voice`);
|
|
3599
3450
|
}
|
|
3600
3451
|
}
|
|
3601
|
-
return
|
|
3452
|
+
return i;
|
|
3602
3453
|
}
|
|
3603
3454
|
/**
|
|
3604
3455
|
* Export bank to SysEx format
|
|
3605
3456
|
* @returns {Uint8Array} Full SysEx data (4104 bytes)
|
|
3606
3457
|
*/
|
|
3607
3458
|
toSysEx() {
|
|
3608
|
-
const
|
|
3609
|
-
let
|
|
3610
|
-
|
|
3611
|
-
|
|
3459
|
+
const e = new Uint8Array(C.SYSEX_SIZE);
|
|
3460
|
+
let i = 0;
|
|
3461
|
+
C.SYSEX_HEADER.forEach((s) => {
|
|
3462
|
+
e[i++] = s;
|
|
3612
3463
|
});
|
|
3613
|
-
for (const
|
|
3614
|
-
for (let
|
|
3615
|
-
|
|
3616
|
-
const
|
|
3617
|
-
return
|
|
3464
|
+
for (const s of this.voices)
|
|
3465
|
+
for (let r = 0; r < C.VOICE_SIZE; r++)
|
|
3466
|
+
e[i++] = s.data[r];
|
|
3467
|
+
const n = e.subarray(C.SYSEX_HEADER_SIZE, C.SYSEX_HEADER_SIZE + C.VOICE_DATA_SIZE);
|
|
3468
|
+
return e[i++] = C._calculateChecksum(n, C.VOICE_DATA_SIZE), e[i++] = C.SYSEX_END, e;
|
|
3618
3469
|
}
|
|
3619
3470
|
/**
|
|
3620
3471
|
* Convert bank to JSON format
|
|
3621
3472
|
* @returns {object} Bank data in JSON format
|
|
3622
3473
|
*/
|
|
3623
3474
|
toJSON() {
|
|
3624
|
-
const
|
|
3625
|
-
const
|
|
3475
|
+
const e = this.voices.map((i, n) => {
|
|
3476
|
+
const s = i.toJSON();
|
|
3626
3477
|
return {
|
|
3627
|
-
index:
|
|
3628
|
-
...
|
|
3478
|
+
index: n + 1,
|
|
3479
|
+
...s
|
|
3629
3480
|
};
|
|
3630
3481
|
});
|
|
3631
3482
|
return {
|
|
3632
3483
|
version: "1.0",
|
|
3633
3484
|
name: this.name || "",
|
|
3634
|
-
voices:
|
|
3485
|
+
voices: e
|
|
3635
3486
|
};
|
|
3636
3487
|
}
|
|
3637
|
-
}
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3488
|
+
};
|
|
3489
|
+
// SysEx header
|
|
3490
|
+
_(C, "SYSEX_START", 240), _(C, "SYSEX_END", 247), _(C, "SYSEX_YAMAHA_ID", 67), _(C, "SYSEX_SUB_STATUS", 0), _(C, "SYSEX_FORMAT_32_VOICES", 9), _(C, "SYSEX_BYTE_COUNT_MSB", 32), _(C, "SYSEX_BYTE_COUNT_LSB", 0), _(C, "SYSEX_HEADER", [
|
|
3491
|
+
C.SYSEX_START,
|
|
3492
|
+
C.SYSEX_YAMAHA_ID,
|
|
3493
|
+
C.SYSEX_SUB_STATUS,
|
|
3494
|
+
C.SYSEX_FORMAT_32_VOICES,
|
|
3495
|
+
C.SYSEX_BYTE_COUNT_MSB,
|
|
3496
|
+
C.SYSEX_BYTE_COUNT_LSB
|
|
3497
|
+
]), _(C, "SYSEX_HEADER_SIZE", 6), // Bank structure
|
|
3498
|
+
_(C, "VOICE_DATA_SIZE", 4096), // 32 voices × 128 bytes
|
|
3499
|
+
_(C, "SYSEX_SIZE", 4104), // Header(6) + Data(4096) + Checksum(1) + End(1)
|
|
3500
|
+
_(C, "VOICE_SIZE", 128), // Bytes per voice in packed format
|
|
3501
|
+
_(C, "NUM_VOICES", 32), // Checksum
|
|
3502
|
+
_(C, "CHECKSUM_MODULO", 128), _(C, "MASK_7BIT", 127);
|
|
3503
|
+
let M = C;
|
|
3504
|
+
function ot(c) {
|
|
3505
|
+
return c[0] !== 240 || c[c.length - 1] !== 247 ? null : {
|
|
3506
|
+
manufacturerId: c[1],
|
|
3507
|
+
payload: c.slice(2, -1),
|
|
3508
|
+
raw: c
|
|
3643
3509
|
};
|
|
3644
3510
|
}
|
|
3645
|
-
function
|
|
3646
|
-
return [240,
|
|
3511
|
+
function _t(c, e) {
|
|
3512
|
+
return [240, c, ...e, 247];
|
|
3647
3513
|
}
|
|
3648
|
-
function
|
|
3649
|
-
return
|
|
3514
|
+
function at(c) {
|
|
3515
|
+
return c.length >= 2 && c[0] === 240 && c[c.length - 1] === 247;
|
|
3650
3516
|
}
|
|
3651
|
-
function
|
|
3652
|
-
const
|
|
3653
|
-
for (let
|
|
3654
|
-
const
|
|
3655
|
-
let
|
|
3656
|
-
const
|
|
3657
|
-
for (let
|
|
3658
|
-
const
|
|
3659
|
-
|
|
3660
|
-
}
|
|
3661
|
-
|
|
3662
|
-
}
|
|
3663
|
-
return
|
|
3517
|
+
function ct(c) {
|
|
3518
|
+
const e = [];
|
|
3519
|
+
for (let i = 0; i < c.length; i += 7) {
|
|
3520
|
+
const n = c.slice(i, i + 7);
|
|
3521
|
+
let s = 0;
|
|
3522
|
+
const r = [];
|
|
3523
|
+
for (let E = 0; E < n.length; E++) {
|
|
3524
|
+
const o = n[E];
|
|
3525
|
+
o & 128 && (s |= 1 << E), r.push(o & 127);
|
|
3526
|
+
}
|
|
3527
|
+
e.push(s, ...r);
|
|
3528
|
+
}
|
|
3529
|
+
return e;
|
|
3664
3530
|
}
|
|
3665
|
-
function
|
|
3666
|
-
const
|
|
3667
|
-
for (let
|
|
3668
|
-
const
|
|
3669
|
-
for (let
|
|
3670
|
-
let
|
|
3671
|
-
|
|
3531
|
+
function At(c) {
|
|
3532
|
+
const e = [];
|
|
3533
|
+
for (let i = 0; i < c.length; i += 8) {
|
|
3534
|
+
const n = c[i], s = Math.min(7, c.length - i - 1);
|
|
3535
|
+
for (let r = 0; r < s; r++) {
|
|
3536
|
+
let E = c[i + 1 + r];
|
|
3537
|
+
n & 1 << r && (E |= 128), e.push(E);
|
|
3672
3538
|
}
|
|
3673
3539
|
}
|
|
3674
|
-
return
|
|
3540
|
+
return e;
|
|
3675
3541
|
}
|
|
3676
|
-
function
|
|
3677
|
-
return Number.isInteger(
|
|
3542
|
+
function Ct(c) {
|
|
3543
|
+
return Number.isInteger(c) && c >= 1 && c <= 16;
|
|
3678
3544
|
}
|
|
3679
|
-
function
|
|
3680
|
-
return Number.isInteger(
|
|
3545
|
+
function Pt(c) {
|
|
3546
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3681
3547
|
}
|
|
3682
|
-
function
|
|
3683
|
-
return Number.isInteger(
|
|
3548
|
+
function ut(c) {
|
|
3549
|
+
return Number.isInteger(c) && c >= 0 && c <= 31;
|
|
3684
3550
|
}
|
|
3685
|
-
function
|
|
3686
|
-
return Number.isInteger(
|
|
3551
|
+
function lt(c) {
|
|
3552
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3687
3553
|
}
|
|
3688
|
-
function
|
|
3689
|
-
return Number.isInteger(
|
|
3554
|
+
function ht(c) {
|
|
3555
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3690
3556
|
}
|
|
3691
|
-
function
|
|
3692
|
-
return Number.isInteger(
|
|
3557
|
+
function St(c) {
|
|
3558
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3693
3559
|
}
|
|
3694
|
-
function
|
|
3695
|
-
return Number.isInteger(
|
|
3560
|
+
function Nt(c) {
|
|
3561
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3696
3562
|
}
|
|
3697
|
-
function
|
|
3698
|
-
return Number.isInteger(
|
|
3563
|
+
function Dt(c) {
|
|
3564
|
+
return Number.isInteger(c) && c >= 0 && c <= 16383;
|
|
3699
3565
|
}
|
|
3700
|
-
function
|
|
3701
|
-
return Number.isInteger(
|
|
3566
|
+
function Ot(c, e) {
|
|
3567
|
+
return Number.isInteger(c) && c >= 0 && c <= 127 && Number.isInteger(e) && e >= 0 && e <= 127;
|
|
3702
3568
|
}
|
|
3703
|
-
async function
|
|
3569
|
+
async function dt(c = {}) {
|
|
3704
3570
|
const {
|
|
3705
|
-
onStatusUpdate:
|
|
3706
|
-
onConnectionUpdate:
|
|
3707
|
-
inputChannel:
|
|
3708
|
-
outputChannel:
|
|
3709
|
-
output:
|
|
3710
|
-
sysex:
|
|
3711
|
-
onReady:
|
|
3712
|
-
onError:
|
|
3571
|
+
onStatusUpdate: e,
|
|
3572
|
+
onConnectionUpdate: i,
|
|
3573
|
+
inputChannel: n = 1,
|
|
3574
|
+
outputChannel: s = 1,
|
|
3575
|
+
output: r,
|
|
3576
|
+
sysex: E,
|
|
3577
|
+
onReady: o,
|
|
3578
|
+
onError: l,
|
|
3713
3579
|
selector: S,
|
|
3714
|
-
watchDOM:
|
|
3580
|
+
watchDOM: u,
|
|
3715
3581
|
...a
|
|
3716
|
-
} =
|
|
3582
|
+
} = c, h = await j({
|
|
3717
3583
|
autoConnect: !1,
|
|
3718
|
-
sysex:
|
|
3719
|
-
inputChannel:
|
|
3720
|
-
outputChannel:
|
|
3584
|
+
sysex: E,
|
|
3585
|
+
inputChannel: n,
|
|
3586
|
+
outputChannel: s,
|
|
3721
3587
|
selector: S || "[data-midi-cc]",
|
|
3722
|
-
watchDOM:
|
|
3723
|
-
onError:
|
|
3588
|
+
watchDOM: u,
|
|
3589
|
+
onError: l,
|
|
3724
3590
|
...a
|
|
3725
|
-
}), N = new
|
|
3591
|
+
}), N = new k({
|
|
3726
3592
|
midiController: h,
|
|
3727
|
-
onStatusUpdate:
|
|
3593
|
+
onStatusUpdate: e || (() => {
|
|
3728
3594
|
}),
|
|
3729
|
-
onConnectionUpdate:
|
|
3595
|
+
onConnectionUpdate: i || (() => {
|
|
3730
3596
|
}),
|
|
3731
|
-
channel:
|
|
3597
|
+
channel: s
|
|
3732
3598
|
});
|
|
3733
|
-
if (
|
|
3599
|
+
if (r)
|
|
3734
3600
|
try {
|
|
3735
|
-
await h.device.connectOutput(
|
|
3601
|
+
await h.device.connectOutput(r), N.currentOutput = h.device.getCurrentOutput(), N.updateConnectionStatus();
|
|
3736
3602
|
} catch (d) {
|
|
3737
|
-
|
|
3603
|
+
l ? l(d) : console.error("Failed to connect to MIDI device:", d.message);
|
|
3738
3604
|
}
|
|
3739
|
-
return
|
|
3605
|
+
return o && o(h, N), N;
|
|
3740
3606
|
}
|
|
3741
|
-
async function
|
|
3742
|
-
const
|
|
3743
|
-
await
|
|
3744
|
-
const
|
|
3607
|
+
async function j(c = {}) {
|
|
3608
|
+
const e = new J(c);
|
|
3609
|
+
await e.init();
|
|
3610
|
+
const i = c.selector || "[data-midi-cc]";
|
|
3745
3611
|
{
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3612
|
+
const n = new $(e, i);
|
|
3613
|
+
n.bindAll(), c.watchDOM && n.enableAutoBinding(), e._binder = n;
|
|
3748
3614
|
}
|
|
3749
|
-
return
|
|
3615
|
+
return e;
|
|
3750
3616
|
}
|
|
3751
3617
|
export {
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3618
|
+
p as CONN,
|
|
3619
|
+
p as CONNECTION_EVENTS,
|
|
3620
|
+
P as CONTROLLER_EVENTS,
|
|
3621
|
+
P as CTRL,
|
|
3622
|
+
M as DX7Bank,
|
|
3623
|
+
v as DX7Error,
|
|
3624
|
+
R as DX7ParseError,
|
|
3625
|
+
O as DX7ValidationError,
|
|
3626
|
+
K as DX7Voice,
|
|
3627
|
+
$ as DataAttributeBinder,
|
|
3628
|
+
w as EventEmitter,
|
|
3629
|
+
F as MIDIAccessError,
|
|
3630
|
+
Q as MIDIConnection,
|
|
3631
|
+
y as MIDIConnectionError,
|
|
3632
|
+
J as MIDIController,
|
|
3633
|
+
U as MIDIDeviceError,
|
|
3634
|
+
k as MIDIDeviceManager,
|
|
3635
|
+
g as MIDIError,
|
|
3636
|
+
m as MIDIValidationError,
|
|
3637
|
+
D as clamp,
|
|
3638
|
+
j as createMIDIController,
|
|
3639
|
+
dt as createMIDIDeviceManager,
|
|
3640
|
+
_t as createSysEx,
|
|
3641
|
+
q as decode14BitValue,
|
|
3642
|
+
At as decode7Bit,
|
|
3643
|
+
rt as denormalize14BitValue,
|
|
3644
|
+
tt as denormalizeValue,
|
|
3645
|
+
z as encode14BitValue,
|
|
3646
|
+
ct as encode7Bit,
|
|
3647
|
+
nt as frequencyToNote,
|
|
3648
|
+
Et as getCCName,
|
|
3649
|
+
at as isSysEx,
|
|
3650
|
+
ut as isValid14BitCC,
|
|
3651
|
+
Pt as isValidCC,
|
|
3652
|
+
Ct as isValidChannel,
|
|
3653
|
+
lt as isValidMIDIValue,
|
|
3654
|
+
ht as isValidNote,
|
|
3655
|
+
Dt as isValidPitchBend,
|
|
3656
|
+
Ot as isValidPitchBendBytes,
|
|
3657
|
+
Nt as isValidProgramChange,
|
|
3658
|
+
St as isValidVelocity,
|
|
3659
|
+
W as normalize14BitValue,
|
|
3660
|
+
Z as normalizeValue,
|
|
3661
|
+
et as noteNameToNumber,
|
|
3662
|
+
it as noteNumberToName,
|
|
3663
|
+
st as noteToFrequency,
|
|
3664
|
+
ot as parseSysEx
|
|
3799
3665
|
};
|