@marmooo/midy 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/midy-GM1.d.ts +11 -10
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +114 -123
- package/esm/midy-GM2.d.ts +26 -42
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +295 -328
- package/esm/midy-GMLite.d.ts +12 -11
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +124 -141
- package/esm/midy.d.ts +27 -43
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +335 -352
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +11 -10
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +114 -123
- package/script/midy-GM2.d.ts +26 -42
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +295 -328
- package/script/midy-GMLite.d.ts +12 -11
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +124 -141
- package/script/midy.d.ts +27 -43
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +335 -352
package/esm/midy-GM1.d.ts
CHANGED
|
@@ -54,9 +54,9 @@ export class MidyGM1 {
|
|
|
54
54
|
channels: any[];
|
|
55
55
|
initSoundFontTable(): any[];
|
|
56
56
|
addSoundFont(soundFont: any): void;
|
|
57
|
-
loadSoundFont(
|
|
58
|
-
loadMIDI(
|
|
59
|
-
|
|
57
|
+
loadSoundFont(input: any): Promise<void>;
|
|
58
|
+
loadMIDI(input: any): Promise<void>;
|
|
59
|
+
createChannelAudioNodes(audioContext: any): {
|
|
60
60
|
gainL: any;
|
|
61
61
|
gainR: any;
|
|
62
62
|
merger: any;
|
|
@@ -84,7 +84,7 @@ export class MidyGM1 {
|
|
|
84
84
|
seekTo(second: any): void;
|
|
85
85
|
calcTotalTime(): number;
|
|
86
86
|
currentTime(): number;
|
|
87
|
-
processScheduledNotes(channel: any, callback: any): void;
|
|
87
|
+
processScheduledNotes(channel: any, scheduleTime: any, callback: any): void;
|
|
88
88
|
processActiveNotes(channel: any, scheduleTime: any, callback: any): void;
|
|
89
89
|
cbToRatio(cb: any): number;
|
|
90
90
|
rateToCent(rate: any): number;
|
|
@@ -101,15 +101,15 @@ export class MidyGM1 {
|
|
|
101
101
|
getAudioBuffer(programNumber: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
|
|
102
102
|
createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
103
103
|
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
104
|
-
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any
|
|
104
|
+
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
105
105
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
|
|
106
106
|
disconnectNote(note: any): void;
|
|
107
|
-
|
|
108
|
-
scheduleNoteOff(channelNumber: any,
|
|
107
|
+
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
108
|
+
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): void;
|
|
109
109
|
findNoteOffTarget(channel: any, noteNumber: any): any;
|
|
110
|
-
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any):
|
|
111
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any):
|
|
112
|
-
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<
|
|
110
|
+
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): void;
|
|
111
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
112
|
+
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<void>;
|
|
113
113
|
handleProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
114
114
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
115
115
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
@@ -173,6 +173,7 @@ export class MidyGM1 {
|
|
|
173
173
|
declare class Note {
|
|
174
174
|
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
175
175
|
index: number;
|
|
176
|
+
ending: boolean;
|
|
176
177
|
bufferSource: any;
|
|
177
178
|
filterNode: any;
|
|
178
179
|
filterDepth: any;
|
package/esm/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AA4FA;IAwBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAlDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,2BAAqC;IAgBnC,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCAWC;IAED,yCAcC;IAED,oCAiBC;IAED;;;;MAeC;IAED,yCAaC;IAED,6DA2BC;IAED,4DASC;IAED,+EAkDC;IAED,mCAOC;IAED,0BA8DC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA6EC;IAED,kGAeC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4EASC;IAED,yEASC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,+GA0BC;IAED,gHAyCC;IAED,0EAiBC;IAED,kGAsCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAaC;IAED,sDASC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;IAED,sFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EAgCC;IAED,qCAcC;IAED,kGAWC;IAED,wDAUC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,2DAMC;IAED,uDAkBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,iEAKC;IAED,uEAQC;IAED,mEAKC;IAED,yEAQC;IAED,gFAGC;IAED,yCAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AAv9CD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|
package/esm/midy-GM1.js
CHANGED
|
@@ -8,6 +8,12 @@ class Note {
|
|
|
8
8
|
writable: true,
|
|
9
9
|
value: -1
|
|
10
10
|
});
|
|
11
|
+
Object.defineProperty(this, "ending", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: false
|
|
16
|
+
});
|
|
11
17
|
Object.defineProperty(this, "bufferSource", {
|
|
12
18
|
enumerable: true,
|
|
13
19
|
configurable: true,
|
|
@@ -64,13 +70,11 @@ const defaultControllerState = {
|
|
|
64
70
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
65
71
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
66
72
|
link: { type: 127, defaultValue: 0 },
|
|
67
|
-
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
68
73
|
modulationDepth: { type: 128 + 1, defaultValue: 0 },
|
|
69
74
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
70
75
|
volume: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
71
76
|
pan: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
72
77
|
expression: { type: 128 + 11, defaultValue: 1 },
|
|
73
|
-
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
74
78
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
75
79
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
76
80
|
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
@@ -99,6 +103,16 @@ class ControllerState {
|
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
}
|
|
106
|
+
const volumeEnvelopeKeys = [
|
|
107
|
+
"volDelay",
|
|
108
|
+
"volAttack",
|
|
109
|
+
"volHold",
|
|
110
|
+
"volDecay",
|
|
111
|
+
"volSustain",
|
|
112
|
+
"volRelease",
|
|
113
|
+
"initialAttenuation",
|
|
114
|
+
];
|
|
115
|
+
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
102
116
|
const filterEnvelopeKeys = [
|
|
103
117
|
"modEnvToPitch",
|
|
104
118
|
"initialFilterFc",
|
|
@@ -108,20 +122,18 @@ const filterEnvelopeKeys = [
|
|
|
108
122
|
"modHold",
|
|
109
123
|
"modDecay",
|
|
110
124
|
"modSustain",
|
|
111
|
-
"modRelease",
|
|
112
|
-
"playbackRate",
|
|
113
125
|
];
|
|
114
126
|
const filterEnvelopeKeySet = new Set(filterEnvelopeKeys);
|
|
115
|
-
const
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
127
|
+
const pitchEnvelopeKeys = [
|
|
128
|
+
"modEnvToPitch",
|
|
129
|
+
"modDelay",
|
|
130
|
+
"modAttack",
|
|
131
|
+
"modHold",
|
|
132
|
+
"modDecay",
|
|
133
|
+
"modSustain",
|
|
134
|
+
"playbackRate",
|
|
123
135
|
];
|
|
124
|
-
const
|
|
136
|
+
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
125
137
|
export class MidyGM1 {
|
|
126
138
|
constructor(audioContext) {
|
|
127
139
|
Object.defineProperty(this, "mode", {
|
|
@@ -289,24 +301,44 @@ export class MidyGM1 {
|
|
|
289
301
|
}
|
|
290
302
|
}
|
|
291
303
|
}
|
|
292
|
-
async loadSoundFont(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
304
|
+
async loadSoundFont(input) {
|
|
305
|
+
let uint8Array;
|
|
306
|
+
if (typeof input === "string") {
|
|
307
|
+
const response = await fetch(input);
|
|
308
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
309
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
310
|
+
}
|
|
311
|
+
else if (input instanceof Uint8Array) {
|
|
312
|
+
uint8Array = input;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
316
|
+
}
|
|
317
|
+
const parsed = parse(uint8Array);
|
|
296
318
|
const soundFont = new SoundFont(parsed);
|
|
297
319
|
this.addSoundFont(soundFont);
|
|
298
320
|
}
|
|
299
|
-
async loadMIDI(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
321
|
+
async loadMIDI(input) {
|
|
322
|
+
let uint8Array;
|
|
323
|
+
if (typeof input === "string") {
|
|
324
|
+
const response = await fetch(input);
|
|
325
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
326
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
327
|
+
}
|
|
328
|
+
else if (input instanceof Uint8Array) {
|
|
329
|
+
uint8Array = input;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
333
|
+
}
|
|
334
|
+
const midi = parseMidi(uint8Array);
|
|
303
335
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
304
336
|
const midiData = this.extractMidiData(midi);
|
|
305
337
|
this.instruments = midiData.instruments;
|
|
306
338
|
this.timeline = midiData.timeline;
|
|
307
339
|
this.totalTime = this.calcTotalTime();
|
|
308
340
|
}
|
|
309
|
-
|
|
341
|
+
createChannelAudioNodes(audioContext) {
|
|
310
342
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
311
343
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
312
344
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
@@ -327,7 +359,7 @@ export class MidyGM1 {
|
|
|
327
359
|
isDrum: false,
|
|
328
360
|
state: new ControllerState(),
|
|
329
361
|
...this.constructor.channelSettings,
|
|
330
|
-
...this.
|
|
362
|
+
...this.createChannelAudioNodes(audioContext),
|
|
331
363
|
scheduledNotes: [],
|
|
332
364
|
sustainNotes: [],
|
|
333
365
|
};
|
|
@@ -381,12 +413,13 @@ export class MidyGM1 {
|
|
|
381
413
|
const delay = this.startDelay - resumeTime;
|
|
382
414
|
const startTime = event.startTime + delay;
|
|
383
415
|
switch (event.type) {
|
|
384
|
-
case "noteOn":
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
416
|
+
case "noteOn":
|
|
417
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
418
|
+
break;
|
|
419
|
+
case "noteOff": {
|
|
420
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
421
|
+
if (notePromise)
|
|
422
|
+
this.notePromises.push(notePromise);
|
|
390
423
|
break;
|
|
391
424
|
}
|
|
392
425
|
case "controller":
|
|
@@ -485,6 +518,7 @@ export class MidyGM1 {
|
|
|
485
518
|
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
486
519
|
}
|
|
487
520
|
extractMidiData(midi) {
|
|
521
|
+
this.audioBufferCounter.clear();
|
|
488
522
|
const instruments = new Set();
|
|
489
523
|
const timeline = [];
|
|
490
524
|
const tmpChannels = new Array(this.channels.length);
|
|
@@ -548,38 +582,13 @@ export class MidyGM1 {
|
|
|
548
582
|
prevTempoTicks = event.ticks;
|
|
549
583
|
}
|
|
550
584
|
}
|
|
551
|
-
const activeNotes = new Array(this.channels.length * 128);
|
|
552
|
-
for (let i = 0; i < activeNotes.length; i++) {
|
|
553
|
-
activeNotes[i] = [];
|
|
554
|
-
}
|
|
555
|
-
for (let i = 0; i < timeline.length; i++) {
|
|
556
|
-
const event = timeline[i];
|
|
557
|
-
switch (event.type) {
|
|
558
|
-
case "noteOn": {
|
|
559
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
560
|
-
activeNotes[index].push(event);
|
|
561
|
-
break;
|
|
562
|
-
}
|
|
563
|
-
case "noteOff": {
|
|
564
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
565
|
-
const noteOn = activeNotes[index].pop();
|
|
566
|
-
if (noteOn) {
|
|
567
|
-
noteOn.noteOffEvent = event;
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
const eventString = JSON.stringify(event, null, 2);
|
|
571
|
-
console.warn(`noteOff without matching noteOn: ${eventString}`);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
585
|
return { instruments, timeline };
|
|
577
586
|
}
|
|
578
587
|
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
579
588
|
const channel = this.channels[channelNumber];
|
|
580
589
|
const promises = [];
|
|
581
590
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
582
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force
|
|
591
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
583
592
|
this.notePromises.push(promise);
|
|
584
593
|
promises.push(promise);
|
|
585
594
|
});
|
|
@@ -588,8 +597,8 @@ export class MidyGM1 {
|
|
|
588
597
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
589
598
|
const channel = this.channels[channelNumber];
|
|
590
599
|
const promises = [];
|
|
591
|
-
this.processScheduledNotes(channel, (note) => {
|
|
592
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
|
|
600
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
601
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
593
602
|
this.notePromises.push(promise);
|
|
594
603
|
promises.push(promise);
|
|
595
604
|
});
|
|
@@ -647,7 +656,7 @@ export class MidyGM1 {
|
|
|
647
656
|
const now = this.audioContext.currentTime;
|
|
648
657
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
649
658
|
}
|
|
650
|
-
processScheduledNotes(channel, callback) {
|
|
659
|
+
processScheduledNotes(channel, scheduleTime, callback) {
|
|
651
660
|
const scheduledNotes = channel.scheduledNotes;
|
|
652
661
|
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
653
662
|
const note = scheduledNotes[i];
|
|
@@ -655,6 +664,8 @@ export class MidyGM1 {
|
|
|
655
664
|
continue;
|
|
656
665
|
if (note.ending)
|
|
657
666
|
continue;
|
|
667
|
+
if (note.startTime < scheduleTime)
|
|
668
|
+
continue;
|
|
658
669
|
callback(note);
|
|
659
670
|
}
|
|
660
671
|
}
|
|
@@ -666,11 +677,8 @@ export class MidyGM1 {
|
|
|
666
677
|
continue;
|
|
667
678
|
if (note.ending)
|
|
668
679
|
continue;
|
|
669
|
-
const noteOffEvent = note.noteOffEvent;
|
|
670
|
-
if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
|
|
671
|
-
continue;
|
|
672
680
|
if (scheduleTime < note.startTime)
|
|
673
|
-
|
|
681
|
+
break;
|
|
674
682
|
callback(note);
|
|
675
683
|
}
|
|
676
684
|
}
|
|
@@ -694,7 +702,7 @@ export class MidyGM1 {
|
|
|
694
702
|
return tuning + pitch;
|
|
695
703
|
}
|
|
696
704
|
updateChannelDetune(channel, scheduleTime) {
|
|
697
|
-
this.processScheduledNotes(channel, (note) => {
|
|
705
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
698
706
|
this.updateDetune(channel, note, scheduleTime);
|
|
699
707
|
});
|
|
700
708
|
}
|
|
@@ -838,13 +846,13 @@ export class MidyGM1 {
|
|
|
838
846
|
if (prev) {
|
|
839
847
|
const [prevNote, prevChannelNumber] = prev;
|
|
840
848
|
if (prevNote && !prevNote.ending) {
|
|
841
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
|
|
849
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
842
850
|
startTime, true);
|
|
843
851
|
}
|
|
844
852
|
}
|
|
845
853
|
this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
|
|
846
854
|
}
|
|
847
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime
|
|
855
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
848
856
|
const channel = this.channels[channelNumber];
|
|
849
857
|
const bankNumber = channel.bank;
|
|
850
858
|
const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
|
|
@@ -856,7 +864,6 @@ export class MidyGM1 {
|
|
|
856
864
|
return;
|
|
857
865
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
858
866
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
859
|
-
note.noteOffEvent = noteOffEvent;
|
|
860
867
|
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
861
868
|
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
862
869
|
if (0.5 <= channel.state.sustainPedal) {
|
|
@@ -866,12 +873,6 @@ export class MidyGM1 {
|
|
|
866
873
|
const scheduledNotes = channel.scheduledNotes;
|
|
867
874
|
note.index = scheduledNotes.length;
|
|
868
875
|
scheduledNotes.push(note);
|
|
869
|
-
if (noteOffEvent) {
|
|
870
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
|
|
871
|
-
if (notePromise) {
|
|
872
|
-
this.notePromises.push(notePromise);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
876
|
}
|
|
876
877
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
877
878
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -887,34 +888,36 @@ export class MidyGM1 {
|
|
|
887
888
|
note.modulationLFO.stop();
|
|
888
889
|
}
|
|
889
890
|
}
|
|
890
|
-
|
|
891
|
+
releaseNote(channel, note, endTime) {
|
|
892
|
+
const volRelease = endTime + note.voiceParams.volRelease;
|
|
893
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
894
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
895
|
+
note.filterNode.frequency
|
|
896
|
+
.cancelScheduledValues(endTime)
|
|
897
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
891
898
|
note.volumeEnvelopeNode.gain
|
|
892
899
|
.cancelScheduledValues(endTime)
|
|
893
|
-
.linearRampToValueAtTime(0,
|
|
894
|
-
note.ending = true;
|
|
895
|
-
this.scheduleTask(() => {
|
|
896
|
-
note.bufferSource.loop = false;
|
|
897
|
-
}, stopTime);
|
|
900
|
+
.linearRampToValueAtTime(0, volRelease);
|
|
898
901
|
return new Promise((resolve) => {
|
|
899
|
-
|
|
900
|
-
|
|
902
|
+
this.scheduleTask(() => {
|
|
903
|
+
const bufferSource = note.bufferSource;
|
|
904
|
+
bufferSource.loop = false;
|
|
905
|
+
bufferSource.stop(stopTime);
|
|
901
906
|
this.disconnectNote(note);
|
|
907
|
+
channel.scheduledNotes[note.index] = undefined;
|
|
902
908
|
resolve();
|
|
903
|
-
};
|
|
904
|
-
note.bufferSource.stop(stopTime);
|
|
909
|
+
}, stopTime);
|
|
905
910
|
});
|
|
906
911
|
}
|
|
907
|
-
scheduleNoteOff(channelNumber,
|
|
912
|
+
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
908
913
|
const channel = this.channels[channelNumber];
|
|
909
914
|
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
910
915
|
return;
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
const stopTime = Math.min(volRelease, modRelease);
|
|
917
|
-
return this.stopNote(channel, note, endTime, stopTime);
|
|
916
|
+
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
917
|
+
if (!note)
|
|
918
|
+
return;
|
|
919
|
+
note.ending = true;
|
|
920
|
+
this.releaseNote(channel, note, endTime);
|
|
918
921
|
}
|
|
919
922
|
findNoteOffTarget(channel, noteNumber) {
|
|
920
923
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -931,16 +934,14 @@ export class MidyGM1 {
|
|
|
931
934
|
}
|
|
932
935
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
933
936
|
scheduleTime ??= this.audioContext.currentTime;
|
|
934
|
-
|
|
935
|
-
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
936
|
-
return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
|
|
937
|
+
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
937
938
|
}
|
|
938
939
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
939
940
|
const velocity = halfVelocity * 2;
|
|
940
941
|
const channel = this.channels[channelNumber];
|
|
941
942
|
const promises = [];
|
|
942
943
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
943
|
-
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
|
|
944
|
+
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
944
945
|
promises.push(promise);
|
|
945
946
|
}
|
|
946
947
|
channel.sustainNotes = [];
|
|
@@ -1054,11 +1055,12 @@ export class MidyGM1 {
|
|
|
1054
1055
|
return state;
|
|
1055
1056
|
}
|
|
1056
1057
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1057
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1058
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1058
1059
|
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1059
1060
|
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1060
|
-
let
|
|
1061
|
-
let
|
|
1061
|
+
let applyVolumeEnvelope = false;
|
|
1062
|
+
let applyFilterEnvelope = false;
|
|
1063
|
+
let applyPitchEnvelope = false;
|
|
1062
1064
|
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1063
1065
|
const prevValue = note.voiceParams[key];
|
|
1064
1066
|
if (value === prevValue)
|
|
@@ -1067,32 +1069,21 @@ export class MidyGM1 {
|
|
|
1067
1069
|
if (key in this.voiceParamsHandlers) {
|
|
1068
1070
|
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1069
1071
|
}
|
|
1070
|
-
else
|
|
1071
|
-
if (
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
if (key in voiceParams)
|
|
1078
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1079
|
-
}
|
|
1080
|
-
this.setFilterEnvelope(note, scheduleTime);
|
|
1081
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1082
|
-
}
|
|
1083
|
-
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1084
|
-
if (appliedVolumeEnvelope)
|
|
1085
|
-
continue;
|
|
1086
|
-
appliedVolumeEnvelope = true;
|
|
1087
|
-
const noteVoiceParams = note.voiceParams;
|
|
1088
|
-
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1089
|
-
const key = volumeEnvelopeKeys[i];
|
|
1090
|
-
if (key in voiceParams)
|
|
1091
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1092
|
-
}
|
|
1093
|
-
this.setVolumeEnvelope(note, scheduleTime);
|
|
1072
|
+
else {
|
|
1073
|
+
if (volumeEnvelopeKeySet.has(key))
|
|
1074
|
+
applyVolumeEnvelope = true;
|
|
1075
|
+
if (filterEnvelopeKeySet.has(key))
|
|
1076
|
+
applyFilterEnvelope = true;
|
|
1077
|
+
if (pitchEnvelopeKeySet.has(key))
|
|
1078
|
+
applyPitchEnvelope = true;
|
|
1094
1079
|
}
|
|
1095
1080
|
}
|
|
1081
|
+
if (applyVolumeEnvelope)
|
|
1082
|
+
this.setVolumeEnvelope(note, scheduleTime);
|
|
1083
|
+
if (applyFilterEnvelope)
|
|
1084
|
+
this.setFilterEnvelope(note, scheduleTime);
|
|
1085
|
+
if (applyPitchEnvelope)
|
|
1086
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1096
1087
|
});
|
|
1097
1088
|
}
|
|
1098
1089
|
createControlChangeHandlers() {
|
|
@@ -1123,7 +1114,7 @@ export class MidyGM1 {
|
|
|
1123
1114
|
}
|
|
1124
1115
|
updateModulation(channel, scheduleTime) {
|
|
1125
1116
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1126
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1117
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1127
1118
|
if (note.modulationDepth) {
|
|
1128
1119
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1129
1120
|
}
|
|
@@ -1184,7 +1175,7 @@ export class MidyGM1 {
|
|
|
1184
1175
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1185
1176
|
channel.state.sustainPedal = value / 127;
|
|
1186
1177
|
if (64 <= value) {
|
|
1187
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1178
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1188
1179
|
channel.sustainNotes.push(note);
|
|
1189
1180
|
});
|
|
1190
1181
|
}
|