@marmooo/midy 0.1.3 → 0.1.5

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.
Files changed (35) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.4 → soundfont-parser@0.0.6}/+esm.d.ts +16 -20
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +180 -0
  4. package/esm/midy-GM1.d.ts +32 -3
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +93 -87
  7. package/esm/midy-GM2.d.ts +56 -3
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +142 -126
  10. package/esm/midy-GMLite.d.ts +32 -3
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +91 -85
  13. package/esm/midy.d.ts +87 -4
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +234 -140
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.4 → soundfont-parser@0.0.6}/+esm.d.ts +16 -20
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +190 -0
  20. package/script/midy-GM1.d.ts +32 -3
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +93 -87
  23. package/script/midy-GM2.d.ts +56 -3
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +142 -126
  26. package/script/midy-GMLite.d.ts +32 -3
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +91 -85
  29. package/script/midy.d.ts +87 -4
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +234 -140
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +0 -1
  33. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js +0 -162
  34. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +0 -1
  35. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js +0 -169
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultInstrumentZone = exports.SoundFont = exports.RangeGeneratorNames = void 0;
4
+ exports.createInstrumentGeneratorObject = S;
5
+ exports.createPresetGeneratorObject = V;
6
+ exports.isRangeGenerator = L;
7
+ exports.parse = p;
8
+ exports.timecentToSecond = H;
9
+ /**
10
+ * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
11
+ * Original file: /npm/@marmooo/soundfont-parser@0.0.6/esm/mod.js
12
+ *
13
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
14
+ */
15
+ class e {
16
+ constructor(e, t) { Object.defineProperty(this, "data", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "offset", { enumerable: !0, configurable: !0, writable: !0, value: t }); }
17
+ readString(e) { const t = this.data.subarray(this.offset, this.offset += e), n = String.fromCharCode(...Array.from(t)), r = n.indexOf("\0"); return r > 0 ? n.substring(0, r) : n; }
18
+ readWORD() { return this.data[this.offset++] | this.data[this.offset++] << 8; }
19
+ readDWORD(e = !1) { return e ? (this.data[this.offset++] << 24 | this.data[this.offset++] << 16 | this.data[this.offset++] << 8 | this.data[this.offset++]) >>> 0 : (this.data[this.offset++] | this.data[this.offset++] << 8 | this.data[this.offset++] << 16 | this.data[this.offset++] << 24) >>> 0; }
20
+ readByte() { return this.data[this.offset++]; }
21
+ readAt(e) { return this.data[this.offset + e]; }
22
+ readUInt8() { return this.readByte(); }
23
+ readInt8() { return this.readByte() << 24 >> 24; }
24
+ readUInt16() { return this.readWORD(); }
25
+ readInt16() { return this.readWORD() << 16 >> 16; }
26
+ readUInt32() { return this.readDWORD(); }
27
+ }
28
+ function t(t, n, a) { const o = new e(t, n), s = o.readString(4), i = o.readDWORD(a); return new r(s, i, o.offset); }
29
+ function n(e, n = 0, r, { padding: a = !0, bigEndian: o = !1 } = {}) { const s = [], i = r + n; let l = n; for (; l < i;) {
30
+ const r = t(e, l, o);
31
+ l = r.offset + r.size, a && 1 == (l - n & 1) && l++, s.push(r);
32
+ } return s; }
33
+ class r {
34
+ constructor(e, t, n) { Object.defineProperty(this, "type", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "size", { enumerable: !0, configurable: !0, writable: !0, value: t }), Object.defineProperty(this, "offset", { enumerable: !0, configurable: !0, writable: !0, value: n }); }
35
+ }
36
+ const a = ["startAddrsOffset", "endAddrsOffset", "startloopAddrsOffset", "endloopAddrsOffset", "startAddrsCoarseOffset", "modLfoToPitch", "vibLfoToPitch", "modEnvToPitch", "initialFilterFc", "initialFilterQ", "modLfoToFilterFc", "modEnvToFilterFc", "endAddrsCoarseOffset", "modLfoToVolume", void 0, "chorusEffectsSend", "reverbEffectsSend", "pan", void 0, void 0, void 0, "delayModLFO", "freqModLFO", "delayVibLFO", "freqVibLFO", "delayModEnv", "attackModEnv", "holdModEnv", "decayModEnv", "sustainModEnv", "releaseModEnv", "keynumToModEnvHold", "keynumToModEnvDecay", "delayVolEnv", "attackVolEnv", "holdVolEnv", "decayVolEnv", "sustainVolEnv", "releaseVolEnv", "keynumToVolEnvHold", "keynumToVolEnvDecay", "instrument", void 0, "keyRange", "velRange", "startloopAddrsCoarseOffset", "keynum", "velocity", "initialAttenuation", void 0, "endloopAddrsCoarseOffset", "coarseTune", "fineTune", "sampleID", "sampleModes", void 0, "scaleTuning", "exclusiveClass", "overridingRootKey"];
37
+ class o {
38
+ constructor(e, t) { Object.defineProperty(this, "major", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "minor", { enumerable: !0, configurable: !0, writable: !0, value: t }); }
39
+ static parse(e) { const t = e.readInt8(), n = e.readInt8(); return new o(t, n); }
40
+ }
41
+ class s {
42
+ constructor(e, t, n, r, a, o, s, i, l, d, u) { Object.defineProperty(this, "comment", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "copyright", { enumerable: !0, configurable: !0, writable: !0, value: t }), Object.defineProperty(this, "creationDate", { enumerable: !0, configurable: !0, writable: !0, value: n }), Object.defineProperty(this, "engineer", { enumerable: !0, configurable: !0, writable: !0, value: r }), Object.defineProperty(this, "name", { enumerable: !0, configurable: !0, writable: !0, value: a }), Object.defineProperty(this, "product", { enumerable: !0, configurable: !0, writable: !0, value: o }), Object.defineProperty(this, "software", { enumerable: !0, configurable: !0, writable: !0, value: s }), Object.defineProperty(this, "version", { enumerable: !0, configurable: !0, writable: !0, value: i }), Object.defineProperty(this, "soundEngine", { enumerable: !0, configurable: !0, writable: !0, value: l }), Object.defineProperty(this, "romName", { enumerable: !0, configurable: !0, writable: !0, value: d }), Object.defineProperty(this, "romVersion", { enumerable: !0, configurable: !0, writable: !0, value: u }); }
43
+ static parse(t, n) { function r(e) { for (let t = 0; t < n.length; t++)
44
+ if (n[t].type === e)
45
+ return n[t]; } function a(n) { return new e(t, n.offset); } function i(e) { const t = r(e); return t ? a(t).readString(t.size) : null; } function l(e) { const t = r(e); return t ? o.parse(a(t)) : null; } const d = i("ICMT"), u = i("ICOP"), f = i("ICRD"), c = i("IENG"), b = i("INAM"), m = i("IPRD"), p = i("ISFT"), h = l("ifil"), v = i("isng"), g = i("irom"), w = l("iver"); return new s(d, u, f, c, b, m, p, h, v, g, w); }
46
+ }
47
+ class i {
48
+ constructor(e, t) { Object.defineProperty(this, "generatorIndex", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "modulatorIndex", { enumerable: !0, configurable: !0, writable: !0, value: t }); }
49
+ static parse(e) { const t = e.readWORD(), n = e.readWORD(); return new i(t, n); }
50
+ }
51
+ class l {
52
+ constructor(e, t, n, r, a, o, s) { Object.defineProperty(this, "presetName", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "preset", { enumerable: !0, configurable: !0, writable: !0, value: t }), Object.defineProperty(this, "bank", { enumerable: !0, configurable: !0, writable: !0, value: n }), Object.defineProperty(this, "presetBagIndex", { enumerable: !0, configurable: !0, writable: !0, value: r }), Object.defineProperty(this, "library", { enumerable: !0, configurable: !0, writable: !0, value: a }), Object.defineProperty(this, "genre", { enumerable: !0, configurable: !0, writable: !0, value: o }), Object.defineProperty(this, "morphology", { enumerable: !0, configurable: !0, writable: !0, value: s }); }
53
+ get isEnd() { return "EOP" === this.presetName; }
54
+ static parse(e) { const t = e.readString(20), n = e.readWORD(), r = e.readWORD(), a = e.readWORD(), o = e.readDWORD(), s = e.readDWORD(), i = e.readDWORD(); return new l(t, n, r, a, o, s, i); }
55
+ }
56
+ class d {
57
+ constructor(e, t) { Object.defineProperty(this, "lo", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), Object.defineProperty(this, "hi", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), this.lo = e, this.hi = t; }
58
+ in(e) { return this.lo <= e && e <= this.hi; }
59
+ static parse(e) { const t = e.readByte(), n = e.readByte(); return new d(t, n); }
60
+ }
61
+ class u {
62
+ constructor(e, t, n, r, a) { Object.defineProperty(this, "sourceOper", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "destinationOper", { enumerable: !0, configurable: !0, writable: !0, value: t }), Object.defineProperty(this, "value", { enumerable: !0, configurable: !0, writable: !0, value: n }), Object.defineProperty(this, "amountSourceOper", { enumerable: !0, configurable: !0, writable: !0, value: r }), Object.defineProperty(this, "transOper", { enumerable: !0, configurable: !0, writable: !0, value: a }); }
63
+ get isEnd() { return 0 === this.sourceOper && 0 === this.destinationOper && 0 === this.value && 0 === this.amountSourceOper && 0 === this.transOper; }
64
+ static parse(e) { const t = e.readWORD(), n = e.readWORD(), r = e.readInt16(), a = e.readWORD(), o = e.readWORD(); return new u(t, n, r, a, o); }
65
+ }
66
+ class f {
67
+ constructor(e, t) { Object.defineProperty(this, "code", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "value", { enumerable: !0, configurable: !0, writable: !0, value: t }); }
68
+ get type() { return a[this.code]; }
69
+ get isEnd() { return 0 === this.code && 0 === this.value; }
70
+ static parse(e) { const t = e.readWORD(); let n; switch (a[t]) {
71
+ case "keyRange":
72
+ case "velRange":
73
+ n = d.parse(e);
74
+ break;
75
+ default: n = e.readInt16();
76
+ } return new f(t, n); }
77
+ }
78
+ class c {
79
+ constructor() { Object.defineProperty(this, "instrumentName", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), Object.defineProperty(this, "instrumentBagIndex", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }); }
80
+ get isEnd() { return "EOI" === this.instrumentName; }
81
+ static parse(e) { const t = new c; return t.instrumentName = e.readString(20), t.instrumentBagIndex = e.readWORD(), t; }
82
+ }
83
+ class b {
84
+ constructor(e, t, n, r, a, o, s, i, l, d) { Object.defineProperty(this, "sampleName", { enumerable: !0, configurable: !0, writable: !0, value: e }), Object.defineProperty(this, "start", { enumerable: !0, configurable: !0, writable: !0, value: t }), Object.defineProperty(this, "end", { enumerable: !0, configurable: !0, writable: !0, value: n }), Object.defineProperty(this, "loopStart", { enumerable: !0, configurable: !0, writable: !0, value: r }), Object.defineProperty(this, "loopEnd", { enumerable: !0, configurable: !0, writable: !0, value: a }), Object.defineProperty(this, "sampleRate", { enumerable: !0, configurable: !0, writable: !0, value: o }), Object.defineProperty(this, "originalPitch", { enumerable: !0, configurable: !0, writable: !0, value: s }), Object.defineProperty(this, "pitchCorrection", { enumerable: !0, configurable: !0, writable: !0, value: i }), Object.defineProperty(this, "sampleLink", { enumerable: !0, configurable: !0, writable: !0, value: l }), Object.defineProperty(this, "sampleType", { enumerable: !0, configurable: !0, writable: !0, value: d }); }
85
+ get isEnd() { return "EOS" === this.sampleName; }
86
+ static parse(e, t) { const n = e.readString(20), r = e.readDWORD(), a = e.readDWORD(); let o = e.readDWORD(), s = e.readDWORD(); const i = e.readDWORD(), l = e.readByte(), d = e.readInt8(), u = e.readWORD(), f = e.readWORD(); return t || (o -= r, s -= r), new b(n, r, a, o, s, i, l, d, u, f); }
87
+ }
88
+ class m {
89
+ constructor(e, t, n) { Object.defineProperty(this, "min", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), Object.defineProperty(this, "max", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), Object.defineProperty(this, "value", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), this.min = e, this.value = t, this.max = n; }
90
+ clamp() { return Math.max(this.min, Math.min(this.value, this.max)); }
91
+ }
92
+ function p(e, r = {}) { const a = n(e, 0, e.length, r); if (1 !== a.length)
93
+ throw new Error("wrong chunk length"); const o = a[0]; if (null === o)
94
+ throw new Error("chunk not found"); function i(e, t, n) { const r = h(e, t, "LIST", "pdta"); if (9 !== r.length)
95
+ throw new Error("invalid pdta chunk"); return { presetHeaders: w(r[0], t), presetZone: y(r[1], t), presetModulators: P(r[2], t), presetGenerators: R(r[3], t), instruments: O(r[4], t), instrumentZone: E(r[5], t), instrumentModulators: D(r[6], t), instrumentGenerators: j(r[7], t), sampleHeaders: k(r[8], t, n) }; } const l = function (e, n, r = {}) { const a = h(e, n, "RIFF", "sfbk", r); if (3 !== a.length)
96
+ throw new Error("invalid sfbk structure"); const o = function (e, t) { const n = h(e, t, "LIST", "INFO"); return s.parse(t, n); }(a[0], n), l = 3 === o.version.major; return l && "LIST" !== a[2].type && (a[2] = t(n, a[2].offset - 9, !1)), { info: o, samplingData: v(a[1], n), ...i(a[2], n, l) }; }(o, e, r), d = 3 === l.info.version.major; return { ...l, samples: T(l.sampleHeaders, l.samplingData.offsetMSB, l.samplingData.offsetLSB, e, d) }; }
97
+ function h(t, r, a, o, s = {}) { if (t.type !== a)
98
+ throw new Error("invalid chunk type:" + t.type); const i = new e(r, t.offset), l = i.readString(4); if (l !== o)
99
+ throw new Error("invalid signature:" + l); return n(r, i.offset, t.size - 4, s); }
100
+ function v(e, t) { const n = h(e, t, "LIST", "sdta"); return { offsetMSB: n[0].offset, offsetLSB: n[1]?.offset }; }
101
+ function g(t, n, r, a, o, s) { const i = []; if (t.type !== r)
102
+ throw new Error("invalid chunk type:" + t.type); const l = new e(n, t.offset), d = t.offset + t.size; for (; l.offset < d;) {
103
+ const e = a.parse(l, s);
104
+ if (o && o(e))
105
+ break;
106
+ i.push(e);
107
+ } return i; }
108
+ const w = (e, t) => g(e, t, "phdr", l, (e => e.isEnd)), y = (e, t) => g(e, t, "pbag", i), O = (e, t) => g(e, t, "inst", c, (e => e.isEnd)), E = (e, t) => g(e, t, "ibag", i), P = (e, t) => g(e, t, "pmod", u, (e => e.isEnd)), D = (e, t) => g(e, t, "imod", u, (e => e.isEnd)), R = (e, t) => g(e, t, "pgen", f, (e => e.isEnd)), j = (e, t) => g(e, t, "igen", f), k = (e, t, n) => g(e, t, "shdr", b, (e => e.isEnd), n);
109
+ function T(e, t, n, r, a) { return e.map((e => { let { start: n, end: o } = e; return a || (n *= 2, o *= 2), new Uint8Array(r.subarray(t + n, t + o)); })); }
110
+ const I = new Map(a.map(((e, t) => [e, t]))), M = ["keyRange", "velRange"], F = new Set(["startAddrsOffset", "endAddrsOffset", "startloopAddrsOffset", "endloopAddrsOffset", "startAddrsCoarseOffset", "endAddrsCoarseOffset", "startloopAddrsCoarseOffset", "endloopAddrsCoarseOffset", "sampleModes", "exclusiveClass", "overridingRootKey", "keynum", "velocity"].map((e => I.get(e)))), A = [["keynum", "keyRange"], ["velocity", "velRange"]];
111
+ exports.RangeGeneratorNames = M;
112
+ function L(e) { return M.includes(e); }
113
+ function V(e) { const t = {}; for (let n = 0; n < e.length; n++) {
114
+ const r = e[n], a = r.type;
115
+ if (void 0 !== a && !F.has(r.code))
116
+ if (L(a))
117
+ t[a] = r.value;
118
+ else {
119
+ const e = a, n = W[e];
120
+ t[e] = new m(n.min, r.value, n.max);
121
+ }
122
+ } return t; }
123
+ function S(e) { const t = {}; for (let n = 0; n < e.length; n++) {
124
+ const r = e[n], a = r.type;
125
+ if (void 0 !== a)
126
+ if (L(a))
127
+ t[a] = r.value;
128
+ else {
129
+ const e = a, n = W[e];
130
+ t[e] = new m(n.min, r.value, n.max);
131
+ }
132
+ } for (let e = 0; e < A.length; e++) {
133
+ const [n, r] = A[e], a = t[n];
134
+ a instanceof m && 0 <= a.value && (t[r] = new d(a.value, a.value));
135
+ } return t; }
136
+ const C = -32768, x = 32767, W = { startAddrsOffset: new m(0, 0, x), endAddrsOffset: new m(C, 0, 0), startloopAddrsOffset: new m(C, 0, x), endloopAddrsOffset: new m(C, 0, x), startAddrsCoarseOffset: new m(0, 0, x), modLfoToPitch: new m(-12e3, 0, 12e3), vibLfoToPitch: new m(-12e3, 0, 12e3), modEnvToPitch: new m(-12e3, 0, 12e3), initialFilterFc: new m(1500, 13500, 13500), initialFilterQ: new m(0, 0, 960), modLfoToFilterFc: new m(-12e3, 0, 12e3), modEnvToFilterFc: new m(-12e3, 0, 12e3), endAddrsCoarseOffset: new m(C, 0, 0), modLfoToVolume: new m(-960, 0, 960), chorusEffectsSend: new m(0, 0, 1e3), reverbEffectsSend: new m(0, 0, 1e3), pan: new m(-500, 0, 500), delayModLFO: new m(-12e3, -12e3, 5e3), freqModLFO: new m(-16e3, 0, 4500), delayVibLFO: new m(-12e3, -12e3, 5e3), freqVibLFO: new m(-16e3, 0, 4500), delayModEnv: new m(-12e3, -12e3, 5e3), attackModEnv: new m(-12e3, -12e3, 8e3), holdModEnv: new m(-12e3, -12e3, 5e3), decayModEnv: new m(-12e3, -12e3, 8e3), sustainModEnv: new m(0, 0, 1e3), releaseModEnv: new m(-12e3, -12e3, 8e3), keynumToModEnvHold: new m(-1200, 0, 1200), keynumToModEnvDecay: new m(-1200, 0, 1200), delayVolEnv: new m(-12e3, -12e3, 5e3), attackVolEnv: new m(-12e3, -12e3, 8e3), holdVolEnv: new m(-12e3, -12e3, 5e3), decayVolEnv: new m(-12e3, -12e3, 8e3), sustainVolEnv: new m(0, 0, 1440), releaseVolEnv: new m(-12e3, -12e3, 8e3), keynumToVolEnvHold: new m(-1200, 0, 1200), keynumToVolEnvDecay: new m(-1200, 0, 1200), instrument: new m(-1, -1, x), keyRange: new d(0, 127), velRange: new d(0, 127), startloopAddrsCoarseOffset: new m(C, 0, x), keynum: new m(-1, -1, 127), velocity: new m(-1, -1, 127), initialAttenuation: new m(0, 0, 1440), endloopAddrsCoarseOffset: new m(C, 0, x), coarseTune: new m(-120, 0, 120), fineTune: new m(-99, 0, 99), sampleID: new m(-1, -1, x), sampleModes: new m(0, 0, 3), scaleTuning: new m(0, 100, 100), exclusiveClass: new m(0, 0, 127), overridingRootKey: new m(-1, -1, 127) };
137
+ exports.defaultInstrumentZone = W;
138
+ class B {
139
+ constructor(e) { Object.defineProperty(this, "parsed", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), this.parsed = e; }
140
+ getGenerators(e, t, n, r) { const a = new Array(r - n); for (let o = n; o < r; o++) {
141
+ const r = t[o].generatorIndex, s = t[o + 1].generatorIndex;
142
+ a[o - n] = e.slice(r, s);
143
+ } return a; }
144
+ getPresetGenerators(e) { const t = this.parsed.presetHeaders[e], n = this.parsed.presetHeaders[e + 1], r = n ? n.presetBagIndex : this.parsed.presetZone.length - 1; return this.getGenerators(this.parsed.presetGenerators, this.parsed.presetZone, t.presetBagIndex, r); }
145
+ getInstrumentGenerators(e) { const t = this.parsed.instruments[e], n = this.parsed.instruments[e + 1], r = n ? n.instrumentBagIndex : this.parsed.instrumentZone.length - 1; return this.getGenerators(this.parsed.instrumentGenerators, this.parsed.instrumentZone, t.instrumentBagIndex, r); }
146
+ findInstrumentZone(e, t, n) { const r = this.getInstrumentGenerators(e); let a; for (let e = 0; e < r.length; e++) {
147
+ const o = S(r[e]);
148
+ if (void 0 !== o.sampleID) {
149
+ if ((!o.keyRange || o.keyRange.in(t)) && (!o.velRange || o.velRange.in(n)))
150
+ return a ? { ...a, ...o } : o;
151
+ }
152
+ else
153
+ a = o;
154
+ } }
155
+ findInstrument(e, t, n) { const r = this.getPresetGenerators(e); let a; for (let e = 0; e < r.length; e++) {
156
+ const o = V(r[e]);
157
+ if (void 0 === o.instrument) {
158
+ a = o;
159
+ continue;
160
+ }
161
+ if (o.keyRange && !o.keyRange.in(t))
162
+ continue;
163
+ if (o.velRange && !o.velRange.in(n))
164
+ continue;
165
+ const s = this.findInstrumentZone(o.instrument.value, t, n);
166
+ if (s)
167
+ return a ? this.getInstrument({ ...a, ...o }, s) : this.getInstrument(o, s);
168
+ } return null; }
169
+ getInstrument(e, t) { const n = { ...W, ...t }, r = Object.keys(e); for (let t = 0; t < r.length; t++) {
170
+ const a = r[t];
171
+ if (L(a))
172
+ continue;
173
+ const o = n[a], s = e[a];
174
+ n[a] = new m(o.min, o.value + s.value, o.max);
175
+ } return n; }
176
+ getInstrumentKey(e, t, n, r) { const a = this.parsed.presetHeaders.findIndex((n => n.preset === t && n.bank === e)); if (a < 0)
177
+ return console.warn("preset not found: bank=%s instrument=%s", e, t), null; const o = this.findInstrument(a, n, r); if (!o)
178
+ return console.warn("instrument not found: bank=%s instrument=%s", e, t), null; const s = {}, i = Object.keys(o); for (let e = 0; e < i.length; e++) {
179
+ const t = i[e];
180
+ L(t) ? s[t] = o[t] : s[t] = o[t].clamp();
181
+ } const l = H(s.holdModEnv + (n - 60) * s.keynumToModEnvHold), d = H(s.decayModEnv + (n - 60) * s.keynumToModEnvDecay), u = H(s.holdVolEnv + (n - 60) * s.keynumToVolEnvHold), f = H(s.decayVolEnv + (n - 60) * s.keynumToVolEnvDecay), c = this.parsed.samples[s.sampleID], b = this.parsed.sampleHeaders[s.sampleID], m = s.coarseTune + s.fineTune / 100, p = -1 === s.overridingRootKey ? b.originalPitch : s.overridingRootKey, h = m + b.pitchCorrection / 100 - p, v = s.scaleTuning / 100; return { start: 32768 * s.startAddrsCoarseOffset + s.startAddrsOffset, end: 32768 * s.endAddrsCoarseOffset + s.endAddrsOffset, loopStart: b.loopStart + 32768 * s.startloopAddrsCoarseOffset + s.startloopAddrsOffset, loopEnd: b.loopEnd + 32768 * s.endloopAddrsCoarseOffset + s.endloopAddrsOffset, modLfoToPitch: s.modLfoToPitch, vibLfoToPitch: s.vibLfoToPitch, modEnvToPitch: s.modEnvToPitch, initialFilterFc: s.initialFilterFc, initialFilterQ: s.initialFilterQ, modLfoToFilterFc: s.modLfoToFilterFc, modEnvToFilterFc: s.modEnvToFilterFc, modLfoToVolume: s.modLfoToVolume, chorusEffectsSend: s.chorusEffectsSend / 1e3, reverbEffectsSend: s.reverbEffectsSend / 1e3, pan: s.pan, delayModLFO: H(s.delayModLFO), freqModLFO: s.freqModLFO, delayVibLFO: H(s.delayVibLFO), freqVibLFO: s.freqVibLFO, modDelay: H(s.delayModEnv), modAttack: H(s.attackModEnv), modHold: l, modDecay: d, modSustain: s.sustainModEnv / 1e3, modRelease: H(s.releaseModEnv), volDelay: H(s.delayVolEnv), volAttack: H(s.attackVolEnv), volHold: u, volDecay: f, volSustain: s.sustainVolEnv / 1e3, volRelease: H(s.releaseVolEnv), initialAttenuation: s.initialAttenuation, playbackRate: e => Math.pow(Math.pow(2, 1 / 12), (e + h) * v), sample: c, sampleRate: b.sampleRate, sampleName: b.sampleName, sampleModes: s.sampleModes, exclusiveClass: s.exclusiveClass }; }
182
+ getPresetNames() { const e = {}, t = this.parsed.presetHeaders; for (let n = 0; n < t.length; n++) {
183
+ const r = t[n];
184
+ e[r.bank] || (e[r.bank] = {}), e[r.bank][r.preset] = r.presetName;
185
+ } return e; }
186
+ }
187
+ exports.SoundFont = B;
188
+ function H(e) { return Math.pow(2, e / 1200); }
189
+ exports.default = null;
190
+ //# sourceMappingURL=/sm/2fff1026e5ca926c33bde270b79b6c4b0445a8dd5401fdf4af3e7cae85b156e4.map
@@ -39,6 +39,20 @@ export class MidyGM1 {
39
39
  notePromises: any[];
40
40
  audioContext: any;
41
41
  masterGain: any;
42
+ controlChangeHandlers: {
43
+ 1: (channelNumber: any, modulation: any) => void;
44
+ 6: (channelNumber: any, value: any) => void;
45
+ 7: (channelNumber: any, volume: any) => void;
46
+ 10: (channelNumber: any, pan: any) => void;
47
+ 11: (channelNumber: any, expression: any) => void;
48
+ 38: (channelNumber: any, value: any) => void;
49
+ 64: (channelNumber: any, value: any) => void;
50
+ 100: (channelNumber: any, value: any) => void;
51
+ 101: (channelNumber: any, value: any) => void;
52
+ 120: (channelNumber: any) => Promise<void>;
53
+ 121: (channelNumber: any) => void;
54
+ 123: (channelNumber: any) => Promise<void>;
55
+ };
42
56
  channels: any[];
43
57
  initSoundFontTable(): any[];
44
58
  addSoundFont(soundFont: any): void;
@@ -79,19 +93,34 @@ export class MidyGM1 {
79
93
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
80
94
  setVolumeEnvelope(note: any): void;
81
95
  setPitch(note: any, semitoneOffset: any): void;
82
- setFilterNode(channel: any, note: any): void;
96
+ clampCutoffFrequency(frequency: any): number;
97
+ setFilterEnvelope(note: any): void;
83
98
  startModulation(channel: any, note: any, startTime: any): void;
84
99
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
85
100
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
86
101
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
87
- scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
102
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
88
103
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
89
104
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
90
105
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
91
106
  handleProgramChange(channelNumber: any, program: any): void;
92
107
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
93
108
  setPitchBend(channelNumber: any, pitchBend: any): void;
94
- handleControlChange(channelNumber: any, controller: any, value: any): void | Promise<void>;
109
+ createControlChangeHandlers(): {
110
+ 1: (channelNumber: any, modulation: any) => void;
111
+ 6: (channelNumber: any, value: any) => void;
112
+ 7: (channelNumber: any, volume: any) => void;
113
+ 10: (channelNumber: any, pan: any) => void;
114
+ 11: (channelNumber: any, expression: any) => void;
115
+ 38: (channelNumber: any, value: any) => void;
116
+ 64: (channelNumber: any, value: any) => void;
117
+ 100: (channelNumber: any, value: any) => void;
118
+ 101: (channelNumber: any, value: any) => void;
119
+ 120: (channelNumber: any) => Promise<void>;
120
+ 121: (channelNumber: any) => void;
121
+ 123: (channelNumber: any) => Promise<void>;
122
+ };
123
+ handleControlChange(channelNumber: any, controller: any, value: any): void;
95
124
  updateModulation(channel: any): void;
96
125
  setModulationDepth(channelNumber: any, modulation: any): void;
97
126
  setVolume(channelNumber: any, volume: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAwBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAC5C,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,mFAmBC;IAED,yDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,mCAcC;IAED,+CAwBC;IAED,6CA6BC;IAED,+DA0BC;IAED,wHA8BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,2FA+BC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5iCD;IAUE,gFAKC;IAdD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAwBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IA/CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,mFAmBC;IAED,yDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAmCC;IAED,kGA8BC;IAED,0EAGC;IAED,uIA2CC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;MAeC;IAED,2EASC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAnjCD;IAUE,gFAKC;IAdD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGM1 = void 0;
4
4
  const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js");
5
+ const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js");
6
6
  class Note {
7
7
  constructor(noteNumber, velocity, startTime, instrumentKey) {
8
8
  Object.defineProperty(this, "bufferSource", {
@@ -165,6 +165,7 @@ class MidyGM1 {
165
165
  });
166
166
  this.audioContext = audioContext;
167
167
  this.masterGain = new GainNode(audioContext);
168
+ this.controlChangeHandlers = this.createControlChangeHandlers();
168
169
  this.channels = this.createChannels(audioContext);
169
170
  this.masterGain.connect(audioContext.destination);
170
171
  this.GM1SystemOn();
@@ -435,12 +436,13 @@ class MidyGM1 {
435
436
  const now = this.audioContext.currentTime;
436
437
  const channel = this.channels[channelNumber];
437
438
  channel.scheduledNotes.forEach((noteList) => {
438
- noteList.forEach((note) => {
439
- if (note) {
440
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
441
- this.notePromises.push(promise);
442
- }
443
- });
439
+ for (let i = 0; i < noteList.length; i++) {
440
+ const note = noteList[i];
441
+ if (!note)
442
+ continue;
443
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
444
+ this.notePromises.push(promise);
445
+ }
444
446
  });
445
447
  channel.scheduledNotes.clear();
446
448
  await Promise.all(this.notePromises);
@@ -532,7 +534,6 @@ class MidyGM1 {
532
534
  }
533
535
  setVolumeEnvelope(note) {
534
536
  const { instrumentKey, startTime } = note;
535
- note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
536
537
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
537
538
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
538
539
  const volDelay = startTime + instrumentKey.volDelay;
@@ -540,6 +541,8 @@ class MidyGM1 {
540
541
  const volHold = volAttack + instrumentKey.volHold;
541
542
  const volDecay = volHold + instrumentKey.volDecay;
542
543
  note.volumeNode.gain
544
+ .cancelScheduledValues(startTime)
545
+ .setValueAtTime(0, startTime)
543
546
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
544
547
  .exponentialRampToValueAtTime(attackVolume, volAttack)
545
548
  .setValueAtTime(attackVolume, volHold)
@@ -563,29 +566,27 @@ class MidyGM1 {
563
566
  .setValueAtTime(peekPitch, modHold)
564
567
  .linearRampToValueAtTime(basePitch, modDecay);
565
568
  }
566
- setFilterNode(channel, note) {
567
- const { instrumentKey, noteNumber, startTime } = note;
568
- const softPedalFactor = 1 -
569
- (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
570
- const maxFreq = this.audioContext.sampleRate / 2;
571
- const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
572
- softPedalFactor;
573
- const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
574
- const sustainFreq = (baseFreq +
575
- (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
576
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
577
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
578
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
569
+ clampCutoffFrequency(frequency) {
570
+ const minFrequency = 20; // min Hz of initialFilterFc
571
+ const maxFrequency = 20000; // max Hz of initialFilterFc
572
+ return Math.max(minFrequency, Math.min(frequency, maxFrequency));
573
+ }
574
+ setFilterEnvelope(note) {
575
+ const { instrumentKey, startTime } = note;
576
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
577
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc);
578
+ const sustainFreq = baseFreq +
579
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
580
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
581
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
582
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
579
583
  const modDelay = startTime + instrumentKey.modDelay;
580
584
  const modAttack = modDelay + instrumentKey.modAttack;
581
585
  const modHold = modAttack + instrumentKey.modHold;
582
586
  const modDecay = modHold + instrumentKey.modDecay;
583
- note.filterNode = new BiquadFilterNode(this.audioContext, {
584
- type: "lowpass",
585
- Q: instrumentKey.initialFilterQ / 10, // dB
586
- frequency: adjustedBaseFreq,
587
- });
588
587
  note.filterNode.frequency
588
+ .cancelScheduledValues(startTime)
589
+ .setValueAtTime(adjustedBaseFreq, startTime)
589
590
  .setValueAtTime(adjustedBaseFreq, modDelay)
590
591
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
591
592
  .setValueAtTime(adjustedPeekFreq, modHold)
@@ -622,8 +623,13 @@ class MidyGM1 {
622
623
  const semitoneOffset = this.calcSemitoneOffset(channel);
623
624
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
624
625
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
625
- this.setFilterNode(channel, note);
626
+ note.volumeNode = new GainNode(this.audioContext);
627
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
628
+ type: "lowpass",
629
+ Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
630
+ });
626
631
  this.setVolumeEnvelope(note);
632
+ this.setFilterEnvelope(note);
627
633
  if (0 < channel.modulationDepth) {
628
634
  this.setPitch(note, semitoneOffset);
629
635
  this.startModulation(channel, note, startTime);
@@ -644,7 +650,7 @@ class MidyGM1 {
644
650
  return;
645
651
  const soundFont = this.soundFonts[soundFontIndex];
646
652
  const isSF3 = soundFont.parsed.info.version.major === 3;
647
- const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
653
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
648
654
  if (!instrumentKey)
649
655
  return;
650
656
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
@@ -662,7 +668,7 @@ class MidyGM1 {
662
668
  const now = this.audioContext.currentTime;
663
669
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
664
670
  }
665
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
671
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
666
672
  const channel = this.channels[channelNumber];
667
673
  if (stopPedal && channel.sustainPedal)
668
674
  return;
@@ -675,14 +681,11 @@ class MidyGM1 {
675
681
  continue;
676
682
  if (note.ending)
677
683
  continue;
678
- const velocityRate = (velocity + 127) / 127;
679
- const volEndTime = stopTime +
680
- note.instrumentKey.volRelease * velocityRate;
684
+ const volEndTime = stopTime + note.instrumentKey.volRelease;
681
685
  note.volumeNode.gain
682
686
  .cancelScheduledValues(stopTime)
683
687
  .linearRampToValueAtTime(0, volEndTime);
684
- const modRelease = stopTime +
685
- note.instrumentKey.modRelease * velocityRate;
688
+ const modRelease = stopTime + note.instrumentKey.modRelease;
686
689
  note.filterNode.frequency
687
690
  .cancelScheduledValues(stopTime)
688
691
  .linearRampToValueAtTime(0, modRelease);
@@ -696,12 +699,11 @@ class MidyGM1 {
696
699
  note.bufferSource.disconnect();
697
700
  note.volumeNode.disconnect();
698
701
  note.filterNode.disconnect();
699
- if (note.volumeDepth)
702
+ if (note.modulationDepth) {
700
703
  note.volumeDepth.disconnect();
701
- if (note.modulationDepth)
702
704
  note.modulationDepth.disconnect();
703
- if (note.modulationLFO)
704
705
  note.modulationLFO.stop();
706
+ }
705
707
  resolve();
706
708
  };
707
709
  note.bufferSource.stop(volEndTime);
@@ -718,13 +720,14 @@ class MidyGM1 {
718
720
  const promises = [];
719
721
  channel.sustainPedal = false;
720
722
  channel.scheduledNotes.forEach((noteList) => {
721
- noteList.forEach((note) => {
722
- if (note) {
723
- const { noteNumber } = note;
724
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
725
- promises.push(promise);
726
- }
727
- });
723
+ for (let i = 0; i < noteList.length; i++) {
724
+ const note = noteList[i];
725
+ if (!note)
726
+ continue;
727
+ const { noteNumber } = note;
728
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
729
+ promises.push(promise);
730
+ }
728
731
  });
729
732
  return promises;
730
733
  }
@@ -762,47 +765,46 @@ class MidyGM1 {
762
765
  channel.pitchBendRange * 100;
763
766
  this.updateDetune(channel, detuneChange);
764
767
  }
768
+ createControlChangeHandlers() {
769
+ return {
770
+ 1: this.setModulationDepth,
771
+ 6: this.dataEntryMSB,
772
+ 7: this.setVolume,
773
+ 10: this.setPan,
774
+ 11: this.setExpression,
775
+ 38: this.dataEntryLSB,
776
+ 64: this.setSustainPedal,
777
+ 100: this.setRPNLSB,
778
+ 101: this.setRPNMSB,
779
+ 120: this.allSoundOff,
780
+ 121: this.resetAllControllers,
781
+ 123: this.allNotesOff,
782
+ };
783
+ }
765
784
  handleControlChange(channelNumber, controller, value) {
766
- switch (controller) {
767
- case 1:
768
- return this.setModulationDepth(channelNumber, value);
769
- case 6:
770
- return this.dataEntryMSB(channelNumber, value);
771
- case 7:
772
- return this.setVolume(channelNumber, value);
773
- case 10:
774
- return this.setPan(channelNumber, value);
775
- case 11:
776
- return this.setExpression(channelNumber, value);
777
- case 38:
778
- return this.dataEntryLSB(channelNumber, value);
779
- case 64:
780
- return this.setSustainPedal(channelNumber, value);
781
- case 100:
782
- return this.setRPNLSB(channelNumber, value);
783
- case 101:
784
- return this.setRPNMSB(channelNumber, value);
785
- case 120:
786
- return this.allSoundOff(channelNumber);
787
- case 121:
788
- return this.resetAllControllers(channelNumber);
789
- case 123:
790
- return this.allNotesOff(channelNumber);
791
- default:
792
- console.warn(`Unsupported Control change: controller=${controller} value=${value}`);
785
+ const handler = this.controlChangeHandlers[controller];
786
+ if (handler) {
787
+ handler.call(this, channelNumber, value);
788
+ }
789
+ else {
790
+ console.warn(`Unsupported Control change: controller=${controller} value=${value}`);
793
791
  }
794
792
  }
795
793
  updateModulation(channel) {
796
794
  const now = this.audioContext.currentTime;
797
- const activeNotes = this.getActiveNotes(channel, now);
798
- activeNotes.forEach((activeNote) => {
799
- if (activeNote.modulationDepth) {
800
- activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
801
- }
802
- else {
803
- const semitoneOffset = this.calcSemitoneOffset(channel);
804
- this.setPitch(activeNote, semitoneOffset);
805
- this.startModulation(channel, activeNote, now);
795
+ channel.scheduledNotes.forEach((noteList) => {
796
+ for (let i = 0; i < noteList.length; i++) {
797
+ const note = noteList[i];
798
+ if (!note)
799
+ continue;
800
+ if (note.modulationDepth) {
801
+ note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
802
+ }
803
+ else {
804
+ const semitoneOffset = this.calcSemitoneOffset(channel);
805
+ this.setPitch(note, semitoneOffset);
806
+ this.startModulation(channel, note, now);
807
+ }
806
808
  }
807
809
  });
808
810
  }
@@ -910,13 +912,17 @@ class MidyGM1 {
910
912
  }
911
913
  updateDetune(channel, detuneChange) {
912
914
  const now = this.audioContext.currentTime;
913
- const activeNotes = this.getActiveNotes(channel, now);
914
- activeNotes.forEach((activeNote) => {
915
- const { bufferSource } = activeNote;
916
- const detune = bufferSource.detune.value + detuneChange;
917
- bufferSource.detune
918
- .cancelScheduledValues(now)
919
- .setValueAtTime(detune, now);
915
+ channel.scheduledNotes.forEach((noteList) => {
916
+ for (let i = 0; i < noteList.length; i++) {
917
+ const note = noteList[i];
918
+ if (!note)
919
+ continue;
920
+ const { bufferSource } = note;
921
+ const detune = bufferSource.detune.value + detuneChange;
922
+ bufferSource.detune
923
+ .cancelScheduledValues(now)
924
+ .setValueAtTime(detune, now);
925
+ }
920
926
  });
921
927
  }
922
928
  handlePitchBendRangeRPN(channelNumber) {