@marmooo/midy 0.1.1 → 0.1.3
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/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +73 -66
- package/esm/midy-GM1.d.ts +18 -14
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +133 -110
- package/esm/midy-GM2.d.ts +36 -30
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +190 -158
- package/esm/midy-GMLite.d.ts +16 -14
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +123 -112
- package/esm/midy.d.ts +33 -31
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +191 -185
- package/package.json +1 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +75 -68
- package/script/midy-GM1.d.ts +18 -14
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +133 -110
- package/script/midy-GM2.d.ts +36 -30
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +190 -158
- package/script/midy-GMLite.d.ts +16 -14
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +123 -112
- package/script/midy.d.ts +33 -31
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +191 -185
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
package/script/midy-GMLite.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export class MidyGMLite {
|
|
|
11
11
|
};
|
|
12
12
|
static effectSettings: {
|
|
13
13
|
expression: number;
|
|
14
|
-
|
|
14
|
+
modulationDepth: number;
|
|
15
15
|
sustainPedal: boolean;
|
|
16
16
|
rpnMSB: number;
|
|
17
17
|
rpnLSB: number;
|
|
@@ -60,7 +60,8 @@ export class MidyGMLite {
|
|
|
60
60
|
instruments: Set<any>;
|
|
61
61
|
timeline: any[];
|
|
62
62
|
};
|
|
63
|
-
|
|
63
|
+
stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
|
|
64
|
+
stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
|
|
64
65
|
start(): Promise<void>;
|
|
65
66
|
stop(): void;
|
|
66
67
|
pause(): void;
|
|
@@ -70,27 +71,27 @@ export class MidyGMLite {
|
|
|
70
71
|
currentTime(): number;
|
|
71
72
|
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
72
73
|
getActiveNote(noteList: any, time: any): any;
|
|
73
|
-
connectEffects(channel: any, gainNode: any): void;
|
|
74
74
|
cbToRatio(cb: any): number;
|
|
75
75
|
centToHz(cent: any): number;
|
|
76
76
|
calcSemitoneOffset(channel: any): number;
|
|
77
77
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
78
|
-
setVolumeEnvelope(
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
setVolumeEnvelope(note: any): void;
|
|
79
|
+
setPitch(note: any, semitoneOffset: any): void;
|
|
80
|
+
setFilterNode(channel: any, note: any): void;
|
|
81
|
+
startModulation(channel: any, note: any, startTime: any): void;
|
|
81
82
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
82
83
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
83
84
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
84
85
|
scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
|
|
85
86
|
releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
|
|
86
87
|
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
87
|
-
handleMIDIMessage(statusByte: any, data1: any, data2: any): void |
|
|
88
|
+
handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
|
|
88
89
|
handleProgramChange(channelNumber: any, program: any): void;
|
|
89
90
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
|
|
90
91
|
setPitchBend(channelNumber: any, pitchBend: any): void;
|
|
91
|
-
handleControlChange(channelNumber: any, controller: any, value: any): void |
|
|
92
|
+
handleControlChange(channelNumber: any, controller: any, value: any): void | Promise<void>;
|
|
92
93
|
updateModulation(channel: any): void;
|
|
93
|
-
|
|
94
|
+
setModulationDepth(channelNumber: any, modulation: any): void;
|
|
94
95
|
setVolume(channelNumber: any, volume: any): void;
|
|
95
96
|
panToGain(pan: any): {
|
|
96
97
|
gainLeft: number;
|
|
@@ -108,9 +109,9 @@ export class MidyGMLite {
|
|
|
108
109
|
updateDetune(channel: any, detuneChange: any): void;
|
|
109
110
|
handlePitchBendRangeRPN(channelNumber: any): void;
|
|
110
111
|
setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
|
|
111
|
-
allSoundOff(channelNumber: any):
|
|
112
|
+
allSoundOff(channelNumber: any): Promise<void>;
|
|
112
113
|
resetAllControllers(channelNumber: any): void;
|
|
113
|
-
allNotesOff(channelNumber: any):
|
|
114
|
+
allNotesOff(channelNumber: any): Promise<void>;
|
|
114
115
|
handleUniversalNonRealTimeExclusiveMessage(data: any): void;
|
|
115
116
|
GM1SystemOn(): void;
|
|
116
117
|
handleUniversalRealTimeExclusiveMessage(data: any): void;
|
|
@@ -123,10 +124,11 @@ export class MidyGMLite {
|
|
|
123
124
|
declare class Note {
|
|
124
125
|
constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
|
|
125
126
|
bufferSource: any;
|
|
126
|
-
gainNode: any;
|
|
127
127
|
filterNode: any;
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
volumeNode: any;
|
|
129
|
+
volumeDepth: any;
|
|
130
|
+
modulationLFO: any;
|
|
131
|
+
modulationDepth: any;
|
|
130
132
|
noteNumber: any;
|
|
131
133
|
velocity: any;
|
|
132
134
|
startTime: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAsBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAuBhB,kBAAgC;IAChC,gBAA4C;IAC5C,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,mFAmBC;IAED,yDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAcC;IAED,+CAwBC;IAED,6CA6BC;IAED,+DA0BC;IAED,wHA8BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,2FA+BC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA1+BD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|
package/script/midy-GMLite.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MidyGMLite = void 0;
|
|
4
4
|
const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
|
|
5
|
-
const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.
|
|
5
|
+
const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js");
|
|
6
6
|
class Note {
|
|
7
7
|
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
8
8
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -11,25 +11,31 @@ class Note {
|
|
|
11
11
|
writable: true,
|
|
12
12
|
value: void 0
|
|
13
13
|
});
|
|
14
|
-
Object.defineProperty(this, "
|
|
14
|
+
Object.defineProperty(this, "filterNode", {
|
|
15
15
|
enumerable: true,
|
|
16
16
|
configurable: true,
|
|
17
17
|
writable: true,
|
|
18
18
|
value: void 0
|
|
19
19
|
});
|
|
20
|
-
Object.defineProperty(this, "
|
|
20
|
+
Object.defineProperty(this, "volumeNode", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "volumeDepth", {
|
|
21
27
|
enumerable: true,
|
|
22
28
|
configurable: true,
|
|
23
29
|
writable: true,
|
|
24
30
|
value: void 0
|
|
25
31
|
});
|
|
26
|
-
Object.defineProperty(this, "
|
|
32
|
+
Object.defineProperty(this, "modulationLFO", {
|
|
27
33
|
enumerable: true,
|
|
28
34
|
configurable: true,
|
|
29
35
|
writable: true,
|
|
30
36
|
value: void 0
|
|
31
37
|
});
|
|
32
|
-
Object.defineProperty(this, "
|
|
38
|
+
Object.defineProperty(this, "modulationDepth", {
|
|
33
39
|
enumerable: true,
|
|
34
40
|
configurable: true,
|
|
35
41
|
writable: true,
|
|
@@ -147,8 +153,8 @@ class MidyGMLite {
|
|
|
147
153
|
});
|
|
148
154
|
this.audioContext = audioContext;
|
|
149
155
|
this.masterGain = new GainNode(audioContext);
|
|
150
|
-
this.masterGain.connect(audioContext.destination);
|
|
151
156
|
this.channels = this.createChannels(audioContext);
|
|
157
|
+
this.masterGain.connect(audioContext.destination);
|
|
152
158
|
this.GM1SystemOn();
|
|
153
159
|
}
|
|
154
160
|
initSoundFontTable() {
|
|
@@ -192,6 +198,7 @@ class MidyGMLite {
|
|
|
192
198
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
193
199
|
gainL.connect(merger, 0, 0);
|
|
194
200
|
gainR.connect(merger, 0, 1);
|
|
201
|
+
merger.connect(this.masterGain);
|
|
195
202
|
return {
|
|
196
203
|
gainL,
|
|
197
204
|
gainR,
|
|
@@ -315,7 +322,7 @@ class MidyGMLite {
|
|
|
315
322
|
const t = this.audioContext.currentTime + offset;
|
|
316
323
|
queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
|
|
317
324
|
if (this.isPausing) {
|
|
318
|
-
await this.stopNotes();
|
|
325
|
+
await this.stopNotes(0, true);
|
|
319
326
|
this.notePromises = [];
|
|
320
327
|
resolve();
|
|
321
328
|
this.isPausing = false;
|
|
@@ -323,7 +330,7 @@ class MidyGMLite {
|
|
|
323
330
|
return;
|
|
324
331
|
}
|
|
325
332
|
else if (this.isStopping) {
|
|
326
|
-
await this.stopNotes();
|
|
333
|
+
await this.stopNotes(0, true);
|
|
327
334
|
this.notePromises = [];
|
|
328
335
|
resolve();
|
|
329
336
|
this.isStopping = false;
|
|
@@ -331,7 +338,7 @@ class MidyGMLite {
|
|
|
331
338
|
return;
|
|
332
339
|
}
|
|
333
340
|
else if (this.isSeeking) {
|
|
334
|
-
this.stopNotes();
|
|
341
|
+
this.stopNotes(0, true);
|
|
335
342
|
this.startTime = this.audioContext.currentTime;
|
|
336
343
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
337
344
|
offset = this.resumeTime - this.startTime;
|
|
@@ -412,21 +419,24 @@ class MidyGMLite {
|
|
|
412
419
|
}
|
|
413
420
|
return { instruments, timeline };
|
|
414
421
|
}
|
|
415
|
-
|
|
422
|
+
async stopChannelNotes(channelNumber, velocity, stopPedal) {
|
|
416
423
|
const now = this.audioContext.currentTime;
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
this.notePromises.push(promise);
|
|
425
|
-
}
|
|
426
|
-
});
|
|
424
|
+
const channel = this.channels[channelNumber];
|
|
425
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
426
|
+
noteList.forEach((note) => {
|
|
427
|
+
if (note) {
|
|
428
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
|
|
429
|
+
this.notePromises.push(promise);
|
|
430
|
+
}
|
|
427
431
|
});
|
|
428
|
-
channel.scheduledNotes.clear();
|
|
429
432
|
});
|
|
433
|
+
channel.scheduledNotes.clear();
|
|
434
|
+
await Promise.all(this.notePromises);
|
|
435
|
+
}
|
|
436
|
+
stopNotes(velocity, stopPedal) {
|
|
437
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
438
|
+
this.stopChannelNotes(i, velocity, stopPedal);
|
|
439
|
+
}
|
|
430
440
|
return Promise.all(this.notePromises);
|
|
431
441
|
}
|
|
432
442
|
async start() {
|
|
@@ -494,10 +504,6 @@ class MidyGMLite {
|
|
|
494
504
|
}
|
|
495
505
|
return noteList[0];
|
|
496
506
|
}
|
|
497
|
-
connectEffects(channel, gainNode) {
|
|
498
|
-
gainNode.connect(channel.merger);
|
|
499
|
-
merger.connect(this.masterGain);
|
|
500
|
-
}
|
|
501
507
|
cbToRatio(cb) {
|
|
502
508
|
return Math.pow(10, cb / 200);
|
|
503
509
|
}
|
|
@@ -511,42 +517,56 @@ class MidyGMLite {
|
|
|
511
517
|
return instrumentKey.playbackRate(noteNumber) *
|
|
512
518
|
Math.pow(2, semitoneOffset / 12);
|
|
513
519
|
}
|
|
514
|
-
setVolumeEnvelope(
|
|
515
|
-
const { instrumentKey, startTime
|
|
516
|
-
note.
|
|
517
|
-
|
|
518
|
-
if (volume === 0)
|
|
519
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
520
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
521
|
-
volume;
|
|
520
|
+
setVolumeEnvelope(note) {
|
|
521
|
+
const { instrumentKey, startTime } = note;
|
|
522
|
+
note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
|
|
523
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
522
524
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
523
525
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
524
526
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
525
527
|
const volHold = volAttack + instrumentKey.volHold;
|
|
526
528
|
const volDecay = volHold + instrumentKey.volDecay;
|
|
527
|
-
note.
|
|
529
|
+
note.volumeNode.gain
|
|
528
530
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
529
531
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
530
532
|
.setValueAtTime(attackVolume, volHold)
|
|
531
533
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
532
534
|
}
|
|
533
|
-
|
|
534
|
-
const { instrumentKey,
|
|
535
|
+
setPitch(note, semitoneOffset) {
|
|
536
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
537
|
+
const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
|
|
538
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
539
|
+
if (modEnvToPitch === 0)
|
|
540
|
+
return;
|
|
541
|
+
const basePitch = note.bufferSource.playbackRate.value;
|
|
542
|
+
const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
|
|
543
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
544
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
545
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
546
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
547
|
+
note.bufferSource.playbackRate.value
|
|
548
|
+
.setValueAtTime(basePitch, modDelay)
|
|
549
|
+
.exponentialRampToValueAtTime(peekPitch, modAttack)
|
|
550
|
+
.setValueAtTime(peekPitch, modHold)
|
|
551
|
+
.linearRampToValueAtTime(basePitch, modDecay);
|
|
552
|
+
}
|
|
553
|
+
setFilterNode(channel, note) {
|
|
554
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
535
555
|
const softPedalFactor = 1 -
|
|
536
556
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
537
557
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
538
558
|
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
539
559
|
softPedalFactor;
|
|
540
560
|
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
541
|
-
const sustainFreq =
|
|
542
|
-
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)
|
|
561
|
+
const sustainFreq = baseFreq +
|
|
562
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
563
|
+
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
564
|
+
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
565
|
+
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
543
566
|
const modDelay = startTime + instrumentKey.modDelay;
|
|
544
567
|
const modAttack = modDelay + instrumentKey.modAttack;
|
|
545
568
|
const modHold = modAttack + instrumentKey.modHold;
|
|
546
569
|
const modDecay = modHold + instrumentKey.modDecay;
|
|
547
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
548
|
-
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
549
|
-
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
550
570
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
551
571
|
type: "lowpass",
|
|
552
572
|
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
@@ -557,35 +577,49 @@ class MidyGMLite {
|
|
|
557
577
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
558
578
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
559
579
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
560
|
-
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
561
580
|
}
|
|
562
|
-
startModulation(channel, note,
|
|
581
|
+
startModulation(channel, note, startTime) {
|
|
563
582
|
const { instrumentKey } = note;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
});
|
|
567
|
-
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
583
|
+
const { modLfoToPitch, modLfoToVolume } = instrumentKey;
|
|
584
|
+
note.modulationLFO = new OscillatorNode(this.audioContext, {
|
|
568
585
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
569
586
|
});
|
|
570
|
-
note.
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
587
|
+
note.filterDepth = new GainNode(this.audioContext, {
|
|
588
|
+
gain: instrumentKey.modLfoToFilterFc,
|
|
589
|
+
});
|
|
590
|
+
const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
|
|
591
|
+
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
592
|
+
note.modulationDepth = new GainNode(this.audioContext, {
|
|
593
|
+
gain: modulationDepth * modulationDepthSign,
|
|
594
|
+
});
|
|
595
|
+
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
596
|
+
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
597
|
+
note.volumeDepth = new GainNode(this.audioContext, {
|
|
598
|
+
gain: volumeDepth * volumeDepthSign,
|
|
599
|
+
});
|
|
600
|
+
note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
|
|
601
|
+
note.modulationLFO.connect(note.filterDepth);
|
|
602
|
+
note.filterDepth.connect(note.filterNode.frequency);
|
|
603
|
+
note.modulationLFO.connect(note.modulationDepth);
|
|
604
|
+
note.modulationDepth.connect(note.bufferSource.detune);
|
|
605
|
+
note.modulationLFO.connect(note.volumeDepth);
|
|
606
|
+
note.volumeDepth.connect(note.volumeNode.gain);
|
|
575
607
|
}
|
|
576
608
|
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
577
609
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
578
610
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
579
611
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
580
|
-
|
|
581
|
-
this.setVolumeEnvelope(
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
612
|
+
this.setFilterNode(channel, note);
|
|
613
|
+
this.setVolumeEnvelope(note);
|
|
614
|
+
if (0 < channel.modulationDepth) {
|
|
615
|
+
this.setPitch(note, semitoneOffset);
|
|
616
|
+
this.startModulation(channel, note, startTime);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
586
620
|
}
|
|
587
621
|
note.bufferSource.connect(note.filterNode);
|
|
588
|
-
note.filterNode.connect(note.
|
|
622
|
+
note.filterNode.connect(note.volumeNode);
|
|
589
623
|
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
590
624
|
return note;
|
|
591
625
|
}
|
|
@@ -601,7 +635,8 @@ class MidyGMLite {
|
|
|
601
635
|
if (!instrumentKey)
|
|
602
636
|
return;
|
|
603
637
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
604
|
-
|
|
638
|
+
note.volumeNode.connect(channel.gainL);
|
|
639
|
+
note.volumeNode.connect(channel.gainR);
|
|
605
640
|
const scheduledNotes = channel.scheduledNotes;
|
|
606
641
|
if (scheduledNotes.has(noteNumber)) {
|
|
607
642
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -630,17 +665,14 @@ class MidyGMLite {
|
|
|
630
665
|
const velocityRate = (velocity + 127) / 127;
|
|
631
666
|
const volEndTime = stopTime +
|
|
632
667
|
note.instrumentKey.volRelease * velocityRate;
|
|
633
|
-
note.
|
|
668
|
+
note.volumeNode.gain
|
|
634
669
|
.cancelScheduledValues(stopTime)
|
|
635
670
|
.linearRampToValueAtTime(0, volEndTime);
|
|
636
|
-
const
|
|
637
|
-
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
638
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
639
|
-
const modEndTime = stopTime +
|
|
671
|
+
const modRelease = stopTime +
|
|
640
672
|
note.instrumentKey.modRelease * velocityRate;
|
|
641
673
|
note.filterNode.frequency
|
|
642
674
|
.cancelScheduledValues(stopTime)
|
|
643
|
-
.linearRampToValueAtTime(
|
|
675
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
644
676
|
note.ending = true;
|
|
645
677
|
this.scheduleTask(() => {
|
|
646
678
|
note.bufferSource.loop = false;
|
|
@@ -649,15 +681,17 @@ class MidyGMLite {
|
|
|
649
681
|
note.bufferSource.onended = () => {
|
|
650
682
|
scheduledNotes[i] = null;
|
|
651
683
|
note.bufferSource.disconnect();
|
|
684
|
+
note.volumeNode.disconnect();
|
|
652
685
|
note.filterNode.disconnect();
|
|
653
|
-
note.
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
686
|
+
if (note.volumeDepth)
|
|
687
|
+
note.volumeDepth.disconnect();
|
|
688
|
+
if (note.modulationDepth)
|
|
689
|
+
note.modulationDepth.disconnect();
|
|
690
|
+
if (note.modulationLFO)
|
|
691
|
+
note.modulationLFO.stop();
|
|
658
692
|
resolve();
|
|
659
693
|
};
|
|
660
|
-
bufferSource.stop(volEndTime);
|
|
694
|
+
note.bufferSource.stop(volEndTime);
|
|
661
695
|
});
|
|
662
696
|
}
|
|
663
697
|
}
|
|
@@ -670,10 +704,10 @@ class MidyGMLite {
|
|
|
670
704
|
const channel = this.channels[channelNumber];
|
|
671
705
|
const promises = [];
|
|
672
706
|
channel.sustainPedal = false;
|
|
673
|
-
channel.scheduledNotes.forEach((
|
|
674
|
-
|
|
675
|
-
if (
|
|
676
|
-
const { noteNumber } =
|
|
707
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
708
|
+
noteList.forEach((note) => {
|
|
709
|
+
if (note) {
|
|
710
|
+
const { noteNumber } = note;
|
|
677
711
|
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
678
712
|
promises.push(promise);
|
|
679
713
|
}
|
|
@@ -704,13 +738,13 @@ class MidyGMLite {
|
|
|
704
738
|
channel.program = program;
|
|
705
739
|
}
|
|
706
740
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
707
|
-
const pitchBend = msb * 128 + lsb;
|
|
741
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
708
742
|
this.setPitchBend(channelNumber, pitchBend);
|
|
709
743
|
}
|
|
710
744
|
setPitchBend(channelNumber, pitchBend) {
|
|
711
745
|
const channel = this.channels[channelNumber];
|
|
712
746
|
const prevPitchBend = channel.pitchBend;
|
|
713
|
-
channel.pitchBend =
|
|
747
|
+
channel.pitchBend = pitchBend / 8192;
|
|
714
748
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
715
749
|
channel.pitchBendRange * 100;
|
|
716
750
|
this.updateDetune(channel, detuneChange);
|
|
@@ -718,7 +752,7 @@ class MidyGMLite {
|
|
|
718
752
|
handleControlChange(channelNumber, controller, value) {
|
|
719
753
|
switch (controller) {
|
|
720
754
|
case 1:
|
|
721
|
-
return this.
|
|
755
|
+
return this.setModulationDepth(channelNumber, value);
|
|
722
756
|
case 6:
|
|
723
757
|
return this.dataEntryMSB(channelNumber, value);
|
|
724
758
|
case 7:
|
|
@@ -749,18 +783,19 @@ class MidyGMLite {
|
|
|
749
783
|
const now = this.audioContext.currentTime;
|
|
750
784
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
751
785
|
activeNotes.forEach((activeNote) => {
|
|
752
|
-
if (activeNote.
|
|
753
|
-
|
|
754
|
-
gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
|
|
786
|
+
if (activeNote.modulationDepth) {
|
|
787
|
+
activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
755
788
|
}
|
|
756
789
|
else {
|
|
790
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
791
|
+
this.setPitch(activeNote, semitoneOffset);
|
|
757
792
|
this.startModulation(channel, activeNote, now);
|
|
758
793
|
}
|
|
759
794
|
});
|
|
760
795
|
}
|
|
761
|
-
|
|
796
|
+
setModulationDepth(channelNumber, modulation) {
|
|
762
797
|
const channel = this.channels[channelNumber];
|
|
763
|
-
channel.
|
|
798
|
+
channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
|
|
764
799
|
this.updateModulation(channel);
|
|
765
800
|
}
|
|
766
801
|
setVolume(channelNumber, volume) {
|
|
@@ -854,37 +889,13 @@ class MidyGMLite {
|
|
|
854
889
|
this.updateDetune(channel, detuneChange);
|
|
855
890
|
}
|
|
856
891
|
allSoundOff(channelNumber) {
|
|
857
|
-
|
|
858
|
-
const channel = this.channels[channelNumber];
|
|
859
|
-
const velocity = 0;
|
|
860
|
-
const stopPedal = true;
|
|
861
|
-
const promises = [];
|
|
862
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
863
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
864
|
-
if (activeNote) {
|
|
865
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
866
|
-
promises.push(notePromise);
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
return promises;
|
|
892
|
+
return this.stopChannelNotes(channelNumber, 0, true);
|
|
870
893
|
}
|
|
871
894
|
resetAllControllers(channelNumber) {
|
|
872
895
|
Object.assign(this.channels[channelNumber], this.effectSettings);
|
|
873
896
|
}
|
|
874
897
|
allNotesOff(channelNumber) {
|
|
875
|
-
|
|
876
|
-
const channel = this.channels[channelNumber];
|
|
877
|
-
const velocity = 0;
|
|
878
|
-
const stopPedal = false;
|
|
879
|
-
const promises = [];
|
|
880
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
881
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
882
|
-
if (activeNote) {
|
|
883
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
884
|
-
promises.push(notePromise);
|
|
885
|
-
}
|
|
886
|
-
});
|
|
887
|
-
return promises;
|
|
898
|
+
return this.stopChannelNotes(channelNumber, 0, false);
|
|
888
899
|
}
|
|
889
900
|
handleUniversalNonRealTimeExclusiveMessage(data) {
|
|
890
901
|
switch (data[2]) {
|
|
@@ -978,7 +989,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
978
989
|
dataLSB: 0,
|
|
979
990
|
program: 0,
|
|
980
991
|
pitchBend: 0,
|
|
981
|
-
modulationDepthRange:
|
|
992
|
+
modulationDepthRange: 50, // cent
|
|
982
993
|
}
|
|
983
994
|
});
|
|
984
995
|
Object.defineProperty(MidyGMLite, "effectSettings", {
|
|
@@ -987,7 +998,7 @@ Object.defineProperty(MidyGMLite, "effectSettings", {
|
|
|
987
998
|
writable: true,
|
|
988
999
|
value: {
|
|
989
1000
|
expression: 1,
|
|
990
|
-
|
|
1001
|
+
modulationDepth: 0,
|
|
991
1002
|
sustainPedal: false,
|
|
992
1003
|
rpnMSB: 127,
|
|
993
1004
|
rpnLSB: 127,
|