@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/README.md
CHANGED
|
@@ -18,6 +18,11 @@ This library provides several files depending on the implementation level.
|
|
|
18
18
|
[ja v1.0](https://amei.or.jp/midistandardcommittee/Recommended_Practice/GM2_japanese.pdf))
|
|
19
19
|
- midy.js: full implementation (in progress)
|
|
20
20
|
|
|
21
|
+
## Demo
|
|
22
|
+
|
|
23
|
+
- [@marmooo/midi-player](https://marmooo.github.io/midi-player/) - GUI library
|
|
24
|
+
- [Humidy](https://marmooo.github.io/humidy/) - GM2 MIDI mixer app
|
|
25
|
+
|
|
21
26
|
## Usage
|
|
22
27
|
|
|
23
28
|
### Initialization
|
|
@@ -39,8 +44,8 @@ await midy.start();
|
|
|
39
44
|
|
|
40
45
|
```js
|
|
41
46
|
await midy.start();
|
|
42
|
-
midy.stop();
|
|
43
|
-
midy.pause();
|
|
47
|
+
await midy.stop();
|
|
48
|
+
await midy.pause();
|
|
44
49
|
await midy.resume();
|
|
45
50
|
midy.seekTo(second);
|
|
46
51
|
```
|
|
@@ -118,13 +123,6 @@ await midy.loadSoundFont(paths);
|
|
|
118
123
|
deno task build
|
|
119
124
|
```
|
|
120
125
|
|
|
121
|
-
## Test
|
|
122
|
-
|
|
123
|
-
WebAudio only works on web browsers currently, so we are testing this library
|
|
124
|
-
using the following GUI libraries.
|
|
125
|
-
|
|
126
|
-
- [@marmooo/midi-player](https://github.com/marmooo/midi-player)
|
|
127
|
-
|
|
128
126
|
## License
|
|
129
127
|
|
|
130
128
|
Apache-2.0
|
package/esm/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/esm/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"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/esm/midy-GM1.js
CHANGED
|
@@ -244,13 +244,13 @@ export class MidyGM1 {
|
|
|
244
244
|
writable: true,
|
|
245
245
|
value: false
|
|
246
246
|
});
|
|
247
|
-
Object.defineProperty(this, "
|
|
247
|
+
Object.defineProperty(this, "playPromise", {
|
|
248
248
|
enumerable: true,
|
|
249
249
|
configurable: true,
|
|
250
250
|
writable: true,
|
|
251
|
-
value:
|
|
251
|
+
value: void 0
|
|
252
252
|
});
|
|
253
|
-
Object.defineProperty(this, "
|
|
253
|
+
Object.defineProperty(this, "timeline", {
|
|
254
254
|
enumerable: true,
|
|
255
255
|
configurable: true,
|
|
256
256
|
writable: true,
|
|
@@ -262,6 +262,12 @@ export class MidyGM1 {
|
|
|
262
262
|
writable: true,
|
|
263
263
|
value: []
|
|
264
264
|
});
|
|
265
|
+
Object.defineProperty(this, "instruments", {
|
|
266
|
+
enumerable: true,
|
|
267
|
+
configurable: true,
|
|
268
|
+
writable: true,
|
|
269
|
+
value: new Set()
|
|
270
|
+
});
|
|
265
271
|
Object.defineProperty(this, "exclusiveClassNotes", {
|
|
266
272
|
enumerable: true,
|
|
267
273
|
configurable: true,
|
|
@@ -382,7 +388,7 @@ export class MidyGM1 {
|
|
|
382
388
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
383
389
|
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
384
390
|
const { instrument, sampleID } = voice.generators;
|
|
385
|
-
return
|
|
391
|
+
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
386
392
|
}
|
|
387
393
|
createChannelAudioNodes(audioContext) {
|
|
388
394
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
@@ -470,69 +476,80 @@ export class MidyGM1 {
|
|
|
470
476
|
}
|
|
471
477
|
return 0;
|
|
472
478
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
479
|
+
resetAllStates() {
|
|
480
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
481
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
482
|
+
this.voiceCache.clear();
|
|
483
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
484
|
+
this.channels[i].scheduledNotes = [];
|
|
485
|
+
this.resetChannelStates(i);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
489
|
+
if (nextQueueIndex < queueIndex)
|
|
490
|
+
queueIndex = 0;
|
|
491
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
492
|
+
const event = this.timeline[i];
|
|
493
|
+
switch (event.type) {
|
|
494
|
+
case "controller":
|
|
495
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
496
|
+
break;
|
|
497
|
+
case "programChange":
|
|
498
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
499
|
+
break;
|
|
500
|
+
case "pitchBend":
|
|
501
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
502
|
+
break;
|
|
503
|
+
case "sysEx":
|
|
504
|
+
this.handleSysEx(event.data, 0);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async playNotes() {
|
|
509
|
+
if (this.audioContext.state === "suspended") {
|
|
510
|
+
await this.audioContext.resume();
|
|
511
|
+
}
|
|
512
|
+
this.isPlaying = true;
|
|
513
|
+
this.isPaused = false;
|
|
514
|
+
this.startTime = this.audioContext.currentTime;
|
|
515
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
516
|
+
let resumeTime = this.resumeTime - this.startTime;
|
|
517
|
+
let finished = false;
|
|
518
|
+
this.notePromises = [];
|
|
519
|
+
while (queueIndex < this.timeline.length) {
|
|
520
|
+
const now = this.audioContext.currentTime;
|
|
521
|
+
const t = now + resumeTime;
|
|
522
|
+
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
523
|
+
if (this.isPausing) {
|
|
524
|
+
await this.stopNotes(0, true, now);
|
|
525
|
+
await this.audioContext.suspend();
|
|
526
|
+
this.notePromises = [];
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
else if (this.isStopping) {
|
|
530
|
+
await this.stopNotes(0, true, now);
|
|
531
|
+
await this.audioContext.suspend();
|
|
532
|
+
finished = true;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
else if (this.isSeeking) {
|
|
536
|
+
await this.stopNotes(0, true, now);
|
|
537
|
+
this.startTime = this.audioContext.currentTime;
|
|
538
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
539
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
540
|
+
queueIndex = nextQueueIndex;
|
|
541
|
+
resumeTime = this.resumeTime - this.startTime;
|
|
542
|
+
this.isSeeking = false;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const waitTime = now + this.noteCheckInterval;
|
|
546
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
547
|
+
}
|
|
548
|
+
if (finished) {
|
|
480
549
|
this.notePromises = [];
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
this.notePromises = [];
|
|
485
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
486
|
-
this.voiceCache.clear();
|
|
487
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
488
|
-
this.channels[i].scheduledNotes = [];
|
|
489
|
-
this.resetAllStates(i);
|
|
490
|
-
}
|
|
491
|
-
resolve();
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const now = this.audioContext.currentTime;
|
|
495
|
-
const t = now + resumeTime;
|
|
496
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
497
|
-
if (this.isPausing) {
|
|
498
|
-
await this.stopNotes(0, true, now);
|
|
499
|
-
this.notePromises = [];
|
|
500
|
-
this.isPausing = false;
|
|
501
|
-
this.isPaused = true;
|
|
502
|
-
resolve();
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
else if (this.isStopping) {
|
|
506
|
-
await this.stopNotes(0, true, now);
|
|
507
|
-
this.notePromises = [];
|
|
508
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
509
|
-
this.voiceCache.clear();
|
|
510
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
511
|
-
this.channels[i].scheduledNotes = [];
|
|
512
|
-
this.resetAllStates(i);
|
|
513
|
-
}
|
|
514
|
-
this.isStopping = false;
|
|
515
|
-
this.isPaused = false;
|
|
516
|
-
resolve();
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
else if (this.isSeeking) {
|
|
520
|
-
this.stopNotes(0, true, now);
|
|
521
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
522
|
-
this.startTime = this.audioContext.currentTime;
|
|
523
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
524
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
525
|
-
this.isSeeking = false;
|
|
526
|
-
await schedulePlayback();
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
const waitTime = now + this.noteCheckInterval;
|
|
530
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
531
|
-
await schedulePlayback();
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
schedulePlayback();
|
|
535
|
-
});
|
|
550
|
+
this.resetAllStates();
|
|
551
|
+
}
|
|
552
|
+
this.isPlaying = false;
|
|
536
553
|
}
|
|
537
554
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
538
555
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -633,26 +650,32 @@ export class MidyGM1 {
|
|
|
633
650
|
this.resumeTime = 0;
|
|
634
651
|
if (this.voiceCounter.size === 0)
|
|
635
652
|
this.cacheVoiceIds();
|
|
636
|
-
|
|
637
|
-
this.
|
|
653
|
+
this.playPromise = this.playNotes();
|
|
654
|
+
await this.playPromise;
|
|
638
655
|
}
|
|
639
|
-
stop() {
|
|
656
|
+
async stop() {
|
|
640
657
|
if (!this.isPlaying)
|
|
641
658
|
return;
|
|
642
659
|
this.isStopping = true;
|
|
660
|
+
await this.playPromise;
|
|
661
|
+
this.isStopping = false;
|
|
643
662
|
}
|
|
644
|
-
pause() {
|
|
663
|
+
async pause() {
|
|
645
664
|
if (!this.isPlaying || this.isPaused)
|
|
646
665
|
return;
|
|
647
666
|
const now = this.audioContext.currentTime;
|
|
648
667
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
649
668
|
this.isPausing = true;
|
|
669
|
+
await this.playPromise;
|
|
670
|
+
this.isPausing = false;
|
|
671
|
+
this.isPaused = true;
|
|
650
672
|
}
|
|
651
673
|
async resume() {
|
|
652
674
|
if (!this.isPaused)
|
|
653
675
|
return;
|
|
654
|
-
|
|
655
|
-
this.
|
|
676
|
+
this.playPromise = this.playNotes();
|
|
677
|
+
await this.playPromise;
|
|
678
|
+
this.isPaused = false;
|
|
656
679
|
}
|
|
657
680
|
seekTo(second) {
|
|
658
681
|
this.resumeTime = second;
|
|
@@ -1015,13 +1038,17 @@ export class MidyGM1 {
|
|
|
1015
1038
|
this.applyVoiceParams(channel, 14, scheduleTime);
|
|
1016
1039
|
}
|
|
1017
1040
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
channel.state.modulationDepth;
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1041
|
+
if (note.modulationDepth) {
|
|
1042
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch;
|
|
1043
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1044
|
+
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1045
|
+
note.modulationDepth.gain
|
|
1046
|
+
.cancelScheduledValues(scheduleTime)
|
|
1047
|
+
.setValueAtTime(modulationDepth, scheduleTime);
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
this.startModulation(channel, note, scheduleTime);
|
|
1051
|
+
}
|
|
1025
1052
|
}
|
|
1026
1053
|
setModLfoToFilterFc(note, scheduleTime) {
|
|
1027
1054
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
@@ -1053,28 +1080,36 @@ export class MidyGM1 {
|
|
|
1053
1080
|
}
|
|
1054
1081
|
createVoiceParamsHandlers() {
|
|
1055
1082
|
return {
|
|
1056
|
-
modLfoToPitch: (channel, note,
|
|
1083
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1057
1084
|
if (0 < channel.state.modulationDepth) {
|
|
1058
1085
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1059
1086
|
}
|
|
1060
1087
|
},
|
|
1061
|
-
vibLfoToPitch: (_channel, _note,
|
|
1062
|
-
modLfoToFilterFc: (channel, note,
|
|
1088
|
+
vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
|
|
1089
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1063
1090
|
if (0 < channel.state.modulationDepth) {
|
|
1064
1091
|
this.setModLfoToFilterFc(note, scheduleTime);
|
|
1065
1092
|
}
|
|
1066
1093
|
},
|
|
1067
|
-
modLfoToVolume: (channel, note,
|
|
1094
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1068
1095
|
if (0 < channel.state.modulationDepth) {
|
|
1069
1096
|
this.setModLfoToVolume(note, scheduleTime);
|
|
1070
1097
|
}
|
|
1071
1098
|
},
|
|
1072
|
-
chorusEffectsSend: (_channel, _note,
|
|
1073
|
-
reverbEffectsSend: (_channel, _note,
|
|
1074
|
-
delayModLFO: (_channel, note,
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1099
|
+
chorusEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1100
|
+
reverbEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1101
|
+
delayModLFO: (_channel, note, scheduleTime) => {
|
|
1102
|
+
if (0 < channel.state.modulationDepth) {
|
|
1103
|
+
this.setDelayModLFO(note, scheduleTime);
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1107
|
+
if (0 < channel.state.modulationDepth) {
|
|
1108
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
delayVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1112
|
+
freqVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1078
1113
|
};
|
|
1079
1114
|
}
|
|
1080
1115
|
getControllerState(channel, noteNumber, velocity) {
|
|
@@ -1097,7 +1132,7 @@ export class MidyGM1 {
|
|
|
1097
1132
|
continue;
|
|
1098
1133
|
note.voiceParams[key] = value;
|
|
1099
1134
|
if (key in this.voiceParamsHandlers) {
|
|
1100
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1135
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1101
1136
|
}
|
|
1102
1137
|
else {
|
|
1103
1138
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1150,7 +1185,6 @@ export class MidyGM1 {
|
|
|
1150
1185
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1151
1186
|
}
|
|
1152
1187
|
else {
|
|
1153
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1154
1188
|
this.startModulation(channel, note, scheduleTime);
|
|
1155
1189
|
}
|
|
1156
1190
|
});
|
|
@@ -1269,8 +1303,8 @@ export class MidyGM1 {
|
|
|
1269
1303
|
}
|
|
1270
1304
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
1271
1305
|
const channel = this.channels[channelNumber];
|
|
1272
|
-
this.limitData(channel, 0, 127, 0,
|
|
1273
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1306
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
1307
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
1274
1308
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1275
1309
|
}
|
|
1276
1310
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -1278,7 +1312,7 @@ export class MidyGM1 {
|
|
|
1278
1312
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1279
1313
|
const state = channel.state;
|
|
1280
1314
|
const prev = state.pitchWheelSensitivity;
|
|
1281
|
-
const next = value /
|
|
1315
|
+
const next = value / 12800;
|
|
1282
1316
|
state.pitchWheelSensitivity = next;
|
|
1283
1317
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1284
1318
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1287,14 +1321,15 @@ export class MidyGM1 {
|
|
|
1287
1321
|
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
1288
1322
|
const channel = this.channels[channelNumber];
|
|
1289
1323
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1290
|
-
const
|
|
1324
|
+
const value = channel.dataMSB * 128 + channel.dataLSB;
|
|
1325
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
1291
1326
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
1292
1327
|
}
|
|
1293
1328
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1294
1329
|
const channel = this.channels[channelNumber];
|
|
1295
1330
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1296
1331
|
const prev = channel.fineTuning;
|
|
1297
|
-
const next =
|
|
1332
|
+
const next = value;
|
|
1298
1333
|
channel.fineTuning = next;
|
|
1299
1334
|
channel.detune += next - prev;
|
|
1300
1335
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1302,14 +1337,14 @@ export class MidyGM1 {
|
|
|
1302
1337
|
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
1303
1338
|
const channel = this.channels[channelNumber];
|
|
1304
1339
|
this.limitDataMSB(channel, 0, 127);
|
|
1305
|
-
const coarseTuning = channel.dataMSB;
|
|
1340
|
+
const coarseTuning = (channel.dataMSB - 64) * 100;
|
|
1306
1341
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
1307
1342
|
}
|
|
1308
1343
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1309
1344
|
const channel = this.channels[channelNumber];
|
|
1310
1345
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1311
1346
|
const prev = channel.coarseTuning;
|
|
1312
|
-
const next =
|
|
1347
|
+
const next = value;
|
|
1313
1348
|
channel.coarseTuning = next;
|
|
1314
1349
|
channel.detune += next - prev;
|
|
1315
1350
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1318,7 +1353,7 @@ export class MidyGM1 {
|
|
|
1318
1353
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1319
1354
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
1320
1355
|
}
|
|
1321
|
-
|
|
1356
|
+
resetChannelStates(channelNumber) {
|
|
1322
1357
|
const scheduleTime = this.audioContext.currentTime;
|
|
1323
1358
|
const channel = this.channels[channelNumber];
|
|
1324
1359
|
const state = channel.state;
|
|
@@ -1471,7 +1506,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1471
1506
|
rpnMSB: 127,
|
|
1472
1507
|
rpnLSB: 127,
|
|
1473
1508
|
modulationDepthRange: 50, // cent
|
|
1474
|
-
fineTuning: 0, //
|
|
1475
|
-
coarseTuning: 0, //
|
|
1509
|
+
fineTuning: 0, // cent
|
|
1510
|
+
coarseTuning: 0, // cent
|
|
1476
1511
|
}
|
|
1477
1512
|
});
|