@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/package.json
CHANGED
package/script/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/script/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/script/midy-GM1.js
CHANGED
|
@@ -11,6 +11,12 @@ class Note {
|
|
|
11
11
|
writable: true,
|
|
12
12
|
value: -1
|
|
13
13
|
});
|
|
14
|
+
Object.defineProperty(this, "ending", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: false
|
|
19
|
+
});
|
|
14
20
|
Object.defineProperty(this, "bufferSource", {
|
|
15
21
|
enumerable: true,
|
|
16
22
|
configurable: true,
|
|
@@ -67,13 +73,11 @@ const defaultControllerState = {
|
|
|
67
73
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
68
74
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
69
75
|
link: { type: 127, defaultValue: 0 },
|
|
70
|
-
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
71
76
|
modulationDepth: { type: 128 + 1, defaultValue: 0 },
|
|
72
77
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
73
78
|
volume: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
74
79
|
pan: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
75
80
|
expression: { type: 128 + 11, defaultValue: 1 },
|
|
76
|
-
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
77
81
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
78
82
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
79
83
|
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
@@ -102,6 +106,16 @@ class ControllerState {
|
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
108
|
}
|
|
109
|
+
const volumeEnvelopeKeys = [
|
|
110
|
+
"volDelay",
|
|
111
|
+
"volAttack",
|
|
112
|
+
"volHold",
|
|
113
|
+
"volDecay",
|
|
114
|
+
"volSustain",
|
|
115
|
+
"volRelease",
|
|
116
|
+
"initialAttenuation",
|
|
117
|
+
];
|
|
118
|
+
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
105
119
|
const filterEnvelopeKeys = [
|
|
106
120
|
"modEnvToPitch",
|
|
107
121
|
"initialFilterFc",
|
|
@@ -111,20 +125,18 @@ const filterEnvelopeKeys = [
|
|
|
111
125
|
"modHold",
|
|
112
126
|
"modDecay",
|
|
113
127
|
"modSustain",
|
|
114
|
-
"modRelease",
|
|
115
|
-
"playbackRate",
|
|
116
128
|
];
|
|
117
129
|
const filterEnvelopeKeySet = new Set(filterEnvelopeKeys);
|
|
118
|
-
const
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
130
|
+
const pitchEnvelopeKeys = [
|
|
131
|
+
"modEnvToPitch",
|
|
132
|
+
"modDelay",
|
|
133
|
+
"modAttack",
|
|
134
|
+
"modHold",
|
|
135
|
+
"modDecay",
|
|
136
|
+
"modSustain",
|
|
137
|
+
"playbackRate",
|
|
126
138
|
];
|
|
127
|
-
const
|
|
139
|
+
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
128
140
|
class MidyGM1 {
|
|
129
141
|
constructor(audioContext) {
|
|
130
142
|
Object.defineProperty(this, "mode", {
|
|
@@ -292,24 +304,44 @@ class MidyGM1 {
|
|
|
292
304
|
}
|
|
293
305
|
}
|
|
294
306
|
}
|
|
295
|
-
async loadSoundFont(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
307
|
+
async loadSoundFont(input) {
|
|
308
|
+
let uint8Array;
|
|
309
|
+
if (typeof input === "string") {
|
|
310
|
+
const response = await fetch(input);
|
|
311
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
312
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
313
|
+
}
|
|
314
|
+
else if (input instanceof Uint8Array) {
|
|
315
|
+
uint8Array = input;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
319
|
+
}
|
|
320
|
+
const parsed = (0, soundfont_parser_1.parse)(uint8Array);
|
|
299
321
|
const soundFont = new soundfont_parser_1.SoundFont(parsed);
|
|
300
322
|
this.addSoundFont(soundFont);
|
|
301
323
|
}
|
|
302
|
-
async loadMIDI(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
324
|
+
async loadMIDI(input) {
|
|
325
|
+
let uint8Array;
|
|
326
|
+
if (typeof input === "string") {
|
|
327
|
+
const response = await fetch(input);
|
|
328
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
329
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
330
|
+
}
|
|
331
|
+
else if (input instanceof Uint8Array) {
|
|
332
|
+
uint8Array = input;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
336
|
+
}
|
|
337
|
+
const midi = (0, midi_file_1.parseMidi)(uint8Array);
|
|
306
338
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
307
339
|
const midiData = this.extractMidiData(midi);
|
|
308
340
|
this.instruments = midiData.instruments;
|
|
309
341
|
this.timeline = midiData.timeline;
|
|
310
342
|
this.totalTime = this.calcTotalTime();
|
|
311
343
|
}
|
|
312
|
-
|
|
344
|
+
createChannelAudioNodes(audioContext) {
|
|
313
345
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
314
346
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
315
347
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
@@ -330,7 +362,7 @@ class MidyGM1 {
|
|
|
330
362
|
isDrum: false,
|
|
331
363
|
state: new ControllerState(),
|
|
332
364
|
...this.constructor.channelSettings,
|
|
333
|
-
...this.
|
|
365
|
+
...this.createChannelAudioNodes(audioContext),
|
|
334
366
|
scheduledNotes: [],
|
|
335
367
|
sustainNotes: [],
|
|
336
368
|
};
|
|
@@ -384,12 +416,13 @@ class MidyGM1 {
|
|
|
384
416
|
const delay = this.startDelay - resumeTime;
|
|
385
417
|
const startTime = event.startTime + delay;
|
|
386
418
|
switch (event.type) {
|
|
387
|
-
case "noteOn":
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
419
|
+
case "noteOn":
|
|
420
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
421
|
+
break;
|
|
422
|
+
case "noteOff": {
|
|
423
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
424
|
+
if (notePromise)
|
|
425
|
+
this.notePromises.push(notePromise);
|
|
393
426
|
break;
|
|
394
427
|
}
|
|
395
428
|
case "controller":
|
|
@@ -488,6 +521,7 @@ class MidyGM1 {
|
|
|
488
521
|
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
489
522
|
}
|
|
490
523
|
extractMidiData(midi) {
|
|
524
|
+
this.audioBufferCounter.clear();
|
|
491
525
|
const instruments = new Set();
|
|
492
526
|
const timeline = [];
|
|
493
527
|
const tmpChannels = new Array(this.channels.length);
|
|
@@ -551,38 +585,13 @@ class MidyGM1 {
|
|
|
551
585
|
prevTempoTicks = event.ticks;
|
|
552
586
|
}
|
|
553
587
|
}
|
|
554
|
-
const activeNotes = new Array(this.channels.length * 128);
|
|
555
|
-
for (let i = 0; i < activeNotes.length; i++) {
|
|
556
|
-
activeNotes[i] = [];
|
|
557
|
-
}
|
|
558
|
-
for (let i = 0; i < timeline.length; i++) {
|
|
559
|
-
const event = timeline[i];
|
|
560
|
-
switch (event.type) {
|
|
561
|
-
case "noteOn": {
|
|
562
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
563
|
-
activeNotes[index].push(event);
|
|
564
|
-
break;
|
|
565
|
-
}
|
|
566
|
-
case "noteOff": {
|
|
567
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
568
|
-
const noteOn = activeNotes[index].pop();
|
|
569
|
-
if (noteOn) {
|
|
570
|
-
noteOn.noteOffEvent = event;
|
|
571
|
-
}
|
|
572
|
-
else {
|
|
573
|
-
const eventString = JSON.stringify(event, null, 2);
|
|
574
|
-
console.warn(`noteOff without matching noteOn: ${eventString}`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
588
|
return { instruments, timeline };
|
|
580
589
|
}
|
|
581
590
|
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
582
591
|
const channel = this.channels[channelNumber];
|
|
583
592
|
const promises = [];
|
|
584
593
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
585
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force
|
|
594
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
586
595
|
this.notePromises.push(promise);
|
|
587
596
|
promises.push(promise);
|
|
588
597
|
});
|
|
@@ -591,8 +600,8 @@ class MidyGM1 {
|
|
|
591
600
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
592
601
|
const channel = this.channels[channelNumber];
|
|
593
602
|
const promises = [];
|
|
594
|
-
this.processScheduledNotes(channel, (note) => {
|
|
595
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
|
|
603
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
604
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
596
605
|
this.notePromises.push(promise);
|
|
597
606
|
promises.push(promise);
|
|
598
607
|
});
|
|
@@ -650,7 +659,7 @@ class MidyGM1 {
|
|
|
650
659
|
const now = this.audioContext.currentTime;
|
|
651
660
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
652
661
|
}
|
|
653
|
-
processScheduledNotes(channel, callback) {
|
|
662
|
+
processScheduledNotes(channel, scheduleTime, callback) {
|
|
654
663
|
const scheduledNotes = channel.scheduledNotes;
|
|
655
664
|
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
656
665
|
const note = scheduledNotes[i];
|
|
@@ -658,6 +667,8 @@ class MidyGM1 {
|
|
|
658
667
|
continue;
|
|
659
668
|
if (note.ending)
|
|
660
669
|
continue;
|
|
670
|
+
if (note.startTime < scheduleTime)
|
|
671
|
+
continue;
|
|
661
672
|
callback(note);
|
|
662
673
|
}
|
|
663
674
|
}
|
|
@@ -669,11 +680,8 @@ class MidyGM1 {
|
|
|
669
680
|
continue;
|
|
670
681
|
if (note.ending)
|
|
671
682
|
continue;
|
|
672
|
-
const noteOffEvent = note.noteOffEvent;
|
|
673
|
-
if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
|
|
674
|
-
continue;
|
|
675
683
|
if (scheduleTime < note.startTime)
|
|
676
|
-
|
|
684
|
+
break;
|
|
677
685
|
callback(note);
|
|
678
686
|
}
|
|
679
687
|
}
|
|
@@ -697,7 +705,7 @@ class MidyGM1 {
|
|
|
697
705
|
return tuning + pitch;
|
|
698
706
|
}
|
|
699
707
|
updateChannelDetune(channel, scheduleTime) {
|
|
700
|
-
this.processScheduledNotes(channel, (note) => {
|
|
708
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
701
709
|
this.updateDetune(channel, note, scheduleTime);
|
|
702
710
|
});
|
|
703
711
|
}
|
|
@@ -841,13 +849,13 @@ class MidyGM1 {
|
|
|
841
849
|
if (prev) {
|
|
842
850
|
const [prevNote, prevChannelNumber] = prev;
|
|
843
851
|
if (prevNote && !prevNote.ending) {
|
|
844
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
|
|
852
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
845
853
|
startTime, true);
|
|
846
854
|
}
|
|
847
855
|
}
|
|
848
856
|
this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
|
|
849
857
|
}
|
|
850
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime
|
|
858
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
851
859
|
const channel = this.channels[channelNumber];
|
|
852
860
|
const bankNumber = channel.bank;
|
|
853
861
|
const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
|
|
@@ -859,7 +867,6 @@ class MidyGM1 {
|
|
|
859
867
|
return;
|
|
860
868
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
861
869
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
862
|
-
note.noteOffEvent = noteOffEvent;
|
|
863
870
|
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
864
871
|
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
865
872
|
if (0.5 <= channel.state.sustainPedal) {
|
|
@@ -869,12 +876,6 @@ class MidyGM1 {
|
|
|
869
876
|
const scheduledNotes = channel.scheduledNotes;
|
|
870
877
|
note.index = scheduledNotes.length;
|
|
871
878
|
scheduledNotes.push(note);
|
|
872
|
-
if (noteOffEvent) {
|
|
873
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
|
|
874
|
-
if (notePromise) {
|
|
875
|
-
this.notePromises.push(notePromise);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
879
|
}
|
|
879
880
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
880
881
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -890,34 +891,36 @@ class MidyGM1 {
|
|
|
890
891
|
note.modulationLFO.stop();
|
|
891
892
|
}
|
|
892
893
|
}
|
|
893
|
-
|
|
894
|
+
releaseNote(channel, note, endTime) {
|
|
895
|
+
const volRelease = endTime + note.voiceParams.volRelease;
|
|
896
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
897
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
898
|
+
note.filterNode.frequency
|
|
899
|
+
.cancelScheduledValues(endTime)
|
|
900
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
894
901
|
note.volumeEnvelopeNode.gain
|
|
895
902
|
.cancelScheduledValues(endTime)
|
|
896
|
-
.linearRampToValueAtTime(0,
|
|
897
|
-
note.ending = true;
|
|
898
|
-
this.scheduleTask(() => {
|
|
899
|
-
note.bufferSource.loop = false;
|
|
900
|
-
}, stopTime);
|
|
903
|
+
.linearRampToValueAtTime(0, volRelease);
|
|
901
904
|
return new Promise((resolve) => {
|
|
902
|
-
|
|
903
|
-
|
|
905
|
+
this.scheduleTask(() => {
|
|
906
|
+
const bufferSource = note.bufferSource;
|
|
907
|
+
bufferSource.loop = false;
|
|
908
|
+
bufferSource.stop(stopTime);
|
|
904
909
|
this.disconnectNote(note);
|
|
910
|
+
channel.scheduledNotes[note.index] = undefined;
|
|
905
911
|
resolve();
|
|
906
|
-
};
|
|
907
|
-
note.bufferSource.stop(stopTime);
|
|
912
|
+
}, stopTime);
|
|
908
913
|
});
|
|
909
914
|
}
|
|
910
|
-
scheduleNoteOff(channelNumber,
|
|
915
|
+
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
911
916
|
const channel = this.channels[channelNumber];
|
|
912
917
|
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
913
918
|
return;
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
const stopTime = Math.min(volRelease, modRelease);
|
|
920
|
-
return this.stopNote(channel, note, endTime, stopTime);
|
|
919
|
+
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
920
|
+
if (!note)
|
|
921
|
+
return;
|
|
922
|
+
note.ending = true;
|
|
923
|
+
this.releaseNote(channel, note, endTime);
|
|
921
924
|
}
|
|
922
925
|
findNoteOffTarget(channel, noteNumber) {
|
|
923
926
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -934,16 +937,14 @@ class MidyGM1 {
|
|
|
934
937
|
}
|
|
935
938
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
936
939
|
scheduleTime ??= this.audioContext.currentTime;
|
|
937
|
-
|
|
938
|
-
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
939
|
-
return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
|
|
940
|
+
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
940
941
|
}
|
|
941
942
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
942
943
|
const velocity = halfVelocity * 2;
|
|
943
944
|
const channel = this.channels[channelNumber];
|
|
944
945
|
const promises = [];
|
|
945
946
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
946
|
-
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
|
|
947
|
+
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
947
948
|
promises.push(promise);
|
|
948
949
|
}
|
|
949
950
|
channel.sustainNotes = [];
|
|
@@ -1057,11 +1058,12 @@ class MidyGM1 {
|
|
|
1057
1058
|
return state;
|
|
1058
1059
|
}
|
|
1059
1060
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1060
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1061
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1061
1062
|
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1062
1063
|
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1063
|
-
let
|
|
1064
|
-
let
|
|
1064
|
+
let applyVolumeEnvelope = false;
|
|
1065
|
+
let applyFilterEnvelope = false;
|
|
1066
|
+
let applyPitchEnvelope = false;
|
|
1065
1067
|
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1066
1068
|
const prevValue = note.voiceParams[key];
|
|
1067
1069
|
if (value === prevValue)
|
|
@@ -1070,32 +1072,21 @@ class MidyGM1 {
|
|
|
1070
1072
|
if (key in this.voiceParamsHandlers) {
|
|
1071
1073
|
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1072
1074
|
}
|
|
1073
|
-
else
|
|
1074
|
-
if (
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
if (key in voiceParams)
|
|
1081
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1082
|
-
}
|
|
1083
|
-
this.setFilterEnvelope(note, scheduleTime);
|
|
1084
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1085
|
-
}
|
|
1086
|
-
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1087
|
-
if (appliedVolumeEnvelope)
|
|
1088
|
-
continue;
|
|
1089
|
-
appliedVolumeEnvelope = true;
|
|
1090
|
-
const noteVoiceParams = note.voiceParams;
|
|
1091
|
-
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1092
|
-
const key = volumeEnvelopeKeys[i];
|
|
1093
|
-
if (key in voiceParams)
|
|
1094
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1095
|
-
}
|
|
1096
|
-
this.setVolumeEnvelope(note, scheduleTime);
|
|
1075
|
+
else {
|
|
1076
|
+
if (volumeEnvelopeKeySet.has(key))
|
|
1077
|
+
applyVolumeEnvelope = true;
|
|
1078
|
+
if (filterEnvelopeKeySet.has(key))
|
|
1079
|
+
applyFilterEnvelope = true;
|
|
1080
|
+
if (pitchEnvelopeKeySet.has(key))
|
|
1081
|
+
applyPitchEnvelope = true;
|
|
1097
1082
|
}
|
|
1098
1083
|
}
|
|
1084
|
+
if (applyVolumeEnvelope)
|
|
1085
|
+
this.setVolumeEnvelope(note, scheduleTime);
|
|
1086
|
+
if (applyFilterEnvelope)
|
|
1087
|
+
this.setFilterEnvelope(note, scheduleTime);
|
|
1088
|
+
if (applyPitchEnvelope)
|
|
1089
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1099
1090
|
});
|
|
1100
1091
|
}
|
|
1101
1092
|
createControlChangeHandlers() {
|
|
@@ -1126,7 +1117,7 @@ class MidyGM1 {
|
|
|
1126
1117
|
}
|
|
1127
1118
|
updateModulation(channel, scheduleTime) {
|
|
1128
1119
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1129
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1120
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1130
1121
|
if (note.modulationDepth) {
|
|
1131
1122
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1132
1123
|
}
|
|
@@ -1187,7 +1178,7 @@ class MidyGM1 {
|
|
|
1187
1178
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1188
1179
|
channel.state.sustainPedal = value / 127;
|
|
1189
1180
|
if (64 <= value) {
|
|
1190
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1181
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1191
1182
|
channel.sustainNotes.push(note);
|
|
1192
1183
|
});
|
|
1193
1184
|
}
|