@marmooo/midy 0.0.4 → 0.0.5
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.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
- package/esm/midy-GM1.d.ts +24 -34
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +167 -106
- package/esm/midy-GM2.d.ts +123 -21
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +170 -116
- package/esm/midy-GMLite.d.ts +23 -35
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +156 -107
- package/esm/midy.d.ts +25 -23
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +191 -120
- package/package.json +1 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
- package/script/midy-GM1.d.ts +24 -34
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +167 -106
- package/script/midy-GM2.d.ts +123 -21
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +170 -116
- package/script/midy-GMLite.d.ts +23 -35
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +156 -107
- package/script/midy.d.ts +25 -23
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +191 -120
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
package/script/midy-GM1.d.ts
CHANGED
|
@@ -2,9 +2,6 @@ export class MidyGM1 {
|
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
volume: number;
|
|
4
4
|
pan: number;
|
|
5
|
-
vibratoRate: number;
|
|
6
|
-
vibratoDepth: number;
|
|
7
|
-
vibratoDelay: number;
|
|
8
5
|
bank: number;
|
|
9
6
|
dataMSB: number;
|
|
10
7
|
dataLSB: number;
|
|
@@ -44,12 +41,8 @@ export class MidyGM1 {
|
|
|
44
41
|
masterGain: any;
|
|
45
42
|
channels: {
|
|
46
43
|
scheduledNotes: Map<any, any>;
|
|
47
|
-
sostenutoNotes: Map<any, any>;
|
|
48
44
|
gainNode: any;
|
|
49
45
|
pannerNode: any;
|
|
50
|
-
modulationEffect: {
|
|
51
|
-
lfo: any;
|
|
52
|
-
};
|
|
53
46
|
expression: number;
|
|
54
47
|
modulation: number;
|
|
55
48
|
sustainPedal: boolean;
|
|
@@ -58,9 +51,6 @@ export class MidyGM1 {
|
|
|
58
51
|
pitchBendRange: number;
|
|
59
52
|
volume: number;
|
|
60
53
|
pan: number;
|
|
61
|
-
vibratoRate: number;
|
|
62
|
-
vibratoDepth: number;
|
|
63
|
-
vibratoDelay: number;
|
|
64
54
|
bank: number;
|
|
65
55
|
dataMSB: number;
|
|
66
56
|
dataLSB: number;
|
|
@@ -77,18 +67,11 @@ export class MidyGM1 {
|
|
|
77
67
|
setChannelAudioNodes(audioContext: any): {
|
|
78
68
|
gainNode: any;
|
|
79
69
|
pannerNode: any;
|
|
80
|
-
modulationEffect: {
|
|
81
|
-
lfo: any;
|
|
82
|
-
};
|
|
83
70
|
};
|
|
84
71
|
createChannels(audioContext: any): {
|
|
85
72
|
scheduledNotes: Map<any, any>;
|
|
86
|
-
sostenutoNotes: Map<any, any>;
|
|
87
73
|
gainNode: any;
|
|
88
74
|
pannerNode: any;
|
|
89
|
-
modulationEffect: {
|
|
90
|
-
lfo: any;
|
|
91
|
-
};
|
|
92
75
|
expression: number;
|
|
93
76
|
modulation: number;
|
|
94
77
|
sustainPedal: boolean;
|
|
@@ -97,9 +80,6 @@ export class MidyGM1 {
|
|
|
97
80
|
pitchBendRange: number;
|
|
98
81
|
volume: number;
|
|
99
82
|
pan: number;
|
|
100
|
-
vibratoRate: number;
|
|
101
|
-
vibratoDepth: number;
|
|
102
|
-
vibratoDelay: number;
|
|
103
83
|
bank: number;
|
|
104
84
|
dataMSB: number;
|
|
105
85
|
dataLSB: number;
|
|
@@ -109,8 +89,8 @@ export class MidyGM1 {
|
|
|
109
89
|
coarseTuning: number;
|
|
110
90
|
modulationDepthRange: number;
|
|
111
91
|
}[];
|
|
112
|
-
createNoteBuffer(
|
|
113
|
-
createNoteBufferNode(
|
|
92
|
+
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
93
|
+
createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
|
|
114
94
|
convertToFloat32Array(uint8Array: any): Float32Array;
|
|
115
95
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
116
96
|
getQueueIndex(second: any): number;
|
|
@@ -129,22 +109,17 @@ export class MidyGM1 {
|
|
|
129
109
|
seekTo(second: any): void;
|
|
130
110
|
calcTotalTime(): number;
|
|
131
111
|
currentTime(): number;
|
|
132
|
-
getActiveNotes(channel: any): Map<any, any>;
|
|
133
|
-
|
|
134
|
-
createModulationEffect(audioContext: any): {
|
|
135
|
-
lfo: any;
|
|
136
|
-
};
|
|
112
|
+
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
113
|
+
getActiveNote(noteList: any, time: any): any;
|
|
137
114
|
connectNoteEffects(channel: any, gainNode: any): void;
|
|
138
115
|
cbToRatio(cb: any): number;
|
|
139
116
|
centToHz(cent: any): number;
|
|
140
117
|
calcSemitoneOffset(channel: any): any;
|
|
141
|
-
calcPlaybackRate(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
lfoGain: any;
|
|
147
|
-
}>;
|
|
118
|
+
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
119
|
+
setVolumeEnvelope(channel: any, note: any): void;
|
|
120
|
+
setFilterEnvelope(channel: any, note: any): void;
|
|
121
|
+
startModulation(channel: any, note: any, time: any): void;
|
|
122
|
+
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
148
123
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
149
124
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
150
125
|
scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
|
|
@@ -176,4 +151,19 @@ export class MidyGM1 {
|
|
|
176
151
|
handleSysEx(data: any): void;
|
|
177
152
|
scheduleTask(callback: any, startTime: any): Promise<any>;
|
|
178
153
|
}
|
|
154
|
+
declare class Note {
|
|
155
|
+
constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
|
|
156
|
+
bufferSource: any;
|
|
157
|
+
gainNode: any;
|
|
158
|
+
filterNode: any;
|
|
159
|
+
modLFO: any;
|
|
160
|
+
modLFOGain: any;
|
|
161
|
+
vibLFO: any;
|
|
162
|
+
vibLFOGain: any;
|
|
163
|
+
noteNumber: any;
|
|
164
|
+
velocity: any;
|
|
165
|
+
startTime: any;
|
|
166
|
+
instrumentKey: any;
|
|
167
|
+
}
|
|
168
|
+
export {};
|
|
179
169
|
//# sourceMappingURL=midy-GM1.d.ts.map
|
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":"AAuBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,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;IAyBhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAaC;IAED;;;;;;;;;;;;;;;;;;;;QAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAyEC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,iDAmBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIAmDC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,0DAiBC;IAED,mFA+BC;IAED,yDAiBC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,sCAKC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAoBC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,sCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAv+BD;IASE,gFAKC;IAbD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|
package/script/midy-GM1.js
CHANGED
|
@@ -2,7 +2,57 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MidyGM1 = 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.2/+esm.js");
|
|
6
|
+
class Note {
|
|
7
|
+
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
8
|
+
Object.defineProperty(this, "bufferSource", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: void 0
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(this, "gainNode", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "filterNode", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "modLFO", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "modLFOGain", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "vibLFO", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: void 0
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "vibLFOGain", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: void 0
|
|
49
|
+
});
|
|
50
|
+
this.noteNumber = noteNumber;
|
|
51
|
+
this.velocity = velocity;
|
|
52
|
+
this.startTime = startTime;
|
|
53
|
+
this.instrumentKey = instrumentKey;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
6
56
|
class MidyGM1 {
|
|
7
57
|
constructor(audioContext) {
|
|
8
58
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -154,14 +204,11 @@ class MidyGM1 {
|
|
|
154
204
|
const pannerNode = new StereoPannerNode(audioContext, {
|
|
155
205
|
pan: MidyGM1.channelSettings.pan,
|
|
156
206
|
});
|
|
157
|
-
const modulationEffect = this.createModulationEffect(audioContext);
|
|
158
|
-
modulationEffect.lfo.start();
|
|
159
207
|
pannerNode.connect(gainNode);
|
|
160
208
|
gainNode.connect(this.masterGain);
|
|
161
209
|
return {
|
|
162
210
|
gainNode,
|
|
163
211
|
pannerNode,
|
|
164
|
-
modulationEffect,
|
|
165
212
|
};
|
|
166
213
|
}
|
|
167
214
|
createChannels(audioContext) {
|
|
@@ -171,16 +218,15 @@ class MidyGM1 {
|
|
|
171
218
|
...MidyGM1.effectSettings,
|
|
172
219
|
...this.setChannelAudioNodes(audioContext),
|
|
173
220
|
scheduledNotes: new Map(),
|
|
174
|
-
sostenutoNotes: new Map(),
|
|
175
221
|
};
|
|
176
222
|
});
|
|
177
223
|
return channels;
|
|
178
224
|
}
|
|
179
|
-
async createNoteBuffer(
|
|
180
|
-
const sampleEnd =
|
|
225
|
+
async createNoteBuffer(instrumentKey, isSF3) {
|
|
226
|
+
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
181
227
|
if (isSF3) {
|
|
182
|
-
const sample = new Uint8Array(
|
|
183
|
-
sample.set(
|
|
228
|
+
const sample = new Uint8Array(instrumentKey.sample.length);
|
|
229
|
+
sample.set(instrumentKey.sample);
|
|
184
230
|
const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
|
|
185
231
|
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
|
|
186
232
|
const channelData = audioBuffer.getChannelData(channel);
|
|
@@ -189,26 +235,27 @@ class MidyGM1 {
|
|
|
189
235
|
return audioBuffer;
|
|
190
236
|
}
|
|
191
237
|
else {
|
|
192
|
-
const sample =
|
|
238
|
+
const sample = instrumentKey.sample.subarray(0, sampleEnd);
|
|
193
239
|
const floatSample = this.convertToFloat32Array(sample);
|
|
194
240
|
const audioBuffer = new AudioBuffer({
|
|
195
241
|
numberOfChannels: 1,
|
|
196
242
|
length: sample.length,
|
|
197
|
-
sampleRate:
|
|
243
|
+
sampleRate: instrumentKey.sampleRate,
|
|
198
244
|
});
|
|
199
245
|
const channelData = audioBuffer.getChannelData(0);
|
|
200
246
|
channelData.set(floatSample);
|
|
201
247
|
return audioBuffer;
|
|
202
248
|
}
|
|
203
249
|
}
|
|
204
|
-
async createNoteBufferNode(
|
|
250
|
+
async createNoteBufferNode(instrumentKey, isSF3) {
|
|
205
251
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
206
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
252
|
+
const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
|
|
207
253
|
bufferSource.buffer = audioBuffer;
|
|
208
|
-
bufferSource.loop =
|
|
254
|
+
bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
|
|
209
255
|
if (bufferSource.loop) {
|
|
210
|
-
bufferSource.loopStart =
|
|
211
|
-
|
|
256
|
+
bufferSource.loopStart = instrumentKey.loopStart /
|
|
257
|
+
instrumentKey.sampleRate;
|
|
258
|
+
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
212
259
|
}
|
|
213
260
|
return bufferSource;
|
|
214
261
|
}
|
|
@@ -450,30 +497,26 @@ class MidyGM1 {
|
|
|
450
497
|
const now = this.audioContext.currentTime;
|
|
451
498
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
452
499
|
}
|
|
453
|
-
getActiveNotes(channel) {
|
|
500
|
+
getActiveNotes(channel, time) {
|
|
454
501
|
const activeNotes = new Map();
|
|
455
|
-
channel.scheduledNotes.forEach((
|
|
456
|
-
const activeNote = this.
|
|
502
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
503
|
+
const activeNote = this.getActiveNote(noteList, time);
|
|
457
504
|
if (activeNote) {
|
|
458
505
|
activeNotes.set(activeNote.noteNumber, activeNote);
|
|
459
506
|
}
|
|
460
507
|
});
|
|
461
508
|
return activeNotes;
|
|
462
509
|
}
|
|
463
|
-
|
|
464
|
-
for (let i =
|
|
465
|
-
const
|
|
466
|
-
if (
|
|
467
|
-
return
|
|
510
|
+
getActiveNote(noteList, time) {
|
|
511
|
+
for (let i = noteList.length - 1; i >= 0; i--) {
|
|
512
|
+
const note = noteList[i];
|
|
513
|
+
if (!note)
|
|
514
|
+
return;
|
|
515
|
+
if (time < note.startTime)
|
|
516
|
+
continue;
|
|
517
|
+
return (note.ending) ? null : note;
|
|
468
518
|
}
|
|
469
|
-
|
|
470
|
-
createModulationEffect(audioContext) {
|
|
471
|
-
const lfo = new OscillatorNode(audioContext, {
|
|
472
|
-
frequency: 5,
|
|
473
|
-
});
|
|
474
|
-
return {
|
|
475
|
-
lfo,
|
|
476
|
-
};
|
|
519
|
+
return noteList[0];
|
|
477
520
|
}
|
|
478
521
|
connectNoteEffects(channel, gainNode) {
|
|
479
522
|
gainNode.connect(channel.pannerNode);
|
|
@@ -488,71 +531,89 @@ class MidyGM1 {
|
|
|
488
531
|
const tuning = channel.coarseTuning + channel.fineTuning;
|
|
489
532
|
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
490
533
|
}
|
|
491
|
-
calcPlaybackRate(
|
|
492
|
-
return
|
|
534
|
+
calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
|
|
535
|
+
return instrumentKey.playbackRate(noteNumber) *
|
|
536
|
+
Math.pow(2, semitoneOffset / 12);
|
|
493
537
|
}
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
498
|
-
// volume envelope
|
|
499
|
-
const gainNode = new GainNode(this.audioContext, {
|
|
538
|
+
setVolumeEnvelope(channel, note) {
|
|
539
|
+
const { instrumentKey, startTime, velocity } = note;
|
|
540
|
+
note.gainNode = new GainNode(this.audioContext, {
|
|
500
541
|
gain: 0,
|
|
501
542
|
});
|
|
502
543
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
503
544
|
if (volume === 0)
|
|
504
545
|
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
505
|
-
const attackVolume = this.cbToRatio(-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
const
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
546
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
547
|
+
volume;
|
|
548
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
549
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
550
|
+
const volAttack = volDelay + instrumentKey.volAttack;
|
|
551
|
+
const volHold = volAttack + instrumentKey.volHold;
|
|
552
|
+
const volDecay = volHold + instrumentKey.volDecay;
|
|
553
|
+
note.gainNode.gain
|
|
512
554
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
513
555
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
514
556
|
.setValueAtTime(attackVolume, volHold)
|
|
515
557
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
516
|
-
|
|
558
|
+
}
|
|
559
|
+
setFilterEnvelope(channel, note) {
|
|
560
|
+
const { instrumentKey, startTime, noteNumber } = note;
|
|
561
|
+
const softPedalFactor = 1 -
|
|
562
|
+
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
517
563
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
518
|
-
const baseFreq = this.centToHz(
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
564
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
565
|
+
softPedalFactor;
|
|
566
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
567
|
+
const sustainFreq = (baseFreq +
|
|
568
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
|
|
569
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
570
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
571
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
572
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
522
573
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
523
574
|
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
524
575
|
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
525
|
-
|
|
576
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
526
577
|
type: "lowpass",
|
|
527
|
-
Q:
|
|
578
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
528
579
|
frequency: adjustedBaseFreq,
|
|
529
580
|
});
|
|
530
|
-
|
|
531
|
-
const modAttack = modDelay + noteInfo.modAttack;
|
|
532
|
-
const modHold = modAttack + noteInfo.modHold;
|
|
533
|
-
const modDecay = modHold + noteInfo.modDecay;
|
|
534
|
-
filterNode.frequency
|
|
581
|
+
note.filterNode.frequency
|
|
535
582
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
536
583
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
537
584
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
538
585
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
539
|
-
|
|
586
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
587
|
+
}
|
|
588
|
+
startModulation(channel, note, time) {
|
|
589
|
+
const { instrumentKey } = note;
|
|
590
|
+
note.modLFOGain = new GainNode(this.audioContext, {
|
|
591
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
|
|
592
|
+
});
|
|
593
|
+
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
594
|
+
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
595
|
+
});
|
|
596
|
+
note.modLFO.start(time);
|
|
597
|
+
note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
|
|
598
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
|
|
599
|
+
note.modLFO.connect(note.modLFOGain);
|
|
600
|
+
note.modLFOGain.connect(note.bufferSource.detune);
|
|
601
|
+
}
|
|
602
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
603
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
604
|
+
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
605
|
+
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
606
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
607
|
+
this.setVolumeEnvelope(channel, note);
|
|
608
|
+
this.setFilterEnvelope(channel, note);
|
|
540
609
|
if (channel.modulation > 0) {
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
lfoGain = new GainNode(this.audioContext, {
|
|
544
|
-
gain: 0,
|
|
545
|
-
});
|
|
546
|
-
lfoGain.gain
|
|
547
|
-
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
548
|
-
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
549
|
-
channel.modulationEffect.lfo.connect(lfoGain);
|
|
550
|
-
lfoGain.connect(bufferSource.detune);
|
|
610
|
+
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
611
|
+
this.startModulation(channel, note, delayModLFO);
|
|
551
612
|
}
|
|
552
|
-
bufferSource.connect(filterNode);
|
|
553
|
-
filterNode.connect(gainNode);
|
|
554
|
-
bufferSource.start(startTime,
|
|
555
|
-
return
|
|
613
|
+
note.bufferSource.connect(note.filterNode);
|
|
614
|
+
note.filterNode.connect(note.gainNode);
|
|
615
|
+
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
616
|
+
return note;
|
|
556
617
|
}
|
|
557
618
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
558
619
|
const channel = this.channels[channelNumber];
|
|
@@ -562,27 +623,17 @@ class MidyGM1 {
|
|
|
562
623
|
return;
|
|
563
624
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
564
625
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
565
|
-
const
|
|
566
|
-
if (!
|
|
626
|
+
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
627
|
+
if (!instrumentKey)
|
|
567
628
|
return;
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
this.connectNoteEffects(channel, gainNode);
|
|
629
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
630
|
+
this.connectNoteEffects(channel, note.gainNode);
|
|
571
631
|
const scheduledNotes = channel.scheduledNotes;
|
|
572
|
-
const scheduledNote = {
|
|
573
|
-
bufferSource,
|
|
574
|
-
filterNode,
|
|
575
|
-
gainNode,
|
|
576
|
-
lfoGain,
|
|
577
|
-
noteInfo,
|
|
578
|
-
noteNumber,
|
|
579
|
-
startTime,
|
|
580
|
-
};
|
|
581
632
|
if (scheduledNotes.has(noteNumber)) {
|
|
582
|
-
scheduledNotes.get(noteNumber).push(
|
|
633
|
+
scheduledNotes.get(noteNumber).push(note);
|
|
583
634
|
}
|
|
584
635
|
else {
|
|
585
|
-
scheduledNotes.set(noteNumber, [
|
|
636
|
+
scheduledNotes.set(noteNumber, [note]);
|
|
586
637
|
}
|
|
587
638
|
}
|
|
588
639
|
noteOn(channelNumber, noteNumber, velocity) {
|
|
@@ -602,15 +653,15 @@ class MidyGM1 {
|
|
|
602
653
|
continue;
|
|
603
654
|
if (targetNote.ending)
|
|
604
655
|
continue;
|
|
605
|
-
const { bufferSource, filterNode, gainNode,
|
|
656
|
+
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
|
|
606
657
|
const velocityRate = (velocity + 127) / 127;
|
|
607
|
-
const volEndTime = stopTime +
|
|
658
|
+
const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
|
|
608
659
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
609
660
|
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
610
661
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
611
|
-
const baseFreq = this.centToHz(
|
|
662
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
612
663
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
613
|
-
const modEndTime = stopTime +
|
|
664
|
+
const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
|
|
614
665
|
filterNode.frequency
|
|
615
666
|
.cancelScheduledValues(stopTime)
|
|
616
667
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
@@ -624,8 +675,10 @@ class MidyGM1 {
|
|
|
624
675
|
bufferSource.disconnect(0);
|
|
625
676
|
filterNode.disconnect(0);
|
|
626
677
|
gainNode.disconnect(0);
|
|
627
|
-
if (
|
|
628
|
-
|
|
678
|
+
if (modLFOGain)
|
|
679
|
+
modLFOGain.disconnect(0);
|
|
680
|
+
if (modLFO)
|
|
681
|
+
modLFO.stop();
|
|
629
682
|
resolve();
|
|
630
683
|
};
|
|
631
684
|
bufferSource.stop(volEndTime);
|
|
@@ -683,10 +736,10 @@ class MidyGM1 {
|
|
|
683
736
|
const channel = this.channels[channelNumber];
|
|
684
737
|
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
685
738
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
686
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
739
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
687
740
|
activeNotes.forEach((activeNote) => {
|
|
688
|
-
const { bufferSource,
|
|
689
|
-
const playbackRate = calcPlaybackRate(
|
|
741
|
+
const { bufferSource, instrumentKey, noteNumber } = activeNote;
|
|
742
|
+
const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
690
743
|
bufferSource.playbackRate
|
|
691
744
|
.cancelScheduledValues(now)
|
|
692
745
|
.setValueAtTime(playbackRate * pressure, now);
|
|
@@ -723,9 +776,20 @@ class MidyGM1 {
|
|
|
723
776
|
}
|
|
724
777
|
}
|
|
725
778
|
setModulation(channelNumber, modulation) {
|
|
779
|
+
const now = this.audioContext.currentTime;
|
|
726
780
|
const channel = this.channels[channelNumber];
|
|
727
781
|
channel.modulation = (modulation / 127) *
|
|
728
782
|
(channel.modulationDepthRange * 100);
|
|
783
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
784
|
+
activeNotes.forEach((activeNote) => {
|
|
785
|
+
if (activeNote.modLFO) {
|
|
786
|
+
activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
|
|
787
|
+
channel.modulation, now);
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
this.startModulation(channel, activeNote, now);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
729
793
|
}
|
|
730
794
|
setVolume(channelNumber, volume) {
|
|
731
795
|
const channel = this.channels[channelNumber];
|
|
@@ -788,8 +852,8 @@ class MidyGM1 {
|
|
|
788
852
|
const velocity = 0;
|
|
789
853
|
const stopPedal = true;
|
|
790
854
|
const promises = [];
|
|
791
|
-
channel.scheduledNotes.forEach((
|
|
792
|
-
const activeNote = this.
|
|
855
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
856
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
793
857
|
if (activeNote) {
|
|
794
858
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
795
859
|
promises.push(notePromise);
|
|
@@ -806,8 +870,8 @@ class MidyGM1 {
|
|
|
806
870
|
const velocity = 0;
|
|
807
871
|
const stopPedal = false;
|
|
808
872
|
const promises = [];
|
|
809
|
-
channel.scheduledNotes.forEach((
|
|
810
|
-
const activeNote = this.
|
|
873
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
874
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
811
875
|
if (activeNote) {
|
|
812
876
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
813
877
|
promises.push(notePromise);
|
|
@@ -902,9 +966,6 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
902
966
|
value: {
|
|
903
967
|
volume: 100 / 127,
|
|
904
968
|
pan: 0,
|
|
905
|
-
vibratoRate: 5,
|
|
906
|
-
vibratoDepth: 0.5,
|
|
907
|
-
vibratoDelay: 2.5,
|
|
908
969
|
bank: 0,
|
|
909
970
|
dataMSB: 0,
|
|
910
971
|
dataLSB: 0,
|