@marmooo/midy 0.0.9 → 0.1.0

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
@@ -4,8 +4,8 @@ export class Midy {
4
4
  volume: number;
5
5
  pan: number;
6
6
  portamentoTime: number;
7
- reverb: number;
8
- chorus: number;
7
+ reverbSendLevel: number;
8
+ chorusSendLevel: number;
9
9
  vibratoRate: number;
10
10
  vibratoDepth: number;
11
11
  vibratoDelay: number;
@@ -44,15 +44,22 @@ export class Midy {
44
44
  reverbAlgorithm: (audioContext: any) => {
45
45
  input: any;
46
46
  output: any;
47
- dryGain: any;
48
- wetGain: any;
49
47
  };
50
48
  });
51
49
  ticksPerBeat: number;
52
50
  totalTime: number;
53
- reverbFactor: number;
54
51
  masterFineTuning: number;
55
52
  masterCoarseTuning: number;
53
+ reverb: {
54
+ time: number;
55
+ feedback: number;
56
+ };
57
+ chorus: {
58
+ modRate: number;
59
+ modDepth: number;
60
+ feedback: number;
61
+ sendToReverb: number;
62
+ };
56
63
  mono: boolean;
57
64
  omni: boolean;
58
65
  noteCheckInterval: number;
@@ -74,8 +81,6 @@ export class Midy {
74
81
  reverbAlgorithm: (audioContext: any) => {
75
82
  input: any;
76
83
  output: any;
77
- dryGain: any;
78
- wetGain: any;
79
84
  };
80
85
  };
81
86
  audioContext: any;
@@ -83,8 +88,6 @@ export class Midy {
83
88
  reverbAlgorithm: (audioContext: any) => {
84
89
  input: any;
85
90
  output: any;
86
- dryGain: any;
87
- wetGain: any;
88
91
  };
89
92
  };
90
93
  masterGain: any;
@@ -100,8 +103,6 @@ export class Midy {
100
103
  reverbEffect: {
101
104
  input: any;
102
105
  output: any;
103
- dryGain: any;
104
- wetGain: any;
105
106
  };
106
107
  chorusEffect: {
107
108
  lfo: any;
@@ -134,20 +135,18 @@ export class Midy {
134
135
  currentTime(): number;
135
136
  getActiveNotes(channel: any, time: any): Map<any, any>;
136
137
  getActiveNote(noteList: any, time: any): any;
137
- createConvolutionReverb(audioContext: any, options?: {}): {
138
+ createConvolutionReverbImpulse(audioContext: any, decay: any, preDecay: any): any;
139
+ createConvolutionReverb(audioContext: any, impulse: any): {
138
140
  input: any;
139
141
  output: any;
140
- dryGain: any;
141
- wetGain: any;
142
142
  convolverNode: any;
143
143
  };
144
144
  createCombFilter(audioContext: any, input: any, delay: any, feedback: any): any;
145
145
  createAllpassFilter(audioContext: any, input: any, delay: any, feedback: any): any;
146
- createSchroederReverb(audioContext: any, options?: {}): {
146
+ generateDistributedArray(center: any, count: any, varianceRatio?: number, randomness?: number): any[];
147
+ createSchroederReverb(audioContext: any, combDelays: any, combFeedbacks: any, allpassDelays: any, allpassFeedbacks: any): {
147
148
  input: any;
148
149
  output: any;
149
- dryGain: any;
150
- wetGain: any;
151
150
  };
152
151
  createChorusEffect(audioContext: any, options?: {}): {
153
152
  lfo: any;
@@ -196,8 +195,8 @@ export class Midy {
196
195
  updateChannelGain(channel: any): void;
197
196
  setSustainPedal(channelNumber: any, value: any): void;
198
197
  setPortamento(channelNumber: any, value: any): void;
199
- setReverbSendLevel(channelNumber: any, reverb: any): void;
200
- setChorusSendLevel(channelNumber: any, chorus: any): void;
198
+ setReverbSendLevel(channelNumber: any, reverbSendLevel: any): void;
199
+ setChorusSendLevel(channelNumber: any, chorusSendLevel: any): void;
201
200
  setSostenutoPedal(channelNumber: any, value: any): void;
202
201
  setSoftPedal(channelNumber: any, softPedal: any): void;
203
202
  setVibratoRate(channelNumber: any, vibratoRate: any): void;
@@ -237,6 +236,19 @@ export class Midy {
237
236
  setMasterFineTuning(fineTuning: any): void;
238
237
  handleMasterCoarseTuningSysEx(data: any): void;
239
238
  setMasterCoarseTuning(coarseTuning: any): void;
239
+ handleGlobalParameterControl(data: any): void;
240
+ handleReverbParameter(data: any): void;
241
+ setReverbType(type: any): void;
242
+ getReverbTimeFromType(type: any): number | undefined;
243
+ setReverbTime(value: any): void;
244
+ getReverbTime(value: any): number;
245
+ calcDelay(rt60: any, feedback: any): number;
246
+ handleChorusParameter(data: any): void;
247
+ setChorusType(type: any): void;
248
+ setChorusModRate(value: any): void;
249
+ setChorusModDepth(value: any): void;
250
+ setChorusFeedback(value: any): void;
251
+ setChorusSendToReverb(value: any): void;
240
252
  handleExclusiveMessage(data: any): void;
241
253
  handleSysEx(data: any): void;
242
254
  scheduleTask(callback: any, startTime: any): Promise<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;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"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
package/esm/midy.js CHANGED
@@ -64,12 +64,6 @@ export class Midy {
64
64
  writable: true,
65
65
  value: 0
66
66
  });
67
- Object.defineProperty(this, "reverbFactor", {
68
- enumerable: true,
69
- configurable: true,
70
- writable: true,
71
- value: 0.1
72
- });
73
67
  Object.defineProperty(this, "masterFineTuning", {
74
68
  enumerable: true,
75
69
  configurable: true,
@@ -82,6 +76,26 @@ export class Midy {
82
76
  writable: true,
83
77
  value: 0
84
78
  }); // cb
79
+ Object.defineProperty(this, "reverb", {
80
+ enumerable: true,
81
+ configurable: true,
82
+ writable: true,
83
+ value: {
84
+ time: this.getReverbTime(64),
85
+ feedback: 0.2,
86
+ }
87
+ });
88
+ Object.defineProperty(this, "chorus", {
89
+ enumerable: true,
90
+ configurable: true,
91
+ writable: true,
92
+ value: {
93
+ modRate: 3 * 0.122,
94
+ modDepth: (3 + 1) / 3.2,
95
+ feedback: 8 * 0.763,
96
+ sendToReverb: 0 * 0.787,
97
+ }
98
+ });
85
99
  Object.defineProperty(this, "mono", {
86
100
  enumerable: true,
87
101
  configurable: true,
@@ -190,8 +204,19 @@ export class Midy {
190
204
  writable: true,
191
205
  value: {
192
206
  reverbAlgorithm: (audioContext) => {
193
- // return this.createConvolutionReverb(audioContext);
194
- return this.createSchroederReverb(audioContext);
207
+ const { time: rt60, feedback } = this.reverb;
208
+ // const delay = this.calcDelay(rt60, feedback);
209
+ // const impulse = this.createConvolutionReverbImpulse(
210
+ // audioContext,
211
+ // rt60,
212
+ // delay,
213
+ // );
214
+ // return this.createConvolutionReverb(audioContext, impulse);
215
+ const combFeedbacks = this.generateDistributedArray(feedback, 4);
216
+ const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
217
+ const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
218
+ const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
219
+ return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
195
220
  },
196
221
  }
197
222
  });
@@ -596,12 +621,7 @@ export class Midy {
596
621
  }
597
622
  return noteList[0];
598
623
  }
599
- createConvolutionReverb(audioContext, options = {}) {
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);
624
+ createConvolutionReverbImpulse(audioContext, decay, preDecay) {
605
625
  const sampleRate = audioContext.sampleRate;
606
626
  const length = sampleRate * decay;
607
627
  const impulse = new AudioBuffer({
@@ -621,18 +641,17 @@ export class Midy {
621
641
  channelData[i] = (Math.random() * 2 - 1) * attenuation;
622
642
  }
623
643
  }
644
+ return impulse;
645
+ }
646
+ createConvolutionReverb(audioContext, impulse) {
647
+ const output = new GainNode(audioContext);
624
648
  const convolverNode = new ConvolverNode(audioContext, {
625
649
  buffer: impulse,
626
650
  });
627
- input.connect(convolverNode);
628
- convolverNode.connect(wetGain);
629
- wetGain.connect(output);
630
- dryGain.connect(output);
651
+ convolverNode.connect(output);
631
652
  return {
632
- input,
653
+ input: convolverNode,
633
654
  output,
634
- dryGain,
635
- wetGain,
636
655
  convolverNode,
637
656
  };
638
657
  }
@@ -660,17 +679,24 @@ export class Midy {
660
679
  delayNode.connect(passGain);
661
680
  return passGain;
662
681
  }
682
+ generateDistributedArray(center, count, varianceRatio = 0.1, randomness = 0.05) {
683
+ const variance = center * varianceRatio;
684
+ const array = new Array(count);
685
+ for (let i = 0; i < count; i++) {
686
+ const fraction = i / (count - 1 || 1);
687
+ const value = center - variance + fraction * 2 * variance;
688
+ array[i] = value * (1 - (Math.random() * 2 - 1) * randomness);
689
+ }
690
+ return array;
691
+ }
663
692
  // https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
664
693
  // 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;
694
+ createSchroederReverb(audioContext, combDelays, combFeedbacks, allpassDelays, allpassFeedbacks) {
667
695
  const input = new GainNode(audioContext);
668
696
  const output = new GainNode(audioContext);
669
697
  const mergerGain = new GainNode(audioContext, {
670
698
  gain: 1 / (combDelays.length * 2),
671
699
  });
672
- const dryGain = new GainNode(audioContext, { gain: 1 - mix });
673
- const wetGain = new GainNode(audioContext, { gain: mix });
674
700
  for (let i = 0; i < combDelays.length; i++) {
675
701
  const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
676
702
  comb.connect(mergerGain);
@@ -680,11 +706,8 @@ export class Midy {
680
706
  const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
681
707
  allpasses.push(allpass);
682
708
  }
683
- allpasses.at(-1).connect(wetGain);
684
- input.connect(dryGain);
685
- dryGain.connect(output);
686
- wetGain.connect(output);
687
- return { input, output, dryGain, wetGain };
709
+ allpasses.at(-1).connect(output);
710
+ return { input, output };
688
711
  }
689
712
  createChorusEffect(audioContext, options = {}) {
690
713
  const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
@@ -719,11 +742,9 @@ export class Midy {
719
742
  }
720
743
  connectEffects(channel, gainNode) {
721
744
  gainNode.connect(channel.merger);
722
- if (channel.reverb === 0) {
723
- if (channel.chorus === 0) { // no effect
724
- channel.merger.connect(this.masterGain);
725
- }
726
- else { // chorus
745
+ channel.merger.connect(this.masterGain);
746
+ if (channel.reverbSendLevel === 0) {
747
+ if (channel.chorusSendLevel !== 0) { // chorus
727
748
  channel.chorusEffect.delayNodes.forEach((delayNode) => {
728
749
  channel.merger.connect(delayNode);
729
750
  });
@@ -731,7 +752,7 @@ export class Midy {
731
752
  }
732
753
  }
733
754
  else {
734
- if (channel.chorus === 0) { // reverb
755
+ if (channel.chorusSendLevel === 0) { // reverb
735
756
  channel.merger.connect(channel.reverbEffect.input);
736
757
  channel.reverbEffect.output.connect(this.masterGain);
737
758
  }
@@ -1187,23 +1208,22 @@ export class Midy {
1187
1208
  this.releaseSustainPedal(channelNumber, value);
1188
1209
  }
1189
1210
  }
1211
+ // TODO
1190
1212
  setPortamento(channelNumber, value) {
1191
1213
  this.channels[channelNumber].portamento = value >= 64;
1192
1214
  }
1193
- setReverbSendLevel(channelNumber, reverb) {
1215
+ setReverbSendLevel(channelNumber, reverbSendLevel) {
1194
1216
  const now = this.audioContext.currentTime;
1195
1217
  const channel = this.channels[channelNumber];
1196
1218
  const reverbEffect = channel.reverbEffect;
1197
- channel.reverb = reverb / 127 * this.reverbFactor;
1198
- reverbEffect.dryGain.gain.cancelScheduledValues(now);
1199
- reverbEffect.dryGain.gain.setValueAtTime(1 - channel.reverb, now);
1200
- reverbEffect.wetGain.gain.cancelScheduledValues(now);
1201
- reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
1219
+ channel.reverbSendLevel = reverbSendLevel / 127;
1220
+ reverbEffect.output.gain.cancelScheduledValues(now);
1221
+ reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1202
1222
  }
1203
- setChorusSendLevel(channelNumber, chorus) {
1223
+ setChorusSendLevel(channelNumber, chorusSendLevel) {
1204
1224
  const channel = this.channels[channelNumber];
1205
- channel.chorus = chorus / 127;
1206
- channel.chorusEffect.lfoGain = channel.chorus;
1225
+ channel.chorusSendLevel = chorusSendLevel / 127;
1226
+ channel.chorusEffect.lfoGain = channel.chorusSendLevel;
1207
1227
  }
1208
1228
  setSostenutoPedal(channelNumber, value) {
1209
1229
  const isOn = value >= 64;
@@ -1427,11 +1447,11 @@ export class Midy {
1427
1447
  this.GM2SystemOn();
1428
1448
  break;
1429
1449
  default:
1430
- console.warn(`Unsupported Exclusive Message ${data}`);
1450
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1431
1451
  }
1432
1452
  break;
1433
1453
  default:
1434
- console.warn(`Unsupported Exclusive Message ${data}`);
1454
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1435
1455
  }
1436
1456
  }
1437
1457
  GM1SystemOn() {
@@ -1462,9 +1482,10 @@ export class Midy {
1462
1482
  return this.handleMasterFineTuningSysEx(data);
1463
1483
  case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1464
1484
  return this.handleMasterCoarseTuningSysEx(data);
1465
- // case 5: // TODO: Global Parameter Control
1485
+ case 5:
1486
+ return this.handleGlobalParameterControl(data);
1466
1487
  default:
1467
- console.warn(`Unsupported Exclusive Message ${data}`);
1488
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1468
1489
  }
1469
1490
  break;
1470
1491
  case 8:
@@ -1473,7 +1494,7 @@ export class Midy {
1473
1494
  // // TODO
1474
1495
  // return this.handleScaleOctaveTuning1ByteFormat();
1475
1496
  default:
1476
- console.warn(`Unsupported Exclusive Message ${data}`);
1497
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1477
1498
  }
1478
1499
  break;
1479
1500
  case 9:
@@ -1485,7 +1506,7 @@ export class Midy {
1485
1506
  // // TODO
1486
1507
  // return this.setControlChange();
1487
1508
  default:
1488
- console.warn(`Unsupported Exclusive Message ${data}`);
1509
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1489
1510
  }
1490
1511
  break;
1491
1512
  case 10:
@@ -1494,11 +1515,11 @@ export class Midy {
1494
1515
  // // TODO
1495
1516
  // return this.handleKeyBasedInstrumentControl();
1496
1517
  default:
1497
- console.warn(`Unsupported Exclusive Message ${data}`);
1518
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1498
1519
  }
1499
1520
  break;
1500
1521
  default:
1501
- console.warn(`Unsupported Exclusive Message ${data}`);
1522
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1502
1523
  }
1503
1524
  }
1504
1525
  handleMasterVolumeSysEx(data) {
@@ -1539,8 +1560,124 @@ export class Midy {
1539
1560
  this.masterCoarseTuning = coarseTuning - 64;
1540
1561
  }
1541
1562
  }
1563
+ handleGlobalParameterControl(data) {
1564
+ if (data[5] === 1) {
1565
+ switch (data[6]) {
1566
+ case 1:
1567
+ return this.handleReverbParameter(data);
1568
+ case 2:
1569
+ return this.handleChorusParameter(data);
1570
+ default:
1571
+ console.warn(`Unsupported Global Parameter Control Message: ${data}`);
1572
+ }
1573
+ }
1574
+ else {
1575
+ console.warn(`Unsupported Global Parameter Control Message: ${data}`);
1576
+ }
1577
+ }
1578
+ handleReverbParameter(data) {
1579
+ switch (data[7]) {
1580
+ case 0:
1581
+ return this.setReverbType(data[8]);
1582
+ case 1:
1583
+ return this.setReverbTime(data[8]);
1584
+ }
1585
+ }
1586
+ setReverbType(type) {
1587
+ this.reverb.time = this.getReverbTimeFromType(type);
1588
+ this.reverb.feedback = (type === 8) ? 0.1 : 0.2;
1589
+ const { audioContext, channels, options } = this;
1590
+ for (let i = 0; i < channels.length; i++) {
1591
+ channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
1592
+ }
1593
+ }
1594
+ getReverbTimeFromType(type) {
1595
+ switch (type) {
1596
+ case 0:
1597
+ return this.getReverbTime(44);
1598
+ case 1:
1599
+ return this.getReverbTime(50);
1600
+ case 2:
1601
+ return this.getReverbTime(56);
1602
+ case 3:
1603
+ return this.getReverbTime(64);
1604
+ case 4:
1605
+ return this.getReverbTime(64);
1606
+ case 8:
1607
+ return this.getReverbTime(50);
1608
+ default:
1609
+ console.warn(`Unsupported Reverb Time: ${type}`);
1610
+ }
1611
+ }
1612
+ setReverbTime(value) {
1613
+ this.reverb.time = this.getReverbTime(value);
1614
+ const { audioContext, channels, options } = this;
1615
+ for (let i = 0; i < channels.length; i++) {
1616
+ channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
1617
+ }
1618
+ }
1619
+ getReverbTime(value) {
1620
+ return Math.pow(Math.E, (value - 40) * 0.025);
1621
+ }
1622
+ // mean free path equation
1623
+ // https://repository.dl.itc.u-tokyo.ac.jp/record/8550/files/A31912.pdf
1624
+ // 江田和司, 拡散性制御に基づく室内音響設計に向けた音場解析に関する研究, 2015
1625
+ // V: room size (m^3)
1626
+ // S: room surface area (m^2)
1627
+ // meanFreePath = 4V / S (m)
1628
+ // delay estimation using mean free path
1629
+ // t: degree Celsius, generally used 20
1630
+ // c: speed of sound = 331.5 + 0.61t = 331.5 * 0.61 * 20 = 343.7 (m/s)
1631
+ // delay = meanFreePath / c (s)
1632
+ // feedback equation
1633
+ // RT60 means that the energy is reduced to Math.pow(10, -6).
1634
+ // Since energy is proportional to the square of the amplitude,
1635
+ // the amplitude is reduced to Math.pow(10, -3).
1636
+ // When this is done through n feedbacks,
1637
+ // Math.pow(feedback, n) = Math.pow(10, -3)
1638
+ // Math.pow(feedback, RT60 / delay) = Math.pow(10, -3)
1639
+ // RT60 / delay * Math.log10(feedback) = -3
1640
+ // RT60 = -3 * delay / Math.log10(feedback)
1641
+ // feedback = Math.pow(10, -3 * delay / RT60)
1642
+ // delay estimation using ideal feedback
1643
+ // A suitable average sound absorption coefficient is 0.18-0.28.
1644
+ // Since the structure of the hall is complex,
1645
+ // It would be easier to determine the delay based on the ideal feedback.
1646
+ // delay = -RT60 * Math.log10(feedback) / 3
1647
+ calcDelay(rt60, feedback) {
1648
+ return -rt60 * Math.log10(feedback) / 3;
1649
+ }
1650
+ handleChorusParameter(data) {
1651
+ switch (data[7]) {
1652
+ case 0:
1653
+ return this.setChorusType(data[8]);
1654
+ case 1:
1655
+ return this.setChorusModRate(data[8]);
1656
+ case 2:
1657
+ return this.setChorusModDepth(data[8]);
1658
+ case 3:
1659
+ return this.setChorusFeedback(data[8]);
1660
+ case 4:
1661
+ return this.setChorusSendToReverb(data[8]);
1662
+ }
1663
+ }
1664
+ setChorusType(type) {
1665
+ // TODO
1666
+ }
1667
+ setChorusModRate(value) {
1668
+ // TODO
1669
+ }
1670
+ setChorusModDepth(value) {
1671
+ // TODO
1672
+ }
1673
+ setChorusFeedback(value) {
1674
+ // TODO
1675
+ }
1676
+ setChorusSendToReverb(value) {
1677
+ // TODO
1678
+ }
1542
1679
  handleExclusiveMessage(data) {
1543
- console.warn(`Unsupported Exclusive Message ${data}`);
1680
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1544
1681
  }
1545
1682
  handleSysEx(data) {
1546
1683
  switch (data[0]) {
@@ -1573,8 +1710,8 @@ Object.defineProperty(Midy, "channelSettings", {
1573
1710
  volume: 100 / 127,
1574
1711
  pan: 64,
1575
1712
  portamentoTime: 0,
1576
- reverb: 0,
1577
- chorus: 0,
1713
+ reverbSendLevel: 0,
1714
+ chorusSendLevel: 0,
1578
1715
  vibratoRate: 5,
1579
1716
  vibratoDepth: 0.5,
1580
1717
  vibratoDelay: 2.5,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.0.9",
3
+ "version": "0.1.0",
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",
@@ -48,8 +48,6 @@ export class MidyGM1 {
48
48
  gainL: any;
49
49
  gainR: any;
50
50
  merger: any;
51
- reverbEffect: any;
52
- chorusEffect: any;
53
51
  };
54
52
  createChannels(audioContext: any): any[];
55
53
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<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,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"}
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;;;;MAcC;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;AAnjCD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -192,14 +192,10 @@ class MidyGM1 {
192
192
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
193
193
  gainL.connect(merger, 0, 0);
194
194
  gainR.connect(merger, 0, 1);
195
- const reverbEffect = this.createConvolutionReverb(audioContext);
196
- const chorusEffect = this.createChorusEffect(audioContext);
197
195
  return {
198
196
  gainL,
199
197
  gainR,
200
198
  merger,
201
- reverbEffect,
202
- chorusEffect,
203
199
  };
204
200
  }
205
201
  createChannels(audioContext) {
@@ -959,11 +955,11 @@ class MidyGM1 {
959
955
  case 2: // GM System Off
960
956
  break;
961
957
  default:
962
- console.warn(`Unsupported Exclusive Message ${data}`);
958
+ console.warn(`Unsupported Exclusive Message: ${data}`);
963
959
  }
964
960
  break;
965
961
  default:
966
- console.warn(`Unsupported Exclusive Message ${data}`);
962
+ console.warn(`Unsupported Exclusive Message: ${data}`);
967
963
  }
968
964
  }
969
965
  GM1SystemOn() {
@@ -982,11 +978,11 @@ class MidyGM1 {
982
978
  case 1:
983
979
  return this.handleMasterVolumeSysEx(data);
984
980
  default:
985
- console.warn(`Unsupported Exclusive Message ${data}`);
981
+ console.warn(`Unsupported Exclusive Message: ${data}`);
986
982
  }
987
983
  break;
988
984
  default:
989
- console.warn(`Unsupported Exclusive Message ${data}`);
985
+ console.warn(`Unsupported Exclusive Message: ${data}`);
990
986
  }
991
987
  }
992
988
  handleMasterVolumeSysEx(data) {
@@ -1004,7 +1000,7 @@ class MidyGM1 {
1004
1000
  }
1005
1001
  }
1006
1002
  handleExclusiveMessage(data) {
1007
- console.warn(`Unsupported Exclusive Message ${data}`);
1003
+ console.warn(`Unsupported Exclusive Message: ${data}`);
1008
1004
  }
1009
1005
  handleSysEx(data) {
1010
1006
  switch (data[0]) {