midiwire 0.11.3 → 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 -1429
- package/dist/midiwire.umd.js +1 -1
- package/package.json +6 -6
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 o = {
|
|
|
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,201 +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
|
-
static DEFAULT_OSC_SYNC = 1;
|
|
2712
|
-
static DEFAULT_LFO_KEY_SYNC = 1;
|
|
2713
|
-
// MIDI notes
|
|
2714
|
-
static MIDI_OCTAVE_OFFSET = -2;
|
|
2715
|
-
// For displaying MIDI notes (MIDI 0 = C-2)
|
|
2716
|
-
static MIDI_BREAK_POINT_OFFSET = 21;
|
|
2533
|
+
const t = class t {
|
|
2717
2534
|
// Offset for breakpoint display
|
|
2718
2535
|
/**
|
|
2719
2536
|
* Create a DX7Voice from raw 128-byte data
|
|
@@ -2721,24 +2538,24 @@ class n {
|
|
|
2721
2538
|
* @param {number} index - Voice index (0-31)
|
|
2722
2539
|
* @throws {DX7ValidationError} If data length is not exactly 128 bytes
|
|
2723
2540
|
*/
|
|
2724
|
-
constructor(
|
|
2725
|
-
if (
|
|
2726
|
-
throw new
|
|
2727
|
-
`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}`,
|
|
2728
2545
|
"length",
|
|
2729
|
-
|
|
2546
|
+
e.length
|
|
2730
2547
|
);
|
|
2731
|
-
this.index =
|
|
2548
|
+
this.index = i, this.data = new Uint8Array(e), this.name = this._extractName(), this._unpackedCache = null;
|
|
2732
2549
|
}
|
|
2733
2550
|
/**
|
|
2734
2551
|
* Extract the voice name from the data (10 characters at offset 118)
|
|
2735
2552
|
* @private
|
|
2736
2553
|
*/
|
|
2737
2554
|
_extractName() {
|
|
2738
|
-
const
|
|
2739
|
-
return Array.from(
|
|
2740
|
-
let
|
|
2741
|
-
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);
|
|
2742
2559
|
}).join("").trim();
|
|
2743
2560
|
}
|
|
2744
2561
|
/**
|
|
@@ -2747,14 +2564,14 @@ class n {
|
|
|
2747
2564
|
* @returns {number} Parameter value (0-127)
|
|
2748
2565
|
* @throws {DX7ValidationError} If offset is out of range
|
|
2749
2566
|
*/
|
|
2750
|
-
getParameter(
|
|
2751
|
-
if (
|
|
2752
|
-
throw new
|
|
2753
|
-
`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})`,
|
|
2754
2571
|
"offset",
|
|
2755
|
-
|
|
2572
|
+
e
|
|
2756
2573
|
);
|
|
2757
|
-
return this.data[
|
|
2574
|
+
return this.data[e] & t.MASK_7BIT;
|
|
2758
2575
|
}
|
|
2759
2576
|
/**
|
|
2760
2577
|
* Get a parameter value from the unpacked 169-byte format
|
|
@@ -2762,28 +2579,28 @@ class n {
|
|
|
2762
2579
|
* @returns {number} Parameter value (0-127)
|
|
2763
2580
|
* @throws {DX7ValidationError} If offset is out of range
|
|
2764
2581
|
*/
|
|
2765
|
-
getUnpackedParameter(
|
|
2766
|
-
if (
|
|
2767
|
-
throw new
|
|
2768
|
-
`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})`,
|
|
2769
2586
|
"offset",
|
|
2770
|
-
|
|
2587
|
+
e
|
|
2771
2588
|
);
|
|
2772
|
-
return this._unpackedCache || (this._unpackedCache = this.unpack()), this._unpackedCache[
|
|
2589
|
+
return this._unpackedCache || (this._unpackedCache = this.unpack()), this._unpackedCache[e] & t.MASK_7BIT;
|
|
2773
2590
|
}
|
|
2774
2591
|
/**
|
|
2775
2592
|
* Set a raw parameter value in the packed data
|
|
2776
2593
|
* @param {number} offset - Byte offset in the voice data
|
|
2777
2594
|
* @param {number} value - Parameter value (0-127)
|
|
2778
2595
|
*/
|
|
2779
|
-
setParameter(
|
|
2780
|
-
if (
|
|
2781
|
-
throw new
|
|
2782
|
-
`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})`,
|
|
2783
2600
|
"offset",
|
|
2784
|
-
|
|
2601
|
+
e
|
|
2785
2602
|
);
|
|
2786
|
-
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());
|
|
2787
2604
|
}
|
|
2788
2605
|
/**
|
|
2789
2606
|
* Unpack the voice data to 169-byte unpacked format
|
|
@@ -2791,199 +2608,199 @@ class n {
|
|
|
2791
2608
|
* @returns {Uint8Array} 169 bytes of unpacked voice data (138 operator + 8 pitch EG + 13 global + 10 name = 169 bytes)
|
|
2792
2609
|
*/
|
|
2793
2610
|
unpack() {
|
|
2794
|
-
const
|
|
2795
|
-
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;
|
|
2796
2613
|
}
|
|
2797
2614
|
/**
|
|
2798
2615
|
* Unpack all 6 operators from packed to unpacked format
|
|
2799
2616
|
* @private
|
|
2800
2617
|
*/
|
|
2801
|
-
_unpackOperators(
|
|
2802
|
-
for (let
|
|
2803
|
-
const
|
|
2804
|
-
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);
|
|
2805
2622
|
}
|
|
2806
2623
|
}
|
|
2807
2624
|
/**
|
|
2808
2625
|
* Unpack a single operator's parameters
|
|
2809
2626
|
* @private
|
|
2810
2627
|
*/
|
|
2811
|
-
_unpackOperator(
|
|
2812
|
-
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);
|
|
2813
2630
|
}
|
|
2814
2631
|
/**
|
|
2815
2632
|
* Unpack operator EG rates and levels
|
|
2816
2633
|
* @private
|
|
2817
2634
|
*/
|
|
2818
|
-
_unpackOperatorEG(
|
|
2819
|
-
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;
|
|
2820
2637
|
}
|
|
2821
2638
|
/**
|
|
2822
2639
|
* Unpack operator keyboard scaling parameters
|
|
2823
2640
|
* @private
|
|
2824
2641
|
*/
|
|
2825
|
-
_unpackOperatorScaling(
|
|
2826
|
-
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;
|
|
2827
2644
|
}
|
|
2828
2645
|
/**
|
|
2829
2646
|
* Unpack operator bit-packed parameters (curves, rate scaling, mod sens)
|
|
2830
2647
|
* @private
|
|
2831
2648
|
*/
|
|
2832
|
-
_unpackOperatorPackedParams(
|
|
2833
|
-
const
|
|
2834
|
-
s
|
|
2835
|
-
const
|
|
2836
|
-
s
|
|
2837
|
-
const
|
|
2838
|
-
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;
|
|
2839
2656
|
}
|
|
2840
2657
|
/**
|
|
2841
2658
|
* Unpack operator frequency parameters
|
|
2842
2659
|
* @private
|
|
2843
2660
|
*/
|
|
2844
|
-
_unpackOperatorFrequency(
|
|
2845
|
-
const
|
|
2846
|
-
s
|
|
2847
|
-
const
|
|
2848
|
-
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;
|
|
2849
2666
|
}
|
|
2850
2667
|
/**
|
|
2851
2668
|
* Unpack pitch envelope generator parameters
|
|
2852
2669
|
* @private
|
|
2853
2670
|
*/
|
|
2854
|
-
_unpackPitchEG(
|
|
2855
|
-
|
|
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;
|
|
2856
2673
|
}
|
|
2857
2674
|
/**
|
|
2858
2675
|
* Unpack global voice parameters (algorithm, feedback, LFO, etc.)
|
|
2859
2676
|
* @private
|
|
2860
2677
|
*/
|
|
2861
|
-
_unpackGlobalParams(
|
|
2862
|
-
|
|
2863
|
-
const
|
|
2864
|
-
|
|
2865
|
-
const
|
|
2866
|
-
|
|
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;
|
|
2867
2684
|
}
|
|
2868
2685
|
/**
|
|
2869
2686
|
* Copy voice name from packed to unpacked format
|
|
2870
2687
|
* @private
|
|
2871
2688
|
*/
|
|
2872
|
-
_unpackName(
|
|
2873
|
-
for (let
|
|
2874
|
-
|
|
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;
|
|
2875
2692
|
}
|
|
2876
2693
|
/**
|
|
2877
2694
|
* Pack 169-byte unpacked data to 128-byte format
|
|
2878
2695
|
* @param {Array<number>|Uint8Array} unpacked - 169 bytes of unpacked data (159 parameters + 10 name bytes)
|
|
2879
2696
|
* @returns {Uint8Array} 128 bytes of packed data
|
|
2880
2697
|
*/
|
|
2881
|
-
static pack(
|
|
2882
|
-
if (
|
|
2883
|
-
throw new
|
|
2884
|
-
`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}`,
|
|
2885
2702
|
"length",
|
|
2886
|
-
|
|
2703
|
+
e.length
|
|
2887
2704
|
);
|
|
2888
|
-
const
|
|
2889
|
-
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;
|
|
2890
2707
|
}
|
|
2891
2708
|
/**
|
|
2892
2709
|
* Pack all 6 operators from unpacked to packed format
|
|
2893
2710
|
* @private
|
|
2894
2711
|
*/
|
|
2895
|
-
static _packOperators(
|
|
2896
|
-
for (let
|
|
2897
|
-
const
|
|
2898
|
-
|
|
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);
|
|
2899
2716
|
}
|
|
2900
2717
|
}
|
|
2901
2718
|
/**
|
|
2902
2719
|
* Pack a single operator's parameters
|
|
2903
2720
|
* @private
|
|
2904
2721
|
*/
|
|
2905
|
-
static _packOperator(
|
|
2906
|
-
|
|
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);
|
|
2907
2724
|
}
|
|
2908
2725
|
/**
|
|
2909
2726
|
* Pack operator EG rates and levels
|
|
2910
2727
|
* @private
|
|
2911
2728
|
*/
|
|
2912
|
-
static _packOperatorEG(
|
|
2913
|
-
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];
|
|
2914
2731
|
}
|
|
2915
2732
|
/**
|
|
2916
2733
|
* Pack operator keyboard scaling parameters
|
|
2917
2734
|
* @private
|
|
2918
2735
|
*/
|
|
2919
|
-
static _packOperatorScaling(
|
|
2920
|
-
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];
|
|
2921
2738
|
}
|
|
2922
2739
|
/**
|
|
2923
2740
|
* Pack operator bit-packed parameters
|
|
2924
2741
|
* @private
|
|
2925
2742
|
*/
|
|
2926
|
-
static _packOperatorPackedParams(
|
|
2927
|
-
const
|
|
2928
|
-
s
|
|
2929
|
-
const
|
|
2930
|
-
s
|
|
2931
|
-
const S =
|
|
2932
|
-
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];
|
|
2933
2750
|
}
|
|
2934
2751
|
/**
|
|
2935
2752
|
* Pack operator frequency parameters
|
|
2936
2753
|
* @private
|
|
2937
2754
|
*/
|
|
2938
|
-
static _packOperatorFrequency(
|
|
2939
|
-
const
|
|
2940
|
-
s
|
|
2941
|
-
const
|
|
2942
|
-
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;
|
|
2943
2760
|
}
|
|
2944
2761
|
/**
|
|
2945
2762
|
* Pack pitch envelope generator parameters
|
|
2946
2763
|
* @private
|
|
2947
2764
|
*/
|
|
2948
|
-
static _packPitchEG(
|
|
2949
|
-
|
|
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];
|
|
2950
2767
|
}
|
|
2951
2768
|
/**
|
|
2952
2769
|
* Pack global voice parameters (algorithm, feedback, LFO, etc.)
|
|
2953
2770
|
* @private
|
|
2954
2771
|
*/
|
|
2955
|
-
static _packGlobalParams(
|
|
2956
|
-
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2959
|
-
const
|
|
2960
|
-
|
|
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];
|
|
2961
2778
|
}
|
|
2962
2779
|
/**
|
|
2963
2780
|
* Copy voice name from unpacked to packed format
|
|
2964
2781
|
* @private
|
|
2965
2782
|
*/
|
|
2966
|
-
static _packName(
|
|
2967
|
-
for (let
|
|
2968
|
-
|
|
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];
|
|
2969
2786
|
}
|
|
2970
2787
|
/**
|
|
2971
2788
|
* Create a default/empty voice
|
|
2972
2789
|
* @param {number} index - Voice index
|
|
2973
2790
|
* @returns {DX7Voice}
|
|
2974
2791
|
*/
|
|
2975
|
-
static createDefault(
|
|
2976
|
-
const
|
|
2977
|
-
for (let
|
|
2978
|
-
const
|
|
2979
|
-
|
|
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;
|
|
2980
2797
|
}
|
|
2981
|
-
|
|
2982
|
-
const
|
|
2983
|
-
for (let
|
|
2984
|
-
|
|
2985
|
-
const
|
|
2986
|
-
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);
|
|
2987
2804
|
}
|
|
2988
2805
|
/**
|
|
2989
2806
|
* Create a voice from unpacked 169-byte data
|
|
@@ -2991,9 +2808,9 @@ class n {
|
|
|
2991
2808
|
* @param {number} index - Voice index
|
|
2992
2809
|
* @returns {DX7Voice}
|
|
2993
2810
|
*/
|
|
2994
|
-
static fromUnpacked(
|
|
2995
|
-
const
|
|
2996
|
-
return new n
|
|
2811
|
+
static fromUnpacked(e, i = 0) {
|
|
2812
|
+
const n = t.pack(e);
|
|
2813
|
+
return new t(n, i);
|
|
2997
2814
|
}
|
|
2998
2815
|
/**
|
|
2999
2816
|
* Load a DX7 voice from a single voice SYX file
|
|
@@ -3002,38 +2819,38 @@ class n {
|
|
|
3002
2819
|
* @throws {DX7ParseError} If file has invalid VCED header
|
|
3003
2820
|
* @throws {Error} If file cannot be read (FileReader error)
|
|
3004
2821
|
*/
|
|
3005
|
-
static async fromFile(
|
|
3006
|
-
return new Promise((
|
|
3007
|
-
const
|
|
3008
|
-
|
|
2822
|
+
static async fromFile(e) {
|
|
2823
|
+
return new Promise((i, n) => {
|
|
2824
|
+
const s = new FileReader();
|
|
2825
|
+
s.onload = (r) => {
|
|
3009
2826
|
try {
|
|
3010
|
-
const
|
|
3011
|
-
if (
|
|
3012
|
-
throw new
|
|
3013
|
-
const
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
),
|
|
3017
|
-
|
|
3018
|
-
`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.`
|
|
3019
2836
|
);
|
|
3020
|
-
const
|
|
2837
|
+
const u = new Uint8Array(t.UNPACKED_SIZE);
|
|
3021
2838
|
let a = 0;
|
|
3022
|
-
for (let h = 0; h <
|
|
3023
|
-
const N = (
|
|
3024
|
-
|
|
3025
|
-
const d =
|
|
3026
|
-
|
|
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++];
|
|
3027
2844
|
}
|
|
3028
|
-
|
|
3029
|
-
for (let h = 0; h <
|
|
3030
|
-
|
|
3031
|
-
const
|
|
3032
|
-
|
|
3033
|
-
} catch (
|
|
3034
|
-
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);
|
|
3035
2852
|
}
|
|
3036
|
-
},
|
|
2853
|
+
}, s.onerror = () => n(new Error("Failed to read file")), s.readAsArrayBuffer(e);
|
|
3037
2854
|
});
|
|
3038
2855
|
}
|
|
3039
2856
|
/**
|
|
@@ -3044,43 +2861,43 @@ class n {
|
|
|
3044
2861
|
* @throws {DX7ParseError} If VCED header is invalid
|
|
3045
2862
|
* @throws {DX7ValidationError} If data length is invalid
|
|
3046
2863
|
*/
|
|
3047
|
-
static fromSysEx(
|
|
3048
|
-
const
|
|
3049
|
-
let
|
|
3050
|
-
if (
|
|
3051
|
-
if (
|
|
3052
|
-
throw new
|
|
3053
|
-
|
|
3054
|
-
} else if (
|
|
3055
|
-
|
|
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;
|
|
3056
2873
|
else
|
|
3057
|
-
throw new
|
|
3058
|
-
`Invalid data length: expected ${
|
|
2874
|
+
throw new O(
|
|
2875
|
+
`Invalid data length: expected ${t.PACKED_SIZE} or ${t.VCED_SIZE} bytes, got ${n.length}`,
|
|
3059
2876
|
"length",
|
|
3060
|
-
|
|
2877
|
+
n.length
|
|
3061
2878
|
);
|
|
3062
|
-
if (
|
|
3063
|
-
throw new
|
|
3064
|
-
`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}`,
|
|
3065
2882
|
"length",
|
|
3066
|
-
|
|
2883
|
+
s.length
|
|
3067
2884
|
);
|
|
3068
|
-
if (
|
|
3069
|
-
const
|
|
3070
|
-
let
|
|
3071
|
-
for (let
|
|
3072
|
-
const S = (
|
|
3073
|
-
|
|
3074
|
-
const
|
|
3075
|
-
|
|
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++];
|
|
3076
2893
|
}
|
|
3077
|
-
|
|
3078
|
-
for (let
|
|
3079
|
-
|
|
3080
|
-
const
|
|
3081
|
-
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);
|
|
3082
2899
|
}
|
|
3083
|
-
return new
|
|
2900
|
+
return new t(s, i);
|
|
3084
2901
|
}
|
|
3085
2902
|
/**
|
|
3086
2903
|
* Create a DX7Voice from a JSON object
|
|
@@ -3089,27 +2906,27 @@ class n {
|
|
|
3089
2906
|
* @returns {DX7Voice}
|
|
3090
2907
|
* @throws {DX7ValidationError} If JSON structure is invalid
|
|
3091
2908
|
*/
|
|
3092
|
-
static fromJSON(
|
|
3093
|
-
if (!
|
|
3094
|
-
throw new
|
|
3095
|
-
const
|
|
3096
|
-
if (
|
|
3097
|
-
throw new
|
|
3098
|
-
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);
|
|
3099
2916
|
if (Number.isNaN(T))
|
|
3100
|
-
throw new
|
|
2917
|
+
throw new O(`Invalid parameter value for ${h}: ${A}`, h, A);
|
|
3101
2918
|
if (T < N || T > d)
|
|
3102
|
-
throw new
|
|
2919
|
+
throw new O(
|
|
3103
2920
|
`Parameter ${h} out of range: ${T} (must be ${N}-${d})`,
|
|
3104
2921
|
h,
|
|
3105
2922
|
T
|
|
3106
2923
|
);
|
|
3107
|
-
|
|
3108
|
-
},
|
|
3109
|
-
const
|
|
3110
|
-
return
|
|
3111
|
-
},
|
|
3112
|
-
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 = {
|
|
3113
2930
|
TRIANGLE: 0,
|
|
3114
2931
|
"SAW DOWN": 1,
|
|
3115
2932
|
"SAW UP": 2,
|
|
@@ -3117,113 +2934,113 @@ class n {
|
|
|
3117
2934
|
SINE: 4,
|
|
3118
2935
|
"SAMPLE & HOLD": 5
|
|
3119
2936
|
};
|
|
3120
|
-
return
|
|
3121
|
-
},
|
|
2937
|
+
return A[a] !== void 0 ? A[a] : 0;
|
|
2938
|
+
}, o = (a) => {
|
|
3122
2939
|
if (!a || typeof a != "string") return 60;
|
|
3123
|
-
const
|
|
3124
|
-
if (!
|
|
3125
|
-
const [, h, N] =
|
|
3126
|
-
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;
|
|
3127
2944
|
};
|
|
3128
|
-
if (!Array.isArray(
|
|
3129
|
-
throw new
|
|
3130
|
-
for (let a = 0; a <
|
|
3131
|
-
const
|
|
3132
|
-
if (!
|
|
3133
|
-
throw new
|
|
3134
|
-
if (!
|
|
3135
|
-
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(
|
|
3136
2953
|
`Invalid EG rates for operator ${a}`,
|
|
3137
2954
|
`operators[${a}].eg.rates`,
|
|
3138
|
-
|
|
2955
|
+
A.eg?.rates
|
|
3139
2956
|
);
|
|
3140
|
-
if (!
|
|
3141
|
-
throw new
|
|
2957
|
+
if (!A.eg || !Array.isArray(A.eg.levels) || A.eg.levels.length !== 4)
|
|
2958
|
+
throw new O(
|
|
3142
2959
|
`Invalid EG levels for operator ${a}`,
|
|
3143
2960
|
`operators[${a}].eg.levels`,
|
|
3144
|
-
|
|
2961
|
+
A.eg?.levels
|
|
3145
2962
|
);
|
|
3146
2963
|
}
|
|
3147
|
-
if (
|
|
3148
|
-
throw new
|
|
3149
|
-
`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`,
|
|
3150
2967
|
"operators",
|
|
3151
|
-
|
|
2968
|
+
e.operators
|
|
3152
2969
|
);
|
|
3153
|
-
for (let a = 0; a <
|
|
3154
|
-
const
|
|
3155
|
-
if (!
|
|
3156
|
-
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(
|
|
3157
2974
|
`Invalid EG rates for operator ${a}`,
|
|
3158
2975
|
`operators[${a}].eg.rates`,
|
|
3159
|
-
|
|
2976
|
+
A.eg?.rates
|
|
3160
2977
|
);
|
|
3161
|
-
if (
|
|
3162
|
-
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(
|
|
3163
2980
|
`Invalid EG levels for operator ${a}`,
|
|
3164
2981
|
`operators[${a}].eg.levels`,
|
|
3165
|
-
|
|
2982
|
+
A.eg?.levels
|
|
3166
2983
|
);
|
|
3167
|
-
|
|
3168
|
-
const N =
|
|
3169
|
-
|
|
3170
|
-
h +
|
|
3171
|
-
|
|
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,
|
|
3172
2989
|
`operators[${a}].scale.left.depth`,
|
|
3173
2990
|
0,
|
|
3174
2991
|
99
|
|
3175
|
-
),
|
|
3176
|
-
h +
|
|
3177
|
-
|
|
2992
|
+
), s(
|
|
2993
|
+
h + t.UNPACKED_OP_R_SCALE_DEPTH,
|
|
2994
|
+
A.scale?.right?.depth || 0,
|
|
3178
2995
|
`operators[${a}].scale.right.depth`,
|
|
3179
2996
|
0,
|
|
3180
2997
|
99
|
|
3181
|
-
),
|
|
3182
|
-
h +
|
|
3183
|
-
|
|
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,
|
|
3184
3001
|
`operators[${a}].key.scaling`,
|
|
3185
3002
|
0,
|
|
3186
3003
|
7
|
|
3187
3004
|
);
|
|
3188
|
-
const d = Number(
|
|
3189
|
-
|
|
3190
|
-
h +
|
|
3191
|
-
|
|
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,
|
|
3192
3009
|
`operators[${a}].output.ampModSens`,
|
|
3193
3010
|
0,
|
|
3194
3011
|
3
|
|
3195
|
-
),
|
|
3196
|
-
h +
|
|
3197
|
-
|
|
3012
|
+
), s(
|
|
3013
|
+
h + t.UNPACKED_OP_OUTPUT_LEVEL,
|
|
3014
|
+
A.output?.level || 0,
|
|
3198
3015
|
`operators[${a}].output.level`,
|
|
3199
3016
|
0,
|
|
3200
3017
|
99
|
|
3201
3018
|
);
|
|
3202
|
-
const T =
|
|
3203
|
-
|
|
3204
|
-
h +
|
|
3205
|
-
|
|
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,
|
|
3206
3023
|
`operators[${a}].key.velocity`,
|
|
3207
3024
|
0,
|
|
3208
3025
|
7
|
|
3209
3026
|
);
|
|
3210
3027
|
}
|
|
3211
|
-
if (!
|
|
3212
|
-
throw new
|
|
3213
|
-
if (!
|
|
3214
|
-
throw new
|
|
3215
|
-
if (
|
|
3216
|
-
throw new
|
|
3217
|
-
if (
|
|
3218
|
-
throw new
|
|
3219
|
-
const
|
|
3220
|
-
|
|
3221
|
-
const S = Number(
|
|
3222
|
-
|
|
3223
|
-
const
|
|
3224
|
-
for (let a = 0; a <
|
|
3225
|
-
|
|
3226
|
-
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);
|
|
3227
3044
|
}
|
|
3228
3045
|
/**
|
|
3229
3046
|
* Export voice to DX7 single voice SysEx format (VCED format)
|
|
@@ -3232,146 +3049,178 @@ class n {
|
|
|
3232
3049
|
* @returns {Uint8Array} Single voice SysEx data (163 bytes)
|
|
3233
3050
|
*/
|
|
3234
3051
|
toSysEx() {
|
|
3235
|
-
const
|
|
3236
|
-
let
|
|
3237
|
-
|
|
3238
|
-
for (let
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
const
|
|
3242
|
-
|
|
3243
|
-
}
|
|
3244
|
-
|
|
3245
|
-
for (let
|
|
3246
|
-
|
|
3247
|
-
const
|
|
3248
|
-
|
|
3249
|
-
|
|
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
|
|
3250
3067
|
);
|
|
3251
|
-
return
|
|
3068
|
+
return i[n++] = M._calculateChecksum(s, t.VCED_DATA_SIZE), i[n++] = t.VCED_SYSEX_END, i;
|
|
3252
3069
|
}
|
|
3253
3070
|
/**
|
|
3254
3071
|
* Convert voice to JSON format
|
|
3255
3072
|
* @returns {object} Voice data in JSON format
|
|
3256
3073
|
*/
|
|
3257
3074
|
toJSON() {
|
|
3258
|
-
const
|
|
3259
|
-
const
|
|
3260
|
-
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}`;
|
|
3261
3078
|
};
|
|
3262
|
-
for (let
|
|
3263
|
-
const
|
|
3264
|
-
|
|
3265
|
-
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,
|
|
3266
3083
|
osc: {
|
|
3267
|
-
detune:
|
|
3084
|
+
detune: e[o + t.UNPACKED_OP_OSC_DETUNE],
|
|
3268
3085
|
freq: {
|
|
3269
|
-
coarse:
|
|
3270
|
-
fine:
|
|
3271
|
-
mode:
|
|
3086
|
+
coarse: e[o + t.UNPACKED_OP_FREQ_COARSE],
|
|
3087
|
+
fine: e[o + t.UNPACKED_OP_FREQ_FINE],
|
|
3088
|
+
mode: l
|
|
3272
3089
|
}
|
|
3273
3090
|
},
|
|
3274
3091
|
eg: {
|
|
3275
3092
|
rates: [
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
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]
|
|
3280
3097
|
],
|
|
3281
3098
|
levels: [
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
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]
|
|
3286
3103
|
]
|
|
3287
3104
|
},
|
|
3288
3105
|
key: {
|
|
3289
|
-
velocity:
|
|
3290
|
-
scaling:
|
|
3291
|
-
breakPoint:
|
|
3292
|
-
|
|
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
|
|
3293
3110
|
)
|
|
3294
3111
|
},
|
|
3295
3112
|
output: {
|
|
3296
|
-
level:
|
|
3297
|
-
ampModSens:
|
|
3113
|
+
level: e[o + t.UNPACKED_OP_OUTPUT_LEVEL],
|
|
3114
|
+
ampModSens: e[o + t.UNPACKED_OP_AMP_MOD_SENS]
|
|
3298
3115
|
},
|
|
3299
3116
|
scale: {
|
|
3300
3117
|
left: {
|
|
3301
|
-
depth:
|
|
3302
|
-
curve:
|
|
3118
|
+
depth: e[o + t.UNPACKED_OP_L_SCALE_DEPTH],
|
|
3119
|
+
curve: n(e[o + t.UNPACKED_OP_L_CURVE])
|
|
3303
3120
|
},
|
|
3304
3121
|
right: {
|
|
3305
|
-
depth:
|
|
3306
|
-
curve:
|
|
3122
|
+
depth: e[o + t.UNPACKED_OP_R_SCALE_DEPTH],
|
|
3123
|
+
curve: n(e[o + t.UNPACKED_OP_R_CURVE])
|
|
3307
3124
|
}
|
|
3308
3125
|
}
|
|
3309
3126
|
});
|
|
3310
3127
|
}
|
|
3311
3128
|
return {
|
|
3312
3129
|
name: this.name || "(Empty)",
|
|
3313
|
-
operators:
|
|
3130
|
+
operators: i,
|
|
3314
3131
|
pitchEG: {
|
|
3315
3132
|
rates: [
|
|
3316
|
-
t
|
|
3317
|
-
t
|
|
3318
|
-
t
|
|
3319
|
-
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]
|
|
3320
3137
|
],
|
|
3321
3138
|
levels: [
|
|
3322
|
-
t
|
|
3323
|
-
t
|
|
3324
|
-
t
|
|
3325
|
-
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]
|
|
3326
3143
|
]
|
|
3327
3144
|
},
|
|
3328
3145
|
lfo: {
|
|
3329
|
-
speed: t
|
|
3330
|
-
delay: t
|
|
3331
|
-
pmDepth: t
|
|
3332
|
-
amDepth: t
|
|
3333
|
-
keySync: t
|
|
3334
|
-
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])
|
|
3335
3152
|
},
|
|
3336
3153
|
global: {
|
|
3337
|
-
algorithm: t
|
|
3338
|
-
feedback: t
|
|
3339
|
-
oscKeySync: t
|
|
3340
|
-
pitchModSens: t
|
|
3341
|
-
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
|
|
3342
3159
|
}
|
|
3343
3160
|
};
|
|
3344
3161
|
}
|
|
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
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
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 {
|
|
3375
3224
|
/**
|
|
3376
3225
|
* Create a DX7Bank instance. Can be initialized with SYX data or created empty.
|
|
3377
3226
|
* When data is provided, it's validated and parsed. When no data is provided,
|
|
@@ -3396,12 +3245,12 @@ class l {
|
|
|
3396
3245
|
* const voiceData = new Uint8Array(4096); // 32 voices × 128 bytes
|
|
3397
3246
|
* const bank = new DX7Bank(voiceData, "Raw Bank");
|
|
3398
3247
|
*/
|
|
3399
|
-
constructor(
|
|
3400
|
-
if (this.voices = new Array(
|
|
3401
|
-
this._load(
|
|
3248
|
+
constructor(e, i = "") {
|
|
3249
|
+
if (this.voices = new Array(C.NUM_VOICES), this.name = i, e)
|
|
3250
|
+
this._load(e);
|
|
3402
3251
|
else
|
|
3403
|
-
for (let
|
|
3404
|
-
this.voices[
|
|
3252
|
+
for (let n = 0; n < C.NUM_VOICES; n++)
|
|
3253
|
+
this.voices[n] = K.createDefault(n);
|
|
3405
3254
|
}
|
|
3406
3255
|
/**
|
|
3407
3256
|
* Calculate DX7 SysEx checksum
|
|
@@ -3410,55 +3259,55 @@ class l {
|
|
|
3410
3259
|
* @param {number} size - Number of bytes
|
|
3411
3260
|
* @returns {number} Checksum byte
|
|
3412
3261
|
*/
|
|
3413
|
-
static _calculateChecksum(
|
|
3414
|
-
let
|
|
3415
|
-
for (let
|
|
3416
|
-
|
|
3417
|
-
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;
|
|
3418
3267
|
}
|
|
3419
3268
|
/**
|
|
3420
3269
|
* Load and validate bank data
|
|
3421
3270
|
* @private
|
|
3422
3271
|
* @param {Array<number>|ArrayBuffer|Uint8Array} data
|
|
3423
3272
|
*/
|
|
3424
|
-
_load(
|
|
3425
|
-
const
|
|
3426
|
-
let
|
|
3427
|
-
if (
|
|
3428
|
-
const
|
|
3429
|
-
for (let
|
|
3430
|
-
if (
|
|
3431
|
-
throw new
|
|
3432
|
-
`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)}`,
|
|
3433
3282
|
"header",
|
|
3434
|
-
|
|
3283
|
+
l
|
|
3435
3284
|
);
|
|
3436
|
-
|
|
3437
|
-
} else if (
|
|
3438
|
-
|
|
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;
|
|
3439
3288
|
else
|
|
3440
|
-
throw new
|
|
3441
|
-
`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}`,
|
|
3442
3291
|
"length",
|
|
3443
|
-
|
|
3292
|
+
i.length
|
|
3444
3293
|
);
|
|
3445
|
-
if (
|
|
3446
|
-
throw new
|
|
3447
|
-
`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}`,
|
|
3448
3297
|
"length",
|
|
3449
|
-
|
|
3298
|
+
n.length
|
|
3450
3299
|
);
|
|
3451
|
-
const
|
|
3452
|
-
if (
|
|
3453
|
-
const
|
|
3454
|
-
|
|
3455
|
-
`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.`
|
|
3456
3305
|
);
|
|
3457
3306
|
}
|
|
3458
|
-
this.voices = new Array(
|
|
3459
|
-
for (let
|
|
3460
|
-
const
|
|
3461
|
-
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);
|
|
3462
3311
|
}
|
|
3463
3312
|
}
|
|
3464
3313
|
/**
|
|
@@ -3484,22 +3333,22 @@ class l {
|
|
|
3484
3333
|
* const voiceFromBank2 = bank2.getVoice(5);
|
|
3485
3334
|
* bank1.replaceVoice(0, voiceFromBank2); // Copy voice from bank2 to bank1
|
|
3486
3335
|
*/
|
|
3487
|
-
replaceVoice(
|
|
3488
|
-
if (
|
|
3489
|
-
throw new
|
|
3490
|
-
const
|
|
3491
|
-
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);
|
|
3492
3341
|
}
|
|
3493
3342
|
/**
|
|
3494
3343
|
* Add a voice to the first empty slot
|
|
3495
3344
|
* @param {DX7Voice} voice - Voice to add
|
|
3496
3345
|
* @returns {number} Index where voice was added, or -1 if bank is full
|
|
3497
3346
|
*/
|
|
3498
|
-
addVoice(
|
|
3499
|
-
for (let
|
|
3500
|
-
const
|
|
3501
|
-
if (
|
|
3502
|
-
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;
|
|
3503
3352
|
}
|
|
3504
3353
|
return -1;
|
|
3505
3354
|
}
|
|
@@ -3515,24 +3364,24 @@ class l {
|
|
|
3515
3364
|
* @param {number} index - Voice index (0-31)
|
|
3516
3365
|
* @returns {DX7Voice|null}
|
|
3517
3366
|
*/
|
|
3518
|
-
getVoice(
|
|
3519
|
-
return
|
|
3367
|
+
getVoice(e) {
|
|
3368
|
+
return e < 0 || e >= this.voices.length ? null : this.voices[e];
|
|
3520
3369
|
}
|
|
3521
3370
|
/**
|
|
3522
3371
|
* Get all voice names
|
|
3523
3372
|
* @returns {string[]}
|
|
3524
3373
|
*/
|
|
3525
3374
|
getVoiceNames() {
|
|
3526
|
-
return this.voices.map((
|
|
3375
|
+
return this.voices.map((e) => e.name);
|
|
3527
3376
|
}
|
|
3528
3377
|
/**
|
|
3529
3378
|
* Find a voice by name (case-insensitive, partial match)
|
|
3530
3379
|
* @param {string} name - Voice name to search for
|
|
3531
3380
|
* @returns {DX7Voice|null}
|
|
3532
3381
|
*/
|
|
3533
|
-
findVoiceByName(
|
|
3534
|
-
const
|
|
3535
|
-
return this.voices.find((
|
|
3382
|
+
findVoiceByName(e) {
|
|
3383
|
+
const i = e.toLowerCase();
|
|
3384
|
+
return this.voices.find((n) => n.name.toLowerCase().includes(i)) || null;
|
|
3536
3385
|
}
|
|
3537
3386
|
/**
|
|
3538
3387
|
* Load a DX7 bank from a file
|
|
@@ -3542,22 +3391,22 @@ class l {
|
|
|
3542
3391
|
* @throws {DX7ValidationError} If data is not valid DX7 SYX format
|
|
3543
3392
|
* @throws {Error} If file cannot be read (FileReader error)
|
|
3544
3393
|
*/
|
|
3545
|
-
static async fromFile(
|
|
3546
|
-
return new Promise((
|
|
3547
|
-
const
|
|
3548
|
-
|
|
3394
|
+
static async fromFile(e) {
|
|
3395
|
+
return new Promise((i, n) => {
|
|
3396
|
+
const s = new FileReader();
|
|
3397
|
+
s.onload = async (r) => {
|
|
3549
3398
|
try {
|
|
3550
|
-
const
|
|
3551
|
-
if (
|
|
3552
|
-
|
|
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));
|
|
3553
3402
|
else {
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3403
|
+
const l = E.replace(/\.[^/.]+$/, ""), S = new C(r.target.result, l);
|
|
3404
|
+
i(S);
|
|
3556
3405
|
}
|
|
3557
|
-
} catch (
|
|
3558
|
-
E
|
|
3406
|
+
} catch (E) {
|
|
3407
|
+
n(E);
|
|
3559
3408
|
}
|
|
3560
|
-
},
|
|
3409
|
+
}, s.onerror = () => n(new Error("Failed to read file")), s.readAsArrayBuffer(e);
|
|
3561
3410
|
});
|
|
3562
3411
|
}
|
|
3563
3412
|
/**
|
|
@@ -3568,8 +3417,8 @@ class l {
|
|
|
3568
3417
|
* @throws {DX7ParseError} If SysEx header is invalid
|
|
3569
3418
|
* @throws {DX7ValidationError} If data length is invalid
|
|
3570
3419
|
*/
|
|
3571
|
-
static fromSysEx(
|
|
3572
|
-
return new
|
|
3420
|
+
static fromSysEx(e, i = "") {
|
|
3421
|
+
return new C(e, i);
|
|
3573
3422
|
}
|
|
3574
3423
|
/**
|
|
3575
3424
|
* Create a DX7Bank from a JSON object
|
|
@@ -3577,225 +3426,240 @@ class l {
|
|
|
3577
3426
|
* @returns {DX7Bank}
|
|
3578
3427
|
* @throws {DX7ValidationError} If JSON structure is invalid
|
|
3579
3428
|
*/
|
|
3580
|
-
static fromJSON(
|
|
3581
|
-
if (!
|
|
3582
|
-
throw new
|
|
3583
|
-
const
|
|
3584
|
-
if (
|
|
3585
|
-
throw new
|
|
3586
|
-
|
|
3587
|
-
`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.`
|
|
3588
3437
|
);
|
|
3589
|
-
const
|
|
3590
|
-
for (let
|
|
3591
|
-
const
|
|
3592
|
-
if (!
|
|
3593
|
-
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`);
|
|
3594
3443
|
continue;
|
|
3595
3444
|
}
|
|
3596
3445
|
try {
|
|
3597
|
-
const { index:
|
|
3598
|
-
|
|
3599
|
-
} catch (
|
|
3600
|
-
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`);
|
|
3601
3450
|
}
|
|
3602
3451
|
}
|
|
3603
|
-
return
|
|
3452
|
+
return i;
|
|
3604
3453
|
}
|
|
3605
3454
|
/**
|
|
3606
3455
|
* Export bank to SysEx format
|
|
3607
3456
|
* @returns {Uint8Array} Full SysEx data (4104 bytes)
|
|
3608
3457
|
*/
|
|
3609
3458
|
toSysEx() {
|
|
3610
|
-
const
|
|
3611
|
-
let
|
|
3612
|
-
|
|
3613
|
-
|
|
3459
|
+
const e = new Uint8Array(C.SYSEX_SIZE);
|
|
3460
|
+
let i = 0;
|
|
3461
|
+
C.SYSEX_HEADER.forEach((s) => {
|
|
3462
|
+
e[i++] = s;
|
|
3614
3463
|
});
|
|
3615
|
-
for (const
|
|
3616
|
-
for (let
|
|
3617
|
-
|
|
3618
|
-
const
|
|
3619
|
-
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;
|
|
3620
3469
|
}
|
|
3621
3470
|
/**
|
|
3622
3471
|
* Convert bank to JSON format
|
|
3623
3472
|
* @returns {object} Bank data in JSON format
|
|
3624
3473
|
*/
|
|
3625
3474
|
toJSON() {
|
|
3626
|
-
const
|
|
3627
|
-
const
|
|
3475
|
+
const e = this.voices.map((i, n) => {
|
|
3476
|
+
const s = i.toJSON();
|
|
3628
3477
|
return {
|
|
3629
|
-
index:
|
|
3630
|
-
...
|
|
3478
|
+
index: n + 1,
|
|
3479
|
+
...s
|
|
3631
3480
|
};
|
|
3632
3481
|
});
|
|
3633
3482
|
return {
|
|
3634
3483
|
version: "1.0",
|
|
3635
3484
|
name: this.name || "",
|
|
3636
|
-
voices:
|
|
3485
|
+
voices: e
|
|
3637
3486
|
};
|
|
3638
3487
|
}
|
|
3639
|
-
}
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
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
|
|
3645
3509
|
};
|
|
3646
3510
|
}
|
|
3647
|
-
function
|
|
3648
|
-
return [240,
|
|
3511
|
+
function _t(c, e) {
|
|
3512
|
+
return [240, c, ...e, 247];
|
|
3649
3513
|
}
|
|
3650
|
-
function
|
|
3651
|
-
return
|
|
3514
|
+
function at(c) {
|
|
3515
|
+
return c.length >= 2 && c[0] === 240 && c[c.length - 1] === 247;
|
|
3652
3516
|
}
|
|
3653
|
-
function
|
|
3654
|
-
const
|
|
3655
|
-
for (let
|
|
3656
|
-
const
|
|
3657
|
-
let
|
|
3658
|
-
const
|
|
3659
|
-
for (let
|
|
3660
|
-
const
|
|
3661
|
-
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
}
|
|
3665
|
-
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;
|
|
3666
3530
|
}
|
|
3667
|
-
function
|
|
3668
|
-
const
|
|
3669
|
-
for (let
|
|
3670
|
-
const
|
|
3671
|
-
for (let
|
|
3672
|
-
let
|
|
3673
|
-
|
|
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);
|
|
3674
3538
|
}
|
|
3675
3539
|
}
|
|
3676
|
-
return
|
|
3540
|
+
return e;
|
|
3677
3541
|
}
|
|
3678
|
-
function
|
|
3679
|
-
return Number.isInteger(
|
|
3542
|
+
function Ct(c) {
|
|
3543
|
+
return Number.isInteger(c) && c >= 1 && c <= 16;
|
|
3680
3544
|
}
|
|
3681
|
-
function
|
|
3682
|
-
return Number.isInteger(
|
|
3545
|
+
function Pt(c) {
|
|
3546
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3683
3547
|
}
|
|
3684
|
-
function
|
|
3685
|
-
return Number.isInteger(
|
|
3548
|
+
function ut(c) {
|
|
3549
|
+
return Number.isInteger(c) && c >= 0 && c <= 31;
|
|
3686
3550
|
}
|
|
3687
|
-
function
|
|
3688
|
-
return Number.isInteger(
|
|
3551
|
+
function lt(c) {
|
|
3552
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3689
3553
|
}
|
|
3690
|
-
function
|
|
3691
|
-
return Number.isInteger(
|
|
3554
|
+
function ht(c) {
|
|
3555
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3692
3556
|
}
|
|
3693
|
-
function
|
|
3694
|
-
return Number.isInteger(
|
|
3557
|
+
function St(c) {
|
|
3558
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3695
3559
|
}
|
|
3696
|
-
function
|
|
3697
|
-
return Number.isInteger(
|
|
3560
|
+
function Nt(c) {
|
|
3561
|
+
return Number.isInteger(c) && c >= 0 && c <= 127;
|
|
3698
3562
|
}
|
|
3699
|
-
function
|
|
3700
|
-
return Number.isInteger(
|
|
3563
|
+
function Dt(c) {
|
|
3564
|
+
return Number.isInteger(c) && c >= 0 && c <= 16383;
|
|
3701
3565
|
}
|
|
3702
|
-
function
|
|
3703
|
-
return Number.isInteger(
|
|
3566
|
+
function Ot(c, e) {
|
|
3567
|
+
return Number.isInteger(c) && c >= 0 && c <= 127 && Number.isInteger(e) && e >= 0 && e <= 127;
|
|
3704
3568
|
}
|
|
3705
|
-
async function
|
|
3569
|
+
async function dt(c = {}) {
|
|
3706
3570
|
const {
|
|
3707
|
-
onStatusUpdate:
|
|
3708
|
-
onConnectionUpdate:
|
|
3709
|
-
inputChannel:
|
|
3710
|
-
outputChannel:
|
|
3711
|
-
output:
|
|
3712
|
-
sysex:
|
|
3713
|
-
onReady:
|
|
3714
|
-
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,
|
|
3715
3579
|
selector: S,
|
|
3716
|
-
watchDOM:
|
|
3580
|
+
watchDOM: u,
|
|
3717
3581
|
...a
|
|
3718
|
-
} =
|
|
3582
|
+
} = c, h = await j({
|
|
3719
3583
|
autoConnect: !1,
|
|
3720
|
-
sysex:
|
|
3721
|
-
inputChannel:
|
|
3722
|
-
outputChannel:
|
|
3584
|
+
sysex: E,
|
|
3585
|
+
inputChannel: n,
|
|
3586
|
+
outputChannel: s,
|
|
3723
3587
|
selector: S || "[data-midi-cc]",
|
|
3724
|
-
watchDOM:
|
|
3725
|
-
onError:
|
|
3588
|
+
watchDOM: u,
|
|
3589
|
+
onError: l,
|
|
3726
3590
|
...a
|
|
3727
|
-
}), N = new
|
|
3591
|
+
}), N = new k({
|
|
3728
3592
|
midiController: h,
|
|
3729
|
-
onStatusUpdate:
|
|
3593
|
+
onStatusUpdate: e || (() => {
|
|
3730
3594
|
}),
|
|
3731
|
-
onConnectionUpdate:
|
|
3595
|
+
onConnectionUpdate: i || (() => {
|
|
3732
3596
|
}),
|
|
3733
|
-
channel:
|
|
3597
|
+
channel: s
|
|
3734
3598
|
});
|
|
3735
|
-
if (
|
|
3599
|
+
if (r)
|
|
3736
3600
|
try {
|
|
3737
|
-
await h.device.connectOutput(
|
|
3601
|
+
await h.device.connectOutput(r), N.currentOutput = h.device.getCurrentOutput(), N.updateConnectionStatus();
|
|
3738
3602
|
} catch (d) {
|
|
3739
|
-
|
|
3603
|
+
l ? l(d) : console.error("Failed to connect to MIDI device:", d.message);
|
|
3740
3604
|
}
|
|
3741
|
-
return
|
|
3605
|
+
return o && o(h, N), N;
|
|
3742
3606
|
}
|
|
3743
|
-
async function
|
|
3744
|
-
const
|
|
3745
|
-
await
|
|
3746
|
-
const
|
|
3607
|
+
async function j(c = {}) {
|
|
3608
|
+
const e = new J(c);
|
|
3609
|
+
await e.init();
|
|
3610
|
+
const i = c.selector || "[data-midi-cc]";
|
|
3747
3611
|
{
|
|
3748
|
-
const
|
|
3749
|
-
|
|
3612
|
+
const n = new $(e, i);
|
|
3613
|
+
n.bindAll(), c.watchDOM && n.enableAutoBinding(), e._binder = n;
|
|
3750
3614
|
}
|
|
3751
|
-
return
|
|
3615
|
+
return e;
|
|
3752
3616
|
}
|
|
3753
3617
|
export {
|
|
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
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
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
|
|
3801
3665
|
};
|