@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/README.md
CHANGED
|
@@ -77,8 +77,8 @@ There are functions that handle SysEx data as is, as well as simplified
|
|
|
77
77
|
functions.
|
|
78
78
|
|
|
79
79
|
```js
|
|
80
|
-
midy.handleSysEx(data); // [F0 F6 04 01 xx xx F7] data
|
|
81
|
-
midy.handleMasterVolumeSysEx(data); // [F0 F6 04 01 xx xx F7] data
|
|
80
|
+
midy.handleSysEx(data, scheduleTime); // [F0 F6 04 01 xx xx F7] data
|
|
81
|
+
midy.handleMasterVolumeSysEx(data, scheduleTime); // [F0 F6 04 01 xx xx F7] data
|
|
82
82
|
midy.setMasterVolume(volume); // [0-1] volume
|
|
83
83
|
```
|
|
84
84
|
|
package/esm/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/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;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/esm/midy-GM1.js
CHANGED
|
@@ -170,6 +170,12 @@ const volumeEnvelopeKeys = [
|
|
|
170
170
|
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
171
171
|
export class MidyGM1 {
|
|
172
172
|
constructor(audioContext) {
|
|
173
|
+
Object.defineProperty(this, "mode", {
|
|
174
|
+
enumerable: true,
|
|
175
|
+
configurable: true,
|
|
176
|
+
writable: true,
|
|
177
|
+
value: "GM1"
|
|
178
|
+
});
|
|
173
179
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
174
180
|
enumerable: true,
|
|
175
181
|
configurable: true,
|
|
@@ -292,10 +298,16 @@ export class MidyGM1 {
|
|
|
292
298
|
});
|
|
293
299
|
this.audioContext = audioContext;
|
|
294
300
|
this.masterVolume = new GainNode(audioContext);
|
|
301
|
+
this.scheduler = new GainNode(audioContext, { gain: 0 });
|
|
302
|
+
this.schedulerBuffer = new AudioBuffer({
|
|
303
|
+
length: 1,
|
|
304
|
+
sampleRate: audioContext.sampleRate,
|
|
305
|
+
});
|
|
295
306
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
296
307
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
297
308
|
this.channels = this.createChannels(audioContext);
|
|
298
309
|
this.masterVolume.connect(audioContext.destination);
|
|
310
|
+
this.scheduler.connect(audioContext.destination);
|
|
299
311
|
this.GM1SystemOn();
|
|
300
312
|
}
|
|
301
313
|
initSoundFontTable() {
|
|
@@ -355,6 +367,7 @@ export class MidyGM1 {
|
|
|
355
367
|
state: new ControllerState(),
|
|
356
368
|
...this.setChannelAudioNodes(audioContext),
|
|
357
369
|
scheduledNotes: new SparseMap(128),
|
|
370
|
+
sustainNotes: [],
|
|
358
371
|
};
|
|
359
372
|
});
|
|
360
373
|
return channels;
|
|
@@ -388,7 +401,7 @@ export class MidyGM1 {
|
|
|
388
401
|
return audioBuffer;
|
|
389
402
|
}
|
|
390
403
|
}
|
|
391
|
-
|
|
404
|
+
createBufferSource(audioBuffer, voiceParams) {
|
|
392
405
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
393
406
|
bufferSource.buffer = audioBuffer;
|
|
394
407
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
@@ -412,7 +425,7 @@ export class MidyGM1 {
|
|
|
412
425
|
}
|
|
413
426
|
/* falls through */
|
|
414
427
|
case "noteOff": {
|
|
415
|
-
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime);
|
|
428
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
416
429
|
if (notePromise) {
|
|
417
430
|
this.notePromises.push(notePromise);
|
|
418
431
|
}
|
|
@@ -576,15 +589,10 @@ export class MidyGM1 {
|
|
|
576
589
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
577
590
|
const channel = this.channels[channelNumber];
|
|
578
591
|
const promises = [];
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
continue;
|
|
584
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
585
|
-
this.notePromises.push(promise);
|
|
586
|
-
promises.push(promise);
|
|
587
|
-
}
|
|
592
|
+
this.processScheduledNotes(channel, (note) => {
|
|
593
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
594
|
+
this.notePromises.push(promise);
|
|
595
|
+
promises.push(promise);
|
|
588
596
|
});
|
|
589
597
|
channel.scheduledNotes.clear();
|
|
590
598
|
return Promise.all(promises);
|
|
@@ -640,14 +648,12 @@ export class MidyGM1 {
|
|
|
640
648
|
const now = this.audioContext.currentTime;
|
|
641
649
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
642
650
|
}
|
|
643
|
-
processScheduledNotes(channel,
|
|
651
|
+
processScheduledNotes(channel, callback) {
|
|
644
652
|
channel.scheduledNotes.forEach((noteList) => {
|
|
645
653
|
for (let i = 0; i < noteList.length; i++) {
|
|
646
654
|
const note = noteList[i];
|
|
647
655
|
if (!note)
|
|
648
656
|
continue;
|
|
649
|
-
if (scheduleTime < note.startTime)
|
|
650
|
-
continue;
|
|
651
657
|
callback(note);
|
|
652
658
|
}
|
|
653
659
|
});
|
|
@@ -693,7 +699,7 @@ export class MidyGM1 {
|
|
|
693
699
|
return tuning + pitch;
|
|
694
700
|
}
|
|
695
701
|
updateChannelDetune(channel, scheduleTime) {
|
|
696
|
-
this.processScheduledNotes(channel,
|
|
702
|
+
this.processScheduledNotes(channel, (note) => {
|
|
697
703
|
this.updateDetune(channel, note, scheduleTime);
|
|
698
704
|
});
|
|
699
705
|
}
|
|
@@ -811,7 +817,7 @@ export class MidyGM1 {
|
|
|
811
817
|
const voiceParams = voice.getAllParams(controllerState);
|
|
812
818
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
813
819
|
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
814
|
-
note.bufferSource = this.
|
|
820
|
+
note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
|
|
815
821
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
816
822
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
817
823
|
type: "lowpass",
|
|
@@ -842,15 +848,17 @@ export class MidyGM1 {
|
|
|
842
848
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
843
849
|
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
844
850
|
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
851
|
+
if (0.5 <= channel.state.sustainPedal) {
|
|
852
|
+
channel.sustainNotes.push(note);
|
|
853
|
+
}
|
|
845
854
|
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
846
855
|
if (exclusiveClass !== 0) {
|
|
847
856
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
848
857
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
849
858
|
const [prevNote, prevChannelNumber] = prevEntry;
|
|
850
|
-
if (!prevNote.ending) {
|
|
859
|
+
if (prevNote && !prevNote.ending) {
|
|
851
860
|
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
852
|
-
startTime,
|
|
853
|
-
true);
|
|
861
|
+
startTime, true);
|
|
854
862
|
}
|
|
855
863
|
}
|
|
856
864
|
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
@@ -894,7 +902,7 @@ export class MidyGM1 {
|
|
|
894
902
|
}
|
|
895
903
|
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
896
904
|
const channel = this.channels[channelNumber];
|
|
897
|
-
if (!force && 0.5
|
|
905
|
+
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
898
906
|
return;
|
|
899
907
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
900
908
|
return;
|
|
@@ -922,11 +930,11 @@ export class MidyGM1 {
|
|
|
922
930
|
const velocity = halfVelocity * 2;
|
|
923
931
|
const channel = this.channels[channelNumber];
|
|
924
932
|
const promises = [];
|
|
925
|
-
|
|
926
|
-
const
|
|
927
|
-
const promise = this.noteOff(channelNumber, noteNumber, velocity);
|
|
933
|
+
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
934
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
928
935
|
promises.push(promise);
|
|
929
|
-
}
|
|
936
|
+
}
|
|
937
|
+
channel.sustainNotes = [];
|
|
930
938
|
return promises;
|
|
931
939
|
}
|
|
932
940
|
handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
|
|
@@ -956,8 +964,10 @@ export class MidyGM1 {
|
|
|
956
964
|
this.setPitchBend(channelNumber, pitchBend, scheduleTime);
|
|
957
965
|
}
|
|
958
966
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
959
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
960
967
|
const channel = this.channels[channelNumber];
|
|
968
|
+
if (channel.isDrum)
|
|
969
|
+
return;
|
|
970
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
961
971
|
const state = channel.state;
|
|
962
972
|
const prev = state.pitchWheel * 2 - 1;
|
|
963
973
|
const next = (value - 8192) / 8192;
|
|
@@ -1037,48 +1047,43 @@ export class MidyGM1 {
|
|
|
1037
1047
|
return state;
|
|
1038
1048
|
}
|
|
1039
1049
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1050
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1051
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1052
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1053
|
+
let appliedFilterEnvelope = false;
|
|
1054
|
+
let appliedVolumeEnvelope = false;
|
|
1055
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1056
|
+
const prevValue = note.voiceParams[key];
|
|
1057
|
+
if (value === prevValue)
|
|
1044
1058
|
continue;
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
if (value === prevValue)
|
|
1059
|
+
note.voiceParams[key] = value;
|
|
1060
|
+
if (key in this.voiceParamsHandlers) {
|
|
1061
|
+
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1062
|
+
}
|
|
1063
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1064
|
+
if (appliedFilterEnvelope)
|
|
1052
1065
|
continue;
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
continue;
|
|
1060
|
-
appliedFilterEnvelope = true;
|
|
1061
|
-
const noteVoiceParams = note.voiceParams;
|
|
1062
|
-
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1063
|
-
const key = filterEnvelopeKeys[i];
|
|
1064
|
-
if (key in voiceParams)
|
|
1065
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1066
|
-
}
|
|
1067
|
-
this.setFilterEnvelope(note, scheduleTime);
|
|
1068
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1066
|
+
appliedFilterEnvelope = true;
|
|
1067
|
+
const noteVoiceParams = note.voiceParams;
|
|
1068
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1069
|
+
const key = filterEnvelopeKeys[i];
|
|
1070
|
+
if (key in voiceParams)
|
|
1071
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1069
1072
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1073
|
+
this.setFilterEnvelope(note, scheduleTime);
|
|
1074
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1075
|
+
}
|
|
1076
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1077
|
+
if (appliedVolumeEnvelope)
|
|
1078
|
+
continue;
|
|
1079
|
+
appliedVolumeEnvelope = true;
|
|
1080
|
+
const noteVoiceParams = note.voiceParams;
|
|
1081
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1082
|
+
const key = volumeEnvelopeKeys[i];
|
|
1083
|
+
if (key in voiceParams)
|
|
1084
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1081
1085
|
}
|
|
1086
|
+
this.setVolumeEnvelope(note, scheduleTime);
|
|
1082
1087
|
}
|
|
1083
1088
|
}
|
|
1084
1089
|
});
|
|
@@ -1111,9 +1116,8 @@ export class MidyGM1 {
|
|
|
1111
1116
|
}
|
|
1112
1117
|
}
|
|
1113
1118
|
updateModulation(channel, scheduleTime) {
|
|
1114
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1115
1119
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1116
|
-
this.processScheduledNotes(channel,
|
|
1120
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1117
1121
|
if (note.modulationDepth) {
|
|
1118
1122
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1119
1123
|
}
|
|
@@ -1125,10 +1129,14 @@ export class MidyGM1 {
|
|
|
1125
1129
|
}
|
|
1126
1130
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1127
1131
|
const channel = this.channels[channelNumber];
|
|
1132
|
+
if (channel.isDrum)
|
|
1133
|
+
return;
|
|
1134
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1128
1135
|
channel.state.modulationDepth = modulation / 127;
|
|
1129
1136
|
this.updateModulation(channel, scheduleTime);
|
|
1130
1137
|
}
|
|
1131
1138
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1139
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1132
1140
|
const channel = this.channels[channelNumber];
|
|
1133
1141
|
channel.state.volume = volume / 127;
|
|
1134
1142
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1141,11 +1149,13 @@ export class MidyGM1 {
|
|
|
1141
1149
|
};
|
|
1142
1150
|
}
|
|
1143
1151
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1152
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1144
1153
|
const channel = this.channels[channelNumber];
|
|
1145
1154
|
channel.state.pan = pan / 127;
|
|
1146
1155
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1147
1156
|
}
|
|
1148
1157
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1158
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1149
1159
|
const channel = this.channels[channelNumber];
|
|
1150
1160
|
channel.state.expression = expression / 127;
|
|
1151
1161
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1166,9 +1176,17 @@ export class MidyGM1 {
|
|
|
1166
1176
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1167
1177
|
}
|
|
1168
1178
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1179
|
+
const channel = this.channels[channelNumber];
|
|
1180
|
+
if (channel.isDrum)
|
|
1181
|
+
return;
|
|
1169
1182
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1170
|
-
|
|
1171
|
-
if (
|
|
1183
|
+
channel.state.sustainPedal = value / 127;
|
|
1184
|
+
if (64 <= value) {
|
|
1185
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1186
|
+
channel.sustainNotes.push(note);
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1172
1190
|
this.releaseSustainPedal(channelNumber, value, scheduleTime);
|
|
1173
1191
|
}
|
|
1174
1192
|
}
|
|
@@ -1206,10 +1224,10 @@ export class MidyGM1 {
|
|
|
1206
1224
|
this.handlePitchBendRangeRPN(channelNumber, scheduleTime);
|
|
1207
1225
|
break;
|
|
1208
1226
|
case 1:
|
|
1209
|
-
this.handleFineTuningRPN(channelNumber);
|
|
1227
|
+
this.handleFineTuningRPN(channelNumber, scheduleTime);
|
|
1210
1228
|
break;
|
|
1211
1229
|
case 2:
|
|
1212
|
-
this.handleCoarseTuningRPN(channelNumber);
|
|
1230
|
+
this.handleCoarseTuningRPN(channelNumber, scheduleTime);
|
|
1213
1231
|
break;
|
|
1214
1232
|
default:
|
|
1215
1233
|
console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
|
|
@@ -1232,8 +1250,10 @@ export class MidyGM1 {
|
|
|
1232
1250
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1233
1251
|
}
|
|
1234
1252
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1235
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1236
1253
|
const channel = this.channels[channelNumber];
|
|
1254
|
+
if (channel.isDrum)
|
|
1255
|
+
return;
|
|
1256
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1237
1257
|
const state = channel.state;
|
|
1238
1258
|
const prev = state.pitchWheelSensitivity;
|
|
1239
1259
|
const next = value / 128;
|
|
@@ -1242,33 +1262,39 @@ export class MidyGM1 {
|
|
|
1242
1262
|
this.updateChannelDetune(channel, scheduleTime);
|
|
1243
1263
|
this.applyVoiceParams(channel, 16, scheduleTime);
|
|
1244
1264
|
}
|
|
1245
|
-
handleFineTuningRPN(channelNumber) {
|
|
1265
|
+
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
1246
1266
|
const channel = this.channels[channelNumber];
|
|
1247
1267
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1248
1268
|
const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
|
|
1249
|
-
this.setFineTuning(channelNumber, fineTuning);
|
|
1269
|
+
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
1250
1270
|
}
|
|
1251
|
-
setFineTuning(channelNumber, value) {
|
|
1271
|
+
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1252
1272
|
const channel = this.channels[channelNumber];
|
|
1273
|
+
if (channel.isDrum)
|
|
1274
|
+
return;
|
|
1275
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1253
1276
|
const prev = channel.fineTuning;
|
|
1254
1277
|
const next = (value - 8192) / 8.192; // cent
|
|
1255
1278
|
channel.fineTuning = next;
|
|
1256
1279
|
channel.detune += next - prev;
|
|
1257
|
-
this.updateChannelDetune(channel);
|
|
1280
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
1258
1281
|
}
|
|
1259
|
-
handleCoarseTuningRPN(channelNumber) {
|
|
1282
|
+
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
1260
1283
|
const channel = this.channels[channelNumber];
|
|
1261
1284
|
this.limitDataMSB(channel, 0, 127);
|
|
1262
1285
|
const coarseTuning = channel.dataMSB;
|
|
1263
|
-
this.setCoarseTuning(channelNumber, coarseTuning);
|
|
1286
|
+
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
1264
1287
|
}
|
|
1265
|
-
setCoarseTuning(channelNumber, value) {
|
|
1288
|
+
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1266
1289
|
const channel = this.channels[channelNumber];
|
|
1290
|
+
if (channel.isDrum)
|
|
1291
|
+
return;
|
|
1292
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1267
1293
|
const prev = channel.coarseTuning;
|
|
1268
1294
|
const next = (value - 64) * 100; // cent
|
|
1269
1295
|
channel.coarseTuning = next;
|
|
1270
1296
|
channel.detune += next - prev;
|
|
1271
|
-
this.updateChannelDetune(channel);
|
|
1297
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
1272
1298
|
}
|
|
1273
1299
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
1274
1300
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1300,12 +1326,12 @@ export class MidyGM1 {
|
|
|
1300
1326
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1301
1327
|
return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
|
|
1302
1328
|
}
|
|
1303
|
-
handleUniversalNonRealTimeExclusiveMessage(data,
|
|
1329
|
+
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1304
1330
|
switch (data[2]) {
|
|
1305
1331
|
case 9:
|
|
1306
1332
|
switch (data[3]) {
|
|
1307
1333
|
case 1:
|
|
1308
|
-
this.GM1SystemOn();
|
|
1334
|
+
this.GM1SystemOn(scheduleTime);
|
|
1309
1335
|
break;
|
|
1310
1336
|
case 2: // GM System Off
|
|
1311
1337
|
break;
|
|
@@ -1317,12 +1343,17 @@ export class MidyGM1 {
|
|
|
1317
1343
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1318
1344
|
}
|
|
1319
1345
|
}
|
|
1320
|
-
GM1SystemOn() {
|
|
1346
|
+
GM1SystemOn(scheduleTime) {
|
|
1347
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1348
|
+
this.mode = "GM1";
|
|
1321
1349
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1350
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
1322
1351
|
const channel = this.channels[i];
|
|
1323
1352
|
channel.bank = 0;
|
|
1353
|
+
channel.isDrum = false;
|
|
1324
1354
|
}
|
|
1325
1355
|
this.channels[9].bank = 128;
|
|
1356
|
+
this.channels[9].isDrum = true;
|
|
1326
1357
|
}
|
|
1327
1358
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1328
1359
|
switch (data[2]) {
|
|
@@ -1365,13 +1396,20 @@ export class MidyGM1 {
|
|
|
1365
1396
|
}
|
|
1366
1397
|
scheduleTask(callback, scheduleTime) {
|
|
1367
1398
|
return new Promise((resolve) => {
|
|
1368
|
-
const bufferSource = new AudioBufferSourceNode(this.audioContext
|
|
1399
|
+
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
1400
|
+
buffer: this.schedulerBuffer,
|
|
1401
|
+
});
|
|
1402
|
+
bufferSource.connect(this.scheduler);
|
|
1369
1403
|
bufferSource.onended = () => {
|
|
1370
|
-
|
|
1371
|
-
|
|
1404
|
+
try {
|
|
1405
|
+
callback();
|
|
1406
|
+
}
|
|
1407
|
+
finally {
|
|
1408
|
+
bufferSource.disconnect();
|
|
1409
|
+
resolve();
|
|
1410
|
+
}
|
|
1372
1411
|
};
|
|
1373
1412
|
bufferSource.start(scheduleTime);
|
|
1374
|
-
bufferSource.stop(scheduleTime);
|
|
1375
1413
|
});
|
|
1376
1414
|
}
|
|
1377
1415
|
}
|
|
@@ -1381,6 +1419,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1381
1419
|
writable: true,
|
|
1382
1420
|
value: {
|
|
1383
1421
|
currentBufferSource: null,
|
|
1422
|
+
isDrum: false,
|
|
1384
1423
|
detune: 0,
|
|
1385
1424
|
program: 0,
|
|
1386
1425
|
bank: 0,
|
|
@@ -1388,8 +1427,8 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1388
1427
|
dataLSB: 0,
|
|
1389
1428
|
rpnMSB: 127,
|
|
1390
1429
|
rpnLSB: 127,
|
|
1430
|
+
modulationDepthRange: 50, // cent
|
|
1391
1431
|
fineTuning: 0, // cb
|
|
1392
1432
|
coarseTuning: 0, // cb
|
|
1393
|
-
modulationDepthRange: 50, // cent
|
|
1394
1433
|
}
|
|
1395
1434
|
});
|