@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.js CHANGED
@@ -82,7 +82,7 @@ export class Midy {
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 Midy {
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 Midy {
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 Midy {
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) {
@@ -691,12 +693,10 @@ export class Midy {
691
693
  }
692
694
  // https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
693
695
  // M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
694
- createSchroederReverb(audioContext, combDelays, combFeedbacks, allpassDelays, allpassFeedbacks) {
696
+ createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
695
697
  const input = new GainNode(audioContext);
696
698
  const output = new GainNode(audioContext);
697
- const mergerGain = new GainNode(audioContext, {
698
- gain: 1 / (combDelays.length * 2),
699
- });
699
+ const mergerGain = new GainNode(audioContext);
700
700
  for (let i = 0; i < combDelays.length; i++) {
701
701
  const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
702
702
  comb.connect(mergerGain);
@@ -709,62 +709,49 @@ export class Midy {
709
709
  allpasses.at(-1).connect(output);
710
710
  return { input, output };
711
711
  }
712
- createChorusEffect(audioContext, options = {}) {
713
- const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
714
- const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
715
- const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
712
+ createChorusEffect(audioContext) {
713
+ const input = new GainNode(audioContext);
716
714
  const output = new GainNode(audioContext);
717
- const chorusGains = [];
715
+ const sendGain = new GainNode(audioContext);
716
+ const lfo = new OscillatorNode(audioContext, {
717
+ frequency: this.chorus.modRate,
718
+ });
719
+ const lfoGain = new GainNode(audioContext, {
720
+ gain: this.chorus.modDepth / 2,
721
+ });
722
+ const delayTimes = this.chorus.delayTimes;
718
723
  const delayNodes = [];
719
- const baseGain = 1 / chorusCount;
720
- for (let i = 0; i < chorusCount; i++) {
721
- const randomDelayFactor = (Math.random() - 0.5) * variance;
722
- const delayTime = (i + 1) * delay + randomDelayFactor;
724
+ const feedbackGains = [];
725
+ for (let i = 0; i < delayTimes.length; i++) {
726
+ const delayTime = delayTimes[i];
723
727
  const delayNode = new DelayNode(audioContext, {
724
- maxDelayTime: delayTime,
728
+ maxDelayTime: 0.1, // generally, 5ms < delayTime < 50ms
729
+ delayTime,
730
+ });
731
+ const feedbackGain = new GainNode(audioContext, {
732
+ gain: this.chorus.feedback,
725
733
  });
726
- const chorusGain = new GainNode(audioContext, { gain: baseGain });
727
734
  delayNodes.push(delayNode);
728
- chorusGains.push(chorusGain);
735
+ feedbackGains.push(feedbackGain);
736
+ input.connect(delayNode);
729
737
  lfoGain.connect(delayNode.delayTime);
730
- delayNode.connect(chorusGain);
731
- chorusGain.connect(output);
738
+ delayNode.connect(feedbackGain);
739
+ feedbackGain.connect(delayNode);
740
+ delayNode.connect(output);
732
741
  }
742
+ output.connect(sendGain);
733
743
  lfo.connect(lfoGain);
734
744
  lfo.start();
735
745
  return {
746
+ input,
747
+ output,
748
+ sendGain,
736
749
  lfo,
737
750
  lfoGain,
738
751
  delayNodes,
739
- chorusGains,
740
- output,
752
+ feedbackGains,
741
753
  };
742
754
  }
743
- connectEffects(channel, gainNode) {
744
- gainNode.connect(channel.merger);
745
- channel.merger.connect(this.masterGain);
746
- if (channel.reverbSendLevel === 0) {
747
- if (channel.chorusSendLevel !== 0) { // chorus
748
- channel.chorusEffect.delayNodes.forEach((delayNode) => {
749
- channel.merger.connect(delayNode);
750
- });
751
- channel.chorusEffect.output.connect(this.masterGain);
752
- }
753
- }
754
- else {
755
- if (channel.chorusSendLevel === 0) { // reverb
756
- channel.merger.connect(channel.reverbEffect.input);
757
- channel.reverbEffect.output.connect(this.masterGain);
758
- }
759
- else { // reverb + chorus
760
- channel.chorusEffect.delayNodes.forEach((delayNode) => {
761
- channel.merger.connect(delayNode);
762
- });
763
- channel.merger.connect(channel.reverbEffect.input);
764
- channel.reverbEffect.output.connect(this.masterGain);
765
- }
766
- }
767
- }
768
755
  cbToRatio(cb) {
769
756
  return Math.pow(10, cb / 200);
770
757
  }
@@ -781,14 +768,10 @@ export class Midy {
781
768
  return instrumentKey.playbackRate(noteNumber) *
782
769
  Math.pow(2, semitoneOffset / 12);
783
770
  }
784
- setVolumeEnvelope(channel, note) {
785
- const { instrumentKey, startTime, velocity } = note;
771
+ setVolumeEnvelope(note) {
772
+ const { instrumentKey, startTime } = note;
786
773
  note.gainNode = new GainNode(this.audioContext, { gain: 0 });
787
- let volume = (velocity / 127) * channel.volume * channel.expression;
788
- if (volume === 0)
789
- volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
790
- const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
791
- volume;
774
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
792
775
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
793
776
  const volDelay = startTime + instrumentKey.volDelay;
794
777
  const volAttack = volDelay + instrumentKey.volAttack;
@@ -861,7 +844,7 @@ export class Midy {
861
844
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
862
845
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
863
846
  note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
864
- this.setVolumeEnvelope(channel, note);
847
+ this.setVolumeEnvelope(note);
865
848
  this.setFilterEnvelope(channel, note);
866
849
  if (channel.modulation > 0) {
867
850
  const delayModLFO = startTime + instrumentKey.delayModLFO;
@@ -901,7 +884,8 @@ export class Midy {
901
884
  if (!instrumentKey)
902
885
  return;
903
886
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
904
- this.connectEffects(channel, note.gainNode);
887
+ note.gainNode.connect(channel.gainL);
888
+ note.gainNode.connect(channel.gainR);
905
889
  if (channel.sostenutoPedal) {
906
890
  channel.sostenutoNotes.set(noteNumber, note);
907
891
  }
@@ -1061,13 +1045,13 @@ export class Midy {
1061
1045
  }
1062
1046
  }
1063
1047
  handlePitchBendMessage(channelNumber, lsb, msb) {
1064
- const pitchBend = msb * 128 + lsb;
1048
+ const pitchBend = msb * 128 + lsb - 8192;
1065
1049
  this.setPitchBend(channelNumber, pitchBend);
1066
1050
  }
1067
1051
  setPitchBend(channelNumber, pitchBend) {
1068
1052
  const channel = this.channels[channelNumber];
1069
1053
  const prevPitchBend = channel.pitchBend;
1070
- channel.pitchBend = (pitchBend - 8192) / 8192;
1054
+ channel.pitchBend = pitchBend / 8192;
1071
1055
  const detuneChange = (channel.pitchBend - prevPitchBend) *
1072
1056
  channel.pitchBendRange * 100;
1073
1057
  this.updateDetune(channel, detuneChange);
@@ -1213,17 +1197,30 @@ export class Midy {
1213
1197
  this.channels[channelNumber].portamento = value >= 64;
1214
1198
  }
1215
1199
  setReverbSendLevel(channelNumber, reverbSendLevel) {
1216
- const now = this.audioContext.currentTime;
1217
1200
  const channel = this.channels[channelNumber];
1218
- const reverbEffect = channel.reverbEffect;
1219
- channel.reverbSendLevel = reverbSendLevel / 127;
1220
- reverbEffect.output.gain.cancelScheduledValues(now);
1221
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1201
+ const reverbEffect = this.reverbEffect;
1202
+ if (0 < reverbSendLevel) {
1203
+ const now = this.audioContext.currentTime;
1204
+ channel.reverbSendLevel = reverbSendLevel / 127;
1205
+ reverbEffect.output.gain.cancelScheduledValues(now);
1206
+ reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1207
+ }
1208
+ else if (channel.reverbSendLevel !== 0) {
1209
+ channel.merger.disconnect(reverbEffect.input);
1210
+ }
1222
1211
  }
1223
1212
  setChorusSendLevel(channelNumber, chorusSendLevel) {
1224
1213
  const channel = this.channels[channelNumber];
1225
- channel.chorusSendLevel = chorusSendLevel / 127;
1226
- channel.chorusEffect.lfoGain = channel.chorusSendLevel;
1214
+ const chorusEffect = this.chorusEffect;
1215
+ if (0 < chorusSendLevel) {
1216
+ const now = this.audioContext.currentTime;
1217
+ channel.chorusSendLevel = chorusSendLevel / 127;
1218
+ chorusEffect.output.gain.cancelScheduledValues(now);
1219
+ chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1220
+ }
1221
+ else if (channel.chorusSendLevel !== 0) {
1222
+ channel.merger.disconnect(chorusEffect.input);
1223
+ }
1227
1224
  }
1228
1225
  setSostenutoPedal(channelNumber, value) {
1229
1226
  const isOn = value >= 64;
@@ -1416,7 +1413,7 @@ export class Midy {
1416
1413
  channel.scheduledNotes.forEach((noteList) => {
1417
1414
  const activeNote = this.getActiveNote(noteList, now);
1418
1415
  if (activeNote) {
1419
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
1416
+ const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
1420
1417
  promises.push(notePromise);
1421
1418
  }
1422
1419
  });
@@ -1483,7 +1480,7 @@ export class Midy {
1483
1480
  case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1484
1481
  return this.handleMasterCoarseTuningSysEx(data);
1485
1482
  case 5:
1486
- return this.handleGlobalParameterControl(data);
1483
+ return this.handleGlobalParameterControlSysEx(data);
1487
1484
  default:
1488
1485
  console.warn(`Unsupported Exclusive Message: ${data}`);
1489
1486
  }
@@ -1560,13 +1557,13 @@ export class Midy {
1560
1557
  this.masterCoarseTuning = coarseTuning - 64;
1561
1558
  }
1562
1559
  }
1563
- handleGlobalParameterControl(data) {
1564
- if (data[5] === 1) {
1565
- switch (data[6]) {
1560
+ handleGlobalParameterControlSysEx(data) {
1561
+ if (data[7] === 1) {
1562
+ switch (data[8]) {
1566
1563
  case 1:
1567
- return this.handleReverbParameter(data);
1564
+ return this.handleReverbParameterSysEx(data);
1568
1565
  case 2:
1569
- return this.handleChorusParameter(data);
1566
+ return this.handleChorusParameterSysEx(data);
1570
1567
  default:
1571
1568
  console.warn(`Unsupported Global Parameter Control Message: ${data}`);
1572
1569
  }
@@ -1575,21 +1572,19 @@ export class Midy {
1575
1572
  console.warn(`Unsupported Global Parameter Control Message: ${data}`);
1576
1573
  }
1577
1574
  }
1578
- handleReverbParameter(data) {
1579
- switch (data[7]) {
1575
+ handleReverbParameterSysEx(data) {
1576
+ switch (data[9]) {
1580
1577
  case 0:
1581
- return this.setReverbType(data[8]);
1578
+ return this.setReverbType(data[10]);
1582
1579
  case 1:
1583
- return this.setReverbTime(data[8]);
1580
+ return this.setReverbTime(data[10]);
1584
1581
  }
1585
1582
  }
1586
1583
  setReverbType(type) {
1587
1584
  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
- }
1585
+ this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
1586
+ const { audioContext, options } = this;
1587
+ this.reverbEffect = options.reverbAlgorithm(audioContext);
1593
1588
  }
1594
1589
  getReverbTimeFromType(type) {
1595
1590
  switch (type) {
@@ -1640,41 +1635,111 @@ export class Midy {
1640
1635
  // RT60 = -3 * delay / Math.log10(feedback)
1641
1636
  // feedback = Math.pow(10, -3 * delay / RT60)
1642
1637
  // 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.
1638
+ // The structure of a concert hall is complex,
1639
+ // so estimates based on mean free path are unstable.
1640
+ // It is easier to determine the delay based on ideal feedback.
1641
+ // The average sound absorption coefficient
1642
+ // suitable for playing musical instruments is 0.18 to 0.28.
1646
1643
  // delay = -RT60 * Math.log10(feedback) / 3
1647
1644
  calcDelay(rt60, feedback) {
1648
1645
  return -rt60 * Math.log10(feedback) / 3;
1649
1646
  }
1650
- handleChorusParameter(data) {
1651
- switch (data[7]) {
1647
+ handleChorusParameterSysEx(data) {
1648
+ switch (data[9]) {
1652
1649
  case 0:
1653
- return this.setChorusType(data[8]);
1650
+ return this.setChorusType(data[10]);
1654
1651
  case 1:
1655
- return this.setChorusModRate(data[8]);
1652
+ return this.setChorusModRate(data[10]);
1656
1653
  case 2:
1657
- return this.setChorusModDepth(data[8]);
1654
+ return this.setChorusModDepth(data[10]);
1658
1655
  case 3:
1659
- return this.setChorusFeedback(data[8]);
1656
+ return this.setChorusFeedback(data[10]);
1660
1657
  case 4:
1661
- return this.setChorusSendToReverb(data[8]);
1658
+ return this.setChorusSendToReverb(data[10]);
1662
1659
  }
1663
1660
  }
1664
1661
  setChorusType(type) {
1665
- // TODO
1662
+ switch (type) {
1663
+ case 0:
1664
+ return this.setChorusParameter(3, 5, 0, 0);
1665
+ case 1:
1666
+ return this.setChorusParameter(9, 19, 5, 0);
1667
+ case 2:
1668
+ return this.setChorusParameter(3, 19, 8, 0);
1669
+ case 3:
1670
+ return this.setChorusParameter(9, 16, 16, 0);
1671
+ case 4:
1672
+ return this.setChorusParameter(2, 24, 64, 0);
1673
+ case 5:
1674
+ return this.setChorusParameter(1, 5, 112, 0);
1675
+ default:
1676
+ console.warn(`Unsupported Chorus Type: ${type}`);
1677
+ }
1678
+ }
1679
+ setChorusParameter(modRate, modDepth, feedback, sendToReverb) {
1680
+ this.setChorusModRate(modRate);
1681
+ this.setChorusModDepth(modDepth);
1682
+ this.setChorusFeedback(feedback);
1683
+ this.setChorusSendToReverb(sendToReverb);
1666
1684
  }
1667
1685
  setChorusModRate(value) {
1668
- // TODO
1686
+ const now = this.audioContext.currentTime;
1687
+ const modRate = this.getChorusModRate(value);
1688
+ this.chorus.modRate = modRate;
1689
+ for (let i = 0; i < this.channels.length; i++) {
1690
+ const lfo = this.channels[i].chorusEffect.lfo;
1691
+ lfo.frequency.setValueAtTime(modRate, now);
1692
+ }
1693
+ }
1694
+ getChorusModRate(value) {
1695
+ return value * 0.122; // Hz
1669
1696
  }
1670
1697
  setChorusModDepth(value) {
1671
- // TODO
1698
+ const now = this.audioContext.currentTime;
1699
+ const modDepth = this.getChorusModDepth(value);
1700
+ this.chorus.modDepth = modDepth;
1701
+ for (let i = 0; i < this.channels.length; i++) {
1702
+ const chorusEffect = this.channels[i].chorusEffect;
1703
+ chorusEffect.lfoGain.gain
1704
+ .cancelScheduledValues(now)
1705
+ .setValueAtTime(modDepth / 2, now);
1706
+ }
1707
+ }
1708
+ getChorusModDepth(value) {
1709
+ return (value + 1) / 3200; // second
1672
1710
  }
1673
1711
  setChorusFeedback(value) {
1674
- // TODO
1712
+ const now = this.audioContext.currentTime;
1713
+ const feedback = this.getChorusFeedback(value);
1714
+ this.chorus.feedback = feedback;
1715
+ for (let i = 0; i < this.channels.length; i++) {
1716
+ const chorusEffect = this.channels[i].chorusEffect;
1717
+ for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
1718
+ const feedbackGain = chorusEffect.feedbackGains[j];
1719
+ feedbackGain.gain
1720
+ .cancelScheduledValues(now)
1721
+ .setValueAtTime(feedback, now);
1722
+ }
1723
+ }
1724
+ }
1725
+ getChorusFeedback(value) {
1726
+ return value * 0.00763;
1675
1727
  }
1676
1728
  setChorusSendToReverb(value) {
1677
- // TODO
1729
+ const sendToReverb = this.getChorusSendToReverb(value);
1730
+ if (0 < sendToReverb) {
1731
+ const now = this.audioContext.currentTime;
1732
+ this.chorus.sendToReverb = sendToReverb;
1733
+ this.chorusEffect.sendGain.gain
1734
+ .cancelScheduledValues(now)
1735
+ .setValueAtTime(sendToReverb, now);
1736
+ }
1737
+ else if (this.chorus.sendToReverb !== 0) {
1738
+ this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
1739
+ }
1740
+ }
1741
+ getChorusSendToReverb(value) {
1742
+ return value * 0.00787;
1678
1743
  }
1679
1744
  handleExclusiveMessage(data) {
1680
1745
  console.warn(`Unsupported Exclusive Message: ${data}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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",
@@ -72,12 +72,11 @@ export class MidyGM1 {
72
72
  currentTime(): number;
73
73
  getActiveNotes(channel: any, time: any): Map<any, any>;
74
74
  getActiveNote(noteList: any, time: any): any;
75
- connectEffects(channel: any, gainNode: any): void;
76
75
  cbToRatio(cb: any): number;
77
76
  centToHz(cent: any): number;
78
77
  calcSemitoneOffset(channel: any): any;
79
78
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
80
- setVolumeEnvelope(channel: any, note: any): void;
79
+ setVolumeEnvelope(note: any): void;
81
80
  setFilterEnvelope(channel: any, note: any): void;
82
81
  startModulation(channel: any, note: any, time: any): void;
83
82
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
@@ -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;;;;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"}
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;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,sCAGC;IAED,mFAGC;IAED,mCAcC;IACD,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;AA3iCD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -147,8 +147,8 @@ class MidyGM1 {
147
147
  });
148
148
  this.audioContext = audioContext;
149
149
  this.masterGain = new GainNode(audioContext);
150
- this.masterGain.connect(audioContext.destination);
151
150
  this.channels = this.createChannels(audioContext);
151
+ this.masterGain.connect(audioContext.destination);
152
152
  this.GM1SystemOn();
153
153
  }
154
154
  initSoundFontTable() {
@@ -192,6 +192,7 @@ 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
+ merger.connect(this.masterGain);
195
196
  return {
196
197
  gainL,
197
198
  gainR,
@@ -494,10 +495,6 @@ class MidyGM1 {
494
495
  }
495
496
  return noteList[0];
496
497
  }
497
- connectEffects(channel, gainNode) {
498
- gainNode.connect(channel.merger);
499
- merger.connect(this.masterGain);
500
- }
501
498
  cbToRatio(cb) {
502
499
  return Math.pow(10, cb / 200);
503
500
  }
@@ -512,14 +509,10 @@ class MidyGM1 {
512
509
  return instrumentKey.playbackRate(noteNumber) *
513
510
  Math.pow(2, semitoneOffset / 12);
514
511
  }
515
- setVolumeEnvelope(channel, note) {
516
- const { instrumentKey, startTime, velocity } = note;
512
+ setVolumeEnvelope(note) {
513
+ const { instrumentKey, startTime } = note;
517
514
  note.gainNode = new GainNode(this.audioContext, { gain: 0 });
518
- let volume = (velocity / 127) * channel.volume * channel.expression;
519
- if (volume === 0)
520
- volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
521
- const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
522
- volume;
515
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
523
516
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
524
517
  const volDelay = startTime + instrumentKey.volDelay;
525
518
  const volAttack = volDelay + instrumentKey.volAttack;
@@ -579,7 +572,7 @@ class MidyGM1 {
579
572
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
580
573
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
581
574
  note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
582
- this.setVolumeEnvelope(channel, note);
575
+ this.setVolumeEnvelope(note);
583
576
  this.setFilterEnvelope(channel, note);
584
577
  if (channel.modulation > 0) {
585
578
  const delayModLFO = startTime + instrumentKey.delayModLFO;
@@ -602,7 +595,8 @@ class MidyGM1 {
602
595
  if (!instrumentKey)
603
596
  return;
604
597
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
605
- this.connectEffects(channel, note.gainNode);
598
+ note.gainNode.connect(channel.gainL);
599
+ note.gainNode.connect(channel.gainR);
606
600
  const scheduledNotes = channel.scheduledNotes;
607
601
  if (scheduledNotes.has(noteNumber)) {
608
602
  scheduledNotes.get(noteNumber).push(note);
@@ -705,13 +699,13 @@ class MidyGM1 {
705
699
  channel.program = program;
706
700
  }
707
701
  handlePitchBendMessage(channelNumber, lsb, msb) {
708
- const pitchBend = msb * 128 + lsb;
702
+ const pitchBend = msb * 128 + lsb - 8192;
709
703
  this.setPitchBend(channelNumber, pitchBend);
710
704
  }
711
705
  setPitchBend(channelNumber, pitchBend) {
712
706
  const channel = this.channels[channelNumber];
713
707
  const prevPitchBend = channel.pitchBend;
714
- channel.pitchBend = (pitchBend - 8192) / 8192;
708
+ channel.pitchBend = pitchBend / 8192;
715
709
  const detuneChange = (channel.pitchBend - prevPitchBend) *
716
710
  channel.pitchBendRange * 100;
717
711
  this.updateDetune(channel, detuneChange);
@@ -939,7 +933,7 @@ class MidyGM1 {
939
933
  channel.scheduledNotes.forEach((noteList) => {
940
934
  const activeNote = this.getActiveNote(noteList, now);
941
935
  if (activeNote) {
942
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
936
+ const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
943
937
  promises.push(notePromise);
944
938
  }
945
939
  });
@@ -56,6 +56,7 @@ export class MidyGM2 {
56
56
  modDepth: number;
57
57
  feedback: number;
58
58
  sendToReverb: number;
59
+ delayTimes: any[];
59
60
  };
60
61
  mono: boolean;
61
62
  omni: boolean;
@@ -89,6 +90,19 @@ export class MidyGM2 {
89
90
  };
90
91
  masterGain: any;
91
92
  channels: any[];
93
+ reverbEffect: {
94
+ input: any;
95
+ output: any;
96
+ };
97
+ chorusEffect: {
98
+ input: any;
99
+ output: any;
100
+ sendGain: any;
101
+ lfo: any;
102
+ lfoGain: any;
103
+ delayNodes: any[];
104
+ feedbackGains: any[];
105
+ };
92
106
  initSoundFontTable(): any[];
93
107
  addSoundFont(soundFont: any): void;
94
108
  loadSoundFont(soundFontUrl: any): Promise<void>;
@@ -97,17 +111,6 @@ export class MidyGM2 {
97
111
  gainL: any;
98
112
  gainR: any;
99
113
  merger: any;
100
- reverbEffect: {
101
- input: any;
102
- output: any;
103
- };
104
- chorusEffect: {
105
- lfo: any;
106
- lfoGain: any;
107
- delayNodes: any[];
108
- chorusGains: any[];
109
- output: any;
110
- };
111
114
  };
112
115
  createChannels(audioContext: any): any[];
113
116
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
@@ -141,23 +144,24 @@ export class MidyGM2 {
141
144
  createCombFilter(audioContext: any, input: any, delay: any, feedback: any): any;
142
145
  createAllpassFilter(audioContext: any, input: any, delay: any, feedback: any): any;
143
146
  generateDistributedArray(center: any, count: any, varianceRatio?: number, randomness?: number): any[];
144
- createSchroederReverb(audioContext: any, combDelays: any, combFeedbacks: any, allpassDelays: any, allpassFeedbacks: any): {
147
+ createSchroederReverb(audioContext: any, combFeedbacks: any, combDelays: any, allpassFeedbacks: any, allpassDelays: any): {
145
148
  input: any;
146
149
  output: any;
147
150
  };
148
- createChorusEffect(audioContext: any, options?: {}): {
151
+ createChorusEffect(audioContext: any): {
152
+ input: any;
153
+ output: any;
154
+ sendGain: any;
149
155
  lfo: any;
150
156
  lfoGain: any;
151
157
  delayNodes: any[];
152
- chorusGains: any[];
153
- output: any;
158
+ feedbackGains: any[];
154
159
  };
155
- connectEffects(channel: any, gainNode: any): void;
156
160
  cbToRatio(cb: any): number;
157
161
  centToHz(cent: any): number;
158
162
  calcSemitoneOffset(channel: any): any;
159
163
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
160
- setVolumeEnvelope(channel: any, note: any): void;
164
+ setVolumeEnvelope(note: any): void;
161
165
  setFilterEnvelope(channel: any, note: any): void;
162
166
  startModulation(channel: any, note: any, time: any): void;
163
167
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
@@ -226,19 +230,24 @@ export class MidyGM2 {
226
230
  setMasterFineTuning(fineTuning: any): void;
227
231
  handleMasterCoarseTuningSysEx(data: any): void;
228
232
  setMasterCoarseTuning(coarseTuning: any): void;
229
- handleGlobalParameterControl(data: any): void;
230
- handleReverbParameter(data: any): void;
233
+ handleGlobalParameterControlSysEx(data: any): void;
234
+ handleReverbParameterSysEx(data: any): void;
231
235
  setReverbType(type: any): void;
232
236
  getReverbTimeFromType(type: any): number | undefined;
233
237
  setReverbTime(value: any): void;
234
238
  getReverbTime(value: any): number;
235
239
  calcDelay(rt60: any, feedback: any): number;
236
- handleChorusParameter(data: any): void;
240
+ handleChorusParameterSysEx(data: any): void;
237
241
  setChorusType(type: any): void;
242
+ setChorusParameter(modRate: any, modDepth: any, feedback: any, sendToReverb: any): void;
238
243
  setChorusModRate(value: any): void;
244
+ getChorusModRate(value: any): number;
239
245
  setChorusModDepth(value: any): void;
246
+ getChorusModDepth(value: any): number;
240
247
  setChorusFeedback(value: any): void;
248
+ getChorusFeedback(value: any): number;
241
249
  setChorusSendToReverb(value: any): void;
250
+ getChorusSendToReverb(value: any): number;
242
251
  handleExclusiveMessage(data: any): void;
243
252
  handleSysEx(data: any): void;
244
253
  scheduleTask(callback: any, startTime: any): Promise<any>;