@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
@@ -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"}
@@ -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.1/+esm.js");
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
- ...Midy.channelSettings,
208
- ...Midy.effectSettings,
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
- ...Midy.controllerDestinationSettings,
260
+ ...MidyGM2.controllerDestinationSettings,
214
261
  },
215
262
  };
216
263
  });
217
264
  return channels;
218
265
  }
219
- async createNoteBuffer(noteInfo, isSF3) {
220
- const sampleEnd = noteInfo.sample.length + noteInfo.end;
266
+ async createNoteBuffer(instrumentKey, isSF3) {
267
+ const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
221
268
  if (isSF3) {
222
- const sample = new Uint8Array(noteInfo.sample.length);
223
- sample.set(noteInfo.sample);
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 = noteInfo.sample.subarray(0, sampleEnd);
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: noteInfo.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(noteInfo, isSF3) {
291
+ async createNoteBufferNode(instrumentKey, isSF3) {
245
292
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
246
- const audioBuffer = await this.createNoteBuffer(noteInfo, isSF3);
293
+ const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
247
294
  bufferSource.buffer = audioBuffer;
248
- bufferSource.loop = noteInfo.sampleModes % 2 !== 0;
295
+ bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
249
296
  if (bufferSource.loop) {
250
- bufferSource.loopStart = noteInfo.loopStart / noteInfo.sampleRate;
251
- bufferSource.loopEnd = noteInfo.loopEnd / noteInfo.sampleRate;
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((scheduledNotes) => {
533
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
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
- getActiveChannelNotes(scheduledNotes) {
541
- for (let i = 0; i < scheduledNotes; i++) {
542
- const scheduledNote = scheduledNotes[i];
543
- if (scheduledNote)
544
- return scheduledNote;
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(noteInfo, noteNumber, semitoneOffset) {
664
- return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
707
+ calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
708
+ return instrumentKey.playbackRate(noteNumber) *
709
+ Math.pow(2, semitoneOffset / 12);
665
710
  }
666
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
667
- const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
668
- const semitoneOffset = this.calcSemitoneOffset(channel);
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(-noteInfo.initialAttenuation) * volume;
678
- const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
679
- const volDelay = startTime + noteInfo.volDelay;
680
- const volAttack = volDelay + noteInfo.volAttack;
681
- const volHold = volAttack + noteInfo.volHold;
682
- const volDecay = volHold + noteInfo.volDecay;
683
- gainNode.gain
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
- // filter envelope
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(noteInfo.initialFilterFc) * softPedalFactor;
693
- const peekFreq = this.centToHz(noteInfo.initialFilterFc + noteInfo.modEnvToFilterFc) * softPedalFactor;
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 - noteInfo.modSustain)) * softPedalFactor;
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
- const filterNode = new BiquadFilterNode(this.audioContext, {
749
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
700
750
  type: "lowpass",
701
- Q: noteInfo.initialFilterQ / 10, // dB
751
+ Q: instrumentKey.initialFilterQ / 10, // dB
702
752
  frequency: adjustedBaseFreq,
703
753
  });
704
- const modDelay = startTime + noteInfo.modDelay;
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
- let lfoGain;
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 vibratoDelay = startTime + channel.vibratoDelay;
716
- const vibratoAttack = vibratoDelay + 0.1;
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.start(startTime, noteInfo.start / noteInfo.sampleRate);
733
- return { bufferSource, gainNode, filterNode, lfoGain };
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 noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
753
- if (!noteInfo)
812
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
813
+ if (!instrumentKey)
754
814
  return;
755
- const { bufferSource, gainNode, filterNode, lfoGain } = await this
756
- .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
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(scheduledNote);
822
+ scheduledNotes.get(noteNumber).push(note);
779
823
  }
780
824
  else {
781
- scheduledNotes.set(noteNumber, [scheduledNote]);
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, lfoGain, noteInfo } = targetNote;
847
+ const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
804
848
  const velocityRate = (velocity + 127) / 127;
805
- const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
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(noteInfo.initialFilterFc);
853
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
810
854
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
811
- const modEndTime = stopTime + noteInfo.modRelease * velocityRate;
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 (lfoGain)
826
- lfoGain.disconnect(0);
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, noteInfo, noteNumber } = activeNote;
918
- const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
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((scheduledNotes) => {
1091
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
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((scheduledNotes) => {
1109
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
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,