@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-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/script/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/script/midy-GM2.js
CHANGED
|
@@ -2,7 +2,57 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MidyGM2 = 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 MidyGM2 {
|
|
7
57
|
constructor(audioContext) {
|
|
8
58
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -184,10 +234,8 @@ class MidyGM2 {
|
|
|
184
234
|
const pannerNode = new StereoPannerNode(audioContext, {
|
|
185
235
|
pan: MidyGM2.channelSettings.pan,
|
|
186
236
|
});
|
|
187
|
-
const modulationEffect = this.createModulationEffect(audioContext);
|
|
188
237
|
const reverbEffect = this.createReverbEffect(audioContext);
|
|
189
238
|
const chorusEffect = this.createChorusEffect(audioContext);
|
|
190
|
-
modulationEffect.lfo.start();
|
|
191
239
|
chorusEffect.lfo.start();
|
|
192
240
|
reverbEffect.dryGain.connect(pannerNode);
|
|
193
241
|
reverbEffect.wetGain.connect(pannerNode);
|
|
@@ -196,7 +244,6 @@ class MidyGM2 {
|
|
|
196
244
|
return {
|
|
197
245
|
gainNode,
|
|
198
246
|
pannerNode,
|
|
199
|
-
modulationEffect,
|
|
200
247
|
reverbEffect,
|
|
201
248
|
chorusEffect,
|
|
202
249
|
};
|
|
@@ -204,23 +251,23 @@ class MidyGM2 {
|
|
|
204
251
|
createChannels(audioContext) {
|
|
205
252
|
const channels = Array.from({ length: 16 }, () => {
|
|
206
253
|
return {
|
|
207
|
-
...
|
|
208
|
-
...
|
|
254
|
+
...MidyGM2.channelSettings,
|
|
255
|
+
...MidyGM2.effectSettings,
|
|
209
256
|
...this.setChannelAudioNodes(audioContext),
|
|
210
257
|
scheduledNotes: new Map(),
|
|
211
258
|
sostenutoNotes: new Map(),
|
|
212
259
|
channelPressure: {
|
|
213
|
-
...
|
|
260
|
+
...MidyGM2.controllerDestinationSettings,
|
|
214
261
|
},
|
|
215
262
|
};
|
|
216
263
|
});
|
|
217
264
|
return channels;
|
|
218
265
|
}
|
|
219
|
-
async createNoteBuffer(
|
|
220
|
-
const sampleEnd =
|
|
266
|
+
async createNoteBuffer(instrumentKey, isSF3) {
|
|
267
|
+
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
221
268
|
if (isSF3) {
|
|
222
|
-
const sample = new Uint8Array(
|
|
223
|
-
sample.set(
|
|
269
|
+
const sample = new Uint8Array(instrumentKey.sample.length);
|
|
270
|
+
sample.set(instrumentKey.sample);
|
|
224
271
|
const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
|
|
225
272
|
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
|
|
226
273
|
const channelData = audioBuffer.getChannelData(channel);
|
|
@@ -229,26 +276,27 @@ class MidyGM2 {
|
|
|
229
276
|
return audioBuffer;
|
|
230
277
|
}
|
|
231
278
|
else {
|
|
232
|
-
const sample =
|
|
279
|
+
const sample = instrumentKey.sample.subarray(0, sampleEnd);
|
|
233
280
|
const floatSample = this.convertToFloat32Array(sample);
|
|
234
281
|
const audioBuffer = new AudioBuffer({
|
|
235
282
|
numberOfChannels: 1,
|
|
236
283
|
length: sample.length,
|
|
237
|
-
sampleRate:
|
|
284
|
+
sampleRate: instrumentKey.sampleRate,
|
|
238
285
|
});
|
|
239
286
|
const channelData = audioBuffer.getChannelData(0);
|
|
240
287
|
channelData.set(floatSample);
|
|
241
288
|
return audioBuffer;
|
|
242
289
|
}
|
|
243
290
|
}
|
|
244
|
-
async createNoteBufferNode(
|
|
291
|
+
async createNoteBufferNode(instrumentKey, isSF3) {
|
|
245
292
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
246
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
293
|
+
const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
|
|
247
294
|
bufferSource.buffer = audioBuffer;
|
|
248
|
-
bufferSource.loop =
|
|
295
|
+
bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
|
|
249
296
|
if (bufferSource.loop) {
|
|
250
|
-
bufferSource.loopStart =
|
|
251
|
-
|
|
297
|
+
bufferSource.loopStart = instrumentKey.loopStart /
|
|
298
|
+
instrumentKey.sampleRate;
|
|
299
|
+
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
252
300
|
}
|
|
253
301
|
return bufferSource;
|
|
254
302
|
}
|
|
@@ -527,30 +575,26 @@ class MidyGM2 {
|
|
|
527
575
|
const now = this.audioContext.currentTime;
|
|
528
576
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
529
577
|
}
|
|
530
|
-
getActiveNotes(channel) {
|
|
578
|
+
getActiveNotes(channel, time) {
|
|
531
579
|
const activeNotes = new Map();
|
|
532
|
-
channel.scheduledNotes.forEach((
|
|
533
|
-
const activeNote = this.
|
|
580
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
581
|
+
const activeNote = this.getActiveNote(noteList, time);
|
|
534
582
|
if (activeNote) {
|
|
535
583
|
activeNotes.set(activeNote.noteNumber, activeNote);
|
|
536
584
|
}
|
|
537
585
|
});
|
|
538
586
|
return activeNotes;
|
|
539
587
|
}
|
|
540
|
-
|
|
541
|
-
for (let i =
|
|
542
|
-
const
|
|
543
|
-
if (
|
|
544
|
-
return
|
|
588
|
+
getActiveNote(noteList, time) {
|
|
589
|
+
for (let i = noteList.length - 1; i >= 0; i--) {
|
|
590
|
+
const note = noteList[i];
|
|
591
|
+
if (!note)
|
|
592
|
+
return;
|
|
593
|
+
if (time < note.startTime)
|
|
594
|
+
continue;
|
|
595
|
+
return (note.ending) ? null : note;
|
|
545
596
|
}
|
|
546
|
-
|
|
547
|
-
createModulationEffect(audioContext) {
|
|
548
|
-
const lfo = new OscillatorNode(audioContext, {
|
|
549
|
-
frequency: 5,
|
|
550
|
-
});
|
|
551
|
-
return {
|
|
552
|
-
lfo,
|
|
553
|
-
};
|
|
597
|
+
return noteList[0];
|
|
554
598
|
}
|
|
555
599
|
createReverbEffect(audioContext, options = {}) {
|
|
556
600
|
const { decay = 0.8, preDecay = 0, } = options;
|
|
@@ -660,77 +704,93 @@ class MidyGM2 {
|
|
|
660
704
|
const tuning = masterTuning + channelTuning;
|
|
661
705
|
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
662
706
|
}
|
|
663
|
-
calcPlaybackRate(
|
|
664
|
-
return
|
|
707
|
+
calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
|
|
708
|
+
return instrumentKey.playbackRate(noteNumber) *
|
|
709
|
+
Math.pow(2, semitoneOffset / 12);
|
|
665
710
|
}
|
|
666
|
-
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
670
|
-
// volume envelope
|
|
671
|
-
const gainNode = new GainNode(this.audioContext, {
|
|
711
|
+
setVolumeEnvelope(channel, note) {
|
|
712
|
+
const { instrumentKey, startTime, velocity } = note;
|
|
713
|
+
note.gainNode = new GainNode(this.audioContext, {
|
|
672
714
|
gain: 0,
|
|
673
715
|
});
|
|
674
716
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
675
717
|
if (volume === 0)
|
|
676
718
|
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
677
|
-
const attackVolume = this.cbToRatio(-
|
|
678
|
-
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
const
|
|
682
|
-
const
|
|
683
|
-
|
|
719
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
720
|
+
volume;
|
|
721
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
722
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
723
|
+
const volAttack = volDelay + instrumentKey.volAttack;
|
|
724
|
+
const volHold = volAttack + instrumentKey.volHold;
|
|
725
|
+
const volDecay = volHold + instrumentKey.volDecay;
|
|
726
|
+
note.gainNode.gain
|
|
684
727
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
685
728
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
686
729
|
.setValueAtTime(attackVolume, volHold)
|
|
687
730
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
688
|
-
|
|
731
|
+
}
|
|
732
|
+
setFilterEnvelope(channel, note) {
|
|
733
|
+
const { instrumentKey, startTime, noteNumber } = note;
|
|
689
734
|
const softPedalFactor = 1 -
|
|
690
735
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
691
736
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
692
|
-
const baseFreq = this.centToHz(
|
|
693
|
-
|
|
737
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
738
|
+
softPedalFactor;
|
|
739
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
694
740
|
const sustainFreq = (baseFreq +
|
|
695
|
-
(peekFreq - baseFreq) * (1 -
|
|
741
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
|
|
742
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
743
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
744
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
745
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
696
746
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
697
747
|
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
698
748
|
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
699
|
-
|
|
749
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
700
750
|
type: "lowpass",
|
|
701
|
-
Q:
|
|
751
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
702
752
|
frequency: adjustedBaseFreq,
|
|
703
753
|
});
|
|
704
|
-
|
|
705
|
-
const modAttack = modDelay + noteInfo.modAttack;
|
|
706
|
-
const modHold = modAttack + noteInfo.modHold;
|
|
707
|
-
const modDecay = modHold + noteInfo.modDecay;
|
|
708
|
-
filterNode.frequency
|
|
754
|
+
note.filterNode.frequency
|
|
709
755
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
710
756
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
711
757
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
712
758
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
713
|
-
|
|
759
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
760
|
+
}
|
|
761
|
+
startModulation(channel, note, time) {
|
|
762
|
+
const { instrumentKey } = note;
|
|
763
|
+
note.modLFOGain = new GainNode(this.audioContext, {
|
|
764
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
|
|
765
|
+
});
|
|
766
|
+
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
767
|
+
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
768
|
+
});
|
|
769
|
+
note.modLFO.start(time);
|
|
770
|
+
note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
|
|
771
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
|
|
772
|
+
note.modLFO.connect(note.modLFOGain);
|
|
773
|
+
note.modLFOGain.connect(note.bufferSource.detune);
|
|
774
|
+
}
|
|
775
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
776
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
777
|
+
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
778
|
+
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
779
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
780
|
+
this.setVolumeEnvelope(channel, note);
|
|
781
|
+
this.setFilterEnvelope(channel, note);
|
|
714
782
|
if (channel.modulation > 0) {
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
lfoGain = new GainNode(this.audioContext, {
|
|
718
|
-
gain: 0,
|
|
719
|
-
});
|
|
720
|
-
lfoGain.gain
|
|
721
|
-
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
722
|
-
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
723
|
-
channel.modulationEffect.lfo.connect(lfoGain);
|
|
724
|
-
lfoGain.connect(bufferSource.detune);
|
|
783
|
+
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
784
|
+
this.startModulation(channel, note, delayModLFO);
|
|
725
785
|
}
|
|
726
|
-
bufferSource.connect(filterNode);
|
|
727
|
-
filterNode.connect(gainNode);
|
|
728
786
|
if (this.mono && channel.currentBufferSource) {
|
|
729
787
|
channel.currentBufferSource.stop(startTime);
|
|
730
|
-
channel.currentBufferSource = bufferSource;
|
|
788
|
+
channel.currentBufferSource = note.bufferSource;
|
|
731
789
|
}
|
|
732
|
-
bufferSource.
|
|
733
|
-
|
|
790
|
+
note.bufferSource.connect(note.filterNode);
|
|
791
|
+
note.filterNode.connect(note.gainNode);
|
|
792
|
+
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
793
|
+
return note;
|
|
734
794
|
}
|
|
735
795
|
calcBank(channel, channelNumber) {
|
|
736
796
|
if (channel.bankMSB === 121) {
|
|
@@ -749,36 +809,20 @@ class MidyGM2 {
|
|
|
749
809
|
return;
|
|
750
810
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
751
811
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
752
|
-
const
|
|
753
|
-
if (!
|
|
812
|
+
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
813
|
+
if (!instrumentKey)
|
|
754
814
|
return;
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
this.connectNoteEffects(channel, gainNode);
|
|
815
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
816
|
+
this.connectNoteEffects(channel, note.gainNode);
|
|
758
817
|
if (channel.sostenutoPedal) {
|
|
759
|
-
channel.sostenutoNotes.set(noteNumber,
|
|
760
|
-
gainNode,
|
|
761
|
-
filterNode,
|
|
762
|
-
bufferSource,
|
|
763
|
-
noteNumber,
|
|
764
|
-
noteInfo,
|
|
765
|
-
});
|
|
818
|
+
channel.sostenutoNotes.set(noteNumber, note);
|
|
766
819
|
}
|
|
767
820
|
const scheduledNotes = channel.scheduledNotes;
|
|
768
|
-
const scheduledNote = {
|
|
769
|
-
bufferSource,
|
|
770
|
-
filterNode,
|
|
771
|
-
gainNode,
|
|
772
|
-
lfoGain,
|
|
773
|
-
noteInfo,
|
|
774
|
-
noteNumber,
|
|
775
|
-
startTime,
|
|
776
|
-
};
|
|
777
821
|
if (scheduledNotes.has(noteNumber)) {
|
|
778
|
-
scheduledNotes.get(noteNumber).push(
|
|
822
|
+
scheduledNotes.get(noteNumber).push(note);
|
|
779
823
|
}
|
|
780
824
|
else {
|
|
781
|
-
scheduledNotes.set(noteNumber, [
|
|
825
|
+
scheduledNotes.set(noteNumber, [note]);
|
|
782
826
|
}
|
|
783
827
|
}
|
|
784
828
|
noteOn(channelNumber, noteNumber, velocity) {
|
|
@@ -800,15 +844,15 @@ class MidyGM2 {
|
|
|
800
844
|
continue;
|
|
801
845
|
if (targetNote.ending)
|
|
802
846
|
continue;
|
|
803
|
-
const { bufferSource, filterNode, gainNode,
|
|
847
|
+
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
|
|
804
848
|
const velocityRate = (velocity + 127) / 127;
|
|
805
|
-
const volEndTime = stopTime +
|
|
849
|
+
const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
|
|
806
850
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
807
851
|
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
808
852
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
809
|
-
const baseFreq = this.centToHz(
|
|
853
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
810
854
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
811
|
-
const modEndTime = stopTime +
|
|
855
|
+
const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
|
|
812
856
|
filterNode.frequency
|
|
813
857
|
.cancelScheduledValues(stopTime)
|
|
814
858
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
@@ -822,8 +866,10 @@ class MidyGM2 {
|
|
|
822
866
|
bufferSource.disconnect(0);
|
|
823
867
|
filterNode.disconnect(0);
|
|
824
868
|
gainNode.disconnect(0);
|
|
825
|
-
if (
|
|
826
|
-
|
|
869
|
+
if (modLFOGain)
|
|
870
|
+
modLFOGain.disconnect(0);
|
|
871
|
+
if (modLFO)
|
|
872
|
+
modLFO.stop();
|
|
827
873
|
resolve();
|
|
828
874
|
};
|
|
829
875
|
bufferSource.stop(volEndTime);
|
|
@@ -893,7 +939,7 @@ class MidyGM2 {
|
|
|
893
939
|
const channel = this.channels[channelNumber];
|
|
894
940
|
pressure /= 64;
|
|
895
941
|
channel.channelPressure = pressure;
|
|
896
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
942
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
897
943
|
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
898
944
|
activeNotes.forEach((activeNote) => {
|
|
899
945
|
const gain = activeNote.gainNode.gain.value;
|
|
@@ -912,10 +958,10 @@ class MidyGM2 {
|
|
|
912
958
|
const channel = this.channels[channelNumber];
|
|
913
959
|
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
914
960
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
915
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
961
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
916
962
|
activeNotes.forEach((activeNote) => {
|
|
917
|
-
const { bufferSource,
|
|
918
|
-
const playbackRate = calcPlaybackRate(
|
|
963
|
+
const { bufferSource, instrumentKey, noteNumber } = activeNote;
|
|
964
|
+
const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
919
965
|
bufferSource.playbackRate
|
|
920
966
|
.cancelScheduledValues(now)
|
|
921
967
|
.setValueAtTime(playbackRate * pressure, now);
|
|
@@ -979,9 +1025,20 @@ class MidyGM2 {
|
|
|
979
1025
|
this.channels[channelNumber].bankMSB = msb;
|
|
980
1026
|
}
|
|
981
1027
|
setModulation(channelNumber, modulation) {
|
|
1028
|
+
const now = this.audioContext.currentTime;
|
|
982
1029
|
const channel = this.channels[channelNumber];
|
|
983
1030
|
channel.modulation = (modulation / 127) *
|
|
984
1031
|
(channel.modulationDepthRange * 100);
|
|
1032
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1033
|
+
activeNotes.forEach((activeNote) => {
|
|
1034
|
+
if (activeNote.modLFO) {
|
|
1035
|
+
activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
|
|
1036
|
+
channel.modulation, now);
|
|
1037
|
+
}
|
|
1038
|
+
else {
|
|
1039
|
+
this.startModulation(channel, activeNote, now);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
985
1042
|
}
|
|
986
1043
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
987
1044
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1087,8 +1144,8 @@ class MidyGM2 {
|
|
|
1087
1144
|
const velocity = 0;
|
|
1088
1145
|
const stopPedal = true;
|
|
1089
1146
|
const promises = [];
|
|
1090
|
-
channel.scheduledNotes.forEach((
|
|
1091
|
-
const activeNote = this.
|
|
1147
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1148
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1092
1149
|
if (activeNote) {
|
|
1093
1150
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1094
1151
|
promises.push(notePromise);
|
|
@@ -1105,8 +1162,8 @@ class MidyGM2 {
|
|
|
1105
1162
|
const velocity = 0;
|
|
1106
1163
|
const stopPedal = false;
|
|
1107
1164
|
const promises = [];
|
|
1108
|
-
channel.scheduledNotes.forEach((
|
|
1109
|
-
const activeNote = this.
|
|
1165
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1166
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1110
1167
|
if (activeNote) {
|
|
1111
1168
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1112
1169
|
promises.push(notePromise);
|
|
@@ -1288,9 +1345,6 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1288
1345
|
portamentoTime: 0,
|
|
1289
1346
|
reverb: 0,
|
|
1290
1347
|
chorus: 0,
|
|
1291
|
-
vibratoRate: 5,
|
|
1292
|
-
vibratoDepth: 0.5,
|
|
1293
|
-
vibratoDelay: 2.5,
|
|
1294
1348
|
bank: 121 * 128,
|
|
1295
1349
|
bankMSB: 121,
|
|
1296
1350
|
bankLSB: 0,
|