@marmooo/midy 0.3.5 → 0.3.7
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 +7 -9
- package/esm/midy-GM1.d.ts +29 -26
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +136 -101
- package/esm/midy-GM2.d.ts +41 -37
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +325 -300
- package/esm/midy-GMLite.d.ts +29 -26
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +129 -96
- package/esm/midy.d.ts +43 -38
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +428 -325
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +29 -26
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +136 -101
- package/script/midy-GM2.d.ts +41 -37
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +325 -300
- package/script/midy-GMLite.d.ts +29 -26
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +129 -96
- package/script/midy.d.ts +43 -38
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +428 -325
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marmooo/midy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "A MIDI player/synthesizer written in JavaScript that supports GM-Lite/GM1 and SF2/SF3.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test": "node test_runner.js"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@marmooo/soundfont-parser": "^0.1.
|
|
25
|
+
"@marmooo/soundfont-parser": "^0.1.3",
|
|
26
26
|
"midi-file": "^1.2.4"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
package/script/midy-GM1.d.ts
CHANGED
|
@@ -30,25 +30,26 @@ export class MidyGM1 {
|
|
|
30
30
|
isPaused: boolean;
|
|
31
31
|
isStopping: boolean;
|
|
32
32
|
isSeeking: boolean;
|
|
33
|
+
playPromise: any;
|
|
33
34
|
timeline: any[];
|
|
34
|
-
instruments: any[];
|
|
35
35
|
notePromises: any[];
|
|
36
|
+
instruments: Set<any>;
|
|
36
37
|
exclusiveClassNotes: any[];
|
|
37
38
|
audioContext: any;
|
|
38
39
|
masterVolume: any;
|
|
39
40
|
scheduler: any;
|
|
40
41
|
schedulerBuffer: 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[];
|
|
@@ -58,7 +59,7 @@ export class MidyGM1 {
|
|
|
58
59
|
loadSoundFont(input: any): Promise<void>;
|
|
59
60
|
loadMIDI(input: any): Promise<void>;
|
|
60
61
|
cacheVoiceIds(): void;
|
|
61
|
-
getVoiceId(channel: any, noteNumber: any, velocity: any):
|
|
62
|
+
getVoiceId(channel: any, noteNumber: any, velocity: any): any;
|
|
62
63
|
createChannelAudioNodes(audioContext: any): {
|
|
63
64
|
gainL: any;
|
|
64
65
|
gainR: any;
|
|
@@ -69,7 +70,9 @@ export class MidyGM1 {
|
|
|
69
70
|
createBufferSource(voiceParams: any, audioBuffer: any): any;
|
|
70
71
|
scheduleTimelineEvents(t: any, resumeTime: any, queueIndex: any): Promise<any>;
|
|
71
72
|
getQueueIndex(second: any): number;
|
|
72
|
-
|
|
73
|
+
resetAllStates(): void;
|
|
74
|
+
updateStates(queueIndex: any, nextQueueIndex: any): void;
|
|
75
|
+
playNotes(): Promise<void>;
|
|
73
76
|
ticksToSecond(ticks: any, secondsPerBeat: any): number;
|
|
74
77
|
secondToTicks(second: any, secondsPerBeat: any): number;
|
|
75
78
|
extractMidiData(midi: any): {
|
|
@@ -80,8 +83,8 @@ export class MidyGM1 {
|
|
|
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;
|
|
@@ -122,16 +125,16 @@ export class MidyGM1 {
|
|
|
122
125
|
setDelayModLFO(note: any, scheduleTime: any): void;
|
|
123
126
|
setFreqModLFO(note: any, scheduleTime: any): void;
|
|
124
127
|
createVoiceParamsHandlers(): {
|
|
125
|
-
modLfoToPitch: (channel: any, note: any,
|
|
126
|
-
vibLfoToPitch: (_channel: any, _note: any,
|
|
127
|
-
modLfoToFilterFc: (channel: any, note: any,
|
|
128
|
-
modLfoToVolume: (channel: any, note: any,
|
|
129
|
-
chorusEffectsSend: (_channel: any, _note: any,
|
|
130
|
-
reverbEffectsSend: (_channel: any, _note: any,
|
|
131
|
-
delayModLFO: (_channel: any, note: any,
|
|
132
|
-
freqModLFO: (_channel: any, note: any,
|
|
133
|
-
delayVibLFO: (_channel: any, _note: any,
|
|
134
|
-
freqVibLFO: (_channel: any, _note: any,
|
|
128
|
+
modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
|
|
129
|
+
vibLfoToPitch: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
130
|
+
modLfoToFilterFc: (channel: any, note: any, scheduleTime: any) => void;
|
|
131
|
+
modLfoToVolume: (channel: any, note: any, scheduleTime: any) => void;
|
|
132
|
+
chorusEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
133
|
+
reverbEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
134
|
+
delayModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
135
|
+
freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
136
|
+
delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
137
|
+
freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
135
138
|
};
|
|
136
139
|
getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
|
|
137
140
|
applyVoiceParams(channel: any, controllerType: any, scheduleTime: any): void;
|
|
@@ -162,7 +165,7 @@ export class MidyGM1 {
|
|
|
162
165
|
handleCoarseTuningRPN(channelNumber: any, scheduleTime: any): void;
|
|
163
166
|
setCoarseTuning(channelNumber: any, value: any, scheduleTime: any): void;
|
|
164
167
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
165
|
-
|
|
168
|
+
resetChannelStates(channelNumber: any): void;
|
|
166
169
|
resetAllControllers(channelNumber: any, _value: any, scheduleTime: any): void;
|
|
167
170
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
168
171
|
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
package/script/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AA4FA;
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AA4FA;IAyBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAnDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,4BAAyB;IACzB,0BAAuB;IACvB,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IAgBnC,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAcC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,4DASC;IAED,+EAkDC;IAED,mCAOC;IAED,uBAQC;IAED,yDA2BC;IAED,2BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;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,qCAMC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,6FAyBC;IAED,oGAuCC;IAED,0EAiBC;IAED,kGAmCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAeC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;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,2DAMC;IAED,uDAkBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,iEAMC;IAED,uEAQC;IAED,mEAKC;IAED,yEAQC;IAED,gFAGC;IAED,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AAhiDD;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/script/midy-GM1.js
CHANGED
|
@@ -247,13 +247,13 @@ class MidyGM1 {
|
|
|
247
247
|
writable: true,
|
|
248
248
|
value: false
|
|
249
249
|
});
|
|
250
|
-
Object.defineProperty(this, "
|
|
250
|
+
Object.defineProperty(this, "playPromise", {
|
|
251
251
|
enumerable: true,
|
|
252
252
|
configurable: true,
|
|
253
253
|
writable: true,
|
|
254
|
-
value:
|
|
254
|
+
value: void 0
|
|
255
255
|
});
|
|
256
|
-
Object.defineProperty(this, "
|
|
256
|
+
Object.defineProperty(this, "timeline", {
|
|
257
257
|
enumerable: true,
|
|
258
258
|
configurable: true,
|
|
259
259
|
writable: true,
|
|
@@ -265,6 +265,12 @@ class MidyGM1 {
|
|
|
265
265
|
writable: true,
|
|
266
266
|
value: []
|
|
267
267
|
});
|
|
268
|
+
Object.defineProperty(this, "instruments", {
|
|
269
|
+
enumerable: true,
|
|
270
|
+
configurable: true,
|
|
271
|
+
writable: true,
|
|
272
|
+
value: new Set()
|
|
273
|
+
});
|
|
268
274
|
Object.defineProperty(this, "exclusiveClassNotes", {
|
|
269
275
|
enumerable: true,
|
|
270
276
|
configurable: true,
|
|
@@ -385,7 +391,7 @@ class MidyGM1 {
|
|
|
385
391
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
386
392
|
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
387
393
|
const { instrument, sampleID } = voice.generators;
|
|
388
|
-
return
|
|
394
|
+
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
389
395
|
}
|
|
390
396
|
createChannelAudioNodes(audioContext) {
|
|
391
397
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
@@ -473,69 +479,80 @@ class MidyGM1 {
|
|
|
473
479
|
}
|
|
474
480
|
return 0;
|
|
475
481
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
482
|
+
resetAllStates() {
|
|
483
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
484
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
485
|
+
this.voiceCache.clear();
|
|
486
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
487
|
+
this.channels[i].scheduledNotes = [];
|
|
488
|
+
this.resetChannelStates(i);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
492
|
+
if (nextQueueIndex < queueIndex)
|
|
493
|
+
queueIndex = 0;
|
|
494
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
495
|
+
const event = this.timeline[i];
|
|
496
|
+
switch (event.type) {
|
|
497
|
+
case "controller":
|
|
498
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
499
|
+
break;
|
|
500
|
+
case "programChange":
|
|
501
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
502
|
+
break;
|
|
503
|
+
case "pitchBend":
|
|
504
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
505
|
+
break;
|
|
506
|
+
case "sysEx":
|
|
507
|
+
this.handleSysEx(event.data, 0);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async playNotes() {
|
|
512
|
+
if (this.audioContext.state === "suspended") {
|
|
513
|
+
await this.audioContext.resume();
|
|
514
|
+
}
|
|
515
|
+
this.isPlaying = true;
|
|
516
|
+
this.isPaused = false;
|
|
517
|
+
this.startTime = this.audioContext.currentTime;
|
|
518
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
519
|
+
let resumeTime = this.resumeTime - this.startTime;
|
|
520
|
+
let finished = false;
|
|
521
|
+
this.notePromises = [];
|
|
522
|
+
while (queueIndex < this.timeline.length) {
|
|
523
|
+
const now = this.audioContext.currentTime;
|
|
524
|
+
const t = now + resumeTime;
|
|
525
|
+
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
526
|
+
if (this.isPausing) {
|
|
527
|
+
await this.stopNotes(0, true, now);
|
|
528
|
+
await this.audioContext.suspend();
|
|
529
|
+
this.notePromises = [];
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
else if (this.isStopping) {
|
|
533
|
+
await this.stopNotes(0, true, now);
|
|
534
|
+
await this.audioContext.suspend();
|
|
535
|
+
finished = true;
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
else if (this.isSeeking) {
|
|
539
|
+
await this.stopNotes(0, true, now);
|
|
540
|
+
this.startTime = this.audioContext.currentTime;
|
|
541
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
542
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
543
|
+
queueIndex = nextQueueIndex;
|
|
544
|
+
resumeTime = this.resumeTime - this.startTime;
|
|
545
|
+
this.isSeeking = false;
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const waitTime = now + this.noteCheckInterval;
|
|
549
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
550
|
+
}
|
|
551
|
+
if (finished) {
|
|
483
552
|
this.notePromises = [];
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
this.notePromises = [];
|
|
488
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
489
|
-
this.voiceCache.clear();
|
|
490
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
491
|
-
this.channels[i].scheduledNotes = [];
|
|
492
|
-
this.resetAllStates(i);
|
|
493
|
-
}
|
|
494
|
-
resolve();
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
const now = this.audioContext.currentTime;
|
|
498
|
-
const t = now + resumeTime;
|
|
499
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
500
|
-
if (this.isPausing) {
|
|
501
|
-
await this.stopNotes(0, true, now);
|
|
502
|
-
this.notePromises = [];
|
|
503
|
-
this.isPausing = false;
|
|
504
|
-
this.isPaused = true;
|
|
505
|
-
resolve();
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
else if (this.isStopping) {
|
|
509
|
-
await this.stopNotes(0, true, now);
|
|
510
|
-
this.notePromises = [];
|
|
511
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
512
|
-
this.voiceCache.clear();
|
|
513
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
514
|
-
this.channels[i].scheduledNotes = [];
|
|
515
|
-
this.resetAllStates(i);
|
|
516
|
-
}
|
|
517
|
-
this.isStopping = false;
|
|
518
|
-
this.isPaused = false;
|
|
519
|
-
resolve();
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
else if (this.isSeeking) {
|
|
523
|
-
this.stopNotes(0, true, now);
|
|
524
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
525
|
-
this.startTime = this.audioContext.currentTime;
|
|
526
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
527
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
528
|
-
this.isSeeking = false;
|
|
529
|
-
await schedulePlayback();
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
const waitTime = now + this.noteCheckInterval;
|
|
533
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
534
|
-
await schedulePlayback();
|
|
535
|
-
}
|
|
536
|
-
};
|
|
537
|
-
schedulePlayback();
|
|
538
|
-
});
|
|
553
|
+
this.resetAllStates();
|
|
554
|
+
}
|
|
555
|
+
this.isPlaying = false;
|
|
539
556
|
}
|
|
540
557
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
541
558
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -636,26 +653,32 @@ class MidyGM1 {
|
|
|
636
653
|
this.resumeTime = 0;
|
|
637
654
|
if (this.voiceCounter.size === 0)
|
|
638
655
|
this.cacheVoiceIds();
|
|
639
|
-
|
|
640
|
-
this.
|
|
656
|
+
this.playPromise = this.playNotes();
|
|
657
|
+
await this.playPromise;
|
|
641
658
|
}
|
|
642
|
-
stop() {
|
|
659
|
+
async stop() {
|
|
643
660
|
if (!this.isPlaying)
|
|
644
661
|
return;
|
|
645
662
|
this.isStopping = true;
|
|
663
|
+
await this.playPromise;
|
|
664
|
+
this.isStopping = false;
|
|
646
665
|
}
|
|
647
|
-
pause() {
|
|
666
|
+
async pause() {
|
|
648
667
|
if (!this.isPlaying || this.isPaused)
|
|
649
668
|
return;
|
|
650
669
|
const now = this.audioContext.currentTime;
|
|
651
670
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
652
671
|
this.isPausing = true;
|
|
672
|
+
await this.playPromise;
|
|
673
|
+
this.isPausing = false;
|
|
674
|
+
this.isPaused = true;
|
|
653
675
|
}
|
|
654
676
|
async resume() {
|
|
655
677
|
if (!this.isPaused)
|
|
656
678
|
return;
|
|
657
|
-
|
|
658
|
-
this.
|
|
679
|
+
this.playPromise = this.playNotes();
|
|
680
|
+
await this.playPromise;
|
|
681
|
+
this.isPaused = false;
|
|
659
682
|
}
|
|
660
683
|
seekTo(second) {
|
|
661
684
|
this.resumeTime = second;
|
|
@@ -1018,13 +1041,17 @@ class MidyGM1 {
|
|
|
1018
1041
|
this.applyVoiceParams(channel, 14, scheduleTime);
|
|
1019
1042
|
}
|
|
1020
1043
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
channel.state.modulationDepth;
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1044
|
+
if (note.modulationDepth) {
|
|
1045
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch;
|
|
1046
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1047
|
+
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1048
|
+
note.modulationDepth.gain
|
|
1049
|
+
.cancelScheduledValues(scheduleTime)
|
|
1050
|
+
.setValueAtTime(modulationDepth, scheduleTime);
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
this.startModulation(channel, note, scheduleTime);
|
|
1054
|
+
}
|
|
1028
1055
|
}
|
|
1029
1056
|
setModLfoToFilterFc(note, scheduleTime) {
|
|
1030
1057
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
@@ -1056,28 +1083,36 @@ class MidyGM1 {
|
|
|
1056
1083
|
}
|
|
1057
1084
|
createVoiceParamsHandlers() {
|
|
1058
1085
|
return {
|
|
1059
|
-
modLfoToPitch: (channel, note,
|
|
1086
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1060
1087
|
if (0 < channel.state.modulationDepth) {
|
|
1061
1088
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1062
1089
|
}
|
|
1063
1090
|
},
|
|
1064
|
-
vibLfoToPitch: (_channel, _note,
|
|
1065
|
-
modLfoToFilterFc: (channel, note,
|
|
1091
|
+
vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
|
|
1092
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1066
1093
|
if (0 < channel.state.modulationDepth) {
|
|
1067
1094
|
this.setModLfoToFilterFc(note, scheduleTime);
|
|
1068
1095
|
}
|
|
1069
1096
|
},
|
|
1070
|
-
modLfoToVolume: (channel, note,
|
|
1097
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1071
1098
|
if (0 < channel.state.modulationDepth) {
|
|
1072
1099
|
this.setModLfoToVolume(note, scheduleTime);
|
|
1073
1100
|
}
|
|
1074
1101
|
},
|
|
1075
|
-
chorusEffectsSend: (_channel, _note,
|
|
1076
|
-
reverbEffectsSend: (_channel, _note,
|
|
1077
|
-
delayModLFO: (_channel, note,
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1102
|
+
chorusEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1103
|
+
reverbEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1104
|
+
delayModLFO: (_channel, note, scheduleTime) => {
|
|
1105
|
+
if (0 < channel.state.modulationDepth) {
|
|
1106
|
+
this.setDelayModLFO(note, scheduleTime);
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1110
|
+
if (0 < channel.state.modulationDepth) {
|
|
1111
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1112
|
+
}
|
|
1113
|
+
},
|
|
1114
|
+
delayVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1115
|
+
freqVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1081
1116
|
};
|
|
1082
1117
|
}
|
|
1083
1118
|
getControllerState(channel, noteNumber, velocity) {
|
|
@@ -1100,7 +1135,7 @@ class MidyGM1 {
|
|
|
1100
1135
|
continue;
|
|
1101
1136
|
note.voiceParams[key] = value;
|
|
1102
1137
|
if (key in this.voiceParamsHandlers) {
|
|
1103
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1138
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1104
1139
|
}
|
|
1105
1140
|
else {
|
|
1106
1141
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1153,7 +1188,6 @@ class MidyGM1 {
|
|
|
1153
1188
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1154
1189
|
}
|
|
1155
1190
|
else {
|
|
1156
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1157
1191
|
this.startModulation(channel, note, scheduleTime);
|
|
1158
1192
|
}
|
|
1159
1193
|
});
|
|
@@ -1272,8 +1306,8 @@ class MidyGM1 {
|
|
|
1272
1306
|
}
|
|
1273
1307
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
1274
1308
|
const channel = this.channels[channelNumber];
|
|
1275
|
-
this.limitData(channel, 0, 127, 0,
|
|
1276
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1309
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
1310
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
1277
1311
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1278
1312
|
}
|
|
1279
1313
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -1281,7 +1315,7 @@ class MidyGM1 {
|
|
|
1281
1315
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1282
1316
|
const state = channel.state;
|
|
1283
1317
|
const prev = state.pitchWheelSensitivity;
|
|
1284
|
-
const next = value /
|
|
1318
|
+
const next = value / 12800;
|
|
1285
1319
|
state.pitchWheelSensitivity = next;
|
|
1286
1320
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1287
1321
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1290,14 +1324,15 @@ class MidyGM1 {
|
|
|
1290
1324
|
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
1291
1325
|
const channel = this.channels[channelNumber];
|
|
1292
1326
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1293
|
-
const
|
|
1327
|
+
const value = channel.dataMSB * 128 + channel.dataLSB;
|
|
1328
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
1294
1329
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
1295
1330
|
}
|
|
1296
1331
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1297
1332
|
const channel = this.channels[channelNumber];
|
|
1298
1333
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1299
1334
|
const prev = channel.fineTuning;
|
|
1300
|
-
const next =
|
|
1335
|
+
const next = value;
|
|
1301
1336
|
channel.fineTuning = next;
|
|
1302
1337
|
channel.detune += next - prev;
|
|
1303
1338
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1305,14 +1340,14 @@ class MidyGM1 {
|
|
|
1305
1340
|
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
1306
1341
|
const channel = this.channels[channelNumber];
|
|
1307
1342
|
this.limitDataMSB(channel, 0, 127);
|
|
1308
|
-
const coarseTuning = channel.dataMSB;
|
|
1343
|
+
const coarseTuning = (channel.dataMSB - 64) * 100;
|
|
1309
1344
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
1310
1345
|
}
|
|
1311
1346
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1312
1347
|
const channel = this.channels[channelNumber];
|
|
1313
1348
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1314
1349
|
const prev = channel.coarseTuning;
|
|
1315
|
-
const next =
|
|
1350
|
+
const next = value;
|
|
1316
1351
|
channel.coarseTuning = next;
|
|
1317
1352
|
channel.detune += next - prev;
|
|
1318
1353
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1321,7 +1356,7 @@ class MidyGM1 {
|
|
|
1321
1356
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1322
1357
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
1323
1358
|
}
|
|
1324
|
-
|
|
1359
|
+
resetChannelStates(channelNumber) {
|
|
1325
1360
|
const scheduleTime = this.audioContext.currentTime;
|
|
1326
1361
|
const channel = this.channels[channelNumber];
|
|
1327
1362
|
const state = channel.state;
|
|
@@ -1475,7 +1510,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1475
1510
|
rpnMSB: 127,
|
|
1476
1511
|
rpnLSB: 127,
|
|
1477
1512
|
modulationDepthRange: 50, // cent
|
|
1478
|
-
fineTuning: 0, //
|
|
1479
|
-
coarseTuning: 0, //
|
|
1513
|
+
fineTuning: 0, // cent
|
|
1514
|
+
coarseTuning: 0, // cent
|
|
1480
1515
|
}
|
|
1481
1516
|
});
|