@marmooo/midy 0.3.6 → 0.3.8
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 +38 -20
- package/esm/midy-GM1.d.ts +36 -30
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +184 -142
- package/esm/midy-GM2.d.ts +43 -33
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +324 -279
- package/esm/midy-GMLite.d.ts +35 -30
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +178 -139
- package/esm/midy.d.ts +45 -34
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +414 -302
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +36 -30
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +184 -142
- package/script/midy-GM2.d.ts +43 -33
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +324 -279
- package/script/midy-GMLite.d.ts +35 -30
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +178 -139
- package/script/midy.d.ts +45 -34
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +414 -302
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export class MidyGMLite {
|
|
|
3
3
|
scheduleIndex: number;
|
|
4
4
|
detune: number;
|
|
5
5
|
programNumber: number;
|
|
6
|
-
bank: number;
|
|
7
6
|
dataMSB: number;
|
|
8
7
|
dataLSB: number;
|
|
9
8
|
rpnMSB: number;
|
|
@@ -21,7 +20,7 @@ export class MidyGMLite {
|
|
|
21
20
|
startTime: number;
|
|
22
21
|
resumeTime: number;
|
|
23
22
|
soundFonts: any[];
|
|
24
|
-
soundFontTable:
|
|
23
|
+
soundFontTable: never[][];
|
|
25
24
|
voiceCounter: Map<any, any>;
|
|
26
25
|
voiceCache: Map<any, any>;
|
|
27
26
|
isPlaying: boolean;
|
|
@@ -29,6 +28,7 @@ export class MidyGMLite {
|
|
|
29
28
|
isPaused: boolean;
|
|
30
29
|
isStopping: boolean;
|
|
31
30
|
isSeeking: boolean;
|
|
31
|
+
playPromise: any;
|
|
32
32
|
timeline: any[];
|
|
33
33
|
notePromises: any[];
|
|
34
34
|
instruments: Set<any>;
|
|
@@ -38,21 +38,21 @@ export class MidyGMLite {
|
|
|
38
38
|
masterVolume: any;
|
|
39
39
|
scheduler: any;
|
|
40
40
|
schedulerBuffer: any;
|
|
41
|
+
messageHandlers: any[];
|
|
41
42
|
voiceParamsHandlers: {
|
|
42
|
-
modLfoToPitch: (channel: any, note: any,
|
|
43
|
-
vibLfoToPitch: (_channel: any, _note: any,
|
|
44
|
-
modLfoToFilterFc: (channel: any, note: any,
|
|
45
|
-
modLfoToVolume: (channel: any, note: any,
|
|
46
|
-
chorusEffectsSend: (_channel: any, _note: any,
|
|
47
|
-
reverbEffectsSend: (_channel: any, _note: any,
|
|
48
|
-
delayModLFO: (_channel: any, note: any,
|
|
49
|
-
freqModLFO: (_channel: any, note: any,
|
|
50
|
-
delayVibLFO: (_channel: any, _note: any,
|
|
51
|
-
freqVibLFO: (_channel: any, _note: any,
|
|
43
|
+
modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
|
|
44
|
+
vibLfoToPitch: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
45
|
+
modLfoToFilterFc: (channel: any, note: any, scheduleTime: any) => void;
|
|
46
|
+
modLfoToVolume: (channel: any, note: any, scheduleTime: any) => void;
|
|
47
|
+
chorusEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
48
|
+
reverbEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
49
|
+
delayModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
50
|
+
freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
51
|
+
delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
52
|
+
freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
52
53
|
};
|
|
53
54
|
controlChangeHandlers: any[];
|
|
54
55
|
channels: any[];
|
|
55
|
-
initSoundFontTable(): any[];
|
|
56
56
|
addSoundFont(soundFont: any): void;
|
|
57
57
|
toUint8Array(input: any): Promise<Uint8Array<ArrayBuffer>>;
|
|
58
58
|
loadSoundFont(input: any): Promise<void>;
|
|
@@ -67,11 +67,14 @@ export class MidyGMLite {
|
|
|
67
67
|
createChannels(audioContext: any): any[];
|
|
68
68
|
createAudioBuffer(voiceParams: any): Promise<any>;
|
|
69
69
|
createBufferSource(channel: any, voiceParams: any, audioBuffer: any): any;
|
|
70
|
-
scheduleTimelineEvents(
|
|
70
|
+
scheduleTimelineEvents(scheduleTime: any, queueIndex: any): Promise<any>;
|
|
71
71
|
getQueueIndex(second: any): number;
|
|
72
|
-
|
|
72
|
+
resetAllStates(): void;
|
|
73
|
+
updateStates(queueIndex: any, nextQueueIndex: any): void;
|
|
74
|
+
playNotes(): Promise<void>;
|
|
73
75
|
ticksToSecond(ticks: any, secondsPerBeat: any): number;
|
|
74
76
|
secondToTicks(second: any, secondsPerBeat: any): number;
|
|
77
|
+
getSoundFontId(channel: any): string;
|
|
75
78
|
extractMidiData(midi: any): {
|
|
76
79
|
instruments: Set<any>;
|
|
77
80
|
timeline: any[];
|
|
@@ -80,8 +83,8 @@ export class MidyGMLite {
|
|
|
80
83
|
stopChannelNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
81
84
|
stopNotes(velocity: any, force: any, scheduleTime: any): Promise<any[]>;
|
|
82
85
|
start(): Promise<void>;
|
|
83
|
-
stop(): void
|
|
84
|
-
pause(): void
|
|
86
|
+
stop(): Promise<void>;
|
|
87
|
+
pause(): Promise<void>;
|
|
85
88
|
resume(): Promise<void>;
|
|
86
89
|
seekTo(second: any): void;
|
|
87
90
|
calcTotalTime(): number;
|
|
@@ -113,7 +116,9 @@ export class MidyGMLite {
|
|
|
113
116
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
114
117
|
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): void;
|
|
115
118
|
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
116
|
-
|
|
119
|
+
createMessageHandlers(): any[];
|
|
120
|
+
handleMessage(data: any, scheduleTime: any): void;
|
|
121
|
+
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<void>;
|
|
117
122
|
setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
118
123
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
119
124
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
@@ -123,16 +128,16 @@ export class MidyGMLite {
|
|
|
123
128
|
setDelayModLFO(note: any, scheduleTime: any): void;
|
|
124
129
|
setFreqModLFO(note: any, scheduleTime: any): void;
|
|
125
130
|
createVoiceParamsHandlers(): {
|
|
126
|
-
modLfoToPitch: (channel: any, note: any,
|
|
127
|
-
vibLfoToPitch: (_channel: any, _note: any,
|
|
128
|
-
modLfoToFilterFc: (channel: any, note: any,
|
|
129
|
-
modLfoToVolume: (channel: any, note: any,
|
|
130
|
-
chorusEffectsSend: (_channel: any, _note: any,
|
|
131
|
-
reverbEffectsSend: (_channel: any, _note: any,
|
|
132
|
-
delayModLFO: (_channel: any, note: any,
|
|
133
|
-
freqModLFO: (_channel: any, note: any,
|
|
134
|
-
delayVibLFO: (_channel: any, _note: any,
|
|
135
|
-
freqVibLFO: (_channel: any, _note: any,
|
|
131
|
+
modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
|
|
132
|
+
vibLfoToPitch: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
133
|
+
modLfoToFilterFc: (channel: any, note: any, scheduleTime: any) => void;
|
|
134
|
+
modLfoToVolume: (channel: any, note: any, scheduleTime: any) => void;
|
|
135
|
+
chorusEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
136
|
+
reverbEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
137
|
+
delayModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
138
|
+
freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
139
|
+
delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
140
|
+
freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
136
141
|
};
|
|
137
142
|
getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
|
|
138
143
|
applyVoiceParams(channel: any, controllerType: any, scheduleTime: any): void;
|
|
@@ -158,14 +163,14 @@ export class MidyGMLite {
|
|
|
158
163
|
handlePitchBendRangeRPN(channelNumber: any, scheduleTime: any): void;
|
|
159
164
|
setPitchBendRange(channelNumber: any, value: any, scheduleTime: any): void;
|
|
160
165
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
161
|
-
|
|
166
|
+
resetChannelStates(channelNumber: any): void;
|
|
162
167
|
resetAllControllers(channelNumber: any, _value: any, scheduleTime: any): void;
|
|
163
168
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
164
169
|
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
165
170
|
GM1SystemOn(scheduleTime: any): void;
|
|
166
171
|
handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
167
172
|
handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
|
|
168
|
-
setMasterVolume(
|
|
173
|
+
setMasterVolume(value: any, scheduleTime: any): void;
|
|
169
174
|
handleSysEx(data: any, scheduleTime: any): void;
|
|
170
175
|
scheduleTask(callback: any, scheduleTime: any): Promise<any>;
|
|
171
176
|
}
|
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":"AA0GA;
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AA0GA;IA4BE;;;;;;;;;MASE;IAEF,+BAeC;IArDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,0BAAuD;IACvD,4BAAyB;IACzB,0BAAuB;IACvB,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IACrC,+BAEE;IAcA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF,uBAAmD;IACnD;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAWC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,0EAUC;IAED,yEAqDC;IAED,mCAOC;IAED,uBAQC;IAED,yDA2BC;IAED,2BAwCC;IAED,uDAEC;IAED,wDAEC;IAED,qCAKC;IAED;;;MAwDC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,sBAKC;IAED,uBAQC;IAED,wBAKC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEASC;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,6FAyBC;IAED,oGA2CC;IAED,0EAiBC;IAED,8EAiBC;IAED,kGAiCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAkBC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,+BAmBC;IAED,kDAOC;IAED,uGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAWC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MAiCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDASC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCASC;IAED,4EAaC;IAED,4DAGC;IAED,qDAKC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA9hDD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,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
|
@@ -213,7 +213,7 @@ export class MidyGMLite {
|
|
|
213
213
|
enumerable: true,
|
|
214
214
|
configurable: true,
|
|
215
215
|
writable: true,
|
|
216
|
-
value:
|
|
216
|
+
value: Array.from({ length: 128 }, () => [])
|
|
217
217
|
});
|
|
218
218
|
Object.defineProperty(this, "voiceCounter", {
|
|
219
219
|
enumerable: true,
|
|
@@ -257,6 +257,12 @@ export class MidyGMLite {
|
|
|
257
257
|
writable: true,
|
|
258
258
|
value: false
|
|
259
259
|
});
|
|
260
|
+
Object.defineProperty(this, "playPromise", {
|
|
261
|
+
enumerable: true,
|
|
262
|
+
configurable: true,
|
|
263
|
+
writable: true,
|
|
264
|
+
value: void 0
|
|
265
|
+
});
|
|
260
266
|
Object.defineProperty(this, "timeline", {
|
|
261
267
|
enumerable: true,
|
|
262
268
|
configurable: true,
|
|
@@ -294,6 +300,7 @@ export class MidyGMLite {
|
|
|
294
300
|
length: 1,
|
|
295
301
|
sampleRate: audioContext.sampleRate,
|
|
296
302
|
});
|
|
303
|
+
this.messageHandlers = this.createMessageHandlers();
|
|
297
304
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
298
305
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
299
306
|
this.channels = this.createChannels(audioContext);
|
|
@@ -301,21 +308,14 @@ export class MidyGMLite {
|
|
|
301
308
|
this.scheduler.connect(audioContext.destination);
|
|
302
309
|
this.GM1SystemOn();
|
|
303
310
|
}
|
|
304
|
-
initSoundFontTable() {
|
|
305
|
-
const table = new Array(128);
|
|
306
|
-
for (let i = 0; i < 128; i++) {
|
|
307
|
-
table[i] = new Map();
|
|
308
|
-
}
|
|
309
|
-
return table;
|
|
310
|
-
}
|
|
311
311
|
addSoundFont(soundFont) {
|
|
312
312
|
const index = this.soundFonts.length;
|
|
313
313
|
this.soundFonts.push(soundFont);
|
|
314
314
|
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
315
|
+
const soundFontTable = this.soundFontTable;
|
|
315
316
|
for (let i = 0; i < presetHeaders.length; i++) {
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
banks.set(presetHeader.bank, index);
|
|
317
|
+
const { preset, bank } = presetHeaders[i];
|
|
318
|
+
soundFontTable[preset][bank] = index;
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
async toUint8Array(input) {
|
|
@@ -393,13 +393,16 @@ export class MidyGMLite {
|
|
|
393
393
|
this.GM1SystemOn();
|
|
394
394
|
}
|
|
395
395
|
getVoiceId(channel, noteNumber, velocity) {
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
|
|
396
|
+
const programNumber = channel.programNumber;
|
|
397
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
398
|
+
if (!bankTable)
|
|
399
|
+
return;
|
|
400
|
+
const bank = channel.isDrum ? 128 : 0;
|
|
401
|
+
const soundFontIndex = bankTable[bank];
|
|
399
402
|
if (soundFontIndex === undefined)
|
|
400
403
|
return;
|
|
401
404
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
402
|
-
const voice = soundFont.getVoice(
|
|
405
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
403
406
|
const { instrument, sampleID } = voice.generators;
|
|
404
407
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
405
408
|
}
|
|
@@ -450,13 +453,16 @@ export class MidyGMLite {
|
|
|
450
453
|
}
|
|
451
454
|
return bufferSource;
|
|
452
455
|
}
|
|
453
|
-
async scheduleTimelineEvents(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
456
|
+
async scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
457
|
+
const timeOffset = this.resumeTime - this.startTime;
|
|
458
|
+
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
459
|
+
const schedulingOffset = this.startDelay - timeOffset;
|
|
460
|
+
const timeline = this.timeline;
|
|
461
|
+
while (queueIndex < timeline.length) {
|
|
462
|
+
const event = timeline[queueIndex];
|
|
463
|
+
if (lookAheadCheckTime < event.startTime)
|
|
457
464
|
break;
|
|
458
|
-
const
|
|
459
|
-
const startTime = event.startTime + delay;
|
|
465
|
+
const startTime = event.startTime + schedulingOffset;
|
|
460
466
|
switch (event.type) {
|
|
461
467
|
case "noteOn":
|
|
462
468
|
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
@@ -491,70 +497,77 @@ export class MidyGMLite {
|
|
|
491
497
|
}
|
|
492
498
|
return 0;
|
|
493
499
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
500
|
+
resetAllStates() {
|
|
501
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
502
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
503
|
+
this.voiceCache.clear();
|
|
504
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
505
|
+
this.channels[i].scheduledNotes = [];
|
|
506
|
+
this.resetChannelStates(i);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
510
|
+
if (nextQueueIndex < queueIndex)
|
|
511
|
+
queueIndex = 0;
|
|
512
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
513
|
+
const event = this.timeline[i];
|
|
514
|
+
switch (event.type) {
|
|
515
|
+
case "controller":
|
|
516
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
517
|
+
break;
|
|
518
|
+
case "programChange":
|
|
519
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
520
|
+
break;
|
|
521
|
+
case "pitchBend":
|
|
522
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
523
|
+
break;
|
|
524
|
+
case "sysEx":
|
|
525
|
+
this.handleSysEx(event.data, 0);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async playNotes() {
|
|
530
|
+
if (this.audioContext.state === "suspended") {
|
|
531
|
+
await this.audioContext.resume();
|
|
532
|
+
}
|
|
533
|
+
this.isPlaying = true;
|
|
534
|
+
this.isPaused = false;
|
|
535
|
+
this.startTime = this.audioContext.currentTime;
|
|
536
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
537
|
+
let finished = false;
|
|
538
|
+
this.notePromises = [];
|
|
539
|
+
while (queueIndex < this.timeline.length) {
|
|
540
|
+
const now = this.audioContext.currentTime;
|
|
541
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
542
|
+
if (this.isPausing) {
|
|
543
|
+
await this.stopNotes(0, true, now);
|
|
544
|
+
await this.audioContext.suspend();
|
|
545
|
+
this.notePromises = [];
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
else if (this.isStopping) {
|
|
549
|
+
await this.stopNotes(0, true, now);
|
|
550
|
+
await this.audioContext.suspend();
|
|
551
|
+
finished = true;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
else if (this.isSeeking) {
|
|
555
|
+
await this.stopNotes(0, true, now);
|
|
556
|
+
this.startTime = this.audioContext.currentTime;
|
|
557
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
558
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
559
|
+
queueIndex = nextQueueIndex;
|
|
560
|
+
this.isSeeking = false;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const waitTime = now + this.noteCheckInterval;
|
|
564
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
565
|
+
}
|
|
566
|
+
if (finished) {
|
|
501
567
|
this.notePromises = [];
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
this.notePromises = [];
|
|
506
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
507
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
508
|
-
this.voiceCache.clear();
|
|
509
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
510
|
-
this.resetAllStates(i);
|
|
511
|
-
}
|
|
512
|
-
resolve();
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
const now = this.audioContext.currentTime;
|
|
516
|
-
const t = now + resumeTime;
|
|
517
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
518
|
-
if (this.isPausing) {
|
|
519
|
-
await this.stopNotes(0, true, now);
|
|
520
|
-
this.notePromises = [];
|
|
521
|
-
this.isPausing = false;
|
|
522
|
-
this.isPaused = true;
|
|
523
|
-
resolve();
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
else if (this.isStopping) {
|
|
527
|
-
await this.stopNotes(0, true, now);
|
|
528
|
-
this.notePromises = [];
|
|
529
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
530
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
531
|
-
this.voiceCache.clear();
|
|
532
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
533
|
-
this.resetAllStates(i);
|
|
534
|
-
}
|
|
535
|
-
this.isStopping = false;
|
|
536
|
-
this.isPaused = false;
|
|
537
|
-
resolve();
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
else if (this.isSeeking) {
|
|
541
|
-
this.stopNotes(0, true, now);
|
|
542
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
543
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
544
|
-
this.startTime = this.audioContext.currentTime;
|
|
545
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
546
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
547
|
-
this.isSeeking = false;
|
|
548
|
-
await schedulePlayback();
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
const waitTime = now + this.noteCheckInterval;
|
|
552
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
553
|
-
await schedulePlayback();
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
|
-
schedulePlayback();
|
|
557
|
-
});
|
|
568
|
+
this.resetAllStates();
|
|
569
|
+
}
|
|
570
|
+
this.isPlaying = false;
|
|
558
571
|
}
|
|
559
572
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
560
573
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -562,16 +575,16 @@ export class MidyGMLite {
|
|
|
562
575
|
secondToTicks(second, secondsPerBeat) {
|
|
563
576
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
564
577
|
}
|
|
578
|
+
getSoundFontId(channel) {
|
|
579
|
+
const programNumber = channel.programNumber;
|
|
580
|
+
const bank = channel.isDrum ? "128" : "000";
|
|
581
|
+
const program = programNumber.toString().padStart(3, "0");
|
|
582
|
+
return `${bank}:${program}`;
|
|
583
|
+
}
|
|
565
584
|
extractMidiData(midi) {
|
|
566
585
|
const instruments = new Set();
|
|
567
586
|
const timeline = [];
|
|
568
|
-
const
|
|
569
|
-
for (let i = 0; i < tmpChannels.length; i++) {
|
|
570
|
-
tmpChannels[i] = {
|
|
571
|
-
programNumber: -1,
|
|
572
|
-
bank: this.channels[i].bank,
|
|
573
|
-
};
|
|
574
|
-
}
|
|
587
|
+
const channels = this.channels;
|
|
575
588
|
for (let i = 0; i < midi.tracks.length; i++) {
|
|
576
589
|
const track = midi.tracks[i];
|
|
577
590
|
let currentTicks = 0;
|
|
@@ -581,17 +594,15 @@ export class MidyGMLite {
|
|
|
581
594
|
event.ticks = currentTicks;
|
|
582
595
|
switch (event.type) {
|
|
583
596
|
case "noteOn": {
|
|
584
|
-
const channel =
|
|
585
|
-
|
|
586
|
-
instruments.add(`${channel.bank}:0`);
|
|
587
|
-
channel.programNumber = 0;
|
|
588
|
-
}
|
|
597
|
+
const channel = channels[event.channel];
|
|
598
|
+
instruments.add(this.getSoundFontId(channel));
|
|
589
599
|
break;
|
|
590
600
|
}
|
|
591
601
|
case "programChange": {
|
|
592
|
-
const channel =
|
|
593
|
-
channel
|
|
594
|
-
instruments.add(
|
|
602
|
+
const channel = channels[event.channel];
|
|
603
|
+
this.setProgramChange(event.channel, event.programNumber);
|
|
604
|
+
instruments.add(this.getSoundFontId(channel));
|
|
605
|
+
break;
|
|
595
606
|
}
|
|
596
607
|
}
|
|
597
608
|
delete event.deltaTime;
|
|
@@ -655,26 +666,32 @@ export class MidyGMLite {
|
|
|
655
666
|
this.resumeTime = 0;
|
|
656
667
|
if (this.voiceCounter.size === 0)
|
|
657
668
|
this.cacheVoiceIds();
|
|
658
|
-
|
|
659
|
-
this.
|
|
669
|
+
this.playPromise = this.playNotes();
|
|
670
|
+
await this.playPromise;
|
|
660
671
|
}
|
|
661
|
-
stop() {
|
|
672
|
+
async stop() {
|
|
662
673
|
if (!this.isPlaying)
|
|
663
674
|
return;
|
|
664
675
|
this.isStopping = true;
|
|
676
|
+
await this.playPromise;
|
|
677
|
+
this.isStopping = false;
|
|
665
678
|
}
|
|
666
|
-
pause() {
|
|
679
|
+
async pause() {
|
|
667
680
|
if (!this.isPlaying || this.isPaused)
|
|
668
681
|
return;
|
|
669
682
|
const now = this.audioContext.currentTime;
|
|
670
683
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
671
684
|
this.isPausing = true;
|
|
685
|
+
await this.playPromise;
|
|
686
|
+
this.isPausing = false;
|
|
687
|
+
this.isPaused = true;
|
|
672
688
|
}
|
|
673
689
|
async resume() {
|
|
674
690
|
if (!this.isPaused)
|
|
675
691
|
return;
|
|
676
|
-
|
|
677
|
-
this.
|
|
692
|
+
this.playPromise = this.playNotes();
|
|
693
|
+
await this.playPromise;
|
|
694
|
+
this.isPaused = false;
|
|
678
695
|
}
|
|
679
696
|
seekTo(second) {
|
|
680
697
|
this.resumeTime = second;
|
|
@@ -855,7 +872,7 @@ export class MidyGMLite {
|
|
|
855
872
|
const voiceParams = voice.getAllParams(controllerState);
|
|
856
873
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
857
874
|
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
|
|
858
|
-
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
875
|
+
note.bufferSource = this.createBufferSource(channel, voiceParams, audioBuffer);
|
|
859
876
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
860
877
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
861
878
|
type: "lowpass",
|
|
@@ -891,7 +908,7 @@ export class MidyGMLite {
|
|
|
891
908
|
const channel = this.channels[channelNumber];
|
|
892
909
|
if (!channel.isDrum)
|
|
893
910
|
return;
|
|
894
|
-
const drumExclusiveClass = drumExclusiveClasses[noteNumber];
|
|
911
|
+
const drumExclusiveClass = drumExclusiveClasses[note.noteNumber];
|
|
895
912
|
if (drumExclusiveClass === 0)
|
|
896
913
|
return;
|
|
897
914
|
const index = drumExclusiveClass * this.channels.length + channelNumber;
|
|
@@ -904,13 +921,16 @@ export class MidyGMLite {
|
|
|
904
921
|
}
|
|
905
922
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
906
923
|
const channel = this.channels[channelNumber];
|
|
907
|
-
const
|
|
908
|
-
const
|
|
909
|
-
|
|
924
|
+
const programNumber = channel.programNumber;
|
|
925
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
926
|
+
if (!bankTable)
|
|
927
|
+
return;
|
|
928
|
+
const bank = channel.isDrum ? 128 : 0;
|
|
929
|
+
const soundFontIndex = bankTable[bank];
|
|
910
930
|
if (soundFontIndex === undefined)
|
|
911
931
|
return;
|
|
912
932
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
913
|
-
const voice = soundFont.getVoice(
|
|
933
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
914
934
|
if (!voice)
|
|
915
935
|
return;
|
|
916
936
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
@@ -1017,7 +1037,26 @@ export class MidyGMLite {
|
|
|
1017
1037
|
channel.sustainNotes = [];
|
|
1018
1038
|
return promises;
|
|
1019
1039
|
}
|
|
1020
|
-
|
|
1040
|
+
createMessageHandlers() {
|
|
1041
|
+
const handlers = new Array(256);
|
|
1042
|
+
// Channel Message
|
|
1043
|
+
handlers[0x80] = (data, scheduleTime) => this.noteOff(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1044
|
+
handlers[0x90] = (data, scheduleTime) => this.noteOn(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1045
|
+
handlers[0xB0] = (data, scheduleTime) => this.setControlChange(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1046
|
+
handlers[0xC0] = (data, scheduleTime) => this.setProgramChange(data[0] & 0x0F, data[1], scheduleTime);
|
|
1047
|
+
handlers[0xE0] = (data, scheduleTime) => this.handlePitchBendMessage(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1048
|
+
return handlers;
|
|
1049
|
+
}
|
|
1050
|
+
handleMessage(data, scheduleTime) {
|
|
1051
|
+
const status = data[0];
|
|
1052
|
+
if (status === 0xF0) {
|
|
1053
|
+
return this.handleSysEx(data.subarray(1), scheduleTime);
|
|
1054
|
+
}
|
|
1055
|
+
const handler = this.messageHandlers[status];
|
|
1056
|
+
if (handler)
|
|
1057
|
+
handler(data, scheduleTime);
|
|
1058
|
+
}
|
|
1059
|
+
handleChannelMessage(statusByte, data1, data2, scheduleTime) {
|
|
1021
1060
|
const channelNumber = statusByte & 0x0F;
|
|
1022
1061
|
const messageType = statusByte & 0xF0;
|
|
1023
1062
|
switch (messageType) {
|
|
@@ -1097,28 +1136,36 @@ export class MidyGMLite {
|
|
|
1097
1136
|
}
|
|
1098
1137
|
createVoiceParamsHandlers() {
|
|
1099
1138
|
return {
|
|
1100
|
-
modLfoToPitch: (channel, note,
|
|
1139
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1101
1140
|
if (0 < channel.state.modulationDepth) {
|
|
1102
1141
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1103
1142
|
}
|
|
1104
1143
|
},
|
|
1105
|
-
vibLfoToPitch: (_channel, _note,
|
|
1106
|
-
modLfoToFilterFc: (channel, note,
|
|
1144
|
+
vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
|
|
1145
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1107
1146
|
if (0 < channel.state.modulationDepth) {
|
|
1108
1147
|
this.setModLfoToFilterFc(note, scheduleTime);
|
|
1109
1148
|
}
|
|
1110
1149
|
},
|
|
1111
|
-
modLfoToVolume: (channel, note,
|
|
1150
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1112
1151
|
if (0 < channel.state.modulationDepth) {
|
|
1113
1152
|
this.setModLfoToVolume(note, scheduleTime);
|
|
1114
1153
|
}
|
|
1115
1154
|
},
|
|
1116
|
-
chorusEffectsSend: (_channel, _note,
|
|
1117
|
-
reverbEffectsSend: (_channel, _note,
|
|
1118
|
-
delayModLFO: (_channel, note,
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1155
|
+
chorusEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1156
|
+
reverbEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1157
|
+
delayModLFO: (_channel, note, scheduleTime) => {
|
|
1158
|
+
if (0 < channel.state.modulationDepth) {
|
|
1159
|
+
this.setDelayModLFO(note, scheduleTime);
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1163
|
+
if (0 < channel.state.modulationDepth) {
|
|
1164
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
delayVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1168
|
+
freqVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1122
1169
|
};
|
|
1123
1170
|
}
|
|
1124
1171
|
getControllerState(channel, noteNumber, velocity) {
|
|
@@ -1141,7 +1188,7 @@ export class MidyGMLite {
|
|
|
1141
1188
|
continue;
|
|
1142
1189
|
note.voiceParams[key] = value;
|
|
1143
1190
|
if (key in this.voiceParamsHandlers) {
|
|
1144
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1191
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1145
1192
|
}
|
|
1146
1193
|
else {
|
|
1147
1194
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1298,8 +1345,8 @@ export class MidyGMLite {
|
|
|
1298
1345
|
}
|
|
1299
1346
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
1300
1347
|
const channel = this.channels[channelNumber];
|
|
1301
|
-
this.limitData(channel, 0, 127, 0,
|
|
1302
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1348
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
1349
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
1303
1350
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1304
1351
|
}
|
|
1305
1352
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -1307,7 +1354,7 @@ export class MidyGMLite {
|
|
|
1307
1354
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1308
1355
|
const state = channel.state;
|
|
1309
1356
|
const prev = state.pitchWheelSensitivity;
|
|
1310
|
-
const next = value /
|
|
1357
|
+
const next = value / 12800;
|
|
1311
1358
|
state.pitchWheelSensitivity = next;
|
|
1312
1359
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1313
1360
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1317,7 +1364,7 @@ export class MidyGMLite {
|
|
|
1317
1364
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1318
1365
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
1319
1366
|
}
|
|
1320
|
-
|
|
1367
|
+
resetChannelStates(channelNumber) {
|
|
1321
1368
|
const scheduleTime = this.audioContext.currentTime;
|
|
1322
1369
|
const channel = this.channels[channelNumber];
|
|
1323
1370
|
const state = channel.state;
|
|
@@ -1392,10 +1439,8 @@ export class MidyGMLite {
|
|
|
1392
1439
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1393
1440
|
this.allSoundOff(i, 0, scheduleTime);
|
|
1394
1441
|
const channel = this.channels[i];
|
|
1395
|
-
channel.bank = 0;
|
|
1396
1442
|
channel.isDrum = false;
|
|
1397
1443
|
}
|
|
1398
|
-
this.channels[9].bank = 128;
|
|
1399
1444
|
this.channels[9].isDrum = true;
|
|
1400
1445
|
}
|
|
1401
1446
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
@@ -1416,16 +1461,11 @@ export class MidyGMLite {
|
|
|
1416
1461
|
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
1417
1462
|
this.setMasterVolume(volume, scheduleTime);
|
|
1418
1463
|
}
|
|
1419
|
-
setMasterVolume(
|
|
1464
|
+
setMasterVolume(value, scheduleTime) {
|
|
1420
1465
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
else {
|
|
1425
|
-
this.masterVolume.gain
|
|
1426
|
-
.cancelScheduledValues(scheduleTime)
|
|
1427
|
-
.setValueAtTime(volume * volume, scheduleTime);
|
|
1428
|
-
}
|
|
1466
|
+
this.masterVolume.gain
|
|
1467
|
+
.cancelScheduledValues(scheduleTime)
|
|
1468
|
+
.setValueAtTime(value * value, scheduleTime);
|
|
1429
1469
|
}
|
|
1430
1470
|
handleSysEx(data, scheduleTime) {
|
|
1431
1471
|
switch (data[0]) {
|
|
@@ -1465,7 +1505,6 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1465
1505
|
scheduleIndex: 0,
|
|
1466
1506
|
detune: 0,
|
|
1467
1507
|
programNumber: 0,
|
|
1468
|
-
bank: 0,
|
|
1469
1508
|
dataMSB: 0,
|
|
1470
1509
|
dataLSB: 0,
|
|
1471
1510
|
rpnMSB: 127,
|