@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/esm/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;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -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/esm/midy-GMLite.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseMidi } from "./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js";
|
|
2
|
-
import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.
|
|
2
|
+
import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js";
|
|
3
3
|
class Note {
|
|
4
4
|
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
5
5
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -8,25 +8,31 @@ class Note {
|
|
|
8
8
|
writable: true,
|
|
9
9
|
value: void 0
|
|
10
10
|
});
|
|
11
|
-
Object.defineProperty(this, "
|
|
11
|
+
Object.defineProperty(this, "filterNode", {
|
|
12
12
|
enumerable: true,
|
|
13
13
|
configurable: true,
|
|
14
14
|
writable: true,
|
|
15
15
|
value: void 0
|
|
16
16
|
});
|
|
17
|
-
Object.defineProperty(this, "
|
|
17
|
+
Object.defineProperty(this, "volumeNode", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "volumeDepth", {
|
|
18
24
|
enumerable: true,
|
|
19
25
|
configurable: true,
|
|
20
26
|
writable: true,
|
|
21
27
|
value: void 0
|
|
22
28
|
});
|
|
23
|
-
Object.defineProperty(this, "
|
|
29
|
+
Object.defineProperty(this, "modulationLFO", {
|
|
24
30
|
enumerable: true,
|
|
25
31
|
configurable: true,
|
|
26
32
|
writable: true,
|
|
27
33
|
value: void 0
|
|
28
34
|
});
|
|
29
|
-
Object.defineProperty(this, "
|
|
35
|
+
Object.defineProperty(this, "modulationDepth", {
|
|
30
36
|
enumerable: true,
|
|
31
37
|
configurable: true,
|
|
32
38
|
writable: true,
|
|
@@ -144,8 +150,8 @@ export class MidyGMLite {
|
|
|
144
150
|
});
|
|
145
151
|
this.audioContext = audioContext;
|
|
146
152
|
this.masterGain = new GainNode(audioContext);
|
|
147
|
-
this.masterGain.connect(audioContext.destination);
|
|
148
153
|
this.channels = this.createChannels(audioContext);
|
|
154
|
+
this.masterGain.connect(audioContext.destination);
|
|
149
155
|
this.GM1SystemOn();
|
|
150
156
|
}
|
|
151
157
|
initSoundFontTable() {
|
|
@@ -189,6 +195,7 @@ export class MidyGMLite {
|
|
|
189
195
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
190
196
|
gainL.connect(merger, 0, 0);
|
|
191
197
|
gainR.connect(merger, 0, 1);
|
|
198
|
+
merger.connect(this.masterGain);
|
|
192
199
|
return {
|
|
193
200
|
gainL,
|
|
194
201
|
gainR,
|
|
@@ -312,7 +319,7 @@ export class MidyGMLite {
|
|
|
312
319
|
const t = this.audioContext.currentTime + offset;
|
|
313
320
|
queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
|
|
314
321
|
if (this.isPausing) {
|
|
315
|
-
await this.stopNotes();
|
|
322
|
+
await this.stopNotes(0, true);
|
|
316
323
|
this.notePromises = [];
|
|
317
324
|
resolve();
|
|
318
325
|
this.isPausing = false;
|
|
@@ -320,7 +327,7 @@ export class MidyGMLite {
|
|
|
320
327
|
return;
|
|
321
328
|
}
|
|
322
329
|
else if (this.isStopping) {
|
|
323
|
-
await this.stopNotes();
|
|
330
|
+
await this.stopNotes(0, true);
|
|
324
331
|
this.notePromises = [];
|
|
325
332
|
resolve();
|
|
326
333
|
this.isStopping = false;
|
|
@@ -328,7 +335,7 @@ export class MidyGMLite {
|
|
|
328
335
|
return;
|
|
329
336
|
}
|
|
330
337
|
else if (this.isSeeking) {
|
|
331
|
-
this.stopNotes();
|
|
338
|
+
this.stopNotes(0, true);
|
|
332
339
|
this.startTime = this.audioContext.currentTime;
|
|
333
340
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
334
341
|
offset = this.resumeTime - this.startTime;
|
|
@@ -409,21 +416,24 @@ export class MidyGMLite {
|
|
|
409
416
|
}
|
|
410
417
|
return { instruments, timeline };
|
|
411
418
|
}
|
|
412
|
-
|
|
419
|
+
async stopChannelNotes(channelNumber, velocity, stopPedal) {
|
|
413
420
|
const now = this.audioContext.currentTime;
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
this.notePromises.push(promise);
|
|
422
|
-
}
|
|
423
|
-
});
|
|
421
|
+
const channel = this.channels[channelNumber];
|
|
422
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
423
|
+
noteList.forEach((note) => {
|
|
424
|
+
if (note) {
|
|
425
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
|
|
426
|
+
this.notePromises.push(promise);
|
|
427
|
+
}
|
|
424
428
|
});
|
|
425
|
-
channel.scheduledNotes.clear();
|
|
426
429
|
});
|
|
430
|
+
channel.scheduledNotes.clear();
|
|
431
|
+
await Promise.all(this.notePromises);
|
|
432
|
+
}
|
|
433
|
+
stopNotes(velocity, stopPedal) {
|
|
434
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
435
|
+
this.stopChannelNotes(i, velocity, stopPedal);
|
|
436
|
+
}
|
|
427
437
|
return Promise.all(this.notePromises);
|
|
428
438
|
}
|
|
429
439
|
async start() {
|
|
@@ -491,10 +501,6 @@ export class MidyGMLite {
|
|
|
491
501
|
}
|
|
492
502
|
return noteList[0];
|
|
493
503
|
}
|
|
494
|
-
connectEffects(channel, gainNode) {
|
|
495
|
-
gainNode.connect(channel.merger);
|
|
496
|
-
merger.connect(this.masterGain);
|
|
497
|
-
}
|
|
498
504
|
cbToRatio(cb) {
|
|
499
505
|
return Math.pow(10, cb / 200);
|
|
500
506
|
}
|
|
@@ -508,42 +514,56 @@ export class MidyGMLite {
|
|
|
508
514
|
return instrumentKey.playbackRate(noteNumber) *
|
|
509
515
|
Math.pow(2, semitoneOffset / 12);
|
|
510
516
|
}
|
|
511
|
-
setVolumeEnvelope(
|
|
512
|
-
const { instrumentKey, startTime
|
|
513
|
-
note.
|
|
514
|
-
|
|
515
|
-
if (volume === 0)
|
|
516
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
517
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
518
|
-
volume;
|
|
517
|
+
setVolumeEnvelope(note) {
|
|
518
|
+
const { instrumentKey, startTime } = note;
|
|
519
|
+
note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
|
|
520
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
519
521
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
520
522
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
521
523
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
522
524
|
const volHold = volAttack + instrumentKey.volHold;
|
|
523
525
|
const volDecay = volHold + instrumentKey.volDecay;
|
|
524
|
-
note.
|
|
526
|
+
note.volumeNode.gain
|
|
525
527
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
526
528
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
527
529
|
.setValueAtTime(attackVolume, volHold)
|
|
528
530
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
529
531
|
}
|
|
530
|
-
|
|
531
|
-
const { instrumentKey,
|
|
532
|
+
setPitch(note, semitoneOffset) {
|
|
533
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
534
|
+
const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
|
|
535
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
536
|
+
if (modEnvToPitch === 0)
|
|
537
|
+
return;
|
|
538
|
+
const basePitch = note.bufferSource.playbackRate.value;
|
|
539
|
+
const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
|
|
540
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
541
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
542
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
543
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
544
|
+
note.bufferSource.playbackRate.value
|
|
545
|
+
.setValueAtTime(basePitch, modDelay)
|
|
546
|
+
.exponentialRampToValueAtTime(peekPitch, modAttack)
|
|
547
|
+
.setValueAtTime(peekPitch, modHold)
|
|
548
|
+
.linearRampToValueAtTime(basePitch, modDecay);
|
|
549
|
+
}
|
|
550
|
+
setFilterNode(channel, note) {
|
|
551
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
532
552
|
const softPedalFactor = 1 -
|
|
533
553
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
534
554
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
535
555
|
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
536
556
|
softPedalFactor;
|
|
537
557
|
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
538
|
-
const sustainFreq =
|
|
539
|
-
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)
|
|
558
|
+
const sustainFreq = baseFreq +
|
|
559
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
560
|
+
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
561
|
+
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
562
|
+
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
540
563
|
const modDelay = startTime + instrumentKey.modDelay;
|
|
541
564
|
const modAttack = modDelay + instrumentKey.modAttack;
|
|
542
565
|
const modHold = modAttack + instrumentKey.modHold;
|
|
543
566
|
const modDecay = modHold + instrumentKey.modDecay;
|
|
544
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
545
|
-
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
546
|
-
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
547
567
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
548
568
|
type: "lowpass",
|
|
549
569
|
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
@@ -554,35 +574,49 @@ export class MidyGMLite {
|
|
|
554
574
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
555
575
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
556
576
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
557
|
-
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
558
577
|
}
|
|
559
|
-
startModulation(channel, note,
|
|
578
|
+
startModulation(channel, note, startTime) {
|
|
560
579
|
const { instrumentKey } = note;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
});
|
|
564
|
-
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
580
|
+
const { modLfoToPitch, modLfoToVolume } = instrumentKey;
|
|
581
|
+
note.modulationLFO = new OscillatorNode(this.audioContext, {
|
|
565
582
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
566
583
|
});
|
|
567
|
-
note.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
584
|
+
note.filterDepth = new GainNode(this.audioContext, {
|
|
585
|
+
gain: instrumentKey.modLfoToFilterFc,
|
|
586
|
+
});
|
|
587
|
+
const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
|
|
588
|
+
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
589
|
+
note.modulationDepth = new GainNode(this.audioContext, {
|
|
590
|
+
gain: modulationDepth * modulationDepthSign,
|
|
591
|
+
});
|
|
592
|
+
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
593
|
+
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
594
|
+
note.volumeDepth = new GainNode(this.audioContext, {
|
|
595
|
+
gain: volumeDepth * volumeDepthSign,
|
|
596
|
+
});
|
|
597
|
+
note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
|
|
598
|
+
note.modulationLFO.connect(note.filterDepth);
|
|
599
|
+
note.filterDepth.connect(note.filterNode.frequency);
|
|
600
|
+
note.modulationLFO.connect(note.modulationDepth);
|
|
601
|
+
note.modulationDepth.connect(note.bufferSource.detune);
|
|
602
|
+
note.modulationLFO.connect(note.volumeDepth);
|
|
603
|
+
note.volumeDepth.connect(note.volumeNode.gain);
|
|
572
604
|
}
|
|
573
605
|
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
574
606
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
575
607
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
576
608
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
577
|
-
|
|
578
|
-
this.setVolumeEnvelope(
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
609
|
+
this.setFilterNode(channel, note);
|
|
610
|
+
this.setVolumeEnvelope(note);
|
|
611
|
+
if (0 < channel.modulationDepth) {
|
|
612
|
+
this.setPitch(note, semitoneOffset);
|
|
613
|
+
this.startModulation(channel, note, startTime);
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
583
617
|
}
|
|
584
618
|
note.bufferSource.connect(note.filterNode);
|
|
585
|
-
note.filterNode.connect(note.
|
|
619
|
+
note.filterNode.connect(note.volumeNode);
|
|
586
620
|
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
587
621
|
return note;
|
|
588
622
|
}
|
|
@@ -598,7 +632,8 @@ export class MidyGMLite {
|
|
|
598
632
|
if (!instrumentKey)
|
|
599
633
|
return;
|
|
600
634
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
601
|
-
|
|
635
|
+
note.volumeNode.connect(channel.gainL);
|
|
636
|
+
note.volumeNode.connect(channel.gainR);
|
|
602
637
|
const scheduledNotes = channel.scheduledNotes;
|
|
603
638
|
if (scheduledNotes.has(noteNumber)) {
|
|
604
639
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -627,17 +662,14 @@ export class MidyGMLite {
|
|
|
627
662
|
const velocityRate = (velocity + 127) / 127;
|
|
628
663
|
const volEndTime = stopTime +
|
|
629
664
|
note.instrumentKey.volRelease * velocityRate;
|
|
630
|
-
note.
|
|
665
|
+
note.volumeNode.gain
|
|
631
666
|
.cancelScheduledValues(stopTime)
|
|
632
667
|
.linearRampToValueAtTime(0, volEndTime);
|
|
633
|
-
const
|
|
634
|
-
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
635
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
636
|
-
const modEndTime = stopTime +
|
|
668
|
+
const modRelease = stopTime +
|
|
637
669
|
note.instrumentKey.modRelease * velocityRate;
|
|
638
670
|
note.filterNode.frequency
|
|
639
671
|
.cancelScheduledValues(stopTime)
|
|
640
|
-
.linearRampToValueAtTime(
|
|
672
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
641
673
|
note.ending = true;
|
|
642
674
|
this.scheduleTask(() => {
|
|
643
675
|
note.bufferSource.loop = false;
|
|
@@ -646,15 +678,17 @@ export class MidyGMLite {
|
|
|
646
678
|
note.bufferSource.onended = () => {
|
|
647
679
|
scheduledNotes[i] = null;
|
|
648
680
|
note.bufferSource.disconnect();
|
|
681
|
+
note.volumeNode.disconnect();
|
|
649
682
|
note.filterNode.disconnect();
|
|
650
|
-
note.
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
683
|
+
if (note.volumeDepth)
|
|
684
|
+
note.volumeDepth.disconnect();
|
|
685
|
+
if (note.modulationDepth)
|
|
686
|
+
note.modulationDepth.disconnect();
|
|
687
|
+
if (note.modulationLFO)
|
|
688
|
+
note.modulationLFO.stop();
|
|
655
689
|
resolve();
|
|
656
690
|
};
|
|
657
|
-
bufferSource.stop(volEndTime);
|
|
691
|
+
note.bufferSource.stop(volEndTime);
|
|
658
692
|
});
|
|
659
693
|
}
|
|
660
694
|
}
|
|
@@ -667,10 +701,10 @@ export class MidyGMLite {
|
|
|
667
701
|
const channel = this.channels[channelNumber];
|
|
668
702
|
const promises = [];
|
|
669
703
|
channel.sustainPedal = false;
|
|
670
|
-
channel.scheduledNotes.forEach((
|
|
671
|
-
|
|
672
|
-
if (
|
|
673
|
-
const { noteNumber } =
|
|
704
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
705
|
+
noteList.forEach((note) => {
|
|
706
|
+
if (note) {
|
|
707
|
+
const { noteNumber } = note;
|
|
674
708
|
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
675
709
|
promises.push(promise);
|
|
676
710
|
}
|
|
@@ -701,13 +735,13 @@ export class MidyGMLite {
|
|
|
701
735
|
channel.program = program;
|
|
702
736
|
}
|
|
703
737
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
704
|
-
const pitchBend = msb * 128 + lsb;
|
|
738
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
705
739
|
this.setPitchBend(channelNumber, pitchBend);
|
|
706
740
|
}
|
|
707
741
|
setPitchBend(channelNumber, pitchBend) {
|
|
708
742
|
const channel = this.channels[channelNumber];
|
|
709
743
|
const prevPitchBend = channel.pitchBend;
|
|
710
|
-
channel.pitchBend =
|
|
744
|
+
channel.pitchBend = pitchBend / 8192;
|
|
711
745
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
712
746
|
channel.pitchBendRange * 100;
|
|
713
747
|
this.updateDetune(channel, detuneChange);
|
|
@@ -715,7 +749,7 @@ export class MidyGMLite {
|
|
|
715
749
|
handleControlChange(channelNumber, controller, value) {
|
|
716
750
|
switch (controller) {
|
|
717
751
|
case 1:
|
|
718
|
-
return this.
|
|
752
|
+
return this.setModulationDepth(channelNumber, value);
|
|
719
753
|
case 6:
|
|
720
754
|
return this.dataEntryMSB(channelNumber, value);
|
|
721
755
|
case 7:
|
|
@@ -746,18 +780,19 @@ export class MidyGMLite {
|
|
|
746
780
|
const now = this.audioContext.currentTime;
|
|
747
781
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
748
782
|
activeNotes.forEach((activeNote) => {
|
|
749
|
-
if (activeNote.
|
|
750
|
-
|
|
751
|
-
gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
|
|
783
|
+
if (activeNote.modulationDepth) {
|
|
784
|
+
activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
752
785
|
}
|
|
753
786
|
else {
|
|
787
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
788
|
+
this.setPitch(activeNote, semitoneOffset);
|
|
754
789
|
this.startModulation(channel, activeNote, now);
|
|
755
790
|
}
|
|
756
791
|
});
|
|
757
792
|
}
|
|
758
|
-
|
|
793
|
+
setModulationDepth(channelNumber, modulation) {
|
|
759
794
|
const channel = this.channels[channelNumber];
|
|
760
|
-
channel.
|
|
795
|
+
channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
|
|
761
796
|
this.updateModulation(channel);
|
|
762
797
|
}
|
|
763
798
|
setVolume(channelNumber, volume) {
|
|
@@ -851,37 +886,13 @@ export class MidyGMLite {
|
|
|
851
886
|
this.updateDetune(channel, detuneChange);
|
|
852
887
|
}
|
|
853
888
|
allSoundOff(channelNumber) {
|
|
854
|
-
|
|
855
|
-
const channel = this.channels[channelNumber];
|
|
856
|
-
const velocity = 0;
|
|
857
|
-
const stopPedal = true;
|
|
858
|
-
const promises = [];
|
|
859
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
860
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
861
|
-
if (activeNote) {
|
|
862
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
863
|
-
promises.push(notePromise);
|
|
864
|
-
}
|
|
865
|
-
});
|
|
866
|
-
return promises;
|
|
889
|
+
return this.stopChannelNotes(channelNumber, 0, true);
|
|
867
890
|
}
|
|
868
891
|
resetAllControllers(channelNumber) {
|
|
869
892
|
Object.assign(this.channels[channelNumber], this.effectSettings);
|
|
870
893
|
}
|
|
871
894
|
allNotesOff(channelNumber) {
|
|
872
|
-
|
|
873
|
-
const channel = this.channels[channelNumber];
|
|
874
|
-
const velocity = 0;
|
|
875
|
-
const stopPedal = false;
|
|
876
|
-
const promises = [];
|
|
877
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
878
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
879
|
-
if (activeNote) {
|
|
880
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
881
|
-
promises.push(notePromise);
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
return promises;
|
|
895
|
+
return this.stopChannelNotes(channelNumber, 0, false);
|
|
885
896
|
}
|
|
886
897
|
handleUniversalNonRealTimeExclusiveMessage(data) {
|
|
887
898
|
switch (data[2]) {
|
|
@@ -974,7 +985,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
974
985
|
dataLSB: 0,
|
|
975
986
|
program: 0,
|
|
976
987
|
pitchBend: 0,
|
|
977
|
-
modulationDepthRange:
|
|
988
|
+
modulationDepthRange: 50, // cent
|
|
978
989
|
}
|
|
979
990
|
});
|
|
980
991
|
Object.defineProperty(MidyGMLite, "effectSettings", {
|
|
@@ -983,7 +994,7 @@ Object.defineProperty(MidyGMLite, "effectSettings", {
|
|
|
983
994
|
writable: true,
|
|
984
995
|
value: {
|
|
985
996
|
expression: 1,
|
|
986
|
-
|
|
997
|
+
modulationDepth: 0,
|
|
987
998
|
sustainPedal: false,
|
|
988
999
|
rpnMSB: 127,
|
|
989
1000
|
rpnLSB: 127,
|