@marmooo/midy 0.2.7 → 0.2.9
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 -2
- package/esm/midy-GM1.d.ts +14 -10
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +125 -86
- package/esm/midy-GM2.d.ts +35 -31
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +263 -187
- package/esm/midy-GMLite.d.ts +11 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +118 -76
- package/esm/midy.d.ts +35 -31
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +326 -253
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +14 -10
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +125 -86
- package/script/midy-GM2.d.ts +35 -31
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +263 -187
- package/script/midy-GMLite.d.ts +11 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +118 -76
- package/script/midy.d.ts +35 -31
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +326 -253
package/package.json
CHANGED
package/script/midy-GM1.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class MidyGM1 {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
|
+
isDrum: boolean;
|
|
4
5
|
detune: number;
|
|
5
6
|
program: number;
|
|
6
7
|
bank: number;
|
|
@@ -8,11 +9,12 @@ export class MidyGM1 {
|
|
|
8
9
|
dataLSB: number;
|
|
9
10
|
rpnMSB: number;
|
|
10
11
|
rpnLSB: number;
|
|
12
|
+
modulationDepthRange: number;
|
|
11
13
|
fineTuning: number;
|
|
12
14
|
coarseTuning: number;
|
|
13
|
-
modulationDepthRange: number;
|
|
14
15
|
};
|
|
15
16
|
constructor(audioContext: any);
|
|
17
|
+
mode: string;
|
|
16
18
|
ticksPerBeat: number;
|
|
17
19
|
totalTime: number;
|
|
18
20
|
noteCheckInterval: number;
|
|
@@ -35,6 +37,8 @@ export class MidyGM1 {
|
|
|
35
37
|
exclusiveClassMap: SparseMap;
|
|
36
38
|
audioContext: any;
|
|
37
39
|
masterVolume: any;
|
|
40
|
+
scheduler: any;
|
|
41
|
+
schedulerBuffer: any;
|
|
38
42
|
voiceParamsHandlers: {
|
|
39
43
|
modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
|
|
40
44
|
vibLfoToPitch: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
|
|
@@ -73,7 +77,7 @@ export class MidyGM1 {
|
|
|
73
77
|
};
|
|
74
78
|
createChannels(audioContext: any): any[];
|
|
75
79
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
76
|
-
|
|
80
|
+
createBufferSource(audioBuffer: any, voiceParams: any): any;
|
|
77
81
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
78
82
|
getQueueIndex(second: any): number;
|
|
79
83
|
playNotes(): Promise<any>;
|
|
@@ -93,7 +97,7 @@ export class MidyGM1 {
|
|
|
93
97
|
seekTo(second: any): void;
|
|
94
98
|
calcTotalTime(): number;
|
|
95
99
|
currentTime(): number;
|
|
96
|
-
processScheduledNotes(channel: any,
|
|
100
|
+
processScheduledNotes(channel: any, callback: any): void;
|
|
97
101
|
getActiveNotes(channel: any, scheduleTime: any): SparseMap;
|
|
98
102
|
getActiveNote(noteList: any, scheduleTime: any): any;
|
|
99
103
|
cbToRatio(cb: any): number;
|
|
@@ -115,7 +119,7 @@ export class MidyGM1 {
|
|
|
115
119
|
stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
|
|
116
120
|
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
117
121
|
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
|
|
118
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
122
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
119
123
|
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
120
124
|
handleProgramChange(channelNumber: any, program: any, _scheduleTime: any): void;
|
|
121
125
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
@@ -174,15 +178,15 @@ export class MidyGM1 {
|
|
|
174
178
|
dataEntryMSB(channelNumber: any, value: any, scheduleTime: any): void;
|
|
175
179
|
handlePitchBendRangeRPN(channelNumber: any, scheduleTime: any): void;
|
|
176
180
|
setPitchBendRange(channelNumber: any, value: any, scheduleTime: any): void;
|
|
177
|
-
handleFineTuningRPN(channelNumber: any): void;
|
|
178
|
-
setFineTuning(channelNumber: any, value: any): void;
|
|
179
|
-
handleCoarseTuningRPN(channelNumber: any): void;
|
|
180
|
-
setCoarseTuning(channelNumber: any, value: any): void;
|
|
181
|
+
handleFineTuningRPN(channelNumber: any, scheduleTime: any): void;
|
|
182
|
+
setFineTuning(channelNumber: any, value: any, scheduleTime: any): void;
|
|
183
|
+
handleCoarseTuningRPN(channelNumber: any, scheduleTime: any): void;
|
|
184
|
+
setCoarseTuning(channelNumber: any, value: any, scheduleTime: any): void;
|
|
181
185
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
182
186
|
resetAllControllers(channelNumber: any): void;
|
|
183
187
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
184
|
-
handleUniversalNonRealTimeExclusiveMessage(data: any,
|
|
185
|
-
GM1SystemOn(): void;
|
|
188
|
+
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
189
|
+
GM1SystemOn(scheduleTime: any): void;
|
|
186
190
|
handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
187
191
|
handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
|
|
188
192
|
setMasterVolume(volume: any, scheduleTime: any): void;
|
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":"AAiJA;
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAiJA;IAuBE;;;;;;;;;;;;;MAaE;IAEF,+BAcC;IAnDD,aAAa;IACb,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,6BAAuC;IAkBrC,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,yCAWC;IAED,6DA2BC;IAED,4DASC;IAED,2EAsDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;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,yGAgBC;IAED,gHAwCC;IAED,kGAkDC;IAED,6FAQC;IAED,qFAwBC;IAED,yHAuBC;IAED,yGASC;IAED,4GAeC;IAED,mGA2BC;IAED,gFAGC;IAED,wFAGC;IAED,sEAWC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED;;;;;;;;;;;;;MAeC;IAED,kGAWC;IAED,wDAUC;IAED,iFAMC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAYC;IAED,kFAeC;IAED,2DAMC;IAED,uDAkBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAWC;IAED,iEAKC;IAED,uEASC;IAED,mEAKC;IAED,yEASC;IAED,gFAGC;IAED,8CAqBC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAED,6DAgBC;CACF;AA97CD;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/script/midy-GM1.js
CHANGED
|
@@ -173,6 +173,12 @@ const volumeEnvelopeKeys = [
|
|
|
173
173
|
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
174
174
|
class MidyGM1 {
|
|
175
175
|
constructor(audioContext) {
|
|
176
|
+
Object.defineProperty(this, "mode", {
|
|
177
|
+
enumerable: true,
|
|
178
|
+
configurable: true,
|
|
179
|
+
writable: true,
|
|
180
|
+
value: "GM1"
|
|
181
|
+
});
|
|
176
182
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
177
183
|
enumerable: true,
|
|
178
184
|
configurable: true,
|
|
@@ -295,10 +301,16 @@ class MidyGM1 {
|
|
|
295
301
|
});
|
|
296
302
|
this.audioContext = audioContext;
|
|
297
303
|
this.masterVolume = new GainNode(audioContext);
|
|
304
|
+
this.scheduler = new GainNode(audioContext, { gain: 0 });
|
|
305
|
+
this.schedulerBuffer = new AudioBuffer({
|
|
306
|
+
length: 1,
|
|
307
|
+
sampleRate: audioContext.sampleRate,
|
|
308
|
+
});
|
|
298
309
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
299
310
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
300
311
|
this.channels = this.createChannels(audioContext);
|
|
301
312
|
this.masterVolume.connect(audioContext.destination);
|
|
313
|
+
this.scheduler.connect(audioContext.destination);
|
|
302
314
|
this.GM1SystemOn();
|
|
303
315
|
}
|
|
304
316
|
initSoundFontTable() {
|
|
@@ -358,6 +370,7 @@ class MidyGM1 {
|
|
|
358
370
|
state: new ControllerState(),
|
|
359
371
|
...this.setChannelAudioNodes(audioContext),
|
|
360
372
|
scheduledNotes: new SparseMap(128),
|
|
373
|
+
sustainNotes: [],
|
|
361
374
|
};
|
|
362
375
|
});
|
|
363
376
|
return channels;
|
|
@@ -391,7 +404,7 @@ class MidyGM1 {
|
|
|
391
404
|
return audioBuffer;
|
|
392
405
|
}
|
|
393
406
|
}
|
|
394
|
-
|
|
407
|
+
createBufferSource(audioBuffer, voiceParams) {
|
|
395
408
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
396
409
|
bufferSource.buffer = audioBuffer;
|
|
397
410
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
@@ -415,7 +428,7 @@ class MidyGM1 {
|
|
|
415
428
|
}
|
|
416
429
|
/* falls through */
|
|
417
430
|
case "noteOff": {
|
|
418
|
-
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime);
|
|
431
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
419
432
|
if (notePromise) {
|
|
420
433
|
this.notePromises.push(notePromise);
|
|
421
434
|
}
|
|
@@ -579,15 +592,10 @@ class MidyGM1 {
|
|
|
579
592
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
580
593
|
const channel = this.channels[channelNumber];
|
|
581
594
|
const promises = [];
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
continue;
|
|
587
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
588
|
-
this.notePromises.push(promise);
|
|
589
|
-
promises.push(promise);
|
|
590
|
-
}
|
|
595
|
+
this.processScheduledNotes(channel, (note) => {
|
|
596
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
597
|
+
this.notePromises.push(promise);
|
|
598
|
+
promises.push(promise);
|
|
591
599
|
});
|
|
592
600
|
channel.scheduledNotes.clear();
|
|
593
601
|
return Promise.all(promises);
|
|
@@ -643,14 +651,12 @@ class MidyGM1 {
|
|
|
643
651
|
const now = this.audioContext.currentTime;
|
|
644
652
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
645
653
|
}
|
|
646
|
-
processScheduledNotes(channel,
|
|
654
|
+
processScheduledNotes(channel, callback) {
|
|
647
655
|
channel.scheduledNotes.forEach((noteList) => {
|
|
648
656
|
for (let i = 0; i < noteList.length; i++) {
|
|
649
657
|
const note = noteList[i];
|
|
650
658
|
if (!note)
|
|
651
659
|
continue;
|
|
652
|
-
if (scheduleTime < note.startTime)
|
|
653
|
-
continue;
|
|
654
660
|
callback(note);
|
|
655
661
|
}
|
|
656
662
|
});
|
|
@@ -696,7 +702,7 @@ class MidyGM1 {
|
|
|
696
702
|
return tuning + pitch;
|
|
697
703
|
}
|
|
698
704
|
updateChannelDetune(channel, scheduleTime) {
|
|
699
|
-
this.processScheduledNotes(channel,
|
|
705
|
+
this.processScheduledNotes(channel, (note) => {
|
|
700
706
|
this.updateDetune(channel, note, scheduleTime);
|
|
701
707
|
});
|
|
702
708
|
}
|
|
@@ -814,7 +820,7 @@ class MidyGM1 {
|
|
|
814
820
|
const voiceParams = voice.getAllParams(controllerState);
|
|
815
821
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
816
822
|
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
817
|
-
note.bufferSource = this.
|
|
823
|
+
note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
|
|
818
824
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
819
825
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
820
826
|
type: "lowpass",
|
|
@@ -845,15 +851,17 @@ class MidyGM1 {
|
|
|
845
851
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
846
852
|
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
847
853
|
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
854
|
+
if (0.5 <= channel.state.sustainPedal) {
|
|
855
|
+
channel.sustainNotes.push(note);
|
|
856
|
+
}
|
|
848
857
|
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
849
858
|
if (exclusiveClass !== 0) {
|
|
850
859
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
851
860
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
852
861
|
const [prevNote, prevChannelNumber] = prevEntry;
|
|
853
|
-
if (!prevNote.ending) {
|
|
862
|
+
if (prevNote && !prevNote.ending) {
|
|
854
863
|
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
855
|
-
startTime,
|
|
856
|
-
true);
|
|
864
|
+
startTime, true);
|
|
857
865
|
}
|
|
858
866
|
}
|
|
859
867
|
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
@@ -897,7 +905,7 @@ class MidyGM1 {
|
|
|
897
905
|
}
|
|
898
906
|
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
899
907
|
const channel = this.channels[channelNumber];
|
|
900
|
-
if (!force && 0.5
|
|
908
|
+
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
901
909
|
return;
|
|
902
910
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
903
911
|
return;
|
|
@@ -925,11 +933,11 @@ class MidyGM1 {
|
|
|
925
933
|
const velocity = halfVelocity * 2;
|
|
926
934
|
const channel = this.channels[channelNumber];
|
|
927
935
|
const promises = [];
|
|
928
|
-
|
|
929
|
-
const
|
|
930
|
-
const promise = this.noteOff(channelNumber, noteNumber, velocity);
|
|
936
|
+
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
937
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
931
938
|
promises.push(promise);
|
|
932
|
-
}
|
|
939
|
+
}
|
|
940
|
+
channel.sustainNotes = [];
|
|
933
941
|
return promises;
|
|
934
942
|
}
|
|
935
943
|
handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
|
|
@@ -959,8 +967,10 @@ class MidyGM1 {
|
|
|
959
967
|
this.setPitchBend(channelNumber, pitchBend, scheduleTime);
|
|
960
968
|
}
|
|
961
969
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
962
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
963
970
|
const channel = this.channels[channelNumber];
|
|
971
|
+
if (channel.isDrum)
|
|
972
|
+
return;
|
|
973
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
964
974
|
const state = channel.state;
|
|
965
975
|
const prev = state.pitchWheel * 2 - 1;
|
|
966
976
|
const next = (value - 8192) / 8192;
|
|
@@ -1040,48 +1050,43 @@ class MidyGM1 {
|
|
|
1040
1050
|
return state;
|
|
1041
1051
|
}
|
|
1042
1052
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1053
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1054
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1055
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1056
|
+
let appliedFilterEnvelope = false;
|
|
1057
|
+
let appliedVolumeEnvelope = false;
|
|
1058
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1059
|
+
const prevValue = note.voiceParams[key];
|
|
1060
|
+
if (value === prevValue)
|
|
1047
1061
|
continue;
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (value === prevValue)
|
|
1062
|
+
note.voiceParams[key] = value;
|
|
1063
|
+
if (key in this.voiceParamsHandlers) {
|
|
1064
|
+
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1065
|
+
}
|
|
1066
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1067
|
+
if (appliedFilterEnvelope)
|
|
1055
1068
|
continue;
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
continue;
|
|
1063
|
-
appliedFilterEnvelope = true;
|
|
1064
|
-
const noteVoiceParams = note.voiceParams;
|
|
1065
|
-
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1066
|
-
const key = filterEnvelopeKeys[i];
|
|
1067
|
-
if (key in voiceParams)
|
|
1068
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1069
|
-
}
|
|
1070
|
-
this.setFilterEnvelope(note, scheduleTime);
|
|
1071
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1069
|
+
appliedFilterEnvelope = true;
|
|
1070
|
+
const noteVoiceParams = note.voiceParams;
|
|
1071
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1072
|
+
const key = filterEnvelopeKeys[i];
|
|
1073
|
+
if (key in voiceParams)
|
|
1074
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1072
1075
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1076
|
+
this.setFilterEnvelope(note, scheduleTime);
|
|
1077
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1078
|
+
}
|
|
1079
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1080
|
+
if (appliedVolumeEnvelope)
|
|
1081
|
+
continue;
|
|
1082
|
+
appliedVolumeEnvelope = true;
|
|
1083
|
+
const noteVoiceParams = note.voiceParams;
|
|
1084
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1085
|
+
const key = volumeEnvelopeKeys[i];
|
|
1086
|
+
if (key in voiceParams)
|
|
1087
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1084
1088
|
}
|
|
1089
|
+
this.setVolumeEnvelope(note, scheduleTime);
|
|
1085
1090
|
}
|
|
1086
1091
|
}
|
|
1087
1092
|
});
|
|
@@ -1114,9 +1119,8 @@ class MidyGM1 {
|
|
|
1114
1119
|
}
|
|
1115
1120
|
}
|
|
1116
1121
|
updateModulation(channel, scheduleTime) {
|
|
1117
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1118
1122
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1119
|
-
this.processScheduledNotes(channel,
|
|
1123
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1120
1124
|
if (note.modulationDepth) {
|
|
1121
1125
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1122
1126
|
}
|
|
@@ -1128,10 +1132,14 @@ class MidyGM1 {
|
|
|
1128
1132
|
}
|
|
1129
1133
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1130
1134
|
const channel = this.channels[channelNumber];
|
|
1135
|
+
if (channel.isDrum)
|
|
1136
|
+
return;
|
|
1137
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1131
1138
|
channel.state.modulationDepth = modulation / 127;
|
|
1132
1139
|
this.updateModulation(channel, scheduleTime);
|
|
1133
1140
|
}
|
|
1134
1141
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1142
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1135
1143
|
const channel = this.channels[channelNumber];
|
|
1136
1144
|
channel.state.volume = volume / 127;
|
|
1137
1145
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1144,11 +1152,13 @@ class MidyGM1 {
|
|
|
1144
1152
|
};
|
|
1145
1153
|
}
|
|
1146
1154
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1155
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1147
1156
|
const channel = this.channels[channelNumber];
|
|
1148
1157
|
channel.state.pan = pan / 127;
|
|
1149
1158
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1150
1159
|
}
|
|
1151
1160
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1161
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1152
1162
|
const channel = this.channels[channelNumber];
|
|
1153
1163
|
channel.state.expression = expression / 127;
|
|
1154
1164
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1169,9 +1179,17 @@ class MidyGM1 {
|
|
|
1169
1179
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1170
1180
|
}
|
|
1171
1181
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1182
|
+
const channel = this.channels[channelNumber];
|
|
1183
|
+
if (channel.isDrum)
|
|
1184
|
+
return;
|
|
1172
1185
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1173
|
-
|
|
1174
|
-
if (
|
|
1186
|
+
channel.state.sustainPedal = value / 127;
|
|
1187
|
+
if (64 <= value) {
|
|
1188
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1189
|
+
channel.sustainNotes.push(note);
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1175
1193
|
this.releaseSustainPedal(channelNumber, value, scheduleTime);
|
|
1176
1194
|
}
|
|
1177
1195
|
}
|
|
@@ -1209,10 +1227,10 @@ class MidyGM1 {
|
|
|
1209
1227
|
this.handlePitchBendRangeRPN(channelNumber, scheduleTime);
|
|
1210
1228
|
break;
|
|
1211
1229
|
case 1:
|
|
1212
|
-
this.handleFineTuningRPN(channelNumber);
|
|
1230
|
+
this.handleFineTuningRPN(channelNumber, scheduleTime);
|
|
1213
1231
|
break;
|
|
1214
1232
|
case 2:
|
|
1215
|
-
this.handleCoarseTuningRPN(channelNumber);
|
|
1233
|
+
this.handleCoarseTuningRPN(channelNumber, scheduleTime);
|
|
1216
1234
|
break;
|
|
1217
1235
|
default:
|
|
1218
1236
|
console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
|
|
@@ -1235,8 +1253,10 @@ class MidyGM1 {
|
|
|
1235
1253
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1236
1254
|
}
|
|
1237
1255
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1238
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1239
1256
|
const channel = this.channels[channelNumber];
|
|
1257
|
+
if (channel.isDrum)
|
|
1258
|
+
return;
|
|
1259
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1240
1260
|
const state = channel.state;
|
|
1241
1261
|
const prev = state.pitchWheelSensitivity;
|
|
1242
1262
|
const next = value / 128;
|
|
@@ -1245,33 +1265,39 @@ class MidyGM1 {
|
|
|
1245
1265
|
this.updateChannelDetune(channel, scheduleTime);
|
|
1246
1266
|
this.applyVoiceParams(channel, 16, scheduleTime);
|
|
1247
1267
|
}
|
|
1248
|
-
handleFineTuningRPN(channelNumber) {
|
|
1268
|
+
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
1249
1269
|
const channel = this.channels[channelNumber];
|
|
1250
1270
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1251
1271
|
const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
|
|
1252
|
-
this.setFineTuning(channelNumber, fineTuning);
|
|
1272
|
+
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
1253
1273
|
}
|
|
1254
|
-
setFineTuning(channelNumber, value) {
|
|
1274
|
+
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1255
1275
|
const channel = this.channels[channelNumber];
|
|
1276
|
+
if (channel.isDrum)
|
|
1277
|
+
return;
|
|
1278
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1256
1279
|
const prev = channel.fineTuning;
|
|
1257
1280
|
const next = (value - 8192) / 8.192; // cent
|
|
1258
1281
|
channel.fineTuning = next;
|
|
1259
1282
|
channel.detune += next - prev;
|
|
1260
|
-
this.updateChannelDetune(channel);
|
|
1283
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
1261
1284
|
}
|
|
1262
|
-
handleCoarseTuningRPN(channelNumber) {
|
|
1285
|
+
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
1263
1286
|
const channel = this.channels[channelNumber];
|
|
1264
1287
|
this.limitDataMSB(channel, 0, 127);
|
|
1265
1288
|
const coarseTuning = channel.dataMSB;
|
|
1266
|
-
this.setCoarseTuning(channelNumber, coarseTuning);
|
|
1289
|
+
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
1267
1290
|
}
|
|
1268
|
-
setCoarseTuning(channelNumber, value) {
|
|
1291
|
+
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1269
1292
|
const channel = this.channels[channelNumber];
|
|
1293
|
+
if (channel.isDrum)
|
|
1294
|
+
return;
|
|
1295
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1270
1296
|
const prev = channel.coarseTuning;
|
|
1271
1297
|
const next = (value - 64) * 100; // cent
|
|
1272
1298
|
channel.coarseTuning = next;
|
|
1273
1299
|
channel.detune += next - prev;
|
|
1274
|
-
this.updateChannelDetune(channel);
|
|
1300
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
1275
1301
|
}
|
|
1276
1302
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
1277
1303
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1303,12 +1329,12 @@ class MidyGM1 {
|
|
|
1303
1329
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1304
1330
|
return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
|
|
1305
1331
|
}
|
|
1306
|
-
handleUniversalNonRealTimeExclusiveMessage(data,
|
|
1332
|
+
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1307
1333
|
switch (data[2]) {
|
|
1308
1334
|
case 9:
|
|
1309
1335
|
switch (data[3]) {
|
|
1310
1336
|
case 1:
|
|
1311
|
-
this.GM1SystemOn();
|
|
1337
|
+
this.GM1SystemOn(scheduleTime);
|
|
1312
1338
|
break;
|
|
1313
1339
|
case 2: // GM System Off
|
|
1314
1340
|
break;
|
|
@@ -1320,12 +1346,17 @@ class MidyGM1 {
|
|
|
1320
1346
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1321
1347
|
}
|
|
1322
1348
|
}
|
|
1323
|
-
GM1SystemOn() {
|
|
1349
|
+
GM1SystemOn(scheduleTime) {
|
|
1350
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1351
|
+
this.mode = "GM1";
|
|
1324
1352
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1353
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
1325
1354
|
const channel = this.channels[i];
|
|
1326
1355
|
channel.bank = 0;
|
|
1356
|
+
channel.isDrum = false;
|
|
1327
1357
|
}
|
|
1328
1358
|
this.channels[9].bank = 128;
|
|
1359
|
+
this.channels[9].isDrum = true;
|
|
1329
1360
|
}
|
|
1330
1361
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1331
1362
|
switch (data[2]) {
|
|
@@ -1368,13 +1399,20 @@ class MidyGM1 {
|
|
|
1368
1399
|
}
|
|
1369
1400
|
scheduleTask(callback, scheduleTime) {
|
|
1370
1401
|
return new Promise((resolve) => {
|
|
1371
|
-
const bufferSource = new AudioBufferSourceNode(this.audioContext
|
|
1402
|
+
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
1403
|
+
buffer: this.schedulerBuffer,
|
|
1404
|
+
});
|
|
1405
|
+
bufferSource.connect(this.scheduler);
|
|
1372
1406
|
bufferSource.onended = () => {
|
|
1373
|
-
|
|
1374
|
-
|
|
1407
|
+
try {
|
|
1408
|
+
callback();
|
|
1409
|
+
}
|
|
1410
|
+
finally {
|
|
1411
|
+
bufferSource.disconnect();
|
|
1412
|
+
resolve();
|
|
1413
|
+
}
|
|
1375
1414
|
};
|
|
1376
1415
|
bufferSource.start(scheduleTime);
|
|
1377
|
-
bufferSource.stop(scheduleTime);
|
|
1378
1416
|
});
|
|
1379
1417
|
}
|
|
1380
1418
|
}
|
|
@@ -1385,6 +1423,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1385
1423
|
writable: true,
|
|
1386
1424
|
value: {
|
|
1387
1425
|
currentBufferSource: null,
|
|
1426
|
+
isDrum: false,
|
|
1388
1427
|
detune: 0,
|
|
1389
1428
|
program: 0,
|
|
1390
1429
|
bank: 0,
|
|
@@ -1392,8 +1431,8 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1392
1431
|
dataLSB: 0,
|
|
1393
1432
|
rpnMSB: 127,
|
|
1394
1433
|
rpnLSB: 127,
|
|
1434
|
+
modulationDepthRange: 50, // cent
|
|
1395
1435
|
fineTuning: 0, // cb
|
|
1396
1436
|
coarseTuning: 0, // cb
|
|
1397
|
-
modulationDepthRange: 50, // cent
|
|
1398
1437
|
}
|
|
1399
1438
|
});
|