@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/esm/midy-GMLite.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class MidyGMLite {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
|
+
isDrum: boolean;
|
|
4
5
|
detune: number;
|
|
5
6
|
program: number;
|
|
6
7
|
bank: number;
|
|
@@ -8,8 +9,10 @@ export class MidyGMLite {
|
|
|
8
9
|
dataLSB: number;
|
|
9
10
|
rpnMSB: number;
|
|
10
11
|
rpnLSB: number;
|
|
12
|
+
modulationDepthRange: number;
|
|
11
13
|
};
|
|
12
14
|
constructor(audioContext: any);
|
|
15
|
+
mode: string;
|
|
13
16
|
ticksPerBeat: number;
|
|
14
17
|
totalTime: number;
|
|
15
18
|
noteCheckInterval: number;
|
|
@@ -32,6 +35,8 @@ export class MidyGMLite {
|
|
|
32
35
|
exclusiveClassMap: SparseMap;
|
|
33
36
|
audioContext: any;
|
|
34
37
|
masterVolume: any;
|
|
38
|
+
scheduler: any;
|
|
39
|
+
schedulerBuffer: any;
|
|
35
40
|
voiceParamsHandlers: {
|
|
36
41
|
modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
|
|
37
42
|
vibLfoToPitch: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
|
|
@@ -70,7 +75,8 @@ export class MidyGMLite {
|
|
|
70
75
|
};
|
|
71
76
|
createChannels(audioContext: any): any[];
|
|
72
77
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
73
|
-
|
|
78
|
+
calcLoopMode(channel: any, voiceParams: any): boolean;
|
|
79
|
+
createBufferSource(channel: any, voiceParams: any, audioBuffer: any): any;
|
|
74
80
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
75
81
|
getQueueIndex(second: any): number;
|
|
76
82
|
playNotes(): Promise<any>;
|
|
@@ -90,7 +96,7 @@ export class MidyGMLite {
|
|
|
90
96
|
seekTo(second: any): void;
|
|
91
97
|
calcTotalTime(): number;
|
|
92
98
|
currentTime(): number;
|
|
93
|
-
processScheduledNotes(channel: any,
|
|
99
|
+
processScheduledNotes(channel: any, callback: any): void;
|
|
94
100
|
getActiveNotes(channel: any, scheduleTime: any): SparseMap;
|
|
95
101
|
getActiveNote(noteList: any, scheduleTime: any): any;
|
|
96
102
|
cbToRatio(cb: any): number;
|
|
@@ -112,7 +118,7 @@ export class MidyGMLite {
|
|
|
112
118
|
stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
|
|
113
119
|
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
114
120
|
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
|
|
115
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
121
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
116
122
|
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
117
123
|
handleProgramChange(channelNumber: any, program: any, _scheduleTime: any): void;
|
|
118
124
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
@@ -173,8 +179,8 @@ export class MidyGMLite {
|
|
|
173
179
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
174
180
|
resetAllControllers(channelNumber: any): void;
|
|
175
181
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
176
|
-
handleUniversalNonRealTimeExclusiveMessage(data: any,
|
|
177
|
-
GM1SystemOn(): void;
|
|
182
|
+
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
183
|
+
GM1SystemOn(scheduleTime: any): void;
|
|
178
184
|
handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
179
185
|
handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
|
|
180
186
|
setMasterVolume(volume: any, scheduleTime: any): void;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAiJA;
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAiJA;IAuBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAjDD,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;IAgBrC,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,sDAMC;IAED,0EASC;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,wCAIC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,yGAgBC;IAED,gHA4CC;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,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAWC;IAED,gFAGC;IAED,8CAqBC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAED,6DAgBC;CACF;AAp5CD;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-GMLite.js
CHANGED
|
@@ -170,6 +170,12 @@ const volumeEnvelopeKeys = [
|
|
|
170
170
|
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
171
171
|
export class MidyGMLite {
|
|
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 MidyGMLite {
|
|
|
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 MidyGMLite {
|
|
|
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,10 +401,18 @@ export class MidyGMLite {
|
|
|
388
401
|
return audioBuffer;
|
|
389
402
|
}
|
|
390
403
|
}
|
|
391
|
-
|
|
404
|
+
calcLoopMode(channel, voiceParams) {
|
|
405
|
+
if (channel.isDrum) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
return voiceParams.sampleModes % 2 !== 0;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
createBufferSource(channel, voiceParams, audioBuffer) {
|
|
392
413
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
393
414
|
bufferSource.buffer = audioBuffer;
|
|
394
|
-
bufferSource.loop =
|
|
415
|
+
bufferSource.loop = this.calcLoopMode(channel, voiceParams);
|
|
395
416
|
if (bufferSource.loop) {
|
|
396
417
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
397
418
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -412,7 +433,7 @@ export class MidyGMLite {
|
|
|
412
433
|
}
|
|
413
434
|
/* falls through */
|
|
414
435
|
case "noteOff": {
|
|
415
|
-
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime);
|
|
436
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
416
437
|
if (notePromise) {
|
|
417
438
|
this.notePromises.push(notePromise);
|
|
418
439
|
}
|
|
@@ -576,15 +597,10 @@ export class MidyGMLite {
|
|
|
576
597
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
577
598
|
const channel = this.channels[channelNumber];
|
|
578
599
|
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
|
-
}
|
|
600
|
+
this.processScheduledNotes(channel, (note) => {
|
|
601
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
602
|
+
this.notePromises.push(promise);
|
|
603
|
+
promises.push(promise);
|
|
588
604
|
});
|
|
589
605
|
channel.scheduledNotes.clear();
|
|
590
606
|
return Promise.all(promises);
|
|
@@ -640,14 +656,12 @@ export class MidyGMLite {
|
|
|
640
656
|
const now = this.audioContext.currentTime;
|
|
641
657
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
642
658
|
}
|
|
643
|
-
processScheduledNotes(channel,
|
|
659
|
+
processScheduledNotes(channel, callback) {
|
|
644
660
|
channel.scheduledNotes.forEach((noteList) => {
|
|
645
661
|
for (let i = 0; i < noteList.length; i++) {
|
|
646
662
|
const note = noteList[i];
|
|
647
663
|
if (!note)
|
|
648
664
|
continue;
|
|
649
|
-
if (scheduleTime < note.startTime)
|
|
650
|
-
continue;
|
|
651
665
|
callback(note);
|
|
652
666
|
}
|
|
653
667
|
});
|
|
@@ -691,7 +705,7 @@ export class MidyGMLite {
|
|
|
691
705
|
return pitchWheel * pitchWheelSensitivity;
|
|
692
706
|
}
|
|
693
707
|
updateChannelDetune(channel, scheduleTime) {
|
|
694
|
-
this.processScheduledNotes(channel,
|
|
708
|
+
this.processScheduledNotes(channel, (note) => {
|
|
695
709
|
this.updateDetune(channel, note, scheduleTime);
|
|
696
710
|
});
|
|
697
711
|
}
|
|
@@ -809,7 +823,7 @@ export class MidyGMLite {
|
|
|
809
823
|
const voiceParams = voice.getAllParams(controllerState);
|
|
810
824
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
811
825
|
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
812
|
-
note.bufferSource = this.
|
|
826
|
+
note.bufferSource = this.createBufferSource(channel, voiceParams, audioBuffer);
|
|
813
827
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
814
828
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
815
829
|
type: "lowpass",
|
|
@@ -840,15 +854,17 @@ export class MidyGMLite {
|
|
|
840
854
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
841
855
|
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
842
856
|
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
857
|
+
if (0.5 <= channel.state.sustainPedal) {
|
|
858
|
+
channel.sustainNotes.push(note);
|
|
859
|
+
}
|
|
843
860
|
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
844
861
|
if (exclusiveClass !== 0) {
|
|
845
862
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
846
863
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
847
864
|
const [prevNote, prevChannelNumber] = prevEntry;
|
|
848
|
-
if (!prevNote.ending) {
|
|
865
|
+
if (prevNote && !prevNote.ending) {
|
|
849
866
|
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
850
|
-
startTime,
|
|
851
|
-
true);
|
|
867
|
+
startTime, true);
|
|
852
868
|
}
|
|
853
869
|
}
|
|
854
870
|
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
@@ -892,7 +908,7 @@ export class MidyGMLite {
|
|
|
892
908
|
}
|
|
893
909
|
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
894
910
|
const channel = this.channels[channelNumber];
|
|
895
|
-
if (!force && 0.5
|
|
911
|
+
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
896
912
|
return;
|
|
897
913
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
898
914
|
return;
|
|
@@ -920,11 +936,11 @@ export class MidyGMLite {
|
|
|
920
936
|
const velocity = halfVelocity * 2;
|
|
921
937
|
const channel = this.channels[channelNumber];
|
|
922
938
|
const promises = [];
|
|
923
|
-
|
|
924
|
-
const
|
|
925
|
-
const promise = this.noteOff(channelNumber, noteNumber, velocity);
|
|
939
|
+
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
940
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
926
941
|
promises.push(promise);
|
|
927
|
-
}
|
|
942
|
+
}
|
|
943
|
+
channel.sustainNotes = [];
|
|
928
944
|
return promises;
|
|
929
945
|
}
|
|
930
946
|
handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
|
|
@@ -954,8 +970,10 @@ export class MidyGMLite {
|
|
|
954
970
|
this.setPitchBend(channelNumber, pitchBend, scheduleTime);
|
|
955
971
|
}
|
|
956
972
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
957
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
958
973
|
const channel = this.channels[channelNumber];
|
|
974
|
+
if (channel.isDrum)
|
|
975
|
+
return;
|
|
976
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
959
977
|
const state = channel.state;
|
|
960
978
|
const prev = state.pitchWheel * 2 - 1;
|
|
961
979
|
const next = (value - 8192) / 8192;
|
|
@@ -1035,48 +1053,43 @@ export class MidyGMLite {
|
|
|
1035
1053
|
return state;
|
|
1036
1054
|
}
|
|
1037
1055
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1056
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1057
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1058
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1059
|
+
let appliedFilterEnvelope = false;
|
|
1060
|
+
let appliedVolumeEnvelope = false;
|
|
1061
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1062
|
+
const prevValue = note.voiceParams[key];
|
|
1063
|
+
if (value === prevValue)
|
|
1042
1064
|
continue;
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
if (value === prevValue)
|
|
1065
|
+
note.voiceParams[key] = value;
|
|
1066
|
+
if (key in this.voiceParamsHandlers) {
|
|
1067
|
+
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1068
|
+
}
|
|
1069
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1070
|
+
if (appliedFilterEnvelope)
|
|
1050
1071
|
continue;
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
continue;
|
|
1058
|
-
appliedFilterEnvelope = true;
|
|
1059
|
-
const noteVoiceParams = note.voiceParams;
|
|
1060
|
-
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1061
|
-
const key = filterEnvelopeKeys[i];
|
|
1062
|
-
if (key in voiceParams)
|
|
1063
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1064
|
-
}
|
|
1065
|
-
this.setFilterEnvelope(note, scheduleTime);
|
|
1066
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1072
|
+
appliedFilterEnvelope = true;
|
|
1073
|
+
const noteVoiceParams = note.voiceParams;
|
|
1074
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1075
|
+
const key = filterEnvelopeKeys[i];
|
|
1076
|
+
if (key in voiceParams)
|
|
1077
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1067
1078
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
+
this.setFilterEnvelope(note, scheduleTime);
|
|
1080
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1081
|
+
}
|
|
1082
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1083
|
+
if (appliedVolumeEnvelope)
|
|
1084
|
+
continue;
|
|
1085
|
+
appliedVolumeEnvelope = true;
|
|
1086
|
+
const noteVoiceParams = note.voiceParams;
|
|
1087
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1088
|
+
const key = volumeEnvelopeKeys[i];
|
|
1089
|
+
if (key in voiceParams)
|
|
1090
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1079
1091
|
}
|
|
1092
|
+
this.setVolumeEnvelope(note, scheduleTime);
|
|
1080
1093
|
}
|
|
1081
1094
|
}
|
|
1082
1095
|
});
|
|
@@ -1109,9 +1122,8 @@ export class MidyGMLite {
|
|
|
1109
1122
|
}
|
|
1110
1123
|
}
|
|
1111
1124
|
updateModulation(channel, scheduleTime) {
|
|
1112
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1113
1125
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1114
|
-
this.processScheduledNotes(channel,
|
|
1126
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1115
1127
|
if (note.modulationDepth) {
|
|
1116
1128
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1117
1129
|
}
|
|
@@ -1123,10 +1135,14 @@ export class MidyGMLite {
|
|
|
1123
1135
|
}
|
|
1124
1136
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1125
1137
|
const channel = this.channels[channelNumber];
|
|
1138
|
+
if (channel.isDrum)
|
|
1139
|
+
return;
|
|
1140
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1126
1141
|
channel.state.modulationDepth = modulation / 127;
|
|
1127
1142
|
this.updateModulation(channel, scheduleTime);
|
|
1128
1143
|
}
|
|
1129
1144
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1145
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1130
1146
|
const channel = this.channels[channelNumber];
|
|
1131
1147
|
channel.state.volume = volume / 127;
|
|
1132
1148
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1139,11 +1155,13 @@ export class MidyGMLite {
|
|
|
1139
1155
|
};
|
|
1140
1156
|
}
|
|
1141
1157
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1158
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1142
1159
|
const channel = this.channels[channelNumber];
|
|
1143
1160
|
channel.state.pan = pan / 127;
|
|
1144
1161
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1145
1162
|
}
|
|
1146
1163
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1164
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1147
1165
|
const channel = this.channels[channelNumber];
|
|
1148
1166
|
channel.state.expression = expression / 127;
|
|
1149
1167
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1164,9 +1182,17 @@ export class MidyGMLite {
|
|
|
1164
1182
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1165
1183
|
}
|
|
1166
1184
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1185
|
+
const channel = this.channels[channelNumber];
|
|
1186
|
+
if (channel.isDrum)
|
|
1187
|
+
return;
|
|
1167
1188
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1168
|
-
|
|
1169
|
-
if (
|
|
1189
|
+
channel.state.sustainPedal = value / 127;
|
|
1190
|
+
if (64 <= value) {
|
|
1191
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1192
|
+
channel.sustainNotes.push(note);
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1170
1196
|
this.releaseSustainPedal(channelNumber, value, scheduleTime);
|
|
1171
1197
|
}
|
|
1172
1198
|
}
|
|
@@ -1216,8 +1242,10 @@ export class MidyGMLite {
|
|
|
1216
1242
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1217
1243
|
}
|
|
1218
1244
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1219
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1220
1245
|
const channel = this.channels[channelNumber];
|
|
1246
|
+
if (channel.isDrum)
|
|
1247
|
+
return;
|
|
1248
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1221
1249
|
const state = channel.state;
|
|
1222
1250
|
const prev = state.pitchWheelSensitivity;
|
|
1223
1251
|
const next = value / 128;
|
|
@@ -1256,12 +1284,12 @@ export class MidyGMLite {
|
|
|
1256
1284
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1257
1285
|
return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
|
|
1258
1286
|
}
|
|
1259
|
-
handleUniversalNonRealTimeExclusiveMessage(data,
|
|
1287
|
+
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1260
1288
|
switch (data[2]) {
|
|
1261
1289
|
case 9:
|
|
1262
1290
|
switch (data[3]) {
|
|
1263
1291
|
case 1:
|
|
1264
|
-
this.GM1SystemOn();
|
|
1292
|
+
this.GM1SystemOn(scheduleTime);
|
|
1265
1293
|
break;
|
|
1266
1294
|
case 2: // GM System Off
|
|
1267
1295
|
break;
|
|
@@ -1273,12 +1301,17 @@ export class MidyGMLite {
|
|
|
1273
1301
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1274
1302
|
}
|
|
1275
1303
|
}
|
|
1276
|
-
GM1SystemOn() {
|
|
1304
|
+
GM1SystemOn(scheduleTime) {
|
|
1305
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1306
|
+
this.mode = "GM1";
|
|
1277
1307
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1308
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
1278
1309
|
const channel = this.channels[i];
|
|
1279
1310
|
channel.bank = 0;
|
|
1311
|
+
channel.isDrum = false;
|
|
1280
1312
|
}
|
|
1281
1313
|
this.channels[9].bank = 128;
|
|
1314
|
+
this.channels[9].isDrum = true;
|
|
1282
1315
|
}
|
|
1283
1316
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1284
1317
|
switch (data[2]) {
|
|
@@ -1321,13 +1354,20 @@ export class MidyGMLite {
|
|
|
1321
1354
|
}
|
|
1322
1355
|
scheduleTask(callback, scheduleTime) {
|
|
1323
1356
|
return new Promise((resolve) => {
|
|
1324
|
-
const bufferSource = new AudioBufferSourceNode(this.audioContext
|
|
1357
|
+
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
1358
|
+
buffer: this.schedulerBuffer,
|
|
1359
|
+
});
|
|
1360
|
+
bufferSource.connect(this.scheduler);
|
|
1325
1361
|
bufferSource.onended = () => {
|
|
1326
|
-
|
|
1327
|
-
|
|
1362
|
+
try {
|
|
1363
|
+
callback();
|
|
1364
|
+
}
|
|
1365
|
+
finally {
|
|
1366
|
+
bufferSource.disconnect();
|
|
1367
|
+
resolve();
|
|
1368
|
+
}
|
|
1328
1369
|
};
|
|
1329
1370
|
bufferSource.start(scheduleTime);
|
|
1330
|
-
bufferSource.stop(scheduleTime);
|
|
1331
1371
|
});
|
|
1332
1372
|
}
|
|
1333
1373
|
}
|
|
@@ -1337,6 +1377,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1337
1377
|
writable: true,
|
|
1338
1378
|
value: {
|
|
1339
1379
|
currentBufferSource: null,
|
|
1380
|
+
isDrum: false,
|
|
1340
1381
|
detune: 0,
|
|
1341
1382
|
program: 0,
|
|
1342
1383
|
bank: 0,
|
|
@@ -1344,5 +1385,6 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1344
1385
|
dataLSB: 0,
|
|
1345
1386
|
rpnMSB: 127,
|
|
1346
1387
|
rpnLSB: 127,
|
|
1388
|
+
modulationDepthRange: 50, // cent
|
|
1347
1389
|
}
|
|
1348
1390
|
});
|
package/esm/midy.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class Midy {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
|
+
isDrum: boolean;
|
|
4
5
|
detune: number;
|
|
5
6
|
program: number;
|
|
6
7
|
bank: number;
|
|
@@ -10,9 +11,10 @@ export class Midy {
|
|
|
10
11
|
dataLSB: number;
|
|
11
12
|
rpnMSB: number;
|
|
12
13
|
rpnLSB: number;
|
|
14
|
+
mono: boolean;
|
|
15
|
+
modulationDepthRange: number;
|
|
13
16
|
fineTuning: number;
|
|
14
17
|
coarseTuning: number;
|
|
15
|
-
modulationDepthRange: number;
|
|
16
18
|
};
|
|
17
19
|
constructor(audioContext: any, options?: {
|
|
18
20
|
reverbAlgorithm: (audioContext: any) => {
|
|
@@ -20,6 +22,7 @@ export class Midy {
|
|
|
20
22
|
output: any;
|
|
21
23
|
};
|
|
22
24
|
});
|
|
25
|
+
mode: string;
|
|
23
26
|
ticksPerBeat: number;
|
|
24
27
|
totalTime: number;
|
|
25
28
|
masterFineTuning: number;
|
|
@@ -35,8 +38,6 @@ export class Midy {
|
|
|
35
38
|
sendToReverb: number;
|
|
36
39
|
delayTimes: any[];
|
|
37
40
|
};
|
|
38
|
-
mono: boolean;
|
|
39
|
-
omni: boolean;
|
|
40
41
|
noteCheckInterval: number;
|
|
41
42
|
lookAhead: number;
|
|
42
43
|
startDelay: number;
|
|
@@ -69,6 +70,8 @@ export class Midy {
|
|
|
69
70
|
};
|
|
70
71
|
};
|
|
71
72
|
masterVolume: any;
|
|
73
|
+
scheduler: any;
|
|
74
|
+
schedulerBuffer: any;
|
|
72
75
|
voiceParamsHandlers: {
|
|
73
76
|
modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
|
|
74
77
|
vibLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
|
|
@@ -94,7 +97,7 @@ export class Midy {
|
|
|
94
97
|
64: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
95
98
|
65: (channelNumber: any, value: any) => void;
|
|
96
99
|
66: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
97
|
-
67: (channelNumber: any, softPedal: any,
|
|
100
|
+
67: (channelNumber: any, softPedal: any, scheduleTime: any) => void;
|
|
98
101
|
71: (channelNumber: any, filterResonance: any, scheduleTime: any) => void;
|
|
99
102
|
72: (channelNumber: any, releaseTime: any, _scheduleTime: any) => void;
|
|
100
103
|
73: (channelNumber: any, attackTime: any, scheduleTime: any) => void;
|
|
@@ -112,10 +115,10 @@ export class Midy {
|
|
|
112
115
|
120: (channelNumber: any, _value: any, scheduleTime: any) => Promise<any[]>;
|
|
113
116
|
121: (channelNumber: any) => void;
|
|
114
117
|
123: (channelNumber: any, _value: any, scheduleTime: any) => Promise<any[]>;
|
|
115
|
-
124: () => void;
|
|
116
|
-
125: () => void;
|
|
117
|
-
126: () => void;
|
|
118
|
-
127: () => void;
|
|
118
|
+
124: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
119
|
+
125: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
120
|
+
126: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
121
|
+
127: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
119
122
|
};
|
|
120
123
|
channels: any[];
|
|
121
124
|
reverbEffect: {
|
|
@@ -142,7 +145,8 @@ export class Midy {
|
|
|
142
145
|
};
|
|
143
146
|
createChannels(audioContext: any): any[];
|
|
144
147
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
145
|
-
|
|
148
|
+
calcLoopMode(channel: any, note: any, voiceParams: any): boolean;
|
|
149
|
+
createBufferSource(channel: any, note: any, voiceParams: any, audioBuffer: any): any;
|
|
146
150
|
findPortamentoTarget(queueIndex: any): any;
|
|
147
151
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
148
152
|
getQueueIndex(second: any): number;
|
|
@@ -163,7 +167,7 @@ export class Midy {
|
|
|
163
167
|
seekTo(second: any): void;
|
|
164
168
|
calcTotalTime(): number;
|
|
165
169
|
currentTime(): number;
|
|
166
|
-
processScheduledNotes(channel: any,
|
|
170
|
+
processScheduledNotes(channel: any, callback: any): void;
|
|
167
171
|
getActiveNotes(channel: any, scheduleTime: any): SparseMap;
|
|
168
172
|
getActiveNote(noteList: any, scheduleTime: any): any;
|
|
169
173
|
createConvolutionReverbImpulse(audioContext: any, decay: any, preDecay: any): any;
|
|
@@ -207,14 +211,14 @@ export class Midy {
|
|
|
207
211
|
startVibrato(channel: any, note: any, scheduleTime: any): void;
|
|
208
212
|
getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
|
|
209
213
|
createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
|
|
210
|
-
calcBank(channel: any
|
|
214
|
+
calcBank(channel: any): any;
|
|
211
215
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
|
|
212
216
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
|
|
213
217
|
stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
|
|
214
218
|
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any, portamentoNoteNumber: any): Promise<any> | undefined;
|
|
215
219
|
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
|
|
216
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
217
|
-
releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
|
|
220
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
221
|
+
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
218
222
|
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
219
223
|
handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any, scheduleTime: any): void;
|
|
220
224
|
handleProgramChange(channelNumber: any, program: any, _scheduleTime: any): void;
|
|
@@ -257,7 +261,7 @@ export class Midy {
|
|
|
257
261
|
64: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
258
262
|
65: (channelNumber: any, value: any) => void;
|
|
259
263
|
66: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
260
|
-
67: (channelNumber: any, softPedal: any,
|
|
264
|
+
67: (channelNumber: any, softPedal: any, scheduleTime: any) => void;
|
|
261
265
|
71: (channelNumber: any, filterResonance: any, scheduleTime: any) => void;
|
|
262
266
|
72: (channelNumber: any, releaseTime: any, _scheduleTime: any) => void;
|
|
263
267
|
73: (channelNumber: any, attackTime: any, scheduleTime: any) => void;
|
|
@@ -275,10 +279,10 @@ export class Midy {
|
|
|
275
279
|
120: (channelNumber: any, _value: any, scheduleTime: any) => Promise<any[]>;
|
|
276
280
|
121: (channelNumber: any) => void;
|
|
277
281
|
123: (channelNumber: any, _value: any, scheduleTime: any) => Promise<any[]>;
|
|
278
|
-
124: () => void;
|
|
279
|
-
125: () => void;
|
|
280
|
-
126: () => void;
|
|
281
|
-
127: () => void;
|
|
282
|
+
124: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
283
|
+
125: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
284
|
+
126: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
285
|
+
127: (channelNumber: any, value: any, scheduleTime: any) => void;
|
|
282
286
|
};
|
|
283
287
|
handleControlChange(channelNumber: any, controllerType: any, value: any, scheduleTime: any): void;
|
|
284
288
|
setBankMSB(channelNumber: any, msb: any): void;
|
|
@@ -300,7 +304,7 @@ export class Midy {
|
|
|
300
304
|
setSustainPedal(channelNumber: any, value: any, scheduleTime: any): void;
|
|
301
305
|
setPortamento(channelNumber: any, value: any): void;
|
|
302
306
|
setSostenutoPedal(channelNumber: any, value: any, scheduleTime: any): void;
|
|
303
|
-
setSoftPedal(channelNumber: any, softPedal: any,
|
|
307
|
+
setSoftPedal(channelNumber: any, softPedal: any, scheduleTime: any): void;
|
|
304
308
|
setFilterResonance(channelNumber: any, filterResonance: any, scheduleTime: any): void;
|
|
305
309
|
setReleaseTime(channelNumber: any, releaseTime: any, _scheduleTime: any): void;
|
|
306
310
|
setAttackTime(channelNumber: any, attackTime: any, scheduleTime: any): void;
|
|
@@ -321,22 +325,22 @@ export class Midy {
|
|
|
321
325
|
dataEntryMSB(channelNumber: any, value: any, scheduleTime: any): void;
|
|
322
326
|
handlePitchBendRangeRPN(channelNumber: any, scheduleTime: any): void;
|
|
323
327
|
setPitchBendRange(channelNumber: any, value: any, scheduleTime: any): void;
|
|
324
|
-
handleFineTuningRPN(channelNumber: any): void;
|
|
325
|
-
setFineTuning(channelNumber: any, value: any): void;
|
|
326
|
-
handleCoarseTuningRPN(channelNumber: any): void;
|
|
327
|
-
setCoarseTuning(channelNumber: any, value: any): void;
|
|
328
|
-
handleModulationDepthRangeRPN(channelNumber: any): void;
|
|
329
|
-
setModulationDepthRange(channelNumber: any, modulationDepthRange: any): void;
|
|
328
|
+
handleFineTuningRPN(channelNumber: any, scheduleTime: any): void;
|
|
329
|
+
setFineTuning(channelNumber: any, value: any, scheduleTime: any): void;
|
|
330
|
+
handleCoarseTuningRPN(channelNumber: any, scheduleTime: any): void;
|
|
331
|
+
setCoarseTuning(channelNumber: any, value: any, scheduleTime: any): void;
|
|
332
|
+
handleModulationDepthRangeRPN(channelNumber: any, scheduleTime: any): void;
|
|
333
|
+
setModulationDepthRange(channelNumber: any, modulationDepthRange: any, scheduleTime: any): void;
|
|
330
334
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
331
335
|
resetAllControllers(channelNumber: any): void;
|
|
332
336
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
333
|
-
omniOff(): void;
|
|
334
|
-
omniOn(): void;
|
|
335
|
-
monoOn(): void;
|
|
336
|
-
polyOn(): void;
|
|
337
|
+
omniOff(channelNumber: any, value: any, scheduleTime: any): void;
|
|
338
|
+
omniOn(channelNumber: any, value: any, scheduleTime: any): void;
|
|
339
|
+
monoOn(channelNumber: any, value: any, scheduleTime: any): void;
|
|
340
|
+
polyOn(channelNumber: any, value: any, scheduleTime: any): void;
|
|
337
341
|
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
338
|
-
GM1SystemOn(): void;
|
|
339
|
-
GM2SystemOn(): void;
|
|
342
|
+
GM1SystemOn(scheduleTime: any): void;
|
|
343
|
+
GM2SystemOn(scheduleTime: any): void;
|
|
340
344
|
handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
341
345
|
handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
|
|
342
346
|
setMasterVolume(volume: any, scheduleTime: any): void;
|