@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/script/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;
|
|
@@ -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/script/midy-GMLite.js
CHANGED
|
@@ -173,6 +173,12 @@ const volumeEnvelopeKeys = [
|
|
|
173
173
|
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
174
174
|
class MidyGMLite {
|
|
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 MidyGMLite {
|
|
|
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 MidyGMLite {
|
|
|
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,10 +404,18 @@ class MidyGMLite {
|
|
|
391
404
|
return audioBuffer;
|
|
392
405
|
}
|
|
393
406
|
}
|
|
394
|
-
|
|
407
|
+
calcLoopMode(channel, voiceParams) {
|
|
408
|
+
if (channel.isDrum) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
return voiceParams.sampleModes % 2 !== 0;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
createBufferSource(channel, voiceParams, audioBuffer) {
|
|
395
416
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
396
417
|
bufferSource.buffer = audioBuffer;
|
|
397
|
-
bufferSource.loop =
|
|
418
|
+
bufferSource.loop = this.calcLoopMode(channel, voiceParams);
|
|
398
419
|
if (bufferSource.loop) {
|
|
399
420
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
400
421
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -415,7 +436,7 @@ class MidyGMLite {
|
|
|
415
436
|
}
|
|
416
437
|
/* falls through */
|
|
417
438
|
case "noteOff": {
|
|
418
|
-
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime);
|
|
439
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
419
440
|
if (notePromise) {
|
|
420
441
|
this.notePromises.push(notePromise);
|
|
421
442
|
}
|
|
@@ -579,15 +600,10 @@ class MidyGMLite {
|
|
|
579
600
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
580
601
|
const channel = this.channels[channelNumber];
|
|
581
602
|
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
|
-
}
|
|
603
|
+
this.processScheduledNotes(channel, (note) => {
|
|
604
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
605
|
+
this.notePromises.push(promise);
|
|
606
|
+
promises.push(promise);
|
|
591
607
|
});
|
|
592
608
|
channel.scheduledNotes.clear();
|
|
593
609
|
return Promise.all(promises);
|
|
@@ -643,14 +659,12 @@ class MidyGMLite {
|
|
|
643
659
|
const now = this.audioContext.currentTime;
|
|
644
660
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
645
661
|
}
|
|
646
|
-
processScheduledNotes(channel,
|
|
662
|
+
processScheduledNotes(channel, callback) {
|
|
647
663
|
channel.scheduledNotes.forEach((noteList) => {
|
|
648
664
|
for (let i = 0; i < noteList.length; i++) {
|
|
649
665
|
const note = noteList[i];
|
|
650
666
|
if (!note)
|
|
651
667
|
continue;
|
|
652
|
-
if (scheduleTime < note.startTime)
|
|
653
|
-
continue;
|
|
654
668
|
callback(note);
|
|
655
669
|
}
|
|
656
670
|
});
|
|
@@ -694,7 +708,7 @@ class MidyGMLite {
|
|
|
694
708
|
return pitchWheel * pitchWheelSensitivity;
|
|
695
709
|
}
|
|
696
710
|
updateChannelDetune(channel, scheduleTime) {
|
|
697
|
-
this.processScheduledNotes(channel,
|
|
711
|
+
this.processScheduledNotes(channel, (note) => {
|
|
698
712
|
this.updateDetune(channel, note, scheduleTime);
|
|
699
713
|
});
|
|
700
714
|
}
|
|
@@ -812,7 +826,7 @@ class MidyGMLite {
|
|
|
812
826
|
const voiceParams = voice.getAllParams(controllerState);
|
|
813
827
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
814
828
|
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
815
|
-
note.bufferSource = this.
|
|
829
|
+
note.bufferSource = this.createBufferSource(channel, voiceParams, audioBuffer);
|
|
816
830
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
817
831
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
818
832
|
type: "lowpass",
|
|
@@ -843,15 +857,17 @@ class MidyGMLite {
|
|
|
843
857
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
844
858
|
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
845
859
|
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
860
|
+
if (0.5 <= channel.state.sustainPedal) {
|
|
861
|
+
channel.sustainNotes.push(note);
|
|
862
|
+
}
|
|
846
863
|
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
847
864
|
if (exclusiveClass !== 0) {
|
|
848
865
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
849
866
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
850
867
|
const [prevNote, prevChannelNumber] = prevEntry;
|
|
851
|
-
if (!prevNote.ending) {
|
|
868
|
+
if (prevNote && !prevNote.ending) {
|
|
852
869
|
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
853
|
-
startTime,
|
|
854
|
-
true);
|
|
870
|
+
startTime, true);
|
|
855
871
|
}
|
|
856
872
|
}
|
|
857
873
|
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
@@ -895,7 +911,7 @@ class MidyGMLite {
|
|
|
895
911
|
}
|
|
896
912
|
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
897
913
|
const channel = this.channels[channelNumber];
|
|
898
|
-
if (!force && 0.5
|
|
914
|
+
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
899
915
|
return;
|
|
900
916
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
901
917
|
return;
|
|
@@ -923,11 +939,11 @@ class MidyGMLite {
|
|
|
923
939
|
const velocity = halfVelocity * 2;
|
|
924
940
|
const channel = this.channels[channelNumber];
|
|
925
941
|
const promises = [];
|
|
926
|
-
|
|
927
|
-
const
|
|
928
|
-
const promise = this.noteOff(channelNumber, noteNumber, velocity);
|
|
942
|
+
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
943
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
929
944
|
promises.push(promise);
|
|
930
|
-
}
|
|
945
|
+
}
|
|
946
|
+
channel.sustainNotes = [];
|
|
931
947
|
return promises;
|
|
932
948
|
}
|
|
933
949
|
handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
|
|
@@ -957,8 +973,10 @@ class MidyGMLite {
|
|
|
957
973
|
this.setPitchBend(channelNumber, pitchBend, scheduleTime);
|
|
958
974
|
}
|
|
959
975
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
960
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
961
976
|
const channel = this.channels[channelNumber];
|
|
977
|
+
if (channel.isDrum)
|
|
978
|
+
return;
|
|
979
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
962
980
|
const state = channel.state;
|
|
963
981
|
const prev = state.pitchWheel * 2 - 1;
|
|
964
982
|
const next = (value - 8192) / 8192;
|
|
@@ -1038,48 +1056,43 @@ class MidyGMLite {
|
|
|
1038
1056
|
return state;
|
|
1039
1057
|
}
|
|
1040
1058
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1059
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1060
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1061
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1062
|
+
let appliedFilterEnvelope = false;
|
|
1063
|
+
let appliedVolumeEnvelope = false;
|
|
1064
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1065
|
+
const prevValue = note.voiceParams[key];
|
|
1066
|
+
if (value === prevValue)
|
|
1045
1067
|
continue;
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if (value === prevValue)
|
|
1068
|
+
note.voiceParams[key] = value;
|
|
1069
|
+
if (key in this.voiceParamsHandlers) {
|
|
1070
|
+
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1071
|
+
}
|
|
1072
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1073
|
+
if (appliedFilterEnvelope)
|
|
1053
1074
|
continue;
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
continue;
|
|
1061
|
-
appliedFilterEnvelope = true;
|
|
1062
|
-
const noteVoiceParams = note.voiceParams;
|
|
1063
|
-
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1064
|
-
const key = filterEnvelopeKeys[i];
|
|
1065
|
-
if (key in voiceParams)
|
|
1066
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1067
|
-
}
|
|
1068
|
-
this.setFilterEnvelope(note, scheduleTime);
|
|
1069
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1075
|
+
appliedFilterEnvelope = true;
|
|
1076
|
+
const noteVoiceParams = note.voiceParams;
|
|
1077
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1078
|
+
const key = filterEnvelopeKeys[i];
|
|
1079
|
+
if (key in voiceParams)
|
|
1080
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1070
1081
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
+
this.setFilterEnvelope(note, scheduleTime);
|
|
1083
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1084
|
+
}
|
|
1085
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1086
|
+
if (appliedVolumeEnvelope)
|
|
1087
|
+
continue;
|
|
1088
|
+
appliedVolumeEnvelope = true;
|
|
1089
|
+
const noteVoiceParams = note.voiceParams;
|
|
1090
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1091
|
+
const key = volumeEnvelopeKeys[i];
|
|
1092
|
+
if (key in voiceParams)
|
|
1093
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1082
1094
|
}
|
|
1095
|
+
this.setVolumeEnvelope(note, scheduleTime);
|
|
1083
1096
|
}
|
|
1084
1097
|
}
|
|
1085
1098
|
});
|
|
@@ -1112,9 +1125,8 @@ class MidyGMLite {
|
|
|
1112
1125
|
}
|
|
1113
1126
|
}
|
|
1114
1127
|
updateModulation(channel, scheduleTime) {
|
|
1115
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1116
1128
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1117
|
-
this.processScheduledNotes(channel,
|
|
1129
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1118
1130
|
if (note.modulationDepth) {
|
|
1119
1131
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1120
1132
|
}
|
|
@@ -1126,10 +1138,14 @@ class MidyGMLite {
|
|
|
1126
1138
|
}
|
|
1127
1139
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1128
1140
|
const channel = this.channels[channelNumber];
|
|
1141
|
+
if (channel.isDrum)
|
|
1142
|
+
return;
|
|
1143
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1129
1144
|
channel.state.modulationDepth = modulation / 127;
|
|
1130
1145
|
this.updateModulation(channel, scheduleTime);
|
|
1131
1146
|
}
|
|
1132
1147
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1148
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1133
1149
|
const channel = this.channels[channelNumber];
|
|
1134
1150
|
channel.state.volume = volume / 127;
|
|
1135
1151
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1142,11 +1158,13 @@ class MidyGMLite {
|
|
|
1142
1158
|
};
|
|
1143
1159
|
}
|
|
1144
1160
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1161
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1145
1162
|
const channel = this.channels[channelNumber];
|
|
1146
1163
|
channel.state.pan = pan / 127;
|
|
1147
1164
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1148
1165
|
}
|
|
1149
1166
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1167
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1150
1168
|
const channel = this.channels[channelNumber];
|
|
1151
1169
|
channel.state.expression = expression / 127;
|
|
1152
1170
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1167,9 +1185,17 @@ class MidyGMLite {
|
|
|
1167
1185
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1168
1186
|
}
|
|
1169
1187
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1188
|
+
const channel = this.channels[channelNumber];
|
|
1189
|
+
if (channel.isDrum)
|
|
1190
|
+
return;
|
|
1170
1191
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1171
|
-
|
|
1172
|
-
if (
|
|
1192
|
+
channel.state.sustainPedal = value / 127;
|
|
1193
|
+
if (64 <= value) {
|
|
1194
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1195
|
+
channel.sustainNotes.push(note);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1173
1199
|
this.releaseSustainPedal(channelNumber, value, scheduleTime);
|
|
1174
1200
|
}
|
|
1175
1201
|
}
|
|
@@ -1219,8 +1245,10 @@ class MidyGMLite {
|
|
|
1219
1245
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1220
1246
|
}
|
|
1221
1247
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1222
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1223
1248
|
const channel = this.channels[channelNumber];
|
|
1249
|
+
if (channel.isDrum)
|
|
1250
|
+
return;
|
|
1251
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1224
1252
|
const state = channel.state;
|
|
1225
1253
|
const prev = state.pitchWheelSensitivity;
|
|
1226
1254
|
const next = value / 128;
|
|
@@ -1259,12 +1287,12 @@ class MidyGMLite {
|
|
|
1259
1287
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1260
1288
|
return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
|
|
1261
1289
|
}
|
|
1262
|
-
handleUniversalNonRealTimeExclusiveMessage(data,
|
|
1290
|
+
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1263
1291
|
switch (data[2]) {
|
|
1264
1292
|
case 9:
|
|
1265
1293
|
switch (data[3]) {
|
|
1266
1294
|
case 1:
|
|
1267
|
-
this.GM1SystemOn();
|
|
1295
|
+
this.GM1SystemOn(scheduleTime);
|
|
1268
1296
|
break;
|
|
1269
1297
|
case 2: // GM System Off
|
|
1270
1298
|
break;
|
|
@@ -1276,12 +1304,17 @@ class MidyGMLite {
|
|
|
1276
1304
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1277
1305
|
}
|
|
1278
1306
|
}
|
|
1279
|
-
GM1SystemOn() {
|
|
1307
|
+
GM1SystemOn(scheduleTime) {
|
|
1308
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1309
|
+
this.mode = "GM1";
|
|
1280
1310
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1311
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
1281
1312
|
const channel = this.channels[i];
|
|
1282
1313
|
channel.bank = 0;
|
|
1314
|
+
channel.isDrum = false;
|
|
1283
1315
|
}
|
|
1284
1316
|
this.channels[9].bank = 128;
|
|
1317
|
+
this.channels[9].isDrum = true;
|
|
1285
1318
|
}
|
|
1286
1319
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1287
1320
|
switch (data[2]) {
|
|
@@ -1324,13 +1357,20 @@ class MidyGMLite {
|
|
|
1324
1357
|
}
|
|
1325
1358
|
scheduleTask(callback, scheduleTime) {
|
|
1326
1359
|
return new Promise((resolve) => {
|
|
1327
|
-
const bufferSource = new AudioBufferSourceNode(this.audioContext
|
|
1360
|
+
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
1361
|
+
buffer: this.schedulerBuffer,
|
|
1362
|
+
});
|
|
1363
|
+
bufferSource.connect(this.scheduler);
|
|
1328
1364
|
bufferSource.onended = () => {
|
|
1329
|
-
|
|
1330
|
-
|
|
1365
|
+
try {
|
|
1366
|
+
callback();
|
|
1367
|
+
}
|
|
1368
|
+
finally {
|
|
1369
|
+
bufferSource.disconnect();
|
|
1370
|
+
resolve();
|
|
1371
|
+
}
|
|
1331
1372
|
};
|
|
1332
1373
|
bufferSource.start(scheduleTime);
|
|
1333
|
-
bufferSource.stop(scheduleTime);
|
|
1334
1374
|
});
|
|
1335
1375
|
}
|
|
1336
1376
|
}
|
|
@@ -1341,6 +1381,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1341
1381
|
writable: true,
|
|
1342
1382
|
value: {
|
|
1343
1383
|
currentBufferSource: null,
|
|
1384
|
+
isDrum: false,
|
|
1344
1385
|
detune: 0,
|
|
1345
1386
|
program: 0,
|
|
1346
1387
|
bank: 0,
|
|
@@ -1348,5 +1389,6 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1348
1389
|
dataLSB: 0,
|
|
1349
1390
|
rpnMSB: 127,
|
|
1350
1391
|
rpnLSB: 127,
|
|
1392
|
+
modulationDepthRange: 50, // cent
|
|
1351
1393
|
}
|
|
1352
1394
|
});
|
package/script/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;
|