@marmooo/midy 0.2.9 → 0.3.1
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/README.md +2 -1
- package/esm/midy-GM1.d.ts +11 -7
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +114 -74
- package/esm/midy-GM2.d.ts +17 -11
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +250 -124
- package/esm/midy-GMLite.d.ts +14 -9
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +167 -81
- package/esm/midy.d.ts +20 -14
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +257 -129
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +11 -7
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +114 -74
- package/script/midy-GM2.d.ts +17 -11
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +250 -124
- package/script/midy-GMLite.d.ts +14 -9
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +167 -81
- package/script/midy.d.ts +20 -14
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +257 -129
package/README.md
CHANGED
|
@@ -9,7 +9,8 @@ This library provides several files depending on the implementation level.
|
|
|
9
9
|
[en](https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/gml-v1.pdf),
|
|
10
10
|
[ja](https://amei.or.jp/midistandardcommittee/Recommended_Practice/General_MIDI_Lite_v1.0_japanese.pdf))
|
|
11
11
|
- midy-GM1.js: support minimal GM1 (ref:
|
|
12
|
-
[en](https://
|
|
12
|
+
[en](https://midi.org/midi-1-0-detailed-specification),
|
|
13
|
+
[en full](https://archive.org/details/complete_midi_96-1-3/page/n1/mode/2up),
|
|
13
14
|
[ja](https://amei.or.jp/midistandardcommittee/MIDI1.0.pdf))
|
|
14
15
|
- midy-GM2.js: support minimal GM2 (ref:
|
|
15
16
|
[en v1.2a](https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/GM2-v12a.pdf),
|
package/esm/midy-GM1.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export class MidyGM1 {
|
|
2
2
|
static channelSettings: {
|
|
3
|
-
currentBufferSource: null;
|
|
4
|
-
isDrum: boolean;
|
|
5
3
|
detune: number;
|
|
6
|
-
|
|
4
|
+
programNumber: number;
|
|
7
5
|
bank: number;
|
|
8
6
|
dataMSB: number;
|
|
9
7
|
dataLSB: number;
|
|
@@ -15,6 +13,7 @@ export class MidyGM1 {
|
|
|
15
13
|
};
|
|
16
14
|
constructor(audioContext: any);
|
|
17
15
|
mode: string;
|
|
16
|
+
numChannels: number;
|
|
18
17
|
ticksPerBeat: number;
|
|
19
18
|
totalTime: number;
|
|
20
19
|
noteCheckInterval: number;
|
|
@@ -34,7 +33,7 @@ export class MidyGM1 {
|
|
|
34
33
|
timeline: any[];
|
|
35
34
|
instruments: any[];
|
|
36
35
|
notePromises: any[];
|
|
37
|
-
|
|
36
|
+
exclusiveClassNotes: any[];
|
|
38
37
|
audioContext: any;
|
|
39
38
|
masterVolume: any;
|
|
40
39
|
scheduler: any;
|
|
@@ -88,6 +87,7 @@ export class MidyGM1 {
|
|
|
88
87
|
instruments: Set<any>;
|
|
89
88
|
timeline: any[];
|
|
90
89
|
};
|
|
90
|
+
stopActiveNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
91
91
|
stopChannelNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
92
92
|
stopNotes(velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
93
93
|
start(): Promise<void>;
|
|
@@ -112,16 +112,19 @@ export class MidyGM1 {
|
|
|
112
112
|
clampCutoffFrequency(frequency: any): number;
|
|
113
113
|
setFilterEnvelope(note: any, scheduleTime: any): void;
|
|
114
114
|
startModulation(channel: any, note: any, scheduleTime: any): void;
|
|
115
|
-
getAudioBuffer(
|
|
115
|
+
getAudioBuffer(programNumber: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
|
|
116
116
|
createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
117
|
+
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
117
118
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
118
119
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
|
|
119
|
-
|
|
120
|
+
disconnectNote(note: any): void;
|
|
121
|
+
stopNote(endTime: any, stopTime: any, noteList: any, index: any): Promise<any>;
|
|
122
|
+
findNoteOffTarget(noteList: any): any[] | undefined;
|
|
120
123
|
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
121
124
|
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
|
|
122
125
|
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
123
126
|
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
124
|
-
handleProgramChange(channelNumber: any,
|
|
127
|
+
handleProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
125
128
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
126
129
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
127
130
|
setModLfoToPitch(channel: any, note: any, scheduleTime: any): void;
|
|
@@ -183,6 +186,7 @@ export class MidyGM1 {
|
|
|
183
186
|
handleCoarseTuningRPN(channelNumber: any, scheduleTime: any): void;
|
|
184
187
|
setCoarseTuning(channelNumber: any, value: any, scheduleTime: any): void;
|
|
185
188
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
189
|
+
resetAllStates(channelNumber: any): void;
|
|
186
190
|
resetAllControllers(channelNumber: any): void;
|
|
187
191
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
188
192
|
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
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":"AAiJA;
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAiJA;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;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAaC;IAED,6DA2BC;IAED,4DASC;IAED,2EAsDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,kGAiBC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAMC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDASC;IAED,2DASC;IAED,qDAQC;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,gHAwCC;IAED,0EAiBC;IAED,kGAsCC;IAED,6FAQC;IAED,gCASC;IAED,+EAiBC;IAED,oDAOC;IAED,yHAsBC;IAED,yGASC;IAED,4GAeC;IAED,mGA2BC;IAED,sFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED;;;;;;;;;;;;;MAeC;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,yCAUC;IAGD,8CAqBC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA3/CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,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
|
@@ -176,6 +176,12 @@ export class MidyGM1 {
|
|
|
176
176
|
writable: true,
|
|
177
177
|
value: "GM1"
|
|
178
178
|
});
|
|
179
|
+
Object.defineProperty(this, "numChannels", {
|
|
180
|
+
enumerable: true,
|
|
181
|
+
configurable: true,
|
|
182
|
+
writable: true,
|
|
183
|
+
value: 16
|
|
184
|
+
});
|
|
179
185
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
180
186
|
enumerable: true,
|
|
181
187
|
configurable: true,
|
|
@@ -290,11 +296,11 @@ export class MidyGM1 {
|
|
|
290
296
|
writable: true,
|
|
291
297
|
value: []
|
|
292
298
|
});
|
|
293
|
-
Object.defineProperty(this, "
|
|
299
|
+
Object.defineProperty(this, "exclusiveClassNotes", {
|
|
294
300
|
enumerable: true,
|
|
295
301
|
configurable: true,
|
|
296
302
|
writable: true,
|
|
297
|
-
value: new
|
|
303
|
+
value: new Array(128)
|
|
298
304
|
});
|
|
299
305
|
this.audioContext = audioContext;
|
|
300
306
|
this.masterVolume = new GainNode(audioContext);
|
|
@@ -361,8 +367,10 @@ export class MidyGM1 {
|
|
|
361
367
|
};
|
|
362
368
|
}
|
|
363
369
|
createChannels(audioContext) {
|
|
364
|
-
const channels = Array.from({ length:
|
|
370
|
+
const channels = Array.from({ length: this.numChannels }, () => {
|
|
365
371
|
return {
|
|
372
|
+
currentBufferSource: null,
|
|
373
|
+
isDrum: false,
|
|
366
374
|
...this.constructor.channelSettings,
|
|
367
375
|
state: new ControllerState(),
|
|
368
376
|
...this.setChannelAudioNodes(audioContext),
|
|
@@ -419,7 +427,7 @@ export class MidyGM1 {
|
|
|
419
427
|
const startTime = event.startTime + this.startDelay - offset;
|
|
420
428
|
switch (event.type) {
|
|
421
429
|
case "noteOn":
|
|
422
|
-
if (event.velocity
|
|
430
|
+
if (0 < event.velocity) {
|
|
423
431
|
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
424
432
|
break;
|
|
425
433
|
}
|
|
@@ -467,7 +475,7 @@ export class MidyGM1 {
|
|
|
467
475
|
if (queueIndex >= this.timeline.length) {
|
|
468
476
|
await Promise.all(this.notePromises);
|
|
469
477
|
this.notePromises = [];
|
|
470
|
-
this.
|
|
478
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
471
479
|
this.audioBufferCache.clear();
|
|
472
480
|
resolve();
|
|
473
481
|
return;
|
|
@@ -486,7 +494,7 @@ export class MidyGM1 {
|
|
|
486
494
|
else if (this.isStopping) {
|
|
487
495
|
await this.stopNotes(0, true, now);
|
|
488
496
|
this.notePromises = [];
|
|
489
|
-
this.
|
|
497
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
490
498
|
this.audioBufferCache.clear();
|
|
491
499
|
resolve();
|
|
492
500
|
this.isStopping = false;
|
|
@@ -495,7 +503,7 @@ export class MidyGM1 {
|
|
|
495
503
|
}
|
|
496
504
|
else if (this.isSeeking) {
|
|
497
505
|
this.stopNotes(0, true, now);
|
|
498
|
-
this.
|
|
506
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
499
507
|
this.startTime = this.audioContext.currentTime;
|
|
500
508
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
501
509
|
offset = this.resumeTime - this.startTime;
|
|
@@ -523,7 +531,7 @@ export class MidyGM1 {
|
|
|
523
531
|
extractMidiData(midi) {
|
|
524
532
|
const instruments = new Set();
|
|
525
533
|
const timeline = [];
|
|
526
|
-
const tmpChannels = new Array(
|
|
534
|
+
const tmpChannels = new Array(this.channels.length);
|
|
527
535
|
for (let i = 0; i < tmpChannels.length; i++) {
|
|
528
536
|
tmpChannels[i] = {
|
|
529
537
|
programNumber: -1,
|
|
@@ -586,6 +594,17 @@ export class MidyGM1 {
|
|
|
586
594
|
}
|
|
587
595
|
return { instruments, timeline };
|
|
588
596
|
}
|
|
597
|
+
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
598
|
+
const channel = this.channels[channelNumber];
|
|
599
|
+
const promises = [];
|
|
600
|
+
const activeNotes = this.getActiveNotes(channel, scheduleTime);
|
|
601
|
+
activeNotes.forEach((note) => {
|
|
602
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
|
|
603
|
+
this.notePromises.push(promise);
|
|
604
|
+
promises.push(promise);
|
|
605
|
+
});
|
|
606
|
+
return Promise.all(promises);
|
|
607
|
+
}
|
|
589
608
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
590
609
|
const channel = this.channels[channelNumber];
|
|
591
610
|
const promises = [];
|
|
@@ -615,6 +634,9 @@ export class MidyGM1 {
|
|
|
615
634
|
if (!this.isPlaying)
|
|
616
635
|
return;
|
|
617
636
|
this.isStopping = true;
|
|
637
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
638
|
+
this.resetAllStates(i);
|
|
639
|
+
}
|
|
618
640
|
}
|
|
619
641
|
pause() {
|
|
620
642
|
if (!this.isPlaying || this.isPaused)
|
|
@@ -654,6 +676,8 @@ export class MidyGM1 {
|
|
|
654
676
|
const note = noteList[i];
|
|
655
677
|
if (!note)
|
|
656
678
|
continue;
|
|
679
|
+
if (note.ending)
|
|
680
|
+
continue;
|
|
657
681
|
callback(note);
|
|
658
682
|
}
|
|
659
683
|
});
|
|
@@ -792,8 +816,8 @@ export class MidyGM1 {
|
|
|
792
816
|
note.modulationLFO.connect(note.volumeDepth);
|
|
793
817
|
note.volumeDepth.connect(note.volumeEnvelopeNode.gain);
|
|
794
818
|
}
|
|
795
|
-
async getAudioBuffer(
|
|
796
|
-
const audioBufferId = this.getAudioBufferId(
|
|
819
|
+
async getAudioBuffer(programNumber, noteNumber, velocity, voiceParams, isSF3) {
|
|
820
|
+
const audioBufferId = this.getAudioBufferId(programNumber, noteNumber, velocity);
|
|
797
821
|
const cache = this.audioBufferCache.get(audioBufferId);
|
|
798
822
|
if (cache) {
|
|
799
823
|
cache.counter += 1;
|
|
@@ -816,7 +840,7 @@ export class MidyGM1 {
|
|
|
816
840
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
817
841
|
const voiceParams = voice.getAllParams(controllerState);
|
|
818
842
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
819
|
-
const audioBuffer = await this.getAudioBuffer(channel.
|
|
843
|
+
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
820
844
|
note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
|
|
821
845
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
822
846
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
@@ -834,14 +858,28 @@ export class MidyGM1 {
|
|
|
834
858
|
note.bufferSource.start(startTime);
|
|
835
859
|
return note;
|
|
836
860
|
}
|
|
861
|
+
handleExclusiveClass(note, channelNumber, startTime) {
|
|
862
|
+
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
863
|
+
if (exclusiveClass === 0)
|
|
864
|
+
return;
|
|
865
|
+
const prev = this.exclusiveClassNotes[exclusiveClass];
|
|
866
|
+
if (prev) {
|
|
867
|
+
const [prevNote, prevChannelNumber] = prev;
|
|
868
|
+
if (prevNote && !prevNote.ending) {
|
|
869
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
870
|
+
startTime, true);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
|
|
874
|
+
}
|
|
837
875
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
838
876
|
const channel = this.channels[channelNumber];
|
|
839
877
|
const bankNumber = channel.bank;
|
|
840
|
-
const soundFontIndex = this.soundFontTable[channel.
|
|
878
|
+
const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
|
|
841
879
|
if (soundFontIndex === undefined)
|
|
842
880
|
return;
|
|
843
881
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
844
|
-
const voice = soundFont.getVoice(bankNumber, channel.
|
|
882
|
+
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
845
883
|
if (!voice)
|
|
846
884
|
return;
|
|
847
885
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
@@ -851,32 +889,33 @@ export class MidyGM1 {
|
|
|
851
889
|
if (0.5 <= channel.state.sustainPedal) {
|
|
852
890
|
channel.sustainNotes.push(note);
|
|
853
891
|
}
|
|
854
|
-
|
|
855
|
-
if (exclusiveClass !== 0) {
|
|
856
|
-
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
857
|
-
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
858
|
-
const [prevNote, prevChannelNumber] = prevEntry;
|
|
859
|
-
if (prevNote && !prevNote.ending) {
|
|
860
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
861
|
-
startTime, true);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
865
|
-
}
|
|
892
|
+
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
866
893
|
const scheduledNotes = channel.scheduledNotes;
|
|
867
|
-
|
|
868
|
-
|
|
894
|
+
let noteList = scheduledNotes.get(noteNumber);
|
|
895
|
+
if (noteList) {
|
|
896
|
+
noteList.push(note);
|
|
869
897
|
}
|
|
870
898
|
else {
|
|
871
|
-
|
|
899
|
+
noteList = [note];
|
|
900
|
+
scheduledNotes.set(noteNumber, noteList);
|
|
872
901
|
}
|
|
873
902
|
}
|
|
874
903
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
875
904
|
scheduleTime ??= this.audioContext.currentTime;
|
|
876
905
|
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
|
|
877
906
|
}
|
|
878
|
-
|
|
879
|
-
|
|
907
|
+
disconnectNote(note) {
|
|
908
|
+
note.bufferSource.disconnect();
|
|
909
|
+
note.filterNode.disconnect();
|
|
910
|
+
note.volumeEnvelopeNode.disconnect();
|
|
911
|
+
if (note.modulationDepth) {
|
|
912
|
+
note.volumeDepth.disconnect();
|
|
913
|
+
note.modulationDepth.disconnect();
|
|
914
|
+
note.modulationLFO.stop();
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
stopNote(endTime, stopTime, noteList, index) {
|
|
918
|
+
const note = noteList[index];
|
|
880
919
|
note.volumeEnvelopeNode.gain
|
|
881
920
|
.cancelScheduledValues(endTime)
|
|
882
921
|
.linearRampToValueAtTime(0, stopTime);
|
|
@@ -886,41 +925,43 @@ export class MidyGM1 {
|
|
|
886
925
|
}, stopTime);
|
|
887
926
|
return new Promise((resolve) => {
|
|
888
927
|
note.bufferSource.onended = () => {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
note.filterNode.disconnect();
|
|
892
|
-
note.volumeEnvelopeNode.disconnect();
|
|
893
|
-
if (note.modulationDepth) {
|
|
894
|
-
note.volumeDepth.disconnect();
|
|
895
|
-
note.modulationDepth.disconnect();
|
|
896
|
-
note.modulationLFO.stop();
|
|
897
|
-
}
|
|
928
|
+
noteList[index] = undefined;
|
|
929
|
+
this.disconnectNote(note);
|
|
898
930
|
resolve();
|
|
899
931
|
};
|
|
900
932
|
note.bufferSource.stop(stopTime);
|
|
901
933
|
});
|
|
902
934
|
}
|
|
935
|
+
findNoteOffTarget(noteList) {
|
|
936
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
937
|
+
const note = noteList[i];
|
|
938
|
+
if (!note)
|
|
939
|
+
continue;
|
|
940
|
+
if (note.ending)
|
|
941
|
+
continue;
|
|
942
|
+
return [note, i];
|
|
943
|
+
}
|
|
944
|
+
}
|
|
903
945
|
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
904
946
|
const channel = this.channels[channelNumber];
|
|
905
947
|
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
906
948
|
return;
|
|
907
949
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
908
950
|
return;
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
}
|
|
951
|
+
const noteList = channel.scheduledNotes.get(noteNumber);
|
|
952
|
+
if (!noteList)
|
|
953
|
+
return; // be careful with drum channel
|
|
954
|
+
const noteOffTarget = this.findNoteOffTarget(noteList, endTime);
|
|
955
|
+
if (!noteOffTarget)
|
|
956
|
+
return;
|
|
957
|
+
const [note, i] = noteOffTarget;
|
|
958
|
+
const volRelease = endTime + note.voiceParams.volRelease;
|
|
959
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
960
|
+
note.filterNode.frequency
|
|
961
|
+
.cancelScheduledValues(endTime)
|
|
962
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
963
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
964
|
+
return this.stopNote(endTime, stopTime, noteList, i);
|
|
924
965
|
}
|
|
925
966
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
926
967
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -955,9 +996,9 @@ export class MidyGM1 {
|
|
|
955
996
|
console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
|
|
956
997
|
}
|
|
957
998
|
}
|
|
958
|
-
handleProgramChange(channelNumber,
|
|
999
|
+
handleProgramChange(channelNumber, programNumber, _scheduleTime) {
|
|
959
1000
|
const channel = this.channels[channelNumber];
|
|
960
|
-
channel.
|
|
1001
|
+
channel.programNumber = programNumber;
|
|
961
1002
|
}
|
|
962
1003
|
handlePitchBendMessage(channelNumber, lsb, msb, scheduleTime) {
|
|
963
1004
|
const pitchBend = msb * 128 + lsb;
|
|
@@ -965,8 +1006,6 @@ export class MidyGM1 {
|
|
|
965
1006
|
}
|
|
966
1007
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
967
1008
|
const channel = this.channels[channelNumber];
|
|
968
|
-
if (channel.isDrum)
|
|
969
|
-
return;
|
|
970
1009
|
scheduleTime ??= this.audioContext.currentTime;
|
|
971
1010
|
const state = channel.state;
|
|
972
1011
|
const prev = state.pitchWheel * 2 - 1;
|
|
@@ -1129,8 +1168,6 @@ export class MidyGM1 {
|
|
|
1129
1168
|
}
|
|
1130
1169
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1131
1170
|
const channel = this.channels[channelNumber];
|
|
1132
|
-
if (channel.isDrum)
|
|
1133
|
-
return;
|
|
1134
1171
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1135
1172
|
channel.state.modulationDepth = modulation / 127;
|
|
1136
1173
|
this.updateModulation(channel, scheduleTime);
|
|
@@ -1177,8 +1214,6 @@ export class MidyGM1 {
|
|
|
1177
1214
|
}
|
|
1178
1215
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1179
1216
|
const channel = this.channels[channelNumber];
|
|
1180
|
-
if (channel.isDrum)
|
|
1181
|
-
return;
|
|
1182
1217
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1183
1218
|
channel.state.sustainPedal = value / 127;
|
|
1184
1219
|
if (64 <= value) {
|
|
@@ -1251,8 +1286,6 @@ export class MidyGM1 {
|
|
|
1251
1286
|
}
|
|
1252
1287
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1253
1288
|
const channel = this.channels[channelNumber];
|
|
1254
|
-
if (channel.isDrum)
|
|
1255
|
-
return;
|
|
1256
1289
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1257
1290
|
const state = channel.state;
|
|
1258
1291
|
const prev = state.pitchWheelSensitivity;
|
|
@@ -1270,8 +1303,6 @@ export class MidyGM1 {
|
|
|
1270
1303
|
}
|
|
1271
1304
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1272
1305
|
const channel = this.channels[channelNumber];
|
|
1273
|
-
if (channel.isDrum)
|
|
1274
|
-
return;
|
|
1275
1306
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1276
1307
|
const prev = channel.fineTuning;
|
|
1277
1308
|
const next = (value - 8192) / 8.192; // cent
|
|
@@ -1287,8 +1318,6 @@ export class MidyGM1 {
|
|
|
1287
1318
|
}
|
|
1288
1319
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1289
1320
|
const channel = this.channels[channelNumber];
|
|
1290
|
-
if (channel.isDrum)
|
|
1291
|
-
return;
|
|
1292
1321
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1293
1322
|
const prev = channel.coarseTuning;
|
|
1294
1323
|
const next = (value - 64) * 100; // cent
|
|
@@ -1298,14 +1327,26 @@ export class MidyGM1 {
|
|
|
1298
1327
|
}
|
|
1299
1328
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
1300
1329
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1301
|
-
return this.
|
|
1330
|
+
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
1331
|
+
}
|
|
1332
|
+
resetAllStates(channelNumber) {
|
|
1333
|
+
const channel = this.channels[channelNumber];
|
|
1334
|
+
const state = channel.state;
|
|
1335
|
+
for (const type of Object.keys(defaultControllerState)) {
|
|
1336
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
1337
|
+
}
|
|
1338
|
+
for (const type of Object.keys(this.constructor.channelSettings)) {
|
|
1339
|
+
channel[type] = this.constructor.channelSettings[type];
|
|
1340
|
+
}
|
|
1341
|
+
this.mode = "GM1";
|
|
1302
1342
|
}
|
|
1343
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
|
|
1303
1344
|
resetAllControllers(channelNumber) {
|
|
1304
1345
|
const stateTypes = [
|
|
1346
|
+
"pitchWheel",
|
|
1305
1347
|
"expression",
|
|
1306
1348
|
"modulationDepth",
|
|
1307
1349
|
"sustainPedal",
|
|
1308
|
-
"pitchWheelSensitivity",
|
|
1309
1350
|
];
|
|
1310
1351
|
const channel = this.channels[channelNumber];
|
|
1311
1352
|
const state = channel.state;
|
|
@@ -1324,7 +1365,7 @@ export class MidyGM1 {
|
|
|
1324
1365
|
}
|
|
1325
1366
|
allNotesOff(channelNumber, _value, scheduleTime) {
|
|
1326
1367
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1327
|
-
return this.
|
|
1368
|
+
return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
|
|
1328
1369
|
}
|
|
1329
1370
|
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1330
1371
|
switch (data[2]) {
|
|
@@ -1394,6 +1435,7 @@ export class MidyGM1 {
|
|
|
1394
1435
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1395
1436
|
}
|
|
1396
1437
|
}
|
|
1438
|
+
// https://github.com/marmooo/js-timer-benchmark
|
|
1397
1439
|
scheduleTask(callback, scheduleTime) {
|
|
1398
1440
|
return new Promise((resolve) => {
|
|
1399
1441
|
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
@@ -1418,10 +1460,8 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1418
1460
|
configurable: true,
|
|
1419
1461
|
writable: true,
|
|
1420
1462
|
value: {
|
|
1421
|
-
currentBufferSource: null,
|
|
1422
|
-
isDrum: false,
|
|
1423
1463
|
detune: 0,
|
|
1424
|
-
|
|
1464
|
+
programNumber: 0,
|
|
1425
1465
|
bank: 0,
|
|
1426
1466
|
dataMSB: 0,
|
|
1427
1467
|
dataLSB: 0,
|
package/esm/midy-GM2.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export class MidyGM2 {
|
|
2
2
|
static channelSettings: {
|
|
3
|
-
currentBufferSource: null;
|
|
4
|
-
isDrum: boolean;
|
|
5
3
|
detune: number;
|
|
6
|
-
|
|
4
|
+
programNumber: number;
|
|
7
5
|
bank: number;
|
|
8
6
|
bankMSB: number;
|
|
9
7
|
bankLSB: number;
|
|
@@ -23,8 +21,6 @@ export class MidyGM2 {
|
|
|
23
21
|
};
|
|
24
22
|
});
|
|
25
23
|
mode: string;
|
|
26
|
-
ticksPerBeat: number;
|
|
27
|
-
totalTime: number;
|
|
28
24
|
masterFineTuning: number;
|
|
29
25
|
masterCoarseTuning: number;
|
|
30
26
|
reverb: {
|
|
@@ -38,6 +34,9 @@ export class MidyGM2 {
|
|
|
38
34
|
sendToReverb: number;
|
|
39
35
|
delayTimes: any[];
|
|
40
36
|
};
|
|
37
|
+
numChannels: number;
|
|
38
|
+
ticksPerBeat: number;
|
|
39
|
+
totalTime: number;
|
|
41
40
|
noteCheckInterval: number;
|
|
42
41
|
lookAhead: number;
|
|
43
42
|
startDelay: number;
|
|
@@ -55,7 +54,8 @@ export class MidyGM2 {
|
|
|
55
54
|
timeline: any[];
|
|
56
55
|
instruments: any[];
|
|
57
56
|
notePromises: any[];
|
|
58
|
-
|
|
57
|
+
exclusiveClassNotes: any[];
|
|
58
|
+
drumExclusiveClassNotes: any[];
|
|
59
59
|
defaultOptions: {
|
|
60
60
|
reverbAlgorithm: (audioContext: any) => {
|
|
61
61
|
input: any;
|
|
@@ -135,8 +135,7 @@ export class MidyGM2 {
|
|
|
135
135
|
};
|
|
136
136
|
createChannels(audioContext: any): any[];
|
|
137
137
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
138
|
-
|
|
139
|
-
createBufferSource(channel: any, note: any, voiceParams: any, audioBuffer: any): any;
|
|
138
|
+
createBufferSource(voiceParams: any, audioBuffer: any): any;
|
|
140
139
|
findPortamentoTarget(queueIndex: any): any;
|
|
141
140
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
142
141
|
getQueueIndex(second: any): number;
|
|
@@ -148,6 +147,7 @@ export class MidyGM2 {
|
|
|
148
147
|
instruments: Set<any>;
|
|
149
148
|
timeline: any[];
|
|
150
149
|
};
|
|
150
|
+
stopActiveNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
151
151
|
stopChannelNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
152
152
|
stopNotes(velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
153
153
|
start(): Promise<void>;
|
|
@@ -199,18 +199,23 @@ export class MidyGM2 {
|
|
|
199
199
|
setFilterEnvelope(channel: any, note: any, scheduleTime: any): void;
|
|
200
200
|
startModulation(channel: any, note: any, scheduleTime: any): void;
|
|
201
201
|
startVibrato(channel: any, note: any, scheduleTime: any): void;
|
|
202
|
-
getAudioBuffer(
|
|
202
|
+
getAudioBuffer(programNumber: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
|
|
203
203
|
createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
|
|
204
204
|
calcBank(channel: any): any;
|
|
205
|
+
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
206
|
+
handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
207
|
+
isDrumNoteOffException(channel: any, noteNumber: any): boolean;
|
|
205
208
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
|
|
206
209
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
|
|
207
|
-
|
|
210
|
+
disconnectNote(note: any): void;
|
|
211
|
+
stopNote(endTime: any, stopTime: any, noteList: any, index: any): Promise<any>;
|
|
212
|
+
findNoteOffTarget(noteList: any): any[] | undefined;
|
|
208
213
|
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any, portamentoNoteNumber: any): Promise<any> | undefined;
|
|
209
214
|
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
|
|
210
215
|
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
211
216
|
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
212
217
|
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
213
|
-
handleProgramChange(channelNumber: any,
|
|
218
|
+
handleProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
214
219
|
handleChannelPressure(channelNumber: any, value: any, scheduleTime: any): void;
|
|
215
220
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
216
221
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
@@ -301,6 +306,7 @@ export class MidyGM2 {
|
|
|
301
306
|
handleModulationDepthRangeRPN(channelNumber: any, scheduleTime: any): void;
|
|
302
307
|
setModulationDepthRange(channelNumber: any, modulationDepthRange: any, scheduleTime: any): void;
|
|
303
308
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
309
|
+
resetAllStates(channelNumber: any): void;
|
|
304
310
|
resetAllControllers(channelNumber: any): void;
|
|
305
311
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
306
312
|
omniOff(channelNumber: any, value: any, scheduleTime: any): void;
|
package/esm/midy-GM2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
|