@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/esm/midy-GM2.d.ts
CHANGED
|
@@ -6,9 +6,6 @@ export class MidyGM2 {
|
|
|
6
6
|
portamentoTime: number;
|
|
7
7
|
reverb: number;
|
|
8
8
|
chorus: number;
|
|
9
|
-
vibratoRate: number;
|
|
10
|
-
vibratoDepth: number;
|
|
11
|
-
vibratoDelay: number;
|
|
12
9
|
bank: number;
|
|
13
10
|
bankMSB: number;
|
|
14
11
|
bankLSB: number;
|
|
@@ -65,7 +62,56 @@ export class MidyGM2 {
|
|
|
65
62
|
notePromises: any[];
|
|
66
63
|
audioContext: any;
|
|
67
64
|
masterGain: any;
|
|
68
|
-
channels:
|
|
65
|
+
channels: {
|
|
66
|
+
scheduledNotes: Map<any, any>;
|
|
67
|
+
sostenutoNotes: Map<any, any>;
|
|
68
|
+
channelPressure: {
|
|
69
|
+
pitchControl: number;
|
|
70
|
+
filterCutoffControl: number;
|
|
71
|
+
amplitudeControl: number;
|
|
72
|
+
lfoPitchDepth: number;
|
|
73
|
+
lfoFilterDepth: number;
|
|
74
|
+
lfoAmplitudeDepth: number;
|
|
75
|
+
};
|
|
76
|
+
gainNode: any;
|
|
77
|
+
pannerNode: any;
|
|
78
|
+
reverbEffect: {
|
|
79
|
+
convolverNode: any;
|
|
80
|
+
dryGain: any;
|
|
81
|
+
wetGain: any;
|
|
82
|
+
};
|
|
83
|
+
chorusEffect: {
|
|
84
|
+
lfo: any;
|
|
85
|
+
lfoGain: any;
|
|
86
|
+
delayNodes: any[];
|
|
87
|
+
chorusGains: any[];
|
|
88
|
+
};
|
|
89
|
+
expression: number;
|
|
90
|
+
modulation: number;
|
|
91
|
+
sustainPedal: boolean;
|
|
92
|
+
portamento: boolean;
|
|
93
|
+
sostenutoPedal: boolean;
|
|
94
|
+
softPedal: number;
|
|
95
|
+
rpnMSB: number;
|
|
96
|
+
rpnLSB: number;
|
|
97
|
+
pitchBendRange: number;
|
|
98
|
+
currentBufferSource: null;
|
|
99
|
+
volume: number;
|
|
100
|
+
pan: number;
|
|
101
|
+
portamentoTime: number;
|
|
102
|
+
reverb: number;
|
|
103
|
+
chorus: number;
|
|
104
|
+
bank: number;
|
|
105
|
+
bankMSB: number;
|
|
106
|
+
bankLSB: number;
|
|
107
|
+
dataMSB: number;
|
|
108
|
+
dataLSB: number;
|
|
109
|
+
program: number;
|
|
110
|
+
pitchBend: number;
|
|
111
|
+
fineTuning: number;
|
|
112
|
+
coarseTuning: number;
|
|
113
|
+
modulationDepthRange: number;
|
|
114
|
+
}[];
|
|
69
115
|
initSoundFontTable(): any[];
|
|
70
116
|
addSoundFont(soundFont: any): void;
|
|
71
117
|
loadSoundFont(soundFontUrl: any): Promise<void>;
|
|
@@ -73,9 +119,31 @@ export class MidyGM2 {
|
|
|
73
119
|
setChannelAudioNodes(audioContext: any): {
|
|
74
120
|
gainNode: any;
|
|
75
121
|
pannerNode: any;
|
|
76
|
-
|
|
122
|
+
reverbEffect: {
|
|
123
|
+
convolverNode: any;
|
|
124
|
+
dryGain: any;
|
|
125
|
+
wetGain: any;
|
|
126
|
+
};
|
|
127
|
+
chorusEffect: {
|
|
77
128
|
lfo: any;
|
|
129
|
+
lfoGain: any;
|
|
130
|
+
delayNodes: any[];
|
|
131
|
+
chorusGains: any[];
|
|
78
132
|
};
|
|
133
|
+
};
|
|
134
|
+
createChannels(audioContext: any): {
|
|
135
|
+
scheduledNotes: Map<any, any>;
|
|
136
|
+
sostenutoNotes: Map<any, any>;
|
|
137
|
+
channelPressure: {
|
|
138
|
+
pitchControl: number;
|
|
139
|
+
filterCutoffControl: number;
|
|
140
|
+
amplitudeControl: number;
|
|
141
|
+
lfoPitchDepth: number;
|
|
142
|
+
lfoFilterDepth: number;
|
|
143
|
+
lfoAmplitudeDepth: number;
|
|
144
|
+
};
|
|
145
|
+
gainNode: any;
|
|
146
|
+
pannerNode: any;
|
|
79
147
|
reverbEffect: {
|
|
80
148
|
convolverNode: any;
|
|
81
149
|
dryGain: any;
|
|
@@ -87,10 +155,34 @@ export class MidyGM2 {
|
|
|
87
155
|
delayNodes: any[];
|
|
88
156
|
chorusGains: any[];
|
|
89
157
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
158
|
+
expression: number;
|
|
159
|
+
modulation: number;
|
|
160
|
+
sustainPedal: boolean;
|
|
161
|
+
portamento: boolean;
|
|
162
|
+
sostenutoPedal: boolean;
|
|
163
|
+
softPedal: number;
|
|
164
|
+
rpnMSB: number;
|
|
165
|
+
rpnLSB: number;
|
|
166
|
+
pitchBendRange: number;
|
|
167
|
+
currentBufferSource: null;
|
|
168
|
+
volume: number;
|
|
169
|
+
pan: number;
|
|
170
|
+
portamentoTime: number;
|
|
171
|
+
reverb: number;
|
|
172
|
+
chorus: number;
|
|
173
|
+
bank: number;
|
|
174
|
+
bankMSB: number;
|
|
175
|
+
bankLSB: number;
|
|
176
|
+
dataMSB: number;
|
|
177
|
+
dataLSB: number;
|
|
178
|
+
program: number;
|
|
179
|
+
pitchBend: number;
|
|
180
|
+
fineTuning: number;
|
|
181
|
+
coarseTuning: number;
|
|
182
|
+
modulationDepthRange: number;
|
|
183
|
+
}[];
|
|
184
|
+
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
185
|
+
createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
|
|
94
186
|
convertToFloat32Array(uint8Array: any): Float32Array;
|
|
95
187
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
96
188
|
getQueueIndex(second: any): number;
|
|
@@ -109,11 +201,8 @@ export class MidyGM2 {
|
|
|
109
201
|
seekTo(second: any): void;
|
|
110
202
|
calcTotalTime(): number;
|
|
111
203
|
currentTime(): number;
|
|
112
|
-
getActiveNotes(channel: any): Map<any, any>;
|
|
113
|
-
|
|
114
|
-
createModulationEffect(audioContext: any): {
|
|
115
|
-
lfo: any;
|
|
116
|
-
};
|
|
204
|
+
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
205
|
+
getActiveNote(noteList: any, time: any): any;
|
|
117
206
|
createReverbEffect(audioContext: any, options?: {}): {
|
|
118
207
|
convolverNode: any;
|
|
119
208
|
dryGain: any;
|
|
@@ -129,13 +218,11 @@ export class MidyGM2 {
|
|
|
129
218
|
cbToRatio(cb: any): number;
|
|
130
219
|
centToHz(cent: any): number;
|
|
131
220
|
calcSemitoneOffset(channel: any): any;
|
|
132
|
-
calcPlaybackRate(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
lfoGain: any;
|
|
138
|
-
}>;
|
|
221
|
+
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
222
|
+
setVolumeEnvelope(channel: any, note: any): void;
|
|
223
|
+
setFilterEnvelope(channel: any, note: any): void;
|
|
224
|
+
startModulation(channel: any, note: any, time: any): void;
|
|
225
|
+
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
139
226
|
calcBank(channel: any, channelNumber: any): any;
|
|
140
227
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
141
228
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
@@ -187,4 +274,19 @@ export class MidyGM2 {
|
|
|
187
274
|
handleSysEx(data: any): void;
|
|
188
275
|
scheduleTask(callback: any, startTime: any): Promise<any>;
|
|
189
276
|
}
|
|
277
|
+
declare class Note {
|
|
278
|
+
constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
|
|
279
|
+
bufferSource: any;
|
|
280
|
+
gainNode: any;
|
|
281
|
+
filterNode: any;
|
|
282
|
+
modLFO: any;
|
|
283
|
+
modLFOGain: any;
|
|
284
|
+
vibLFO: any;
|
|
285
|
+
vibLFOGain: any;
|
|
286
|
+
noteNumber: any;
|
|
287
|
+
velocity: any;
|
|
288
|
+
startTime: any;
|
|
289
|
+
instrumentKey: any;
|
|
290
|
+
}
|
|
291
|
+
export {};
|
|
190
292
|
//# 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":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;MAiBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAtED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,cAAa;IACb,cAAa;IACb,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;IA4ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAoBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAcC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAkDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA2GC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAmBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHAiCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAoDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAmBC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,0DAiBC;IAED,mFAuDC;IAED,+CAEC;IAED,yDAiBC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAUC;IAED,uDAGC;IAED,gDAEC;IAED,gDAEC;IAED,+DAuBC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,sCAQC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAv3CD;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/esm/midy-GM2.js
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
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.2/+esm.js";
|
|
3
|
+
class Note {
|
|
4
|
+
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
5
|
+
Object.defineProperty(this, "bufferSource", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "gainNode", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "filterNode", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "modLFO", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: void 0
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "modLFOGain", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "vibLFO", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: void 0
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(this, "vibLFOGain", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: void 0
|
|
46
|
+
});
|
|
47
|
+
this.noteNumber = noteNumber;
|
|
48
|
+
this.velocity = velocity;
|
|
49
|
+
this.startTime = startTime;
|
|
50
|
+
this.instrumentKey = instrumentKey;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
3
53
|
export class MidyGM2 {
|
|
4
54
|
constructor(audioContext) {
|
|
5
55
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -181,10 +231,8 @@ export class MidyGM2 {
|
|
|
181
231
|
const pannerNode = new StereoPannerNode(audioContext, {
|
|
182
232
|
pan: MidyGM2.channelSettings.pan,
|
|
183
233
|
});
|
|
184
|
-
const modulationEffect = this.createModulationEffect(audioContext);
|
|
185
234
|
const reverbEffect = this.createReverbEffect(audioContext);
|
|
186
235
|
const chorusEffect = this.createChorusEffect(audioContext);
|
|
187
|
-
modulationEffect.lfo.start();
|
|
188
236
|
chorusEffect.lfo.start();
|
|
189
237
|
reverbEffect.dryGain.connect(pannerNode);
|
|
190
238
|
reverbEffect.wetGain.connect(pannerNode);
|
|
@@ -193,7 +241,6 @@ export class MidyGM2 {
|
|
|
193
241
|
return {
|
|
194
242
|
gainNode,
|
|
195
243
|
pannerNode,
|
|
196
|
-
modulationEffect,
|
|
197
244
|
reverbEffect,
|
|
198
245
|
chorusEffect,
|
|
199
246
|
};
|
|
@@ -201,23 +248,23 @@ export class MidyGM2 {
|
|
|
201
248
|
createChannels(audioContext) {
|
|
202
249
|
const channels = Array.from({ length: 16 }, () => {
|
|
203
250
|
return {
|
|
204
|
-
...
|
|
205
|
-
...
|
|
251
|
+
...MidyGM2.channelSettings,
|
|
252
|
+
...MidyGM2.effectSettings,
|
|
206
253
|
...this.setChannelAudioNodes(audioContext),
|
|
207
254
|
scheduledNotes: new Map(),
|
|
208
255
|
sostenutoNotes: new Map(),
|
|
209
256
|
channelPressure: {
|
|
210
|
-
...
|
|
257
|
+
...MidyGM2.controllerDestinationSettings,
|
|
211
258
|
},
|
|
212
259
|
};
|
|
213
260
|
});
|
|
214
261
|
return channels;
|
|
215
262
|
}
|
|
216
|
-
async createNoteBuffer(
|
|
217
|
-
const sampleEnd =
|
|
263
|
+
async createNoteBuffer(instrumentKey, isSF3) {
|
|
264
|
+
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
218
265
|
if (isSF3) {
|
|
219
|
-
const sample = new Uint8Array(
|
|
220
|
-
sample.set(
|
|
266
|
+
const sample = new Uint8Array(instrumentKey.sample.length);
|
|
267
|
+
sample.set(instrumentKey.sample);
|
|
221
268
|
const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
|
|
222
269
|
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
|
|
223
270
|
const channelData = audioBuffer.getChannelData(channel);
|
|
@@ -226,26 +273,27 @@ export class MidyGM2 {
|
|
|
226
273
|
return audioBuffer;
|
|
227
274
|
}
|
|
228
275
|
else {
|
|
229
|
-
const sample =
|
|
276
|
+
const sample = instrumentKey.sample.subarray(0, sampleEnd);
|
|
230
277
|
const floatSample = this.convertToFloat32Array(sample);
|
|
231
278
|
const audioBuffer = new AudioBuffer({
|
|
232
279
|
numberOfChannels: 1,
|
|
233
280
|
length: sample.length,
|
|
234
|
-
sampleRate:
|
|
281
|
+
sampleRate: instrumentKey.sampleRate,
|
|
235
282
|
});
|
|
236
283
|
const channelData = audioBuffer.getChannelData(0);
|
|
237
284
|
channelData.set(floatSample);
|
|
238
285
|
return audioBuffer;
|
|
239
286
|
}
|
|
240
287
|
}
|
|
241
|
-
async createNoteBufferNode(
|
|
288
|
+
async createNoteBufferNode(instrumentKey, isSF3) {
|
|
242
289
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
243
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
290
|
+
const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
|
|
244
291
|
bufferSource.buffer = audioBuffer;
|
|
245
|
-
bufferSource.loop =
|
|
292
|
+
bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
|
|
246
293
|
if (bufferSource.loop) {
|
|
247
|
-
bufferSource.loopStart =
|
|
248
|
-
|
|
294
|
+
bufferSource.loopStart = instrumentKey.loopStart /
|
|
295
|
+
instrumentKey.sampleRate;
|
|
296
|
+
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
249
297
|
}
|
|
250
298
|
return bufferSource;
|
|
251
299
|
}
|
|
@@ -524,30 +572,26 @@ export class MidyGM2 {
|
|
|
524
572
|
const now = this.audioContext.currentTime;
|
|
525
573
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
526
574
|
}
|
|
527
|
-
getActiveNotes(channel) {
|
|
575
|
+
getActiveNotes(channel, time) {
|
|
528
576
|
const activeNotes = new Map();
|
|
529
|
-
channel.scheduledNotes.forEach((
|
|
530
|
-
const activeNote = this.
|
|
577
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
578
|
+
const activeNote = this.getActiveNote(noteList, time);
|
|
531
579
|
if (activeNote) {
|
|
532
580
|
activeNotes.set(activeNote.noteNumber, activeNote);
|
|
533
581
|
}
|
|
534
582
|
});
|
|
535
583
|
return activeNotes;
|
|
536
584
|
}
|
|
537
|
-
|
|
538
|
-
for (let i =
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
541
|
-
return
|
|
585
|
+
getActiveNote(noteList, time) {
|
|
586
|
+
for (let i = noteList.length - 1; i >= 0; i--) {
|
|
587
|
+
const note = noteList[i];
|
|
588
|
+
if (!note)
|
|
589
|
+
return;
|
|
590
|
+
if (time < note.startTime)
|
|
591
|
+
continue;
|
|
592
|
+
return (note.ending) ? null : note;
|
|
542
593
|
}
|
|
543
|
-
|
|
544
|
-
createModulationEffect(audioContext) {
|
|
545
|
-
const lfo = new OscillatorNode(audioContext, {
|
|
546
|
-
frequency: 5,
|
|
547
|
-
});
|
|
548
|
-
return {
|
|
549
|
-
lfo,
|
|
550
|
-
};
|
|
594
|
+
return noteList[0];
|
|
551
595
|
}
|
|
552
596
|
createReverbEffect(audioContext, options = {}) {
|
|
553
597
|
const { decay = 0.8, preDecay = 0, } = options;
|
|
@@ -657,77 +701,93 @@ export class MidyGM2 {
|
|
|
657
701
|
const tuning = masterTuning + channelTuning;
|
|
658
702
|
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
659
703
|
}
|
|
660
|
-
calcPlaybackRate(
|
|
661
|
-
return
|
|
704
|
+
calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
|
|
705
|
+
return instrumentKey.playbackRate(noteNumber) *
|
|
706
|
+
Math.pow(2, semitoneOffset / 12);
|
|
662
707
|
}
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
667
|
-
// volume envelope
|
|
668
|
-
const gainNode = new GainNode(this.audioContext, {
|
|
708
|
+
setVolumeEnvelope(channel, note) {
|
|
709
|
+
const { instrumentKey, startTime, velocity } = note;
|
|
710
|
+
note.gainNode = new GainNode(this.audioContext, {
|
|
669
711
|
gain: 0,
|
|
670
712
|
});
|
|
671
713
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
672
714
|
if (volume === 0)
|
|
673
715
|
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
674
|
-
const attackVolume = this.cbToRatio(-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
const
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
|
|
716
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
717
|
+
volume;
|
|
718
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
719
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
720
|
+
const volAttack = volDelay + instrumentKey.volAttack;
|
|
721
|
+
const volHold = volAttack + instrumentKey.volHold;
|
|
722
|
+
const volDecay = volHold + instrumentKey.volDecay;
|
|
723
|
+
note.gainNode.gain
|
|
681
724
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
682
725
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
683
726
|
.setValueAtTime(attackVolume, volHold)
|
|
684
727
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
685
|
-
|
|
728
|
+
}
|
|
729
|
+
setFilterEnvelope(channel, note) {
|
|
730
|
+
const { instrumentKey, startTime, noteNumber } = note;
|
|
686
731
|
const softPedalFactor = 1 -
|
|
687
732
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
688
733
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
689
|
-
const baseFreq = this.centToHz(
|
|
690
|
-
|
|
734
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
735
|
+
softPedalFactor;
|
|
736
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
691
737
|
const sustainFreq = (baseFreq +
|
|
692
|
-
(peekFreq - baseFreq) * (1 -
|
|
738
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
|
|
739
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
740
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
741
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
742
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
693
743
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
694
744
|
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
695
745
|
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
696
|
-
|
|
746
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
697
747
|
type: "lowpass",
|
|
698
|
-
Q:
|
|
748
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
699
749
|
frequency: adjustedBaseFreq,
|
|
700
750
|
});
|
|
701
|
-
|
|
702
|
-
const modAttack = modDelay + noteInfo.modAttack;
|
|
703
|
-
const modHold = modAttack + noteInfo.modHold;
|
|
704
|
-
const modDecay = modHold + noteInfo.modDecay;
|
|
705
|
-
filterNode.frequency
|
|
751
|
+
note.filterNode.frequency
|
|
706
752
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
707
753
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
708
754
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
709
755
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
710
|
-
|
|
756
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
757
|
+
}
|
|
758
|
+
startModulation(channel, note, time) {
|
|
759
|
+
const { instrumentKey } = note;
|
|
760
|
+
note.modLFOGain = new GainNode(this.audioContext, {
|
|
761
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
|
|
762
|
+
});
|
|
763
|
+
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
764
|
+
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
765
|
+
});
|
|
766
|
+
note.modLFO.start(time);
|
|
767
|
+
note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
|
|
768
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
|
|
769
|
+
note.modLFO.connect(note.modLFOGain);
|
|
770
|
+
note.modLFOGain.connect(note.bufferSource.detune);
|
|
771
|
+
}
|
|
772
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
773
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
774
|
+
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
775
|
+
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
776
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
777
|
+
this.setVolumeEnvelope(channel, note);
|
|
778
|
+
this.setFilterEnvelope(channel, note);
|
|
711
779
|
if (channel.modulation > 0) {
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
lfoGain = new GainNode(this.audioContext, {
|
|
715
|
-
gain: 0,
|
|
716
|
-
});
|
|
717
|
-
lfoGain.gain
|
|
718
|
-
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
719
|
-
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
720
|
-
channel.modulationEffect.lfo.connect(lfoGain);
|
|
721
|
-
lfoGain.connect(bufferSource.detune);
|
|
780
|
+
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
781
|
+
this.startModulation(channel, note, delayModLFO);
|
|
722
782
|
}
|
|
723
|
-
bufferSource.connect(filterNode);
|
|
724
|
-
filterNode.connect(gainNode);
|
|
725
783
|
if (this.mono && channel.currentBufferSource) {
|
|
726
784
|
channel.currentBufferSource.stop(startTime);
|
|
727
|
-
channel.currentBufferSource = bufferSource;
|
|
785
|
+
channel.currentBufferSource = note.bufferSource;
|
|
728
786
|
}
|
|
729
|
-
bufferSource.
|
|
730
|
-
|
|
787
|
+
note.bufferSource.connect(note.filterNode);
|
|
788
|
+
note.filterNode.connect(note.gainNode);
|
|
789
|
+
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
790
|
+
return note;
|
|
731
791
|
}
|
|
732
792
|
calcBank(channel, channelNumber) {
|
|
733
793
|
if (channel.bankMSB === 121) {
|
|
@@ -746,36 +806,20 @@ export class MidyGM2 {
|
|
|
746
806
|
return;
|
|
747
807
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
748
808
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
749
|
-
const
|
|
750
|
-
if (!
|
|
809
|
+
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
810
|
+
if (!instrumentKey)
|
|
751
811
|
return;
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
this.connectNoteEffects(channel, gainNode);
|
|
812
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
813
|
+
this.connectNoteEffects(channel, note.gainNode);
|
|
755
814
|
if (channel.sostenutoPedal) {
|
|
756
|
-
channel.sostenutoNotes.set(noteNumber,
|
|
757
|
-
gainNode,
|
|
758
|
-
filterNode,
|
|
759
|
-
bufferSource,
|
|
760
|
-
noteNumber,
|
|
761
|
-
noteInfo,
|
|
762
|
-
});
|
|
815
|
+
channel.sostenutoNotes.set(noteNumber, note);
|
|
763
816
|
}
|
|
764
817
|
const scheduledNotes = channel.scheduledNotes;
|
|
765
|
-
const scheduledNote = {
|
|
766
|
-
bufferSource,
|
|
767
|
-
filterNode,
|
|
768
|
-
gainNode,
|
|
769
|
-
lfoGain,
|
|
770
|
-
noteInfo,
|
|
771
|
-
noteNumber,
|
|
772
|
-
startTime,
|
|
773
|
-
};
|
|
774
818
|
if (scheduledNotes.has(noteNumber)) {
|
|
775
|
-
scheduledNotes.get(noteNumber).push(
|
|
819
|
+
scheduledNotes.get(noteNumber).push(note);
|
|
776
820
|
}
|
|
777
821
|
else {
|
|
778
|
-
scheduledNotes.set(noteNumber, [
|
|
822
|
+
scheduledNotes.set(noteNumber, [note]);
|
|
779
823
|
}
|
|
780
824
|
}
|
|
781
825
|
noteOn(channelNumber, noteNumber, velocity) {
|
|
@@ -797,15 +841,15 @@ export class MidyGM2 {
|
|
|
797
841
|
continue;
|
|
798
842
|
if (targetNote.ending)
|
|
799
843
|
continue;
|
|
800
|
-
const { bufferSource, filterNode, gainNode,
|
|
844
|
+
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
|
|
801
845
|
const velocityRate = (velocity + 127) / 127;
|
|
802
|
-
const volEndTime = stopTime +
|
|
846
|
+
const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
|
|
803
847
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
804
848
|
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
805
849
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
806
|
-
const baseFreq = this.centToHz(
|
|
850
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
807
851
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
808
|
-
const modEndTime = stopTime +
|
|
852
|
+
const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
|
|
809
853
|
filterNode.frequency
|
|
810
854
|
.cancelScheduledValues(stopTime)
|
|
811
855
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
@@ -819,8 +863,10 @@ export class MidyGM2 {
|
|
|
819
863
|
bufferSource.disconnect(0);
|
|
820
864
|
filterNode.disconnect(0);
|
|
821
865
|
gainNode.disconnect(0);
|
|
822
|
-
if (
|
|
823
|
-
|
|
866
|
+
if (modLFOGain)
|
|
867
|
+
modLFOGain.disconnect(0);
|
|
868
|
+
if (modLFO)
|
|
869
|
+
modLFO.stop();
|
|
824
870
|
resolve();
|
|
825
871
|
};
|
|
826
872
|
bufferSource.stop(volEndTime);
|
|
@@ -890,7 +936,7 @@ export class MidyGM2 {
|
|
|
890
936
|
const channel = this.channels[channelNumber];
|
|
891
937
|
pressure /= 64;
|
|
892
938
|
channel.channelPressure = pressure;
|
|
893
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
939
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
894
940
|
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
895
941
|
activeNotes.forEach((activeNote) => {
|
|
896
942
|
const gain = activeNote.gainNode.gain.value;
|
|
@@ -909,10 +955,10 @@ export class MidyGM2 {
|
|
|
909
955
|
const channel = this.channels[channelNumber];
|
|
910
956
|
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
911
957
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
912
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
958
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
913
959
|
activeNotes.forEach((activeNote) => {
|
|
914
|
-
const { bufferSource,
|
|
915
|
-
const playbackRate = calcPlaybackRate(
|
|
960
|
+
const { bufferSource, instrumentKey, noteNumber } = activeNote;
|
|
961
|
+
const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
916
962
|
bufferSource.playbackRate
|
|
917
963
|
.cancelScheduledValues(now)
|
|
918
964
|
.setValueAtTime(playbackRate * pressure, now);
|
|
@@ -976,9 +1022,20 @@ export class MidyGM2 {
|
|
|
976
1022
|
this.channels[channelNumber].bankMSB = msb;
|
|
977
1023
|
}
|
|
978
1024
|
setModulation(channelNumber, modulation) {
|
|
1025
|
+
const now = this.audioContext.currentTime;
|
|
979
1026
|
const channel = this.channels[channelNumber];
|
|
980
1027
|
channel.modulation = (modulation / 127) *
|
|
981
1028
|
(channel.modulationDepthRange * 100);
|
|
1029
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1030
|
+
activeNotes.forEach((activeNote) => {
|
|
1031
|
+
if (activeNote.modLFO) {
|
|
1032
|
+
activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
|
|
1033
|
+
channel.modulation, now);
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
this.startModulation(channel, activeNote, now);
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
982
1039
|
}
|
|
983
1040
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
984
1041
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1084,8 +1141,8 @@ export class MidyGM2 {
|
|
|
1084
1141
|
const velocity = 0;
|
|
1085
1142
|
const stopPedal = true;
|
|
1086
1143
|
const promises = [];
|
|
1087
|
-
channel.scheduledNotes.forEach((
|
|
1088
|
-
const activeNote = this.
|
|
1144
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1145
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1089
1146
|
if (activeNote) {
|
|
1090
1147
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1091
1148
|
promises.push(notePromise);
|
|
@@ -1102,8 +1159,8 @@ export class MidyGM2 {
|
|
|
1102
1159
|
const velocity = 0;
|
|
1103
1160
|
const stopPedal = false;
|
|
1104
1161
|
const promises = [];
|
|
1105
|
-
channel.scheduledNotes.forEach((
|
|
1106
|
-
const activeNote = this.
|
|
1162
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1163
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1107
1164
|
if (activeNote) {
|
|
1108
1165
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1109
1166
|
promises.push(notePromise);
|
|
@@ -1284,9 +1341,6 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1284
1341
|
portamentoTime: 0,
|
|
1285
1342
|
reverb: 0,
|
|
1286
1343
|
chorus: 0,
|
|
1287
|
-
vibratoRate: 5,
|
|
1288
|
-
vibratoDepth: 0.5,
|
|
1289
|
-
vibratoDelay: 2.5,
|
|
1290
1344
|
bank: 121 * 128,
|
|
1291
1345
|
bankMSB: 121,
|
|
1292
1346
|
bankLSB: 0,
|