@marmooo/midy 0.4.0 → 0.4.2
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 +14 -1
- package/esm/midy-GM1.d.ts +8 -7
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +138 -81
- package/esm/midy-GM2.d.ts +8 -7
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +161 -96
- package/esm/midy-GMLite.d.ts +8 -7
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +126 -72
- package/esm/midy.d.ts +17 -12
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +275 -123
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +8 -7
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +138 -81
- package/script/midy-GM2.d.ts +8 -7
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +161 -96
- package/script/midy-GMLite.d.ts +8 -7
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +126 -72
- package/script/midy.d.ts +17 -12
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +275 -123
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marmooo/midy",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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.5",
|
|
26
26
|
"midi-file": "^1.2.4"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
package/script/midy-GM1.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export class MidyGM1 {
|
|
1
|
+
export class MidyGM1 extends EventTarget {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
scheduleIndex: number;
|
|
4
4
|
detune: number;
|
|
@@ -31,6 +31,7 @@ export class MidyGM1 {
|
|
|
31
31
|
isPaused: boolean;
|
|
32
32
|
isStopping: boolean;
|
|
33
33
|
isSeeking: boolean;
|
|
34
|
+
loop: boolean;
|
|
34
35
|
playPromise: any;
|
|
35
36
|
timeline: any[];
|
|
36
37
|
notePromises: any[];
|
|
@@ -69,7 +70,7 @@ export class MidyGM1 {
|
|
|
69
70
|
createChannels(audioContext: any): any[];
|
|
70
71
|
createAudioBuffer(voiceParams: any): Promise<any>;
|
|
71
72
|
createBufferSource(voiceParams: any, audioBuffer: any): any;
|
|
72
|
-
scheduleTimelineEvents(scheduleTime: any, queueIndex: any):
|
|
73
|
+
scheduleTimelineEvents(scheduleTime: any, queueIndex: any): any;
|
|
73
74
|
getQueueIndex(second: any): number;
|
|
74
75
|
resetAllStates(): void;
|
|
75
76
|
updateStates(queueIndex: any, nextQueueIndex: any): void;
|
|
@@ -91,8 +92,8 @@ export class MidyGM1 {
|
|
|
91
92
|
seekTo(second: any): void;
|
|
92
93
|
calcTotalTime(): number;
|
|
93
94
|
currentTime(): number;
|
|
94
|
-
processScheduledNotes(channel: any, callback: any): void
|
|
95
|
-
processActiveNotes(channel: any, scheduleTime: any, callback: any): void
|
|
95
|
+
processScheduledNotes(channel: any, callback: any): Promise<void>;
|
|
96
|
+
processActiveNotes(channel: any, scheduleTime: any, callback: any): Promise<void>;
|
|
96
97
|
cbToRatio(cb: any): number;
|
|
97
98
|
rateToCent(rate: any): number;
|
|
98
99
|
centToRate(cent: any): number;
|
|
@@ -112,13 +113,13 @@ export class MidyGM1 {
|
|
|
112
113
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
113
114
|
disconnectNote(note: any): void;
|
|
114
115
|
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
115
|
-
noteOff(channelNumber: any, noteNumber: any,
|
|
116
|
+
noteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): any;
|
|
116
117
|
setNoteIndex(channel: any, index: any): void;
|
|
117
118
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
118
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any):
|
|
119
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
119
120
|
createMessageHandlers(): any[];
|
|
120
121
|
handleMessage(data: any, scheduleTime: any): void;
|
|
121
|
-
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any):
|
|
122
|
+
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): any;
|
|
122
123
|
setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
123
124
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
124
125
|
setPitchBend(channelNumber: any, value: 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":"
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AA+FA;IA2BE;;;;;;;;;;;MAWE;IAEF,+BAgBC;IAvDD,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,kCAA+B;IAC/B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,cAAa;IACb,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IAiBnC,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,kDASC;IAED,4DASC;IAED,gEAoDC;IAED,mCAOC;IAED,uBASC;IAED,yDAgCC;IAED,2BAyEC;IAED,uDAEC;IAED,wDAEC;IAED,qCAKC;IAED;;;MAwDC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,sBAIC;IAED,uBAMC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAIC;IAED,kEAWC;IAED,kFAYC;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,4GAkCC;IAED,uEAwCC;IAED,0EAiBC;IAED,oEASC;IAED,0FAoBC;IAED,gCASC;IAED,iEAqBC;IAED,4FAmBC;IAED,6CAUC;IAED,qDAUC;IAED,qFAeC;IAED,+BAmBC;IAED,kDAOC;IAED,sFA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAYC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MAiCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDAUC;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,qCASC;IAED,4EAaC;IAED,4DAGC;IAED,qDAKC;IAED,gDAYC;IAGD,6DAgBC;CACF"}
|
package/script/midy-GM1.js
CHANGED
|
@@ -29,12 +29,6 @@ class Note {
|
|
|
29
29
|
writable: true,
|
|
30
30
|
value: false
|
|
31
31
|
});
|
|
32
|
-
Object.defineProperty(this, "pending", {
|
|
33
|
-
enumerable: true,
|
|
34
|
-
configurable: true,
|
|
35
|
-
writable: true,
|
|
36
|
-
value: true
|
|
37
|
-
});
|
|
38
32
|
Object.defineProperty(this, "bufferSource", {
|
|
39
33
|
enumerable: true,
|
|
40
34
|
configurable: true,
|
|
@@ -80,6 +74,9 @@ class Note {
|
|
|
80
74
|
this.noteNumber = noteNumber;
|
|
81
75
|
this.velocity = velocity;
|
|
82
76
|
this.startTime = startTime;
|
|
77
|
+
this.ready = new Promise((resolve) => {
|
|
78
|
+
this.resolveReady = resolve;
|
|
79
|
+
});
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
82
|
// normalized to 0-1 for use with the SF2 modulator model
|
|
@@ -89,11 +86,11 @@ const defaultControllerState = {
|
|
|
89
86
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
90
87
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
91
88
|
link: { type: 127, defaultValue: 0 },
|
|
92
|
-
|
|
89
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
93
90
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
92
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
93
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
97
94
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
98
95
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
99
96
|
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
@@ -153,8 +150,9 @@ const pitchEnvelopeKeys = [
|
|
|
153
150
|
"playbackRate",
|
|
154
151
|
];
|
|
155
152
|
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
156
|
-
class MidyGM1 {
|
|
153
|
+
class MidyGM1 extends EventTarget {
|
|
157
154
|
constructor(audioContext) {
|
|
155
|
+
super();
|
|
158
156
|
Object.defineProperty(this, "mode", {
|
|
159
157
|
enumerable: true,
|
|
160
158
|
configurable: true,
|
|
@@ -269,6 +267,12 @@ class MidyGM1 {
|
|
|
269
267
|
writable: true,
|
|
270
268
|
value: false
|
|
271
269
|
});
|
|
270
|
+
Object.defineProperty(this, "loop", {
|
|
271
|
+
enumerable: true,
|
|
272
|
+
configurable: true,
|
|
273
|
+
writable: true,
|
|
274
|
+
value: false
|
|
275
|
+
});
|
|
272
276
|
Object.defineProperty(this, "playPromise", {
|
|
273
277
|
enumerable: true,
|
|
274
278
|
configurable: true,
|
|
@@ -413,7 +417,7 @@ class MidyGM1 {
|
|
|
413
417
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
414
418
|
}
|
|
415
419
|
createChannelAudioNodes(audioContext) {
|
|
416
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
420
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
417
421
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
418
422
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
419
423
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -441,10 +445,9 @@ class MidyGM1 {
|
|
|
441
445
|
return channels;
|
|
442
446
|
}
|
|
443
447
|
async createAudioBuffer(voiceParams) {
|
|
444
|
-
const sample = voiceParams
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
448
|
+
const { sample, start, end } = voiceParams;
|
|
449
|
+
const sampleEnd = sample.data.length + end;
|
|
450
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
448
451
|
return audioBuffer;
|
|
449
452
|
}
|
|
450
453
|
createBufferSource(voiceParams, audioBuffer) {
|
|
@@ -457,7 +460,7 @@ class MidyGM1 {
|
|
|
457
460
|
}
|
|
458
461
|
return bufferSource;
|
|
459
462
|
}
|
|
460
|
-
|
|
463
|
+
scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
461
464
|
const timeOffset = this.resumeTime - this.startTime;
|
|
462
465
|
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
463
466
|
const schedulingOffset = this.startDelay - timeOffset;
|
|
@@ -469,12 +472,10 @@ class MidyGM1 {
|
|
|
469
472
|
const startTime = event.startTime + schedulingOffset;
|
|
470
473
|
switch (event.type) {
|
|
471
474
|
case "noteOn":
|
|
472
|
-
|
|
475
|
+
this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
473
476
|
break;
|
|
474
477
|
case "noteOff": {
|
|
475
|
-
|
|
476
|
-
if (notePromise)
|
|
477
|
-
this.notePromises.push(notePromise);
|
|
478
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
478
479
|
break;
|
|
479
480
|
}
|
|
480
481
|
case "controller":
|
|
@@ -512,22 +513,23 @@ class MidyGM1 {
|
|
|
512
513
|
}
|
|
513
514
|
}
|
|
514
515
|
updateStates(queueIndex, nextQueueIndex) {
|
|
516
|
+
const now = this.audioContext.currentTime;
|
|
515
517
|
if (nextQueueIndex < queueIndex)
|
|
516
518
|
queueIndex = 0;
|
|
517
519
|
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
518
520
|
const event = this.timeline[i];
|
|
519
521
|
switch (event.type) {
|
|
520
522
|
case "controller":
|
|
521
|
-
this.setControlChange(event.channel, event.controllerType, event.value,
|
|
523
|
+
this.setControlChange(event.channel, event.controllerType, event.value, now - this.resumeTime + event.startTime);
|
|
522
524
|
break;
|
|
523
525
|
case "programChange":
|
|
524
|
-
this.setProgramChange(event.channel, event.programNumber,
|
|
526
|
+
this.setProgramChange(event.channel, event.programNumber, now - this.resumeTime + event.startTime);
|
|
525
527
|
break;
|
|
526
528
|
case "pitchBend":
|
|
527
|
-
this.setPitchBend(event.channel, event.value + 8192,
|
|
529
|
+
this.setPitchBend(event.channel, event.value + 8192, now - this.resumeTime + event.startTime);
|
|
528
530
|
break;
|
|
529
531
|
case "sysEx":
|
|
530
|
-
this.handleSysEx(event.data,
|
|
532
|
+
this.handleSysEx(event.data, now - this.resumeTime + event.startTime);
|
|
531
533
|
}
|
|
532
534
|
}
|
|
533
535
|
}
|
|
@@ -535,44 +537,80 @@ class MidyGM1 {
|
|
|
535
537
|
if (this.audioContext.state === "suspended") {
|
|
536
538
|
await this.audioContext.resume();
|
|
537
539
|
}
|
|
540
|
+
const paused = this.isPaused;
|
|
538
541
|
this.isPlaying = true;
|
|
539
542
|
this.isPaused = false;
|
|
540
543
|
this.startTime = this.audioContext.currentTime;
|
|
544
|
+
if (paused) {
|
|
545
|
+
this.dispatchEvent(new Event("resumed"));
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
this.dispatchEvent(new Event("started"));
|
|
549
|
+
}
|
|
541
550
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
542
|
-
let
|
|
551
|
+
let exitReason;
|
|
543
552
|
this.notePromises = [];
|
|
544
|
-
while (
|
|
553
|
+
while (true) {
|
|
545
554
|
const now = this.audioContext.currentTime;
|
|
555
|
+
if (this.timeline.length <= queueIndex) {
|
|
556
|
+
await this.stopNotes(0, true, now);
|
|
557
|
+
if (this.loop) {
|
|
558
|
+
this.notePromises = [];
|
|
559
|
+
this.resetAllStates();
|
|
560
|
+
this.startTime = this.audioContext.currentTime;
|
|
561
|
+
this.resumeTime = 0;
|
|
562
|
+
queueIndex = 0;
|
|
563
|
+
this.dispatchEvent(new Event("looped"));
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
await this.audioContext.suspend();
|
|
568
|
+
exitReason = "ended";
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
546
572
|
if (this.isPausing) {
|
|
547
573
|
await this.stopNotes(0, true, now);
|
|
548
574
|
await this.audioContext.suspend();
|
|
549
575
|
this.notePromises = [];
|
|
576
|
+
this.isPausing = false;
|
|
577
|
+
exitReason = "paused";
|
|
550
578
|
break;
|
|
551
579
|
}
|
|
552
580
|
else if (this.isStopping) {
|
|
553
581
|
await this.stopNotes(0, true, now);
|
|
554
582
|
await this.audioContext.suspend();
|
|
555
|
-
|
|
583
|
+
this.isStopping = false;
|
|
584
|
+
exitReason = "stopped";
|
|
556
585
|
break;
|
|
557
586
|
}
|
|
558
587
|
else if (this.isSeeking) {
|
|
559
|
-
|
|
588
|
+
this.stopNotes(0, true, now);
|
|
560
589
|
this.startTime = this.audioContext.currentTime;
|
|
561
590
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
562
591
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
563
592
|
queueIndex = nextQueueIndex;
|
|
564
593
|
this.isSeeking = false;
|
|
594
|
+
this.dispatchEvent(new Event("seeked"));
|
|
565
595
|
continue;
|
|
566
596
|
}
|
|
567
|
-
queueIndex =
|
|
597
|
+
queueIndex = this.scheduleTimelineEvents(now, queueIndex);
|
|
568
598
|
const waitTime = now + this.noteCheckInterval;
|
|
569
599
|
await this.scheduleTask(() => { }, waitTime);
|
|
570
600
|
}
|
|
571
|
-
if (
|
|
601
|
+
if (exitReason !== "paused") {
|
|
572
602
|
this.notePromises = [];
|
|
573
603
|
this.resetAllStates();
|
|
574
604
|
}
|
|
575
605
|
this.isPlaying = false;
|
|
606
|
+
if (exitReason === "paused") {
|
|
607
|
+
this.isPaused = true;
|
|
608
|
+
this.dispatchEvent(new Event("paused"));
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
this.isPaused = false;
|
|
612
|
+
this.dispatchEvent(new Event(exitReason));
|
|
613
|
+
}
|
|
576
614
|
}
|
|
577
615
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
578
616
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -679,24 +717,20 @@ class MidyGM1 {
|
|
|
679
717
|
return;
|
|
680
718
|
this.isStopping = true;
|
|
681
719
|
await this.playPromise;
|
|
682
|
-
this.isStopping = false;
|
|
683
720
|
}
|
|
684
721
|
async pause() {
|
|
685
722
|
if (!this.isPlaying || this.isPaused)
|
|
686
723
|
return;
|
|
687
724
|
const now = this.audioContext.currentTime;
|
|
688
|
-
this.resumeTime = now
|
|
725
|
+
this.resumeTime = now + this.resumeTime - this.startTime;
|
|
689
726
|
this.isPausing = true;
|
|
690
727
|
await this.playPromise;
|
|
691
|
-
this.isPausing = false;
|
|
692
|
-
this.isPaused = true;
|
|
693
728
|
}
|
|
694
729
|
async resume() {
|
|
695
730
|
if (!this.isPaused)
|
|
696
731
|
return;
|
|
697
732
|
this.playPromise = this.playNotes();
|
|
698
733
|
await this.playPromise;
|
|
699
|
-
this.isPaused = false;
|
|
700
734
|
}
|
|
701
735
|
seekTo(second) {
|
|
702
736
|
this.resumeTime = second;
|
|
@@ -719,19 +753,23 @@ class MidyGM1 {
|
|
|
719
753
|
const now = this.audioContext.currentTime;
|
|
720
754
|
return now + this.resumeTime - this.startTime;
|
|
721
755
|
}
|
|
722
|
-
processScheduledNotes(channel, callback) {
|
|
756
|
+
async processScheduledNotes(channel, callback) {
|
|
723
757
|
const scheduledNotes = channel.scheduledNotes;
|
|
758
|
+
const tasks = [];
|
|
724
759
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
725
760
|
const note = scheduledNotes[i];
|
|
726
761
|
if (!note)
|
|
727
762
|
continue;
|
|
728
763
|
if (note.ending)
|
|
729
764
|
continue;
|
|
730
|
-
callback(note);
|
|
765
|
+
const task = note.ready.then(() => callback(note));
|
|
766
|
+
tasks.push(task);
|
|
731
767
|
}
|
|
768
|
+
await Promise.all(tasks);
|
|
732
769
|
}
|
|
733
|
-
processActiveNotes(channel, scheduleTime, callback) {
|
|
770
|
+
async processActiveNotes(channel, scheduleTime, callback) {
|
|
734
771
|
const scheduledNotes = channel.scheduledNotes;
|
|
772
|
+
const tasks = [];
|
|
735
773
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
736
774
|
const note = scheduledNotes[i];
|
|
737
775
|
if (!note)
|
|
@@ -740,8 +778,10 @@ class MidyGM1 {
|
|
|
740
778
|
continue;
|
|
741
779
|
if (scheduleTime < note.startTime)
|
|
742
780
|
break;
|
|
743
|
-
callback(note);
|
|
781
|
+
const task = note.ready.then(() => callback(note));
|
|
782
|
+
tasks.push(task);
|
|
744
783
|
}
|
|
784
|
+
await Promise.all(tasks);
|
|
745
785
|
}
|
|
746
786
|
cbToRatio(cb) {
|
|
747
787
|
return Math.pow(10, cb / 200);
|
|
@@ -907,7 +947,13 @@ class MidyGM1 {
|
|
|
907
947
|
}
|
|
908
948
|
note.bufferSource.connect(note.filterNode);
|
|
909
949
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
910
|
-
|
|
950
|
+
if (voiceParams.sample.type === "compressed") {
|
|
951
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
952
|
+
note.bufferSource.start(startTime, offset);
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
note.bufferSource.start(startTime);
|
|
956
|
+
}
|
|
911
957
|
return note;
|
|
912
958
|
}
|
|
913
959
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -957,11 +1003,7 @@ class MidyGM1 {
|
|
|
957
1003
|
return;
|
|
958
1004
|
await this.setNoteAudioNode(channel, note, realtime);
|
|
959
1005
|
this.setNoteRouting(channelNumber, note, startTime);
|
|
960
|
-
note.
|
|
961
|
-
const off = note.offEvent;
|
|
962
|
-
if (off) {
|
|
963
|
-
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
964
|
-
}
|
|
1006
|
+
note.resolveReady();
|
|
965
1007
|
}
|
|
966
1008
|
disconnectNote(note) {
|
|
967
1009
|
note.bufferSource.disconnect();
|
|
@@ -995,7 +1037,7 @@ class MidyGM1 {
|
|
|
995
1037
|
}, stopTime);
|
|
996
1038
|
});
|
|
997
1039
|
}
|
|
998
|
-
noteOff(channelNumber, noteNumber,
|
|
1040
|
+
noteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
999
1041
|
const channel = this.channels[channelNumber];
|
|
1000
1042
|
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
1001
1043
|
return;
|
|
@@ -1003,13 +1045,13 @@ class MidyGM1 {
|
|
|
1003
1045
|
if (index < 0)
|
|
1004
1046
|
return;
|
|
1005
1047
|
const note = channel.scheduledNotes[index];
|
|
1006
|
-
if (note.pending) {
|
|
1007
|
-
note.offEvent = { velocity, startTime: endTime };
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
1048
|
note.ending = true;
|
|
1011
1049
|
this.setNoteIndex(channel, index);
|
|
1012
|
-
|
|
1050
|
+
const promise = note.ready.then(() => {
|
|
1051
|
+
return this.releaseNote(channel, note, endTime);
|
|
1052
|
+
});
|
|
1053
|
+
this.notePromises.push(promise);
|
|
1054
|
+
return promise;
|
|
1013
1055
|
}
|
|
1014
1056
|
setNoteIndex(channel, index) {
|
|
1015
1057
|
let allEnds = true;
|
|
@@ -1095,7 +1137,8 @@ class MidyGM1 {
|
|
|
1095
1137
|
}
|
|
1096
1138
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
1097
1139
|
const channel = this.channels[channelNumber];
|
|
1098
|
-
|
|
1140
|
+
if (!(0 <= scheduleTime))
|
|
1141
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1099
1142
|
const state = channel.state;
|
|
1100
1143
|
const prev = state.pitchWheel * 2 - 1;
|
|
1101
1144
|
const next = (value - 8192) / 8192;
|
|
@@ -1107,11 +1150,12 @@ class MidyGM1 {
|
|
|
1107
1150
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1108
1151
|
if (note.modulationDepth) {
|
|
1109
1152
|
const modLfoToPitch = note.voiceParams.modLfoToPitch;
|
|
1110
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1111
|
-
|
|
1153
|
+
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1154
|
+
channel.state.modulationDepthMSB;
|
|
1155
|
+
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1112
1156
|
note.modulationDepth.gain
|
|
1113
1157
|
.cancelScheduledValues(scheduleTime)
|
|
1114
|
-
.setValueAtTime(
|
|
1158
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1115
1159
|
}
|
|
1116
1160
|
else {
|
|
1117
1161
|
this.startModulation(channel, note, scheduleTime);
|
|
@@ -1148,18 +1192,18 @@ class MidyGM1 {
|
|
|
1148
1192
|
createVoiceParamsHandlers() {
|
|
1149
1193
|
return {
|
|
1150
1194
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1151
|
-
if (0 < channel.state.
|
|
1195
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1152
1196
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1153
1197
|
}
|
|
1154
1198
|
},
|
|
1155
1199
|
vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
|
|
1156
1200
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1157
|
-
if (0 < channel.state.
|
|
1201
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1158
1202
|
this.setModLfoToFilterFc(note, scheduleTime);
|
|
1159
1203
|
}
|
|
1160
1204
|
},
|
|
1161
1205
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1162
|
-
if (0 < channel.state.
|
|
1206
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1163
1207
|
this.setModLfoToVolume(note, scheduleTime);
|
|
1164
1208
|
}
|
|
1165
1209
|
},
|
|
@@ -1171,7 +1215,7 @@ class MidyGM1 {
|
|
|
1171
1215
|
}
|
|
1172
1216
|
},
|
|
1173
1217
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1174
|
-
if (0 < channel.state.
|
|
1218
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1175
1219
|
this.setFreqModLFO(note, scheduleTime);
|
|
1176
1220
|
}
|
|
1177
1221
|
},
|
|
@@ -1246,7 +1290,8 @@ class MidyGM1 {
|
|
|
1246
1290
|
}
|
|
1247
1291
|
}
|
|
1248
1292
|
updateModulation(channel, scheduleTime) {
|
|
1249
|
-
const depth = channel.state.
|
|
1293
|
+
const depth = channel.state.modulationDepthMSB *
|
|
1294
|
+
channel.modulationDepthRange;
|
|
1250
1295
|
this.processScheduledNotes(channel, (note) => {
|
|
1251
1296
|
if (note.modulationDepth) {
|
|
1252
1297
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -1258,14 +1303,16 @@ class MidyGM1 {
|
|
|
1258
1303
|
}
|
|
1259
1304
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1260
1305
|
const channel = this.channels[channelNumber];
|
|
1261
|
-
|
|
1262
|
-
|
|
1306
|
+
if (!(0 <= scheduleTime))
|
|
1307
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1308
|
+
channel.state.modulationDepthMSB = modulation / 127;
|
|
1263
1309
|
this.updateModulation(channel, scheduleTime);
|
|
1264
1310
|
}
|
|
1265
1311
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1266
|
-
|
|
1312
|
+
if (!(0 <= scheduleTime))
|
|
1313
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1267
1314
|
const channel = this.channels[channelNumber];
|
|
1268
|
-
channel.state.
|
|
1315
|
+
channel.state.volumeMSB = volume / 127;
|
|
1269
1316
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1270
1317
|
}
|
|
1271
1318
|
panToGain(pan) {
|
|
@@ -1276,15 +1323,17 @@ class MidyGM1 {
|
|
|
1276
1323
|
};
|
|
1277
1324
|
}
|
|
1278
1325
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1279
|
-
|
|
1326
|
+
if (!(0 <= scheduleTime))
|
|
1327
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1280
1328
|
const channel = this.channels[channelNumber];
|
|
1281
|
-
channel.state.
|
|
1329
|
+
channel.state.panMSB = pan / 127;
|
|
1282
1330
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1283
1331
|
}
|
|
1284
1332
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1285
|
-
|
|
1333
|
+
if (!(0 <= scheduleTime))
|
|
1334
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1286
1335
|
const channel = this.channels[channelNumber];
|
|
1287
|
-
channel.state.
|
|
1336
|
+
channel.state.expressionMSB = expression / 127;
|
|
1288
1337
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1289
1338
|
}
|
|
1290
1339
|
dataEntryLSB(channelNumber, value, scheduleTime) {
|
|
@@ -1293,8 +1342,8 @@ class MidyGM1 {
|
|
|
1293
1342
|
}
|
|
1294
1343
|
updateChannelVolume(channel, scheduleTime) {
|
|
1295
1344
|
const state = channel.state;
|
|
1296
|
-
const volume = state.
|
|
1297
|
-
const { gainLeft, gainRight } = this.panToGain(state.
|
|
1345
|
+
const volume = state.volumeMSB * state.expressionMSB;
|
|
1346
|
+
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
1298
1347
|
channel.gainL.gain
|
|
1299
1348
|
.cancelScheduledValues(scheduleTime)
|
|
1300
1349
|
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
@@ -1304,7 +1353,8 @@ class MidyGM1 {
|
|
|
1304
1353
|
}
|
|
1305
1354
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1306
1355
|
const channel = this.channels[channelNumber];
|
|
1307
|
-
|
|
1356
|
+
if (!(0 <= scheduleTime))
|
|
1357
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1308
1358
|
channel.state.sustainPedal = value / 127;
|
|
1309
1359
|
if (64 <= value) {
|
|
1310
1360
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -1376,7 +1426,8 @@ class MidyGM1 {
|
|
|
1376
1426
|
}
|
|
1377
1427
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1378
1428
|
const channel = this.channels[channelNumber];
|
|
1379
|
-
|
|
1429
|
+
if (!(0 <= scheduleTime))
|
|
1430
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1380
1431
|
const state = channel.state;
|
|
1381
1432
|
const prev = state.pitchWheelSensitivity;
|
|
1382
1433
|
const next = value / 12800;
|
|
@@ -1394,7 +1445,8 @@ class MidyGM1 {
|
|
|
1394
1445
|
}
|
|
1395
1446
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1396
1447
|
const channel = this.channels[channelNumber];
|
|
1397
|
-
|
|
1448
|
+
if (!(0 <= scheduleTime))
|
|
1449
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1398
1450
|
const prev = channel.fineTuning;
|
|
1399
1451
|
const next = value;
|
|
1400
1452
|
channel.fineTuning = next;
|
|
@@ -1409,7 +1461,8 @@ class MidyGM1 {
|
|
|
1409
1461
|
}
|
|
1410
1462
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1411
1463
|
const channel = this.channels[channelNumber];
|
|
1412
|
-
|
|
1464
|
+
if (!(0 <= scheduleTime))
|
|
1465
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1413
1466
|
const prev = channel.coarseTuning;
|
|
1414
1467
|
const next = value;
|
|
1415
1468
|
channel.coarseTuning = next;
|
|
@@ -1417,7 +1470,8 @@ class MidyGM1 {
|
|
|
1417
1470
|
this.updateChannelDetune(channel, scheduleTime);
|
|
1418
1471
|
}
|
|
1419
1472
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
1420
|
-
|
|
1473
|
+
if (!(0 <= scheduleTime))
|
|
1474
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1421
1475
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
1422
1476
|
}
|
|
1423
1477
|
resetChannelStates(channelNumber) {
|
|
@@ -1442,8 +1496,8 @@ class MidyGM1 {
|
|
|
1442
1496
|
resetAllControllers(channelNumber, _value, scheduleTime) {
|
|
1443
1497
|
const keys = [
|
|
1444
1498
|
"pitchWheel",
|
|
1445
|
-
"
|
|
1446
|
-
"
|
|
1499
|
+
"expressionMSB",
|
|
1500
|
+
"modulationDepthMSB",
|
|
1447
1501
|
"sustainPedal",
|
|
1448
1502
|
];
|
|
1449
1503
|
const channel = this.channels[channelNumber];
|
|
@@ -1469,7 +1523,8 @@ class MidyGM1 {
|
|
|
1469
1523
|
}
|
|
1470
1524
|
}
|
|
1471
1525
|
allNotesOff(channelNumber, _value, scheduleTime) {
|
|
1472
|
-
|
|
1526
|
+
if (!(0 <= scheduleTime))
|
|
1527
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1473
1528
|
return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
|
|
1474
1529
|
}
|
|
1475
1530
|
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
@@ -1490,7 +1545,8 @@ class MidyGM1 {
|
|
|
1490
1545
|
}
|
|
1491
1546
|
}
|
|
1492
1547
|
GM1SystemOn(scheduleTime) {
|
|
1493
|
-
|
|
1548
|
+
if (!(0 <= scheduleTime))
|
|
1549
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1494
1550
|
this.mode = "GM1";
|
|
1495
1551
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1496
1552
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -1518,7 +1574,8 @@ class MidyGM1 {
|
|
|
1518
1574
|
this.setMasterVolume(volume, scheduleTime);
|
|
1519
1575
|
}
|
|
1520
1576
|
setMasterVolume(value, scheduleTime) {
|
|
1521
|
-
|
|
1577
|
+
if (!(0 <= scheduleTime))
|
|
1578
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1522
1579
|
this.masterVolume.gain
|
|
1523
1580
|
.cancelScheduledValues(scheduleTime)
|
|
1524
1581
|
.setValueAtTime(value * value, scheduleTime);
|
package/script/midy-GM2.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export class MidyGM2 {
|
|
1
|
+
export class MidyGM2 extends EventTarget {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
scheduleIndex: number;
|
|
4
4
|
detune: number;
|
|
@@ -50,6 +50,7 @@ export class MidyGM2 {
|
|
|
50
50
|
isPaused: boolean;
|
|
51
51
|
isStopping: boolean;
|
|
52
52
|
isSeeking: boolean;
|
|
53
|
+
loop: boolean;
|
|
53
54
|
playPromise: any;
|
|
54
55
|
timeline: any[];
|
|
55
56
|
notePromises: any[];
|
|
@@ -105,7 +106,7 @@ export class MidyGM2 {
|
|
|
105
106
|
createAudioBuffer(voiceParams: any): Promise<any>;
|
|
106
107
|
isLoopDrum(channel: any, noteNumber: any): boolean;
|
|
107
108
|
createBufferSource(channel: any, noteNumber: any, voiceParams: any, audioBuffer: any): any;
|
|
108
|
-
scheduleTimelineEvents(scheduleTime: any, queueIndex: any):
|
|
109
|
+
scheduleTimelineEvents(scheduleTime: any, queueIndex: any): any;
|
|
109
110
|
getQueueIndex(second: any): number;
|
|
110
111
|
resetAllStates(): void;
|
|
111
112
|
updateStates(queueIndex: any, nextQueueIndex: any): void;
|
|
@@ -127,8 +128,8 @@ export class MidyGM2 {
|
|
|
127
128
|
seekTo(second: any): void;
|
|
128
129
|
calcTotalTime(): number;
|
|
129
130
|
currentTime(): number;
|
|
130
|
-
processScheduledNotes(channel: any, callback: any): void
|
|
131
|
-
processActiveNotes(channel: any, scheduleTime: any, callback: any): void
|
|
131
|
+
processScheduledNotes(channel: any, callback: any): Promise<void>;
|
|
132
|
+
processActiveNotes(channel: any, scheduleTime: any, callback: any): Promise<void>;
|
|
132
133
|
createConvolutionReverbImpulse(audioContext: any, decay: any, preDecay: any): any;
|
|
133
134
|
createConvolutionReverb(audioContext: any, impulse: any): {
|
|
134
135
|
input: any;
|
|
@@ -182,11 +183,11 @@ export class MidyGM2 {
|
|
|
182
183
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
183
184
|
disconnectNote(note: any): void;
|
|
184
185
|
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
185
|
-
noteOff(channelNumber: any, noteNumber: any,
|
|
186
|
+
noteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): any;
|
|
186
187
|
setNoteIndex(channel: any, index: any): void;
|
|
187
188
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
188
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any):
|
|
189
|
-
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any):
|
|
189
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
190
|
+
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
|
|
190
191
|
createMessageHandlers(): any[];
|
|
191
192
|
handleMessage(data: any, scheduleTime: any): void;
|
|
192
193
|
activeSensing(): void;
|