@marmooo/midy 0.1.0 → 0.1.2

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-GM2.js CHANGED
@@ -82,7 +82,7 @@ export class MidyGM2 {
82
82
  writable: true,
83
83
  value: {
84
84
  time: this.getReverbTime(64),
85
- feedback: 0.2,
85
+ feedback: 0.8,
86
86
  }
87
87
  });
88
88
  Object.defineProperty(this, "chorus", {
@@ -90,10 +90,11 @@ export class MidyGM2 {
90
90
  configurable: true,
91
91
  writable: true,
92
92
  value: {
93
- modRate: 3 * 0.122,
94
- modDepth: (3 + 1) / 3.2,
95
- feedback: 8 * 0.763,
96
- sendToReverb: 0 * 0.787,
93
+ modRate: this.getChorusModRate(3),
94
+ modDepth: this.getChorusModDepth(19),
95
+ feedback: this.getChorusFeedback(8),
96
+ sendToReverb: this.getChorusSendToReverb(0),
97
+ delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
97
98
  }
98
99
  });
99
100
  Object.defineProperty(this, "mono", {
@@ -223,8 +224,12 @@ export class MidyGM2 {
223
224
  this.audioContext = audioContext;
224
225
  this.options = { ...this.defaultOptions, ...options };
225
226
  this.masterGain = new GainNode(audioContext);
226
- this.masterGain.connect(audioContext.destination);
227
227
  this.channels = this.createChannels(audioContext);
228
+ this.reverbEffect = this.options.reverbAlgorithm(audioContext);
229
+ this.chorusEffect = this.createChorusEffect(audioContext);
230
+ this.chorusEffect.output.connect(this.masterGain);
231
+ this.reverbEffect.output.connect(this.masterGain);
232
+ this.masterGain.connect(audioContext.destination);
228
233
  this.GM2SystemOn();
229
234
  }
230
235
  initSoundFontTable() {
@@ -268,14 +273,11 @@ export class MidyGM2 {
268
273
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
269
274
  gainL.connect(merger, 0, 0);
270
275
  gainR.connect(merger, 0, 1);
271
- const reverbEffect = this.options.reverbAlgorithm(audioContext);
272
- const chorusEffect = this.createChorusEffect(audioContext);
276
+ merger.connect(this.masterGain);
273
277
  return {
274
278
  gainL,
275
279
  gainR,
276
280
  merger,
277
- reverbEffect,
278
- chorusEffect,
279
281
  };
280
282
  }
281
283
  createChannels(audioContext) {
@@ -685,12 +687,10 @@ export class MidyGM2 {
685
687
  }
686
688
  // https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
687
689
  // M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
688
- createSchroederReverb(audioContext, combDelays, combFeedbacks, allpassDelays, allpassFeedbacks) {
690
+ createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
689
691
  const input = new GainNode(audioContext);
690
692
  const output = new GainNode(audioContext);
691
- const mergerGain = new GainNode(audioContext, {
692
- gain: 1 / (combDelays.length * 2),
693
- });
693
+ const mergerGain = new GainNode(audioContext);
694
694
  for (let i = 0; i < combDelays.length; i++) {
695
695
  const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
696
696
  comb.connect(mergerGain);
@@ -703,62 +703,49 @@ export class MidyGM2 {
703
703
  allpasses.at(-1).connect(output);
704
704
  return { input, output };
705
705
  }
706
- createChorusEffect(audioContext, options = {}) {
707
- const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
708
- const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
709
- const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
706
+ createChorusEffect(audioContext) {
707
+ const input = new GainNode(audioContext);
710
708
  const output = new GainNode(audioContext);
711
- const chorusGains = [];
709
+ const sendGain = new GainNode(audioContext);
710
+ const lfo = new OscillatorNode(audioContext, {
711
+ frequency: this.chorus.modRate,
712
+ });
713
+ const lfoGain = new GainNode(audioContext, {
714
+ gain: this.chorus.modDepth / 2,
715
+ });
716
+ const delayTimes = this.chorus.delayTimes;
712
717
  const delayNodes = [];
713
- const baseGain = 1 / chorusCount;
714
- for (let i = 0; i < chorusCount; i++) {
715
- const randomDelayFactor = (Math.random() - 0.5) * variance;
716
- const delayTime = (i + 1) * delay + randomDelayFactor;
718
+ const feedbackGains = [];
719
+ for (let i = 0; i < delayTimes.length; i++) {
720
+ const delayTime = delayTimes[i];
717
721
  const delayNode = new DelayNode(audioContext, {
718
- maxDelayTime: delayTime,
722
+ maxDelayTime: 0.1, // generally, 5ms < delayTime < 50ms
723
+ delayTime,
724
+ });
725
+ const feedbackGain = new GainNode(audioContext, {
726
+ gain: this.chorus.feedback,
719
727
  });
720
- const chorusGain = new GainNode(audioContext, { gain: baseGain });
721
728
  delayNodes.push(delayNode);
722
- chorusGains.push(chorusGain);
729
+ feedbackGains.push(feedbackGain);
730
+ input.connect(delayNode);
723
731
  lfoGain.connect(delayNode.delayTime);
724
- delayNode.connect(chorusGain);
725
- chorusGain.connect(output);
732
+ delayNode.connect(feedbackGain);
733
+ feedbackGain.connect(delayNode);
734
+ delayNode.connect(output);
726
735
  }
736
+ output.connect(sendGain);
727
737
  lfo.connect(lfoGain);
728
738
  lfo.start();
729
739
  return {
740
+ input,
741
+ output,
742
+ sendGain,
730
743
  lfo,
731
744
  lfoGain,
732
745
  delayNodes,
733
- chorusGains,
734
- output,
746
+ feedbackGains,
735
747
  };
736
748
  }
737
- connectEffects(channel, gainNode) {
738
- gainNode.connect(channel.merger);
739
- channel.merger.connect(this.masterGain);
740
- if (channel.reverbSendLevel === 0) {
741
- if (channel.chorusSendLevel !== 0) { // chorus
742
- channel.chorusEffect.delayNodes.forEach((delayNode) => {
743
- channel.merger.connect(delayNode);
744
- });
745
- channel.chorusEffect.output.connect(this.masterGain);
746
- }
747
- }
748
- else {
749
- if (channel.chorusSendLevel === 0) { // reverb
750
- channel.merger.connect(channel.reverbEffect.input);
751
- channel.reverbEffect.output.connect(this.masterGain);
752
- }
753
- else { // reverb + chorus
754
- channel.chorusEffect.delayNodes.forEach((delayNode) => {
755
- channel.merger.connect(delayNode);
756
- });
757
- channel.merger.connect(channel.reverbEffect.input);
758
- channel.reverbEffect.output.connect(this.masterGain);
759
- }
760
- }
761
- }
762
749
  cbToRatio(cb) {
763
750
  return Math.pow(10, cb / 200);
764
751
  }
@@ -775,14 +762,10 @@ export class MidyGM2 {
775
762
  return instrumentKey.playbackRate(noteNumber) *
776
763
  Math.pow(2, semitoneOffset / 12);
777
764
  }
778
- setVolumeEnvelope(channel, note) {
779
- const { instrumentKey, startTime, velocity } = note;
765
+ setVolumeEnvelope(note) {
766
+ const { instrumentKey, startTime } = note;
780
767
  note.gainNode = new GainNode(this.audioContext, { gain: 0 });
781
- let volume = (velocity / 127) * channel.volume * channel.expression;
782
- if (volume === 0)
783
- volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
784
- const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
785
- volume;
768
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
786
769
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
787
770
  const volDelay = startTime + instrumentKey.volDelay;
788
771
  const volAttack = volDelay + instrumentKey.volAttack;
@@ -842,7 +825,7 @@ export class MidyGM2 {
842
825
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
843
826
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
844
827
  note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
845
- this.setVolumeEnvelope(channel, note);
828
+ this.setVolumeEnvelope(note);
846
829
  this.setFilterEnvelope(channel, note);
847
830
  if (channel.modulation > 0) {
848
831
  const delayModLFO = startTime + instrumentKey.delayModLFO;
@@ -878,7 +861,8 @@ export class MidyGM2 {
878
861
  if (!instrumentKey)
879
862
  return;
880
863
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
881
- this.connectEffects(channel, note.gainNode);
864
+ note.gainNode.connect(channel.gainL);
865
+ note.gainNode.connect(channel.gainR);
882
866
  if (channel.sostenutoPedal) {
883
867
  channel.sostenutoNotes.set(noteNumber, note);
884
868
  }
@@ -1021,13 +1005,13 @@ export class MidyGM2 {
1021
1005
  }
1022
1006
  }
1023
1007
  handlePitchBendMessage(channelNumber, lsb, msb) {
1024
- const pitchBend = msb * 128 + lsb;
1008
+ const pitchBend = msb * 128 + lsb - 8192;
1025
1009
  this.setPitchBend(channelNumber, pitchBend);
1026
1010
  }
1027
1011
  setPitchBend(channelNumber, pitchBend) {
1028
1012
  const channel = this.channels[channelNumber];
1029
1013
  const prevPitchBend = channel.pitchBend;
1030
- channel.pitchBend = (pitchBend - 8192) / 8192;
1014
+ channel.pitchBend = pitchBend / 8192;
1031
1015
  const detuneChange = (channel.pitchBend - prevPitchBend) *
1032
1016
  channel.pitchBendRange * 100;
1033
1017
  this.updateDetune(channel, detuneChange);
@@ -1162,17 +1146,30 @@ export class MidyGM2 {
1162
1146
  this.channels[channelNumber].portamento = value >= 64;
1163
1147
  }
1164
1148
  setReverbSendLevel(channelNumber, reverbSendLevel) {
1165
- const now = this.audioContext.currentTime;
1166
1149
  const channel = this.channels[channelNumber];
1167
- const reverbEffect = channel.reverbEffect;
1168
- channel.reverbSendLevel = reverbSendLevel / 127;
1169
- reverbEffect.output.gain.cancelScheduledValues(now);
1170
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1150
+ const reverbEffect = this.reverbEffect;
1151
+ if (0 < reverbSendLevel) {
1152
+ const now = this.audioContext.currentTime;
1153
+ channel.reverbSendLevel = reverbSendLevel / 127;
1154
+ reverbEffect.output.gain.cancelScheduledValues(now);
1155
+ reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1156
+ }
1157
+ else if (channel.reverbSendLevel !== 0) {
1158
+ channel.merger.disconnect(reverbEffect.input);
1159
+ }
1171
1160
  }
1172
1161
  setChorusSendLevel(channelNumber, chorusSendLevel) {
1173
1162
  const channel = this.channels[channelNumber];
1174
- channel.chorusSendLevel = chorusSendLevel / 127;
1175
- channel.chorusEffect.lfoGain = channel.chorusSendLevel;
1163
+ const chorusEffect = this.chorusEffect;
1164
+ if (0 < chorusSendLevel) {
1165
+ const now = this.audioContext.currentTime;
1166
+ channel.chorusSendLevel = chorusSendLevel / 127;
1167
+ chorusEffect.output.gain.cancelScheduledValues(now);
1168
+ chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1169
+ }
1170
+ else if (channel.chorusSendLevel !== 0) {
1171
+ channel.merger.disconnect(chorusEffect.input);
1172
+ }
1176
1173
  }
1177
1174
  setSostenutoPedal(channelNumber, value) {
1178
1175
  const isOn = value >= 64;
@@ -1337,7 +1334,7 @@ export class MidyGM2 {
1337
1334
  channel.scheduledNotes.forEach((noteList) => {
1338
1335
  const activeNote = this.getActiveNote(noteList, now);
1339
1336
  if (activeNote) {
1340
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
1337
+ const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
1341
1338
  promises.push(notePromise);
1342
1339
  }
1343
1340
  });
@@ -1404,7 +1401,7 @@ export class MidyGM2 {
1404
1401
  case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1405
1402
  return this.handleMasterCoarseTuningSysEx(data);
1406
1403
  case 5:
1407
- return this.handleGlobalParameterControl(data);
1404
+ return this.handleGlobalParameterControlSysEx(data);
1408
1405
  default:
1409
1406
  console.warn(`Unsupported Exclusive Message: ${data}`);
1410
1407
  }
@@ -1481,13 +1478,13 @@ export class MidyGM2 {
1481
1478
  this.masterCoarseTuning = coarseTuning - 64;
1482
1479
  }
1483
1480
  }
1484
- handleGlobalParameterControl(data) {
1485
- if (data[5] === 1) {
1486
- switch (data[6]) {
1481
+ handleGlobalParameterControlSysEx(data) {
1482
+ if (data[7] === 1) {
1483
+ switch (data[8]) {
1487
1484
  case 1:
1488
- return this.handleReverbParameter(data);
1485
+ return this.handleReverbParameterSysEx(data);
1489
1486
  case 2:
1490
- return this.handleChorusParameter(data);
1487
+ return this.handleChorusParameterSysEx(data);
1491
1488
  default:
1492
1489
  console.warn(`Unsupported Global Parameter Control Message: ${data}`);
1493
1490
  }
@@ -1496,21 +1493,19 @@ export class MidyGM2 {
1496
1493
  console.warn(`Unsupported Global Parameter Control Message: ${data}`);
1497
1494
  }
1498
1495
  }
1499
- handleReverbParameter(data) {
1500
- switch (data[7]) {
1496
+ handleReverbParameterSysEx(data) {
1497
+ switch (data[9]) {
1501
1498
  case 0:
1502
- return this.setReverbType(data[8]);
1499
+ return this.setReverbType(data[10]);
1503
1500
  case 1:
1504
- return this.setReverbTime(data[8]);
1501
+ return this.setReverbTime(data[10]);
1505
1502
  }
1506
1503
  }
1507
1504
  setReverbType(type) {
1508
1505
  this.reverb.time = this.getReverbTimeFromType(type);
1509
- this.reverb.feedback = (type === 8) ? 0.1 : 0.2;
1510
- const { audioContext, channels, options } = this;
1511
- for (let i = 0; i < channels.length; i++) {
1512
- channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
1513
- }
1506
+ this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
1507
+ const { audioContext, options } = this;
1508
+ this.reverbEffect = options.reverbAlgorithm(audioContext);
1514
1509
  }
1515
1510
  getReverbTimeFromType(type) {
1516
1511
  switch (type) {
@@ -1561,41 +1556,111 @@ export class MidyGM2 {
1561
1556
  // RT60 = -3 * delay / Math.log10(feedback)
1562
1557
  // feedback = Math.pow(10, -3 * delay / RT60)
1563
1558
  // delay estimation using ideal feedback
1564
- // A suitable average sound absorption coefficient is 0.18-0.28.
1565
- // Since the structure of the hall is complex,
1566
- // It would be easier to determine the delay based on the ideal feedback.
1559
+ // The structure of a concert hall is complex,
1560
+ // so estimates based on mean free path are unstable.
1561
+ // It is easier to determine the delay based on ideal feedback.
1562
+ // The average sound absorption coefficient
1563
+ // suitable for playing musical instruments is 0.18 to 0.28.
1567
1564
  // delay = -RT60 * Math.log10(feedback) / 3
1568
1565
  calcDelay(rt60, feedback) {
1569
1566
  return -rt60 * Math.log10(feedback) / 3;
1570
1567
  }
1571
- handleChorusParameter(data) {
1572
- switch (data[7]) {
1568
+ handleChorusParameterSysEx(data) {
1569
+ switch (data[9]) {
1573
1570
  case 0:
1574
- return this.setChorusType(data[8]);
1571
+ return this.setChorusType(data[10]);
1575
1572
  case 1:
1576
- return this.setChorusModRate(data[8]);
1573
+ return this.setChorusModRate(data[10]);
1577
1574
  case 2:
1578
- return this.setChorusModDepth(data[8]);
1575
+ return this.setChorusModDepth(data[10]);
1579
1576
  case 3:
1580
- return this.setChorusFeedback(data[8]);
1577
+ return this.setChorusFeedback(data[10]);
1581
1578
  case 4:
1582
- return this.setChorusSendToReverb(data[8]);
1579
+ return this.setChorusSendToReverb(data[10]);
1583
1580
  }
1584
1581
  }
1585
1582
  setChorusType(type) {
1586
- // TODO
1583
+ switch (type) {
1584
+ case 0:
1585
+ return this.setChorusParameter(3, 5, 0, 0);
1586
+ case 1:
1587
+ return this.setChorusParameter(9, 19, 5, 0);
1588
+ case 2:
1589
+ return this.setChorusParameter(3, 19, 8, 0);
1590
+ case 3:
1591
+ return this.setChorusParameter(9, 16, 16, 0);
1592
+ case 4:
1593
+ return this.setChorusParameter(2, 24, 64, 0);
1594
+ case 5:
1595
+ return this.setChorusParameter(1, 5, 112, 0);
1596
+ default:
1597
+ console.warn(`Unsupported Chorus Type: ${type}`);
1598
+ }
1599
+ }
1600
+ setChorusParameter(modRate, modDepth, feedback, sendToReverb) {
1601
+ this.setChorusModRate(modRate);
1602
+ this.setChorusModDepth(modDepth);
1603
+ this.setChorusFeedback(feedback);
1604
+ this.setChorusSendToReverb(sendToReverb);
1587
1605
  }
1588
1606
  setChorusModRate(value) {
1589
- // TODO
1607
+ const now = this.audioContext.currentTime;
1608
+ const modRate = this.getChorusModRate(value);
1609
+ this.chorus.modRate = modRate;
1610
+ for (let i = 0; i < this.channels.length; i++) {
1611
+ const lfo = this.channels[i].chorusEffect.lfo;
1612
+ lfo.frequency.setValueAtTime(modRate, now);
1613
+ }
1614
+ }
1615
+ getChorusModRate(value) {
1616
+ return value * 0.122; // Hz
1590
1617
  }
1591
1618
  setChorusModDepth(value) {
1592
- // TODO
1619
+ const now = this.audioContext.currentTime;
1620
+ const modDepth = this.getChorusModDepth(value);
1621
+ this.chorus.modDepth = modDepth;
1622
+ for (let i = 0; i < this.channels.length; i++) {
1623
+ const chorusEffect = this.channels[i].chorusEffect;
1624
+ chorusEffect.lfoGain.gain
1625
+ .cancelScheduledValues(now)
1626
+ .setValueAtTime(modDepth / 2, now);
1627
+ }
1628
+ }
1629
+ getChorusModDepth(value) {
1630
+ return (value + 1) / 3200; // second
1593
1631
  }
1594
1632
  setChorusFeedback(value) {
1595
- // TODO
1633
+ const now = this.audioContext.currentTime;
1634
+ const feedback = this.getChorusFeedback(value);
1635
+ this.chorus.feedback = feedback;
1636
+ for (let i = 0; i < this.channels.length; i++) {
1637
+ const chorusEffect = this.channels[i].chorusEffect;
1638
+ for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
1639
+ const feedbackGain = chorusEffect.feedbackGains[j];
1640
+ feedbackGain.gain
1641
+ .cancelScheduledValues(now)
1642
+ .setValueAtTime(feedback, now);
1643
+ }
1644
+ }
1645
+ }
1646
+ getChorusFeedback(value) {
1647
+ return value * 0.00763;
1596
1648
  }
1597
1649
  setChorusSendToReverb(value) {
1598
- // TODO
1650
+ const sendToReverb = this.getChorusSendToReverb(value);
1651
+ if (0 < sendToReverb) {
1652
+ const now = this.audioContext.currentTime;
1653
+ this.chorus.sendToReverb = sendToReverb;
1654
+ this.chorusEffect.sendGain.gain
1655
+ .cancelScheduledValues(now)
1656
+ .setValueAtTime(sendToReverb, now);
1657
+ }
1658
+ else if (this.chorus.sendToReverb !== 0) {
1659
+ this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
1660
+ }
1661
+ }
1662
+ getChorusSendToReverb(value) {
1663
+ return value * 0.00787;
1599
1664
  }
1600
1665
  handleExclusiveMessage(data) {
1601
1666
  console.warn(`Unsupported Exclusive Message: ${data}`);
@@ -70,12 +70,11 @@ export class MidyGMLite {
70
70
  currentTime(): number;
71
71
  getActiveNotes(channel: any, time: any): Map<any, any>;
72
72
  getActiveNote(noteList: any, time: any): any;
73
- connectEffects(channel: any, gainNode: any): void;
74
73
  cbToRatio(cb: any): number;
75
74
  centToHz(cent: any): number;
76
75
  calcSemitoneOffset(channel: any): number;
77
76
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
78
- setVolumeEnvelope(channel: any, note: any): void;
77
+ setVolumeEnvelope(note: any): void;
79
78
  setFilterEnvelope(channel: any, note: any): void;
80
79
  startModulation(channel: any, note: any, time: any): void;
81
80
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,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;IAuBhB,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,yCAEC;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,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAn/BD;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-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,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;IAuBhB,kBAAgC;IAChC,gBAA4C;IAC5C,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;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,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAcC;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,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5+BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -144,8 +144,8 @@ export class MidyGMLite {
144
144
  });
145
145
  this.audioContext = audioContext;
146
146
  this.masterGain = new GainNode(audioContext);
147
- this.masterGain.connect(audioContext.destination);
148
147
  this.channels = this.createChannels(audioContext);
148
+ this.masterGain.connect(audioContext.destination);
149
149
  this.GM1SystemOn();
150
150
  }
151
151
  initSoundFontTable() {
@@ -189,6 +189,7 @@ export class MidyGMLite {
189
189
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
190
190
  gainL.connect(merger, 0, 0);
191
191
  gainR.connect(merger, 0, 1);
192
+ merger.connect(this.masterGain);
192
193
  return {
193
194
  gainL,
194
195
  gainR,
@@ -491,10 +492,6 @@ export class MidyGMLite {
491
492
  }
492
493
  return noteList[0];
493
494
  }
494
- connectEffects(channel, gainNode) {
495
- gainNode.connect(channel.merger);
496
- merger.connect(this.masterGain);
497
- }
498
495
  cbToRatio(cb) {
499
496
  return Math.pow(10, cb / 200);
500
497
  }
@@ -508,14 +505,10 @@ export class MidyGMLite {
508
505
  return instrumentKey.playbackRate(noteNumber) *
509
506
  Math.pow(2, semitoneOffset / 12);
510
507
  }
511
- setVolumeEnvelope(channel, note) {
512
- const { instrumentKey, startTime, velocity } = note;
508
+ setVolumeEnvelope(note) {
509
+ const { instrumentKey, startTime } = note;
513
510
  note.gainNode = new GainNode(this.audioContext, { gain: 0 });
514
- let volume = (velocity / 127) * channel.volume * channel.expression;
515
- if (volume === 0)
516
- volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
517
- const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
518
- volume;
511
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
519
512
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
520
513
  const volDelay = startTime + instrumentKey.volDelay;
521
514
  const volAttack = volDelay + instrumentKey.volAttack;
@@ -575,7 +568,7 @@ export class MidyGMLite {
575
568
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
576
569
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
577
570
  note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
578
- this.setVolumeEnvelope(channel, note);
571
+ this.setVolumeEnvelope(note);
579
572
  this.setFilterEnvelope(channel, note);
580
573
  if (channel.modulation > 0) {
581
574
  const delayModLFO = startTime + instrumentKey.delayModLFO;
@@ -598,7 +591,8 @@ export class MidyGMLite {
598
591
  if (!instrumentKey)
599
592
  return;
600
593
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
601
- this.connectEffects(channel, note.gainNode);
594
+ note.gainNode.connect(channel.gainL);
595
+ note.gainNode.connect(channel.gainR);
602
596
  const scheduledNotes = channel.scheduledNotes;
603
597
  if (scheduledNotes.has(noteNumber)) {
604
598
  scheduledNotes.get(noteNumber).push(note);
@@ -701,13 +695,13 @@ export class MidyGMLite {
701
695
  channel.program = program;
702
696
  }
703
697
  handlePitchBendMessage(channelNumber, lsb, msb) {
704
- const pitchBend = msb * 128 + lsb;
698
+ const pitchBend = msb * 128 + lsb - 8192;
705
699
  this.setPitchBend(channelNumber, pitchBend);
706
700
  }
707
701
  setPitchBend(channelNumber, pitchBend) {
708
702
  const channel = this.channels[channelNumber];
709
703
  const prevPitchBend = channel.pitchBend;
710
- channel.pitchBend = (pitchBend - 8192) / 8192;
704
+ channel.pitchBend = pitchBend / 8192;
711
705
  const detuneChange = (channel.pitchBend - prevPitchBend) *
712
706
  channel.pitchBendRange * 100;
713
707
  this.updateDetune(channel, detuneChange);
@@ -877,7 +871,7 @@ export class MidyGMLite {
877
871
  channel.scheduledNotes.forEach((noteList) => {
878
872
  const activeNote = this.getActiveNote(noteList, now);
879
873
  if (activeNote) {
880
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
874
+ const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
881
875
  promises.push(notePromise);
882
876
  }
883
877
  });
package/esm/midy.d.ts CHANGED
@@ -59,6 +59,7 @@ export class Midy {
59
59
  modDepth: number;
60
60
  feedback: number;
61
61
  sendToReverb: number;
62
+ delayTimes: any[];
62
63
  };
63
64
  mono: boolean;
64
65
  omni: boolean;
@@ -92,6 +93,19 @@ export class Midy {
92
93
  };
93
94
  masterGain: any;
94
95
  channels: any[];
96
+ reverbEffect: {
97
+ input: any;
98
+ output: any;
99
+ };
100
+ chorusEffect: {
101
+ input: any;
102
+ output: any;
103
+ sendGain: any;
104
+ lfo: any;
105
+ lfoGain: any;
106
+ delayNodes: any[];
107
+ feedbackGains: any[];
108
+ };
95
109
  initSoundFontTable(): any[];
96
110
  addSoundFont(soundFont: any): void;
97
111
  loadSoundFont(soundFontUrl: any): Promise<void>;
@@ -100,17 +114,6 @@ export class Midy {
100
114
  gainL: any;
101
115
  gainR: any;
102
116
  merger: any;
103
- reverbEffect: {
104
- input: any;
105
- output: any;
106
- };
107
- chorusEffect: {
108
- lfo: any;
109
- lfoGain: any;
110
- delayNodes: any[];
111
- chorusGains: any[];
112
- output: any;
113
- };
114
117
  };
115
118
  createChannels(audioContext: any): any[];
116
119
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
@@ -144,23 +147,24 @@ export class Midy {
144
147
  createCombFilter(audioContext: any, input: any, delay: any, feedback: any): any;
145
148
  createAllpassFilter(audioContext: any, input: any, delay: any, feedback: any): any;
146
149
  generateDistributedArray(center: any, count: any, varianceRatio?: number, randomness?: number): any[];
147
- createSchroederReverb(audioContext: any, combDelays: any, combFeedbacks: any, allpassDelays: any, allpassFeedbacks: any): {
150
+ createSchroederReverb(audioContext: any, combFeedbacks: any, combDelays: any, allpassFeedbacks: any, allpassDelays: any): {
148
151
  input: any;
149
152
  output: any;
150
153
  };
151
- createChorusEffect(audioContext: any, options?: {}): {
154
+ createChorusEffect(audioContext: any): {
155
+ input: any;
156
+ output: any;
157
+ sendGain: any;
152
158
  lfo: any;
153
159
  lfoGain: any;
154
160
  delayNodes: any[];
155
- chorusGains: any[];
156
- output: any;
161
+ feedbackGains: any[];
157
162
  };
158
- connectEffects(channel: any, gainNode: any): void;
159
163
  cbToRatio(cb: any): number;
160
164
  centToHz(cent: any): number;
161
165
  calcSemitoneOffset(channel: any): any;
162
166
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
163
- setVolumeEnvelope(channel: any, note: any): void;
167
+ setVolumeEnvelope(note: any): void;
164
168
  setFilterEnvelope(channel: any, note: any): void;
165
169
  startModulation(channel: any, note: any, time: any): void;
166
170
  startVibrato(channel: any, note: any, time: any): void;
@@ -236,19 +240,24 @@ export class Midy {
236
240
  setMasterFineTuning(fineTuning: any): void;
237
241
  handleMasterCoarseTuningSysEx(data: any): void;
238
242
  setMasterCoarseTuning(coarseTuning: any): void;
239
- handleGlobalParameterControl(data: any): void;
240
- handleReverbParameter(data: any): void;
243
+ handleGlobalParameterControlSysEx(data: any): void;
244
+ handleReverbParameterSysEx(data: any): void;
241
245
  setReverbType(type: any): void;
242
246
  getReverbTimeFromType(type: any): number | undefined;
243
247
  setReverbTime(value: any): void;
244
248
  getReverbTime(value: any): number;
245
249
  calcDelay(rt60: any, feedback: any): number;
246
- handleChorusParameter(data: any): void;
250
+ handleChorusParameterSysEx(data: any): void;
247
251
  setChorusType(type: any): void;
252
+ setChorusParameter(modRate: any, modDepth: any, feedback: any, sendToReverb: any): void;
248
253
  setChorusModRate(value: any): void;
254
+ getChorusModRate(value: any): number;
249
255
  setChorusModDepth(value: any): void;
256
+ getChorusModDepth(value: any): number;
250
257
  setChorusFeedback(value: any): void;
258
+ getChorusFeedback(value: any): number;
251
259
  setChorusSendToReverb(value: any): void;
260
+ getChorusSendToReverb(value: any): number;
252
261
  handleExclusiveMessage(data: any): void;
253
262
  handleSysEx(data: any): void;
254
263
  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;IAiCE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAgCF;;;;;OAOC;IAjHD,qBAAmB;IACnB,kBAAc;IACd,yBAAqB;IACrB,2BAAuB;IACvB;;;MAGE;IACF;;;;;MAKE;IACF,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;;;;;MA4BE;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,kFAuBC;IAED;;;;MAWC;IAED,gFAUC;IAED,mFAYC;IAED,sGAcC;IAID;;;MAiCC;IAED;;;;;;MAoCC;IAED,kDAsBC;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;IAGD,oDAEC;IAED,mEAOC;IAED,mEAIC;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,yDAiDC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,8CAeC;IAED,uCAOC;IAED,+BAOC;IAED,qDAiBC;IAED,gCAMC;IAED,kCAEC;IA2BD,4CAEC;IAED,uCAaC;IAED,+BAEC;IAED,mCAEC;IAED,oCAEC;IAED,oCAEC;IAED,wCAEC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA/xDD;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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"}