@marmooo/midy 0.3.8 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/esm/midy-GM1.d.ts +8 -26
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +140 -80
- package/esm/midy-GM2.d.ts +8 -31
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +155 -95
- package/esm/midy-GMLite.d.ts +8 -26
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +134 -75
- package/esm/midy.d.ts +15 -37
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +228 -111
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +8 -26
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +140 -80
- package/script/midy-GM2.d.ts +8 -31
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +155 -95
- package/script/midy-GMLite.d.ts +8 -26
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +134 -75
- package/script/midy.d.ts +15 -37
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +228 -111
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ This library provides several files depending on the implementation level.
|
|
|
22
22
|
|
|
23
23
|
- [@marmooo/midi-player](https://marmooo.github.io/midi-player/) - GUI library
|
|
24
24
|
- [Humidy](https://marmooo.github.io/humidy/) - GM2 MIDI mixer app
|
|
25
|
+
- [Timidy](https://marmooo.github.io/timidy/) - Timidity++ style MIDI player
|
|
25
26
|
|
|
26
27
|
## Support Status
|
|
27
28
|
|
|
@@ -54,7 +55,7 @@ All implementations follow the specification.
|
|
|
54
55
|
import { Midy } from "midy.js";
|
|
55
56
|
|
|
56
57
|
const audioContext = new AudioContext();
|
|
57
|
-
await audioContext.suspend();
|
|
58
|
+
if (audioContext.state === "running") await audioContext.suspend();
|
|
58
59
|
const midy = new Midy(audioContext);
|
|
59
60
|
await midy.loadMIDI("test.mid");
|
|
60
61
|
await midy.loadSoundFont("test.sf3");
|
package/esm/midy-GM1.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export class MidyGM1 {
|
|
|
25
25
|
soundFontTable: never[][];
|
|
26
26
|
voiceCounter: Map<any, any>;
|
|
27
27
|
voiceCache: Map<any, any>;
|
|
28
|
+
realtimeVoiceCache: Map<any, any>;
|
|
28
29
|
isPlaying: boolean;
|
|
29
30
|
isPausing: boolean;
|
|
30
31
|
isPaused: boolean;
|
|
@@ -104,21 +105,20 @@ export class MidyGM1 {
|
|
|
104
105
|
clampCutoffFrequency(frequency: any): number;
|
|
105
106
|
setFilterEnvelope(note: any, scheduleTime: any): void;
|
|
106
107
|
startModulation(channel: any, note: any, scheduleTime: any): void;
|
|
107
|
-
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any): Promise<any>;
|
|
108
|
-
|
|
108
|
+
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any, realtime: any): Promise<any>;
|
|
109
|
+
setNoteAudioNode(channel: any, note: any, realtime: any): Promise<any>;
|
|
109
110
|
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
110
|
-
|
|
111
|
-
noteOn(channelNumber: any, noteNumber: any, velocity: any,
|
|
111
|
+
setNoteRouting(channelNumber: any, note: any, startTime: any): void;
|
|
112
|
+
noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
112
113
|
disconnectNote(note: any): void;
|
|
113
114
|
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
114
|
-
|
|
115
|
+
noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
115
116
|
setNoteIndex(channel: any, index: any): void;
|
|
116
117
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
117
|
-
|
|
118
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
118
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
119
119
|
createMessageHandlers(): any[];
|
|
120
120
|
handleMessage(data: any, scheduleTime: any): void;
|
|
121
|
-
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<
|
|
121
|
+
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
122
122
|
setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
123
123
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
124
124
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
@@ -179,22 +179,4 @@ export class MidyGM1 {
|
|
|
179
179
|
handleSysEx(data: any, scheduleTime: any): void;
|
|
180
180
|
scheduleTask(callback: any, scheduleTime: any): Promise<any>;
|
|
181
181
|
}
|
|
182
|
-
declare class Note {
|
|
183
|
-
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
184
|
-
index: number;
|
|
185
|
-
ending: boolean;
|
|
186
|
-
bufferSource: any;
|
|
187
|
-
filterNode: any;
|
|
188
|
-
filterDepth: any;
|
|
189
|
-
volumeEnvelopeNode: any;
|
|
190
|
-
volumeDepth: any;
|
|
191
|
-
modulationLFO: any;
|
|
192
|
-
modulationDepth: any;
|
|
193
|
-
noteNumber: any;
|
|
194
|
-
velocity: any;
|
|
195
|
-
startTime: any;
|
|
196
|
-
voice: any;
|
|
197
|
-
voiceParams: any;
|
|
198
|
-
}
|
|
199
|
-
export {};
|
|
200
182
|
//# sourceMappingURL=midy-GM1.d.ts.map
|
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":"
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AA6FA;IA0BE;;;;;;;;;;;MAWE;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,kCAA+B;IAC/B,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,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,yEAoDC;IAED,mCAOC;IAED,uBASC;IAED,yDA2BC;IAED,2BA8CC;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,sBAIC;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,4GAkCC;IAED,uEAwCC;IAED,0EAiBC;IAED,oEASC;IAED,0FAwBC;IAED,gCASC;IAED,iEAqBC;IAED,gHAqBC;IAED,6CAUC;IAED,qDAUC;IAED,4GAeC;IAED,+BAmBC;IAED,kDAOC;IAED,sGA2BC;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/esm/midy-GM1.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { parseMidi } from "midi-file";
|
|
2
2
|
import { parse, SoundFont } from "@marmooo/soundfont-parser";
|
|
3
3
|
class Note {
|
|
4
|
-
constructor(noteNumber, velocity, startTime
|
|
4
|
+
constructor(noteNumber, velocity, startTime) {
|
|
5
|
+
Object.defineProperty(this, "voice", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "voiceParams", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
5
17
|
Object.defineProperty(this, "index", {
|
|
6
18
|
enumerable: true,
|
|
7
19
|
configurable: true,
|
|
@@ -14,6 +26,12 @@ class Note {
|
|
|
14
26
|
writable: true,
|
|
15
27
|
value: false
|
|
16
28
|
});
|
|
29
|
+
Object.defineProperty(this, "pending", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: true
|
|
34
|
+
});
|
|
17
35
|
Object.defineProperty(this, "bufferSource", {
|
|
18
36
|
enumerable: true,
|
|
19
37
|
configurable: true,
|
|
@@ -59,8 +77,6 @@ class Note {
|
|
|
59
77
|
this.noteNumber = noteNumber;
|
|
60
78
|
this.velocity = velocity;
|
|
61
79
|
this.startTime = startTime;
|
|
62
|
-
this.voice = voice;
|
|
63
|
-
this.voiceParams = voiceParams;
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
82
|
// normalized to 0-1 for use with the SF2 modulator model
|
|
@@ -70,11 +86,11 @@ const defaultControllerState = {
|
|
|
70
86
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
71
87
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
72
88
|
link: { type: 127, defaultValue: 0 },
|
|
73
|
-
|
|
89
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
74
90
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
91
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
92
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
93
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
78
94
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
79
95
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
80
96
|
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
@@ -214,6 +230,12 @@ export class MidyGM1 {
|
|
|
214
230
|
writable: true,
|
|
215
231
|
value: new Map()
|
|
216
232
|
});
|
|
233
|
+
Object.defineProperty(this, "realtimeVoiceCache", {
|
|
234
|
+
enumerable: true,
|
|
235
|
+
configurable: true,
|
|
236
|
+
writable: true,
|
|
237
|
+
value: new Map()
|
|
238
|
+
});
|
|
217
239
|
Object.defineProperty(this, "isPlaying", {
|
|
218
240
|
enumerable: true,
|
|
219
241
|
configurable: true,
|
|
@@ -388,7 +410,7 @@ export class MidyGM1 {
|
|
|
388
410
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
389
411
|
}
|
|
390
412
|
createChannelAudioNodes(audioContext) {
|
|
391
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
413
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
392
414
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
393
415
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
394
416
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -416,10 +438,9 @@ export class MidyGM1 {
|
|
|
416
438
|
return channels;
|
|
417
439
|
}
|
|
418
440
|
async createAudioBuffer(voiceParams) {
|
|
419
|
-
const sample = voiceParams
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
441
|
+
const { sample, start, end } = voiceParams;
|
|
442
|
+
const sampleEnd = sample.data.length + end;
|
|
443
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
423
444
|
return audioBuffer;
|
|
424
445
|
}
|
|
425
446
|
createBufferSource(voiceParams, audioBuffer) {
|
|
@@ -444,12 +465,10 @@ export class MidyGM1 {
|
|
|
444
465
|
const startTime = event.startTime + schedulingOffset;
|
|
445
466
|
switch (event.type) {
|
|
446
467
|
case "noteOn":
|
|
447
|
-
await this.
|
|
468
|
+
await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
448
469
|
break;
|
|
449
470
|
case "noteOff": {
|
|
450
|
-
|
|
451
|
-
if (notePromise)
|
|
452
|
-
this.notePromises.push(notePromise);
|
|
471
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
453
472
|
break;
|
|
454
473
|
}
|
|
455
474
|
case "controller":
|
|
@@ -480,6 +499,7 @@ export class MidyGM1 {
|
|
|
480
499
|
this.exclusiveClassNotes.fill(undefined);
|
|
481
500
|
this.drumExclusiveClassNotes.fill(undefined);
|
|
482
501
|
this.voiceCache.clear();
|
|
502
|
+
this.realtimeVoiceCache.clear();
|
|
483
503
|
for (let i = 0; i < this.channels.length; i++) {
|
|
484
504
|
this.channels[i].scheduledNotes = [];
|
|
485
505
|
this.resetChannelStates(i);
|
|
@@ -517,7 +537,6 @@ export class MidyGM1 {
|
|
|
517
537
|
this.notePromises = [];
|
|
518
538
|
while (queueIndex < this.timeline.length) {
|
|
519
539
|
const now = this.audioContext.currentTime;
|
|
520
|
-
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
521
540
|
if (this.isPausing) {
|
|
522
541
|
await this.stopNotes(0, true, now);
|
|
523
542
|
await this.audioContext.suspend();
|
|
@@ -539,9 +558,16 @@ export class MidyGM1 {
|
|
|
539
558
|
this.isSeeking = false;
|
|
540
559
|
continue;
|
|
541
560
|
}
|
|
561
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
542
562
|
const waitTime = now + this.noteCheckInterval;
|
|
543
563
|
await this.scheduleTask(() => { }, waitTime);
|
|
544
564
|
}
|
|
565
|
+
if (this.timeline.length <= queueIndex) {
|
|
566
|
+
const now = this.audioContext.currentTime;
|
|
567
|
+
await this.stopNotes(0, true, now);
|
|
568
|
+
await this.audioContext.suspend();
|
|
569
|
+
finished = true;
|
|
570
|
+
}
|
|
545
571
|
if (finished) {
|
|
546
572
|
this.notePromises = [];
|
|
547
573
|
this.resetAllStates();
|
|
@@ -616,7 +642,7 @@ export class MidyGM1 {
|
|
|
616
642
|
const channel = this.channels[channelNumber];
|
|
617
643
|
const promises = [];
|
|
618
644
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
619
|
-
const promise = this.
|
|
645
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
620
646
|
this.notePromises.push(promise);
|
|
621
647
|
promises.push(promise);
|
|
622
648
|
});
|
|
@@ -626,7 +652,7 @@ export class MidyGM1 {
|
|
|
626
652
|
const channel = this.channels[channelNumber];
|
|
627
653
|
const promises = [];
|
|
628
654
|
this.processScheduledNotes(channel, (note) => {
|
|
629
|
-
const promise = this.
|
|
655
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
630
656
|
this.notePromises.push(promise);
|
|
631
657
|
promises.push(promise);
|
|
632
658
|
});
|
|
@@ -659,7 +685,7 @@ export class MidyGM1 {
|
|
|
659
685
|
if (!this.isPlaying || this.isPaused)
|
|
660
686
|
return;
|
|
661
687
|
const now = this.audioContext.currentTime;
|
|
662
|
-
this.resumeTime
|
|
688
|
+
this.resumeTime = now - this.startTime - this.startDelay;
|
|
663
689
|
this.isPausing = true;
|
|
664
690
|
await this.playPromise;
|
|
665
691
|
this.isPausing = false;
|
|
@@ -685,11 +711,13 @@ export class MidyGM1 {
|
|
|
685
711
|
if (totalTime < event.startTime)
|
|
686
712
|
totalTime = event.startTime;
|
|
687
713
|
}
|
|
688
|
-
return totalTime;
|
|
714
|
+
return totalTime + this.startDelay;
|
|
689
715
|
}
|
|
690
716
|
currentTime() {
|
|
717
|
+
if (!this.isPlaying)
|
|
718
|
+
return this.resumeTime;
|
|
691
719
|
const now = this.audioContext.currentTime;
|
|
692
|
-
return
|
|
720
|
+
return now + this.resumeTime - this.startTime;
|
|
693
721
|
}
|
|
694
722
|
processScheduledNotes(channel, callback) {
|
|
695
723
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -828,31 +856,42 @@ export class MidyGM1 {
|
|
|
828
856
|
note.modulationLFO.connect(note.volumeDepth);
|
|
829
857
|
note.volumeDepth.connect(note.volumeEnvelopeNode.gain);
|
|
830
858
|
}
|
|
831
|
-
async getAudioBuffer(channel, noteNumber, velocity, voiceParams) {
|
|
859
|
+
async getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime) {
|
|
832
860
|
const audioBufferId = this.getVoiceId(channel, noteNumber, velocity);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
this.voiceCache.delete(audioBufferId);
|
|
838
|
-
}
|
|
839
|
-
return cache.audioBuffer;
|
|
840
|
-
}
|
|
841
|
-
else {
|
|
842
|
-
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
861
|
+
if (realtime) {
|
|
862
|
+
const cachedAudioBuffer = this.realtimeVoiceCache.get(audioBufferId);
|
|
863
|
+
if (cachedAudioBuffer)
|
|
864
|
+
return cachedAudioBuffer;
|
|
843
865
|
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
844
|
-
|
|
845
|
-
this.voiceCache.set(audioBufferId, cache);
|
|
866
|
+
this.realtimeVoiceCache.set(audioBufferId, audioBuffer);
|
|
846
867
|
return audioBuffer;
|
|
847
868
|
}
|
|
869
|
+
else {
|
|
870
|
+
const cache = this.voiceCache.get(audioBufferId);
|
|
871
|
+
if (cache) {
|
|
872
|
+
cache.counter += 1;
|
|
873
|
+
if (cache.maxCount <= cache.counter) {
|
|
874
|
+
this.voiceCache.delete(audioBufferId);
|
|
875
|
+
}
|
|
876
|
+
return cache.audioBuffer;
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
880
|
+
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
881
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
882
|
+
this.voiceCache.set(audioBufferId, cache);
|
|
883
|
+
return audioBuffer;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
848
886
|
}
|
|
849
|
-
async
|
|
887
|
+
async setNoteAudioNode(channel, note, realtime) {
|
|
850
888
|
const now = this.audioContext.currentTime;
|
|
889
|
+
const { noteNumber, velocity, startTime } = note;
|
|
851
890
|
const state = channel.state;
|
|
852
891
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
853
|
-
const voiceParams = voice.getAllParams(controllerState);
|
|
854
|
-
|
|
855
|
-
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
|
|
892
|
+
const voiceParams = note.voice.getAllParams(controllerState);
|
|
893
|
+
note.voiceParams = voiceParams;
|
|
894
|
+
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime);
|
|
856
895
|
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
857
896
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
858
897
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
@@ -868,7 +907,13 @@ export class MidyGM1 {
|
|
|
868
907
|
}
|
|
869
908
|
note.bufferSource.connect(note.filterNode);
|
|
870
909
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
871
|
-
|
|
910
|
+
if (voiceParams.sample.type === "compressed") {
|
|
911
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
912
|
+
note.bufferSource.start(startTime, offset);
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
note.bufferSource.start(startTime);
|
|
916
|
+
}
|
|
872
917
|
return note;
|
|
873
918
|
}
|
|
874
919
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -879,14 +924,31 @@ export class MidyGM1 {
|
|
|
879
924
|
if (prev) {
|
|
880
925
|
const [prevNote, prevChannelNumber] = prev;
|
|
881
926
|
if (prevNote && !prevNote.ending) {
|
|
882
|
-
this.
|
|
927
|
+
this.noteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
883
928
|
startTime, true);
|
|
884
929
|
}
|
|
885
930
|
}
|
|
886
931
|
this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
|
|
887
932
|
}
|
|
888
|
-
|
|
933
|
+
setNoteRouting(channelNumber, note, startTime) {
|
|
934
|
+
const channel = this.channels[channelNumber];
|
|
935
|
+
const volumeEnvelopeNode = note.volumeEnvelopeNode;
|
|
936
|
+
volumeEnvelopeNode.connect(channel.gainL);
|
|
937
|
+
volumeEnvelopeNode.connect(channel.gainR);
|
|
938
|
+
if (0.5 <= channel.state.sustainPedal) {
|
|
939
|
+
channel.sustainNotes.push(note);
|
|
940
|
+
}
|
|
941
|
+
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
942
|
+
}
|
|
943
|
+
async noteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
889
944
|
const channel = this.channels[channelNumber];
|
|
945
|
+
const realtime = startTime === undefined;
|
|
946
|
+
if (realtime)
|
|
947
|
+
startTime = this.audioContext.currentTime;
|
|
948
|
+
const note = new Note(noteNumber, velocity, startTime);
|
|
949
|
+
const scheduledNotes = channel.scheduledNotes;
|
|
950
|
+
note.index = scheduledNotes.length;
|
|
951
|
+
scheduledNotes.push(note);
|
|
890
952
|
const programNumber = channel.programNumber;
|
|
891
953
|
const bankTable = this.soundFontTable[programNumber];
|
|
892
954
|
if (!bankTable)
|
|
@@ -896,23 +958,16 @@ export class MidyGM1 {
|
|
|
896
958
|
if (soundFontIndex === undefined)
|
|
897
959
|
return;
|
|
898
960
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
899
|
-
|
|
900
|
-
if (!voice)
|
|
961
|
+
note.voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
962
|
+
if (!note.voice)
|
|
901
963
|
return;
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
note.
|
|
905
|
-
|
|
906
|
-
|
|
964
|
+
await this.setNoteAudioNode(channel, note, realtime);
|
|
965
|
+
this.setNoteRouting(channelNumber, note, startTime);
|
|
966
|
+
note.pending = false;
|
|
967
|
+
const off = note.offEvent;
|
|
968
|
+
if (off) {
|
|
969
|
+
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
907
970
|
}
|
|
908
|
-
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
909
|
-
const scheduledNotes = channel.scheduledNotes;
|
|
910
|
-
note.index = scheduledNotes.length;
|
|
911
|
-
scheduledNotes.push(note);
|
|
912
|
-
}
|
|
913
|
-
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
914
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
915
|
-
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
|
|
916
971
|
}
|
|
917
972
|
disconnectNote(note) {
|
|
918
973
|
note.bufferSource.disconnect();
|
|
@@ -925,6 +980,7 @@ export class MidyGM1 {
|
|
|
925
980
|
}
|
|
926
981
|
}
|
|
927
982
|
releaseNote(channel, note, endTime) {
|
|
983
|
+
endTime ??= this.audioContext.currentTime;
|
|
928
984
|
const volRelease = endTime + note.voiceParams.volRelease;
|
|
929
985
|
const modRelease = endTime + note.voiceParams.modRelease;
|
|
930
986
|
const stopTime = Math.min(volRelease, modRelease);
|
|
@@ -945,7 +1001,7 @@ export class MidyGM1 {
|
|
|
945
1001
|
}, stopTime);
|
|
946
1002
|
});
|
|
947
1003
|
}
|
|
948
|
-
|
|
1004
|
+
noteOff(channelNumber, noteNumber, velocity, endTime, force) {
|
|
949
1005
|
const channel = this.channels[channelNumber];
|
|
950
1006
|
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
951
1007
|
return;
|
|
@@ -953,9 +1009,15 @@ export class MidyGM1 {
|
|
|
953
1009
|
if (index < 0)
|
|
954
1010
|
return;
|
|
955
1011
|
const note = channel.scheduledNotes[index];
|
|
1012
|
+
if (note.pending) {
|
|
1013
|
+
note.offEvent = { velocity, startTime: endTime };
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
956
1016
|
note.ending = true;
|
|
957
1017
|
this.setNoteIndex(channel, index);
|
|
958
|
-
this.releaseNote(channel, note, endTime);
|
|
1018
|
+
const promise = this.releaseNote(channel, note, endTime);
|
|
1019
|
+
this.notePromises.push(promise);
|
|
1020
|
+
return promise;
|
|
959
1021
|
}
|
|
960
1022
|
setNoteIndex(channel, index) {
|
|
961
1023
|
let allEnds = true;
|
|
@@ -983,16 +1045,12 @@ export class MidyGM1 {
|
|
|
983
1045
|
}
|
|
984
1046
|
return -1;
|
|
985
1047
|
}
|
|
986
|
-
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
987
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
988
|
-
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
989
|
-
}
|
|
990
1048
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
991
1049
|
const velocity = halfVelocity * 2;
|
|
992
1050
|
const channel = this.channels[channelNumber];
|
|
993
1051
|
const promises = [];
|
|
994
1052
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
995
|
-
const promise = this.
|
|
1053
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
996
1054
|
promises.push(promise);
|
|
997
1055
|
}
|
|
998
1056
|
channel.sustainNotes = [];
|
|
@@ -1057,11 +1115,12 @@ export class MidyGM1 {
|
|
|
1057
1115
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1058
1116
|
if (note.modulationDepth) {
|
|
1059
1117
|
const modLfoToPitch = note.voiceParams.modLfoToPitch;
|
|
1060
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1061
|
-
|
|
1118
|
+
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1119
|
+
channel.state.modulationDepthMSB;
|
|
1120
|
+
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1062
1121
|
note.modulationDepth.gain
|
|
1063
1122
|
.cancelScheduledValues(scheduleTime)
|
|
1064
|
-
.setValueAtTime(
|
|
1123
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1065
1124
|
}
|
|
1066
1125
|
else {
|
|
1067
1126
|
this.startModulation(channel, note, scheduleTime);
|
|
@@ -1098,18 +1157,18 @@ export class MidyGM1 {
|
|
|
1098
1157
|
createVoiceParamsHandlers() {
|
|
1099
1158
|
return {
|
|
1100
1159
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1101
|
-
if (0 < channel.state.
|
|
1160
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1102
1161
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1103
1162
|
}
|
|
1104
1163
|
},
|
|
1105
1164
|
vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
|
|
1106
1165
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1107
|
-
if (0 < channel.state.
|
|
1166
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1108
1167
|
this.setModLfoToFilterFc(note, scheduleTime);
|
|
1109
1168
|
}
|
|
1110
1169
|
},
|
|
1111
1170
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1112
|
-
if (0 < channel.state.
|
|
1171
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1113
1172
|
this.setModLfoToVolume(note, scheduleTime);
|
|
1114
1173
|
}
|
|
1115
1174
|
},
|
|
@@ -1121,7 +1180,7 @@ export class MidyGM1 {
|
|
|
1121
1180
|
}
|
|
1122
1181
|
},
|
|
1123
1182
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1124
|
-
if (0 < channel.state.
|
|
1183
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1125
1184
|
this.setFreqModLFO(note, scheduleTime);
|
|
1126
1185
|
}
|
|
1127
1186
|
},
|
|
@@ -1196,7 +1255,8 @@ export class MidyGM1 {
|
|
|
1196
1255
|
}
|
|
1197
1256
|
}
|
|
1198
1257
|
updateModulation(channel, scheduleTime) {
|
|
1199
|
-
const depth = channel.state.
|
|
1258
|
+
const depth = channel.state.modulationDepthMSB *
|
|
1259
|
+
channel.modulationDepthRange;
|
|
1200
1260
|
this.processScheduledNotes(channel, (note) => {
|
|
1201
1261
|
if (note.modulationDepth) {
|
|
1202
1262
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -1209,13 +1269,13 @@ export class MidyGM1 {
|
|
|
1209
1269
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1210
1270
|
const channel = this.channels[channelNumber];
|
|
1211
1271
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1212
|
-
channel.state.
|
|
1272
|
+
channel.state.modulationDepthMSB = modulation / 127;
|
|
1213
1273
|
this.updateModulation(channel, scheduleTime);
|
|
1214
1274
|
}
|
|
1215
1275
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1216
1276
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1217
1277
|
const channel = this.channels[channelNumber];
|
|
1218
|
-
channel.state.
|
|
1278
|
+
channel.state.volumeMSB = volume / 127;
|
|
1219
1279
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1220
1280
|
}
|
|
1221
1281
|
panToGain(pan) {
|
|
@@ -1228,13 +1288,13 @@ export class MidyGM1 {
|
|
|
1228
1288
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1229
1289
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1230
1290
|
const channel = this.channels[channelNumber];
|
|
1231
|
-
channel.state.
|
|
1291
|
+
channel.state.panMSB = pan / 127;
|
|
1232
1292
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1233
1293
|
}
|
|
1234
1294
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1235
1295
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1236
1296
|
const channel = this.channels[channelNumber];
|
|
1237
|
-
channel.state.
|
|
1297
|
+
channel.state.expressionMSB = expression / 127;
|
|
1238
1298
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1239
1299
|
}
|
|
1240
1300
|
dataEntryLSB(channelNumber, value, scheduleTime) {
|
|
@@ -1243,8 +1303,8 @@ export class MidyGM1 {
|
|
|
1243
1303
|
}
|
|
1244
1304
|
updateChannelVolume(channel, scheduleTime) {
|
|
1245
1305
|
const state = channel.state;
|
|
1246
|
-
const volume = state.
|
|
1247
|
-
const { gainLeft, gainRight } = this.panToGain(state.
|
|
1306
|
+
const volume = state.volumeMSB * state.expressionMSB;
|
|
1307
|
+
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
1248
1308
|
channel.gainL.gain
|
|
1249
1309
|
.cancelScheduledValues(scheduleTime)
|
|
1250
1310
|
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
@@ -1392,8 +1452,8 @@ export class MidyGM1 {
|
|
|
1392
1452
|
resetAllControllers(channelNumber, _value, scheduleTime) {
|
|
1393
1453
|
const keys = [
|
|
1394
1454
|
"pitchWheel",
|
|
1395
|
-
"
|
|
1396
|
-
"
|
|
1455
|
+
"expressionMSB",
|
|
1456
|
+
"modulationDepthMSB",
|
|
1397
1457
|
"sustainPedal",
|
|
1398
1458
|
];
|
|
1399
1459
|
const channel = this.channels[channelNumber];
|
package/esm/midy-GM2.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export class MidyGM2 {
|
|
|
44
44
|
soundFontTable: never[][];
|
|
45
45
|
voiceCounter: Map<any, any>;
|
|
46
46
|
voiceCache: Map<any, any>;
|
|
47
|
+
realtimeVoiceCache: Map<any, any>;
|
|
47
48
|
isPlaying: boolean;
|
|
48
49
|
isPausing: boolean;
|
|
49
50
|
isPaused: boolean;
|
|
@@ -173,20 +174,19 @@ export class MidyGM2 {
|
|
|
173
174
|
setFilterEnvelope(channel: any, note: any, scheduleTime: any): void;
|
|
174
175
|
startModulation(channel: any, note: any, scheduleTime: any): void;
|
|
175
176
|
startVibrato(channel: any, note: any, scheduleTime: any): void;
|
|
176
|
-
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any): Promise<any>;
|
|
177
|
-
|
|
177
|
+
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any, realtime: any): Promise<any>;
|
|
178
|
+
setNoteAudioNode(channel: any, note: any, realtime: any): Promise<any>;
|
|
178
179
|
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
179
180
|
handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
180
|
-
|
|
181
|
-
noteOn(channelNumber: any, noteNumber: any, velocity: any,
|
|
181
|
+
setNoteRouting(channelNumber: any, note: any, startTime: any): void;
|
|
182
|
+
noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
182
183
|
disconnectNote(note: any): void;
|
|
183
184
|
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
184
|
-
|
|
185
|
+
noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
185
186
|
setNoteIndex(channel: any, index: any): void;
|
|
186
187
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
188
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
189
|
+
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
190
190
|
createMessageHandlers(): any[];
|
|
191
191
|
handleMessage(data: any, scheduleTime: any): void;
|
|
192
192
|
activeSensing(): void;
|
|
@@ -312,27 +312,4 @@ export class MidyGM2 {
|
|
|
312
312
|
handleSysEx(data: any, scheduleTime: any): void;
|
|
313
313
|
scheduleTask(callback: any, scheduleTime: any): Promise<any>;
|
|
314
314
|
}
|
|
315
|
-
declare class Note {
|
|
316
|
-
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
317
|
-
index: number;
|
|
318
|
-
ending: boolean;
|
|
319
|
-
bufferSource: any;
|
|
320
|
-
filterNode: any;
|
|
321
|
-
filterDepth: any;
|
|
322
|
-
volumeEnvelopeNode: any;
|
|
323
|
-
volumeDepth: any;
|
|
324
|
-
modulationLFO: any;
|
|
325
|
-
modulationDepth: any;
|
|
326
|
-
vibratoLFO: any;
|
|
327
|
-
vibratoDepth: any;
|
|
328
|
-
reverbSend: any;
|
|
329
|
-
chorusSend: any;
|
|
330
|
-
portamentoNoteNumber: number;
|
|
331
|
-
noteNumber: any;
|
|
332
|
-
velocity: any;
|
|
333
|
-
startTime: any;
|
|
334
|
-
voice: any;
|
|
335
|
-
voiceParams: any;
|
|
336
|
-
}
|
|
337
|
-
export {};
|
|
338
315
|
//# sourceMappingURL=midy-GM2.d.ts.map
|
package/esm/midy-GM2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
|