@marmooo/midy 0.0.8 → 0.0.9

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/midy.d.ts CHANGED
@@ -40,7 +40,14 @@ export class Midy {
40
40
  lfoFilterDepth: number;
41
41
  lfoAmplitudeDepth: number;
42
42
  };
43
- constructor(audioContext: any);
43
+ constructor(audioContext: any, options?: {
44
+ reverbAlgorithm: (audioContext: any) => {
45
+ input: any;
46
+ output: any;
47
+ dryGain: any;
48
+ wetGain: any;
49
+ };
50
+ });
44
51
  ticksPerBeat: number;
45
52
  totalTime: number;
46
53
  reverbFactor: number;
@@ -63,69 +70,25 @@ export class Midy {
63
70
  timeline: any[];
64
71
  instruments: any[];
65
72
  notePromises: any[];
66
- audioContext: any;
67
- masterGain: any;
68
- channels: {
69
- scheduledNotes: Map<any, any>;
70
- sostenutoNotes: Map<any, any>;
71
- polyphonicKeyPressure: {
72
- pitchControl: number;
73
- filterCutoffControl: number;
74
- amplitudeControl: number;
75
- lfoPitchDepth: number;
76
- lfoFilterDepth: number;
77
- lfoAmplitudeDepth: number;
78
- };
79
- channelPressure: {
80
- pitchControl: number;
81
- filterCutoffControl: number;
82
- amplitudeControl: number;
83
- lfoPitchDepth: number;
84
- lfoFilterDepth: number;
85
- lfoAmplitudeDepth: number;
86
- };
87
- gainL: any;
88
- gainR: any;
89
- reverbEffect: {
90
- convolverNode: any;
73
+ defaultOptions: {
74
+ reverbAlgorithm: (audioContext: any) => {
75
+ input: any;
76
+ output: any;
91
77
  dryGain: any;
92
78
  wetGain: any;
93
79
  };
94
- chorusEffect: {
95
- lfo: any;
96
- lfoGain: any;
97
- delayNodes: any[];
98
- chorusGains: any[];
80
+ };
81
+ audioContext: any;
82
+ options: {
83
+ reverbAlgorithm: (audioContext: any) => {
84
+ input: any;
85
+ output: any;
86
+ dryGain: any;
87
+ wetGain: any;
99
88
  };
100
- expression: number;
101
- modulation: number;
102
- sustainPedal: boolean;
103
- portamento: boolean;
104
- sostenutoPedal: boolean;
105
- softPedal: number;
106
- rpnMSB: number;
107
- rpnLSB: number;
108
- pitchBendRange: number;
109
- currentBufferSource: null;
110
- volume: number;
111
- pan: number;
112
- portamentoTime: number;
113
- reverb: number;
114
- chorus: number;
115
- vibratoRate: number;
116
- vibratoDepth: number;
117
- vibratoDelay: number;
118
- bank: number;
119
- bankMSB: number;
120
- bankLSB: number;
121
- dataMSB: number;
122
- dataLSB: number;
123
- program: number;
124
- pitchBend: number;
125
- fineTuning: number;
126
- coarseTuning: number;
127
- modulationDepthRange: number;
128
- }[];
89
+ };
90
+ masterGain: any;
91
+ channels: any[];
129
92
  initSoundFontTable(): any[];
130
93
  addSoundFont(soundFont: any): void;
131
94
  loadSoundFont(soundFontUrl: any): Promise<void>;
@@ -133,8 +96,10 @@ export class Midy {
133
96
  setChannelAudioNodes(audioContext: any): {
134
97
  gainL: any;
135
98
  gainR: any;
99
+ merger: any;
136
100
  reverbEffect: {
137
- convolverNode: any;
101
+ input: any;
102
+ output: any;
138
103
  dryGain: any;
139
104
  wetGain: any;
140
105
  };
@@ -143,69 +108,10 @@ export class Midy {
143
108
  lfoGain: any;
144
109
  delayNodes: any[];
145
110
  chorusGains: any[];
111
+ output: any;
146
112
  };
147
113
  };
148
- createChannels(audioContext: any): {
149
- scheduledNotes: Map<any, any>;
150
- sostenutoNotes: Map<any, any>;
151
- polyphonicKeyPressure: {
152
- pitchControl: number;
153
- filterCutoffControl: number;
154
- amplitudeControl: number;
155
- lfoPitchDepth: number;
156
- lfoFilterDepth: number;
157
- lfoAmplitudeDepth: number;
158
- };
159
- channelPressure: {
160
- pitchControl: number;
161
- filterCutoffControl: number;
162
- amplitudeControl: number;
163
- lfoPitchDepth: number;
164
- lfoFilterDepth: number;
165
- lfoAmplitudeDepth: number;
166
- };
167
- gainL: any;
168
- gainR: any;
169
- reverbEffect: {
170
- convolverNode: any;
171
- dryGain: any;
172
- wetGain: any;
173
- };
174
- chorusEffect: {
175
- lfo: any;
176
- lfoGain: any;
177
- delayNodes: any[];
178
- chorusGains: any[];
179
- };
180
- expression: number;
181
- modulation: number;
182
- sustainPedal: boolean;
183
- portamento: boolean;
184
- sostenutoPedal: boolean;
185
- softPedal: number;
186
- rpnMSB: number;
187
- rpnLSB: number;
188
- pitchBendRange: number;
189
- currentBufferSource: null;
190
- volume: number;
191
- pan: number;
192
- portamentoTime: number;
193
- reverb: number;
194
- chorus: number;
195
- vibratoRate: number;
196
- vibratoDepth: number;
197
- vibratoDelay: number;
198
- bank: number;
199
- bankMSB: number;
200
- bankLSB: number;
201
- dataMSB: number;
202
- dataLSB: number;
203
- program: number;
204
- pitchBend: number;
205
- fineTuning: number;
206
- coarseTuning: number;
207
- modulationDepthRange: number;
208
- }[];
114
+ createChannels(audioContext: any): any[];
209
115
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
210
116
  createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
211
117
  convertToFloat32Array(uint8Array: any): Float32Array;
@@ -228,8 +134,18 @@ export class Midy {
228
134
  currentTime(): number;
229
135
  getActiveNotes(channel: any, time: any): Map<any, any>;
230
136
  getActiveNote(noteList: any, time: any): any;
231
- createReverbEffect(audioContext: any, options?: {}): {
137
+ createConvolutionReverb(audioContext: any, options?: {}): {
138
+ input: any;
139
+ output: any;
140
+ dryGain: any;
141
+ wetGain: any;
232
142
  convolverNode: any;
143
+ };
144
+ createCombFilter(audioContext: any, input: any, delay: any, feedback: any): any;
145
+ createAllpassFilter(audioContext: any, input: any, delay: any, feedback: any): any;
146
+ createSchroederReverb(audioContext: any, options?: {}): {
147
+ input: any;
148
+ output: any;
233
149
  dryGain: any;
234
150
  wetGain: any;
235
151
  };
@@ -238,8 +154,9 @@ export class Midy {
238
154
  lfoGain: any;
239
155
  delayNodes: any[];
240
156
  chorusGains: any[];
157
+ output: any;
241
158
  };
242
- connectNoteEffects(channel: any, gainNode: any): void;
159
+ connectEffects(channel: any, gainNode: any): void;
243
160
  cbToRatio(cb: any): number;
244
161
  centToHz(cent: any): number;
245
162
  calcSemitoneOffset(channel: any): any;
package/esm/midy.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAqBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgGC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MAqCC;IAED,sDA6BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAwDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;IAED,mFAkEC;IAED,+CAEC;IAED,qCAcC;IAED,yDAIC;IAED,iEAEC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oDAEC;IAED,0DASC;IAED,0DAIC;IAED,wDAWC;IAED,uDAGC;IAED,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,kFAeC;IAED,2DAMC;IAED,gDAyBC;IAED,wCAEC;IAED,wCAEC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,wDAKC;IAED,6EAKC;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,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAxjDD;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"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IASF;;;;;;;OAOC;IAjFD,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;IA8ClB;;;;;;;MAKE;IAGA,kBAAgC;IAChC;;;;;;;MAAqD;IACrD,gBAA4C;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;;;;MAkBC;IAED,yCAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgGC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;;;MA4CC;IAED,gFAUC;IAED,mFAYC;IAID;;;;;MAuCC;IAED;;;;;;MAoCC;IAED,kDAuBC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAiDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;IAED,mFAkEC;IAED,+CAEC;IAED,qCAcC;IAED,yDAIC;IAED,iEAEC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oDAEC;IAED,0DASC;IAED,0DAIC;IAED,wDAWC;IAED,uDAGC;IAED,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,kFAeC;IAED,2DAMC;IAED,gDAyBC;IAED,wCAEC;IAED,wCAEC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,wDAKC;IAED,6EAKC;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,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5nDD;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.js CHANGED
@@ -51,7 +51,7 @@ class Note {
51
51
  }
52
52
  }
53
53
  export class Midy {
54
- constructor(audioContext) {
54
+ constructor(audioContext, options = this.defaultOptions) {
55
55
  Object.defineProperty(this, "ticksPerBeat", {
56
56
  enumerable: true,
57
57
  configurable: true,
@@ -184,7 +184,19 @@ export class Midy {
184
184
  writable: true,
185
185
  value: []
186
186
  });
187
+ Object.defineProperty(this, "defaultOptions", {
188
+ enumerable: true,
189
+ configurable: true,
190
+ writable: true,
191
+ value: {
192
+ reverbAlgorithm: (audioContext) => {
193
+ // return this.createConvolutionReverb(audioContext);
194
+ return this.createSchroederReverb(audioContext);
195
+ },
196
+ }
197
+ });
187
198
  this.audioContext = audioContext;
199
+ this.options = { ...this.defaultOptions, ...options };
188
200
  this.masterGain = new GainNode(audioContext);
189
201
  this.masterGain.connect(audioContext.destination);
190
202
  this.channels = this.createChannels(audioContext);
@@ -225,23 +237,18 @@ export class Midy {
225
237
  this.totalTime = this.calcTotalTime();
226
238
  }
227
239
  setChannelAudioNodes(audioContext) {
228
- const { gainLeft, gainRight } = this.panToGain(Midy.channelSettings.pan);
240
+ const { gainLeft, gainRight } = this.panToGain(this.constructor.channelSettings.pan);
229
241
  const gainL = new GainNode(audioContext, { gain: gainLeft });
230
242
  const gainR = new GainNode(audioContext, { gain: gainRight });
231
243
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
232
244
  gainL.connect(merger, 0, 0);
233
245
  gainR.connect(merger, 0, 1);
234
- merger.connect(this.masterGain);
235
- const reverbEffect = this.createReverbEffect(audioContext);
246
+ const reverbEffect = this.options.reverbAlgorithm(audioContext);
236
247
  const chorusEffect = this.createChorusEffect(audioContext);
237
- chorusEffect.lfo.start();
238
- reverbEffect.dryGain.connect(gainL);
239
- reverbEffect.dryGain.connect(gainR);
240
- reverbEffect.wetGain.connect(gainL);
241
- reverbEffect.wetGain.connect(gainR);
242
248
  return {
243
249
  gainL,
244
250
  gainR,
251
+ merger,
245
252
  reverbEffect,
246
253
  chorusEffect,
247
254
  };
@@ -249,16 +256,16 @@ export class Midy {
249
256
  createChannels(audioContext) {
250
257
  const channels = Array.from({ length: 16 }, () => {
251
258
  return {
252
- ...Midy.channelSettings,
253
- ...Midy.effectSettings,
259
+ ...this.constructor.channelSettings,
260
+ ...this.constructor.effectSettings,
254
261
  ...this.setChannelAudioNodes(audioContext),
255
262
  scheduledNotes: new Map(),
256
263
  sostenutoNotes: new Map(),
257
264
  polyphonicKeyPressure: {
258
- ...Midy.controllerDestinationSettings,
265
+ ...this.constructor.controllerDestinationSettings,
259
266
  },
260
267
  channelPressure: {
261
- ...Midy.controllerDestinationSettings,
268
+ ...this.constructor.controllerDestinationSettings,
262
269
  },
263
270
  };
264
271
  });
@@ -589,8 +596,12 @@ export class Midy {
589
596
  }
590
597
  return noteList[0];
591
598
  }
592
- createReverbEffect(audioContext, options = {}) {
599
+ createConvolutionReverb(audioContext, options = {}) {
593
600
  const { decay = 0.8, preDecay = 0, } = options;
601
+ const input = new GainNode(audioContext);
602
+ const output = new GainNode(audioContext);
603
+ const dryGain = new GainNode(audioContext);
604
+ const wetGain = new GainNode(audioContext);
594
605
  const sampleRate = audioContext.sampleRate;
595
606
  const length = sampleRate * decay;
596
607
  const impulse = new AudioBuffer({
@@ -604,27 +615,82 @@ export class Midy {
604
615
  for (let i = 0; i < preDecayLength; i++) {
605
616
  channelData[i] = Math.random() * 2 - 1;
606
617
  }
618
+ const attenuationFactor = 1 / (sampleRate * decay);
607
619
  for (let i = preDecayLength; i < length; i++) {
608
- const attenuation = Math.exp(-(i - preDecayLength) / sampleRate / decay);
620
+ const attenuation = Math.exp(-(i - preDecayLength) * attenuationFactor);
609
621
  channelData[i] = (Math.random() * 2 - 1) * attenuation;
610
622
  }
611
623
  }
612
624
  const convolverNode = new ConvolverNode(audioContext, {
613
625
  buffer: impulse,
614
626
  });
615
- const dryGain = new GainNode(audioContext);
616
- const wetGain = new GainNode(audioContext);
627
+ input.connect(convolverNode);
617
628
  convolverNode.connect(wetGain);
629
+ wetGain.connect(output);
630
+ dryGain.connect(output);
618
631
  return {
619
- convolverNode,
632
+ input,
633
+ output,
620
634
  dryGain,
621
635
  wetGain,
636
+ convolverNode,
622
637
  };
623
638
  }
639
+ createCombFilter(audioContext, input, delay, feedback) {
640
+ const delayNode = new DelayNode(audioContext, {
641
+ maxDelayTime: delay,
642
+ delayTime: delay,
643
+ });
644
+ const feedbackGain = new GainNode(audioContext, { gain: feedback });
645
+ input.connect(delayNode);
646
+ delayNode.connect(feedbackGain);
647
+ feedbackGain.connect(delayNode);
648
+ return delayNode;
649
+ }
650
+ createAllpassFilter(audioContext, input, delay, feedback) {
651
+ const delayNode = new DelayNode(audioContext, {
652
+ maxDelayTime: delay,
653
+ delayTime: delay,
654
+ });
655
+ const feedbackGain = new GainNode(audioContext, { gain: feedback });
656
+ const passGain = new GainNode(audioContext, { gain: 1 - feedback });
657
+ input.connect(delayNode);
658
+ delayNode.connect(feedbackGain);
659
+ feedbackGain.connect(delayNode);
660
+ delayNode.connect(passGain);
661
+ return passGain;
662
+ }
663
+ // https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
664
+ // M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
665
+ createSchroederReverb(audioContext, options = {}) {
666
+ const { combDelays = [0.31, 0.34, 0.37, 0.40], combFeedbacks = [0.86, 0.87, 0.88, 0.89], allpassDelays = [0.02, 0.05], allpassFeedbacks = [0.7, 0.7], mix = 0.5, } = options;
667
+ const input = new GainNode(audioContext);
668
+ const output = new GainNode(audioContext);
669
+ const mergerGain = new GainNode(audioContext, {
670
+ gain: 1 / (combDelays.length * 2),
671
+ });
672
+ const dryGain = new GainNode(audioContext, { gain: 1 - mix });
673
+ const wetGain = new GainNode(audioContext, { gain: mix });
674
+ for (let i = 0; i < combDelays.length; i++) {
675
+ const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
676
+ comb.connect(mergerGain);
677
+ }
678
+ const allpasses = [];
679
+ for (let i = 0; i < allpassDelays.length; i++) {
680
+ const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
681
+ allpasses.push(allpass);
682
+ }
683
+ allpasses.at(-1).connect(wetGain);
684
+ input.connect(dryGain);
685
+ dryGain.connect(output);
686
+ wetGain.connect(output);
687
+ return { input, output, dryGain, wetGain };
688
+ }
624
689
  createChorusEffect(audioContext, options = {}) {
625
690
  const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
626
691
  const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
627
692
  const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
693
+ const output = new GainNode(audioContext);
628
694
  const chorusGains = [];
629
695
  const delayNodes = [];
630
696
  const baseGain = 1 / chorusCount;
@@ -634,50 +700,47 @@ export class Midy {
634
700
  const delayNode = new DelayNode(audioContext, {
635
701
  maxDelayTime: delayTime,
636
702
  });
637
- delayNodes.push(delayNode);
638
703
  const chorusGain = new GainNode(audioContext, { gain: baseGain });
704
+ delayNodes.push(delayNode);
639
705
  chorusGains.push(chorusGain);
640
- lfo.connect(lfoGain);
641
706
  lfoGain.connect(delayNode.delayTime);
642
707
  delayNode.connect(chorusGain);
708
+ chorusGain.connect(output);
643
709
  }
710
+ lfo.connect(lfoGain);
711
+ lfo.start();
644
712
  return {
645
713
  lfo,
646
714
  lfoGain,
647
715
  delayNodes,
648
716
  chorusGains,
717
+ output,
649
718
  };
650
719
  }
651
- connectNoteEffects(channel, gainNode) {
720
+ connectEffects(channel, gainNode) {
721
+ gainNode.connect(channel.merger);
652
722
  if (channel.reverb === 0) {
653
723
  if (channel.chorus === 0) { // no effect
654
- gainNode.connect(channel.gainL);
655
- gainNode.connect(channel.gainR);
724
+ channel.merger.connect(this.masterGain);
656
725
  }
657
726
  else { // chorus
658
727
  channel.chorusEffect.delayNodes.forEach((delayNode) => {
659
- gainNode.connect(delayNode);
660
- });
661
- channel.chorusEffect.chorusGains.forEach((chorusGain) => {
662
- chorusGain.connect(channel.gainL);
663
- chorusGain.connect(channel.gainR);
728
+ channel.merger.connect(delayNode);
664
729
  });
730
+ channel.chorusEffect.output.connect(this.masterGain);
665
731
  }
666
732
  }
667
733
  else {
668
734
  if (channel.chorus === 0) { // reverb
669
- gainNode.connect(channel.reverbEffect.convolverNode);
670
- gainNode.connect(channel.reverbEffect.dryGain);
735
+ channel.merger.connect(channel.reverbEffect.input);
736
+ channel.reverbEffect.output.connect(this.masterGain);
671
737
  }
672
738
  else { // reverb + chorus
673
- gainNode.connect(channel.reverbEffect.convolverNode);
674
- gainNode.connect(channel.reverbEffect.dryGain);
675
739
  channel.chorusEffect.delayNodes.forEach((delayNode) => {
676
- gainNode.connect(delayNode);
677
- });
678
- channel.chorusEffect.chorusGains.forEach((chorusGain) => {
679
- chorusGain.connect(channel.reverbEffect.convolverNode);
740
+ channel.merger.connect(delayNode);
680
741
  });
742
+ channel.merger.connect(channel.reverbEffect.input);
743
+ channel.reverbEffect.output.connect(this.masterGain);
681
744
  }
682
745
  }
683
746
  }
@@ -817,7 +880,7 @@ export class Midy {
817
880
  if (!instrumentKey)
818
881
  return;
819
882
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
820
- this.connectNoteEffects(channel, note.gainNode);
883
+ this.connectEffects(channel, note.gainNode);
821
884
  if (channel.sostenutoPedal) {
822
885
  channel.sostenutoNotes.set(noteNumber, note);
823
886
  }
@@ -841,46 +904,48 @@ export class Midy {
841
904
  return;
842
905
  if (!channel.scheduledNotes.has(noteNumber))
843
906
  return;
844
- const targetNotes = channel.scheduledNotes.get(noteNumber);
845
- for (let i = 0; i < targetNotes.length; i++) {
846
- const targetNote = targetNotes[i];
847
- if (!targetNote)
907
+ const scheduledNotes = channel.scheduledNotes.get(noteNumber);
908
+ for (let i = 0; i < scheduledNotes.length; i++) {
909
+ const note = scheduledNotes[i];
910
+ if (!note)
848
911
  continue;
849
- if (targetNote.ending)
912
+ if (note.ending)
850
913
  continue;
851
- const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, vibLFO, vibLFOGain, instrumentKey, } = targetNote;
852
914
  const velocityRate = (velocity + 127) / 127;
853
- const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
854
- gainNode.gain.cancelScheduledValues(stopTime);
855
- gainNode.gain.linearRampToValueAtTime(0, volEndTime);
915
+ const volEndTime = stopTime +
916
+ note.instrumentKey.volRelease * velocityRate;
917
+ note.gainNode.gain
918
+ .cancelScheduledValues(stopTime)
919
+ .linearRampToValueAtTime(0, volEndTime);
856
920
  const maxFreq = this.audioContext.sampleRate / 2;
857
- const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
921
+ const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
858
922
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
859
- const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
860
- filterNode.frequency
923
+ const modEndTime = stopTime +
924
+ note.instrumentKey.modRelease * velocityRate;
925
+ note.filterNode.frequency
861
926
  .cancelScheduledValues(stopTime)
862
927
  .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
863
- targetNote.ending = true;
928
+ note.ending = true;
864
929
  this.scheduleTask(() => {
865
- bufferSource.loop = false;
930
+ note.bufferSource.loop = false;
866
931
  }, stopTime);
867
932
  return new Promise((resolve) => {
868
- bufferSource.onended = () => {
869
- targetNotes[i] = null;
870
- bufferSource.disconnect(0);
871
- filterNode.disconnect(0);
872
- gainNode.disconnect(0);
873
- if (modLFOGain)
874
- modLFOGain.disconnect(0);
875
- if (vibLFOGain)
876
- vibLFOGain.disconnect(0);
877
- if (modLFO)
878
- modLFO.stop();
879
- if (vibLFO)
880
- vibLFO.stop();
933
+ note.bufferSource.onended = () => {
934
+ scheduledNotes[i] = null;
935
+ note.bufferSource.disconnect();
936
+ note.filterNode.disconnect();
937
+ note.gainNode.disconnect();
938
+ if (note.modLFOGain)
939
+ note.modLFOGain.disconnect();
940
+ if (note.vibLFOGain)
941
+ note.vibLFOGain.disconnect();
942
+ if (note.modLFO)
943
+ note.modLFO.stop();
944
+ if (note.vibLFO)
945
+ note.vibLFO.stop();
881
946
  resolve();
882
947
  };
883
- bufferSource.stop(volEndTime);
948
+ note.bufferSource.stop(volEndTime);
884
949
  });
885
950
  }
886
951
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "A MIDI player/synthesizer written in JavaScript that supports GM-Lite/GM1 and SF2/SF3.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,27 +39,7 @@ export class MidyGM1 {
39
39
  notePromises: any[];
40
40
  audioContext: any;
41
41
  masterGain: any;
42
- channels: {
43
- scheduledNotes: Map<any, any>;
44
- gainL: any;
45
- gainR: any;
46
- expression: number;
47
- modulation: number;
48
- sustainPedal: boolean;
49
- rpnMSB: number;
50
- rpnLSB: number;
51
- pitchBendRange: number;
52
- volume: number;
53
- pan: number;
54
- bank: number;
55
- dataMSB: number;
56
- dataLSB: number;
57
- program: number;
58
- pitchBend: number;
59
- fineTuning: number;
60
- coarseTuning: number;
61
- modulationDepthRange: number;
62
- }[];
42
+ channels: any[];
63
43
  initSoundFontTable(): any[];
64
44
  addSoundFont(soundFont: any): void;
65
45
  loadSoundFont(soundFontUrl: any): Promise<void>;
@@ -67,28 +47,11 @@ export class MidyGM1 {
67
47
  setChannelAudioNodes(audioContext: any): {
68
48
  gainL: any;
69
49
  gainR: any;
50
+ merger: any;
51
+ reverbEffect: any;
52
+ chorusEffect: any;
70
53
  };
71
- createChannels(audioContext: any): {
72
- scheduledNotes: Map<any, any>;
73
- gainL: any;
74
- gainR: any;
75
- expression: number;
76
- modulation: number;
77
- sustainPedal: boolean;
78
- rpnMSB: number;
79
- rpnLSB: number;
80
- pitchBendRange: number;
81
- volume: number;
82
- pan: number;
83
- bank: number;
84
- dataMSB: number;
85
- dataLSB: number;
86
- program: number;
87
- pitchBend: number;
88
- fineTuning: number;
89
- coarseTuning: number;
90
- modulationDepthRange: number;
91
- }[];
54
+ createChannels(audioContext: any): any[];
92
55
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
93
56
  createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
94
57
  convertToFloat32Array(uint8Array: any): Float32Array;
@@ -111,7 +74,7 @@ export class MidyGM1 {
111
74
  currentTime(): number;
112
75
  getActiveNotes(channel: any, time: any): Map<any, any>;
113
76
  getActiveNote(noteList: any, time: any): any;
114
- connectNoteEffects(channel: any, gainNode: any): void;
77
+ connectEffects(channel: any, gainNode: any): void;
115
78
  cbToRatio(cb: any): number;
116
79
  centToHz(cent: any): number;
117
80
  calcSemitoneOffset(channel: any): any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAYC;IAED;;;;;;;;;;;;;;;;;;;;QAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,sDAGC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIAmDC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,mFA+BC;IAED,qCAcC;IAED,yDAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAtjCD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;MAkBC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,kDAGC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA8CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,mFA+BC;IAED,qCAcC;IAED,yDAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAvjCD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}