@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.
Files changed (33) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
  4. package/esm/midy-GM1.d.ts +24 -34
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +167 -106
  7. package/esm/midy-GM2.d.ts +123 -21
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +170 -116
  10. package/esm/midy-GMLite.d.ts +23 -35
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +156 -107
  13. package/esm/midy.d.ts +25 -23
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +191 -120
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
  20. package/script/midy-GM1.d.ts +24 -34
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +167 -106
  23. package/script/midy-GM2.d.ts +123 -21
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +170 -116
  26. package/script/midy-GMLite.d.ts +23 -35
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +156 -107
  29. package/script/midy.d.ts +25 -23
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +191 -120
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
  33. 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: any[];
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
- modulationEffect: {
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
- createChannels(audioContext: any): any[];
92
- createNoteBuffer(noteInfo: any, isSF3: any): Promise<any>;
93
- createNoteBufferNode(noteInfo: any, isSF3: any): Promise<any>;
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
- getActiveChannelNotes(scheduledNotes: any): any;
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(noteInfo: any, noteNumber: any, semitoneOffset: any): number;
133
- createNoteAudioChain(channel: any, noteInfo: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<{
134
- bufferSource: any;
135
- gainNode: any;
136
- filterNode: any;
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
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AAMA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,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;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;;;;MAuBC;IAED,yCAcC;IAED,0DAyBC;IAED,8DAUC;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,4CASC;IAED,gDAKC;IAED;;MAOC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,8EAEC;IAED;;;;;OAqFC;IAED,gDAQC;IAED,kGAgDC;IAED,0EAGC;IAED,sIA6CC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAmBC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,0DAiBC;IAED,mFAuDC;IAED,+CAEC;IAED,yDAIC;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"}
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.1/+esm.js";
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
- ...Midy.channelSettings,
205
- ...Midy.effectSettings,
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
- ...Midy.controllerDestinationSettings,
257
+ ...MidyGM2.controllerDestinationSettings,
211
258
  },
212
259
  };
213
260
  });
214
261
  return channels;
215
262
  }
216
- async createNoteBuffer(noteInfo, isSF3) {
217
- const sampleEnd = noteInfo.sample.length + noteInfo.end;
263
+ async createNoteBuffer(instrumentKey, isSF3) {
264
+ const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
218
265
  if (isSF3) {
219
- const sample = new Uint8Array(noteInfo.sample.length);
220
- sample.set(noteInfo.sample);
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 = noteInfo.sample.subarray(0, sampleEnd);
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: noteInfo.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(noteInfo, isSF3) {
288
+ async createNoteBufferNode(instrumentKey, isSF3) {
242
289
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
243
- const audioBuffer = await this.createNoteBuffer(noteInfo, isSF3);
290
+ const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
244
291
  bufferSource.buffer = audioBuffer;
245
- bufferSource.loop = noteInfo.sampleModes % 2 !== 0;
292
+ bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
246
293
  if (bufferSource.loop) {
247
- bufferSource.loopStart = noteInfo.loopStart / noteInfo.sampleRate;
248
- bufferSource.loopEnd = noteInfo.loopEnd / noteInfo.sampleRate;
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((scheduledNotes) => {
530
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
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
- getActiveChannelNotes(scheduledNotes) {
538
- for (let i = 0; i < scheduledNotes; i++) {
539
- const scheduledNote = scheduledNotes[i];
540
- if (scheduledNote)
541
- return scheduledNote;
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(noteInfo, noteNumber, semitoneOffset) {
661
- return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
704
+ calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
705
+ return instrumentKey.playbackRate(noteNumber) *
706
+ Math.pow(2, semitoneOffset / 12);
662
707
  }
663
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
664
- const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
665
- const semitoneOffset = this.calcSemitoneOffset(channel);
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(-noteInfo.initialAttenuation) * volume;
675
- const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
676
- const volDelay = startTime + noteInfo.volDelay;
677
- const volAttack = volDelay + noteInfo.volAttack;
678
- const volHold = volAttack + noteInfo.volHold;
679
- const volDecay = volHold + noteInfo.volDecay;
680
- gainNode.gain
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
- // filter envelope
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(noteInfo.initialFilterFc) * softPedalFactor;
690
- const peekFreq = this.centToHz(noteInfo.initialFilterFc + noteInfo.modEnvToFilterFc) * softPedalFactor;
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 - noteInfo.modSustain)) * softPedalFactor;
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
- const filterNode = new BiquadFilterNode(this.audioContext, {
746
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
697
747
  type: "lowpass",
698
- Q: noteInfo.initialFilterQ / 10, // dB
748
+ Q: instrumentKey.initialFilterQ / 10, // dB
699
749
  frequency: adjustedBaseFreq,
700
750
  });
701
- const modDelay = startTime + noteInfo.modDelay;
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
- let lfoGain;
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 vibratoDelay = startTime + channel.vibratoDelay;
713
- const vibratoAttack = vibratoDelay + 0.1;
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.start(startTime, noteInfo.start / noteInfo.sampleRate);
730
- return { bufferSource, gainNode, filterNode, lfoGain };
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 noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
750
- if (!noteInfo)
809
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
810
+ if (!instrumentKey)
751
811
  return;
752
- const { bufferSource, gainNode, filterNode, lfoGain } = await this
753
- .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
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(scheduledNote);
819
+ scheduledNotes.get(noteNumber).push(note);
776
820
  }
777
821
  else {
778
- scheduledNotes.set(noteNumber, [scheduledNote]);
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, lfoGain, noteInfo } = targetNote;
844
+ const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
801
845
  const velocityRate = (velocity + 127) / 127;
802
- const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
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(noteInfo.initialFilterFc);
850
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
807
851
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
808
- const modEndTime = stopTime + noteInfo.modRelease * velocityRate;
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 (lfoGain)
823
- lfoGain.disconnect(0);
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, noteInfo, noteNumber } = activeNote;
915
- const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
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((scheduledNotes) => {
1088
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
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((scheduledNotes) => {
1106
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
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,