@marmooo/midy 0.2.3 → 0.2.4

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
@@ -86,6 +86,12 @@ class Note {
86
86
  writable: true,
87
87
  value: void 0
88
88
  });
89
+ Object.defineProperty(this, "pressure", {
90
+ enumerable: true,
91
+ configurable: true,
92
+ writable: true,
93
+ value: 0
94
+ });
89
95
  this.noteNumber = noteNumber;
90
96
  this.velocity = velocity;
91
97
  this.startTime = startTime;
@@ -97,7 +103,7 @@ class Note {
97
103
  const defaultControllerState = {
98
104
  noteOnVelocity: { type: 2, defaultValue: 0 },
99
105
  noteOnKeyNumber: { type: 3, defaultValue: 0 },
100
- polyPressure: { type: 10, defaultValue: 0 },
106
+ polyphonicKeyPressure: { type: 10, defaultValue: 0 },
101
107
  channelPressure: { type: 13, defaultValue: 0 },
102
108
  pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
103
109
  pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
@@ -429,12 +435,6 @@ export class Midy {
429
435
  ...this.setChannelAudioNodes(audioContext),
430
436
  scheduledNotes: new Map(),
431
437
  sostenutoNotes: new Map(),
432
- polyphonicKeyPressure: {
433
- ...this.constructor.controllerDestinationSettings,
434
- },
435
- channelPressure: {
436
- ...this.constructor.controllerDestinationSettings,
437
- },
438
438
  };
439
439
  });
440
440
  return channels;
@@ -939,28 +939,31 @@ export class Midy {
939
939
  const pitchWheel = channel.state.pitchWheel * 2 - 1;
940
940
  const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
941
941
  const pitch = pitchWheel * pitchWheelSensitivity;
942
- const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
942
+ const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
943
943
  const pressure = pressureDepth * channel.state.channelPressure;
944
944
  return tuning + pitch + pressure;
945
945
  }
946
946
  calcNoteDetune(channel, note) {
947
947
  return channel.scaleOctaveTuningTable[note.noteNumber % 12];
948
948
  }
949
- updateDetune(channel) {
950
- const now = this.audioContext.currentTime;
949
+ updateChannelDetune(channel) {
951
950
  channel.scheduledNotes.forEach((noteList) => {
952
951
  for (let i = 0; i < noteList.length; i++) {
953
952
  const note = noteList[i];
954
953
  if (!note)
955
954
  continue;
956
- const noteDetune = this.calcNoteDetune(channel, note);
957
- const detune = channel.detune + noteDetune;
958
- note.bufferSource.detune
959
- .cancelScheduledValues(now)
960
- .setValueAtTime(detune, now);
955
+ this.updateDetune(channel, note, 0);
961
956
  }
962
957
  });
963
958
  }
959
+ updateDetune(channel, note, pressure) {
960
+ const now = this.audioContext.currentTime;
961
+ const noteDetune = this.calcNoteDetune(channel, note);
962
+ const detune = channel.detune + noteDetune + pressure;
963
+ note.bufferSource.detune
964
+ .cancelScheduledValues(now)
965
+ .setValueAtTime(detune, now);
966
+ }
964
967
  getPortamentoTime(channel) {
965
968
  const factor = 5 * Math.log(10) / 127;
966
969
  const time = channel.state.portamentoTime;
@@ -978,14 +981,12 @@ export class Midy {
978
981
  .setValueAtTime(0, volDelay)
979
982
  .linearRampToValueAtTime(sustainVolume, portamentoTime);
980
983
  }
981
- setVolumeEnvelope(channel, note) {
984
+ setVolumeEnvelope(channel, note, pressure) {
982
985
  const now = this.audioContext.currentTime;
983
986
  const state = channel.state;
984
987
  const { voiceParams, startTime } = note;
985
- const pressureDepth = channel.pressureTable[2] / 64;
986
- const pressure = 1 + pressureDepth * channel.state.channelPressure;
987
988
  const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
988
- pressure;
989
+ (1 + pressure);
989
990
  const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
990
991
  const volDelay = startTime + voiceParams.volDelay;
991
992
  const volAttack = volDelay + voiceParams.volAttack * state.attackTime * 2;
@@ -1033,10 +1034,8 @@ export class Midy {
1033
1034
  const { voiceParams, noteNumber, startTime } = note;
1034
1035
  const softPedalFactor = 1 -
1035
1036
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1036
- const pressureDepth = (channel.pressureTable[1] - 64) * 15;
1037
- const pressure = pressureDepth * channel.state.channelPressure;
1038
- const baseCent = voiceParams.initialFilterFc + pressure;
1039
- const baseFreq = this.centToHz(baseCent) * softPedalFactor *
1037
+ const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
1038
+ softPedalFactor *
1040
1039
  state.brightness * 2;
1041
1040
  const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
1042
1041
  const sustainFreq = baseFreq +
@@ -1051,15 +1050,17 @@ export class Midy {
1051
1050
  .setValueAtTime(adjustedBaseFreq, modDelay)
1052
1051
  .linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
1053
1052
  }
1054
- setFilterEnvelope(channel, note) {
1053
+ setFilterEnvelope(channel, note, pressure) {
1055
1054
  const now = this.audioContext.currentTime;
1056
1055
  const state = channel.state;
1057
1056
  const { voiceParams, noteNumber, startTime } = note;
1058
1057
  const softPedalFactor = 1 -
1059
1058
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1060
- const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
1059
+ const baseCent = voiceParams.initialFilterFc + pressure;
1060
+ const baseFreq = this.centToHz(baseCent) * softPedalFactor *
1061
+ state.brightness * 2;
1062
+ const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
1061
1063
  softPedalFactor * state.brightness * 2;
1062
- const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
1063
1064
  const sustainFreq = baseFreq +
1064
1065
  (peekFreq - baseFreq) * (1 - voiceParams.modSustain);
1065
1066
  const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
@@ -1086,9 +1087,9 @@ export class Midy {
1086
1087
  gain: voiceParams.modLfoToFilterFc,
1087
1088
  });
1088
1089
  note.modulationDepth = new GainNode(this.audioContext);
1089
- this.setModLfoToPitch(channel, note);
1090
+ this.setModLfoToPitch(channel, note, 0);
1090
1091
  note.volumeDepth = new GainNode(this.audioContext);
1091
- this.setModLfoToVolume(channel, note);
1092
+ this.setModLfoToVolume(note, 0);
1092
1093
  note.modulationLFO.start(startTime + voiceParams.delayModLFO);
1093
1094
  note.modulationLFO.connect(note.filterDepth);
1094
1095
  note.filterDepth.connect(note.filterNode.frequency);
@@ -1101,8 +1102,7 @@ export class Midy {
1101
1102
  const { voiceParams } = note;
1102
1103
  const state = channel.state;
1103
1104
  note.vibratoLFO = new OscillatorNode(this.audioContext, {
1104
- frequency: this.centToHz(voiceParams.freqVibLFO) *
1105
- state.vibratoRate,
1105
+ frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
1106
1106
  });
1107
1107
  note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
1108
1108
  note.vibratoDepth = new GainNode(this.audioContext);
@@ -1131,8 +1131,8 @@ export class Midy {
1131
1131
  }
1132
1132
  else {
1133
1133
  note.portamento = false;
1134
- this.setVolumeEnvelope(channel, note);
1135
- this.setFilterEnvelope(channel, note);
1134
+ this.setVolumeEnvelope(channel, note, 0);
1135
+ this.setFilterEnvelope(channel, note, 0);
1136
1136
  }
1137
1137
  if (0 < state.vibratoDepth) {
1138
1138
  this.startVibrato(channel, note, startTime);
@@ -1347,16 +1347,12 @@ export class Midy {
1347
1347
  handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
1348
1348
  const now = this.audioContext.currentTime;
1349
1349
  const channel = this.channels[channelNumber];
1350
- pressure /= 64;
1350
+ channel.state.polyphonicKeyPressure = pressure / 127;
1351
+ const table = channel.polyphonicKeyPressureTable;
1351
1352
  const activeNotes = this.getActiveNotes(channel, now);
1352
- if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
1353
- if (activeNotes.has(noteNumber)) {
1354
- const activeNote = activeNotes.get(noteNumber);
1355
- const gain = activeNote.gainL.gain.value;
1356
- activeNote.volumeNode.gain
1357
- .cancelScheduledValues(now)
1358
- .setValueAtTime(gain * pressure, now);
1359
- }
1353
+ if (activeNotes.has(noteNumber)) {
1354
+ const note = activeNotes.get(noteNumber);
1355
+ this.applyDestinationSettings(channel, note, table);
1360
1356
  }
1361
1357
  // this.applyVoiceParams(channel, 10);
1362
1358
  }
@@ -1370,11 +1366,11 @@ export class Midy {
1370
1366
  const prev = channel.state.channelPressure;
1371
1367
  const next = value / 127;
1372
1368
  channel.state.channelPressure = next;
1373
- if (channel.pressureTable[0] !== 64) {
1374
- const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
1369
+ if (channel.channelPressureTable[0] !== 64) {
1370
+ const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
1375
1371
  channel.detune += pressureDepth * (next - prev);
1376
1372
  }
1377
- const table = channel.pressureTable;
1373
+ const table = channel.channelPressureTable;
1378
1374
  channel.scheduledNotes.forEach((noteList) => {
1379
1375
  for (let i = 0; i < noteList.length; i++) {
1380
1376
  const note = noteList[i];
@@ -1396,16 +1392,13 @@ export class Midy {
1396
1392
  const next = (value - 8192) / 8192;
1397
1393
  state.pitchWheel = value / 16383;
1398
1394
  channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
1399
- this.updateDetune(channel);
1395
+ this.updateChannelDetune(channel);
1400
1396
  this.applyVoiceParams(channel, 14);
1401
1397
  }
1402
- setModLfoToPitch(channel, note) {
1398
+ setModLfoToPitch(channel, note, pressure) {
1403
1399
  const now = this.audioContext.currentTime;
1404
- const pressureDepth = channel.pressureTable[3] / 127 * 600;
1405
- const pressure = pressureDepth * channel.state.channelPressure;
1406
1400
  const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
1407
- const baseDepth = Math.abs(modLfoToPitch) +
1408
- channel.state.modulationDepth;
1401
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1409
1402
  const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1410
1403
  note.modulationDepth.gain
1411
1404
  .cancelScheduledValues(now)
@@ -1421,22 +1414,18 @@ export class Midy {
1421
1414
  .cancelScheduledValues(now)
1422
1415
  .setValueAtTime(vibratoDepth * vibratoDepthSign, now);
1423
1416
  }
1424
- setModLfoToFilterFc(channel, note) {
1417
+ setModLfoToFilterFc(note, pressure) {
1425
1418
  const now = this.audioContext.currentTime;
1426
- const pressureDepth = channel.pressureTable[4] / 127 * 2400;
1427
- const pressure = pressureDepth * channel.state.channelPressure;
1428
1419
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
1429
1420
  note.filterDepth.gain
1430
1421
  .cancelScheduledValues(now)
1431
1422
  .setValueAtTime(modLfoToFilterFc, now);
1432
1423
  }
1433
- setModLfoToVolume(channel, note) {
1424
+ setModLfoToVolume(note, pressure) {
1434
1425
  const now = this.audioContext.currentTime;
1435
1426
  const modLfoToVolume = note.voiceParams.modLfoToVolume;
1436
1427
  const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
1437
- const pressureDepth = channel.pressureTable[5] / 127;
1438
- const pressure = 1 + pressureDepth * channel.state.channelPressure;
1439
- const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
1428
+ const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
1440
1429
  note.volumeDepth.gain
1441
1430
  .cancelScheduledValues(now)
1442
1431
  .setValueAtTime(volumeDepth, now);
@@ -1509,11 +1498,18 @@ export class Midy {
1509
1498
  .cancelScheduledValues(now)
1510
1499
  .setValueAtTime(freqModLFO, now);
1511
1500
  }
1501
+ setFreqVibLFO(channel, note) {
1502
+ const now = this.audioContext.currentTime;
1503
+ const freqVibLFO = note.voiceParams.freqVibLFO;
1504
+ note.vibratoLFO.frequency
1505
+ .cancelScheduledValues(now)
1506
+ .setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
1507
+ }
1512
1508
  createVoiceParamsHandlers() {
1513
1509
  return {
1514
1510
  modLfoToPitch: (channel, note, _prevValue) => {
1515
1511
  if (0 < channel.state.modulationDepth) {
1516
- this.setModLfoToPitch(channel, note);
1512
+ this.setModLfoToPitch(channel, note, 0);
1517
1513
  }
1518
1514
  },
1519
1515
  vibLfoToPitch: (channel, note, _prevValue) => {
@@ -1523,12 +1519,12 @@ export class Midy {
1523
1519
  },
1524
1520
  modLfoToFilterFc: (channel, note, _prevValue) => {
1525
1521
  if (0 < channel.state.modulationDepth) {
1526
- this.setModLfoToFilterFc(channel, note);
1522
+ this.setModLfoToFilterFc(note, 0);
1527
1523
  }
1528
1524
  },
1529
1525
  modLfoToVolume: (channel, note, _prevValue) => {
1530
1526
  if (0 < channel.state.modulationDepth) {
1531
- this.setModLfoToVolume(channel, note);
1527
+ this.setModLfoToVolume(note, 0);
1532
1528
  }
1533
1529
  },
1534
1530
  chorusEffectsSend: (channel, note, prevValue) => {
@@ -1554,11 +1550,7 @@ export class Midy {
1554
1550
  },
1555
1551
  freqVibLFO: (channel, note, _prevValue) => {
1556
1552
  if (0 < channel.state.vibratoDepth) {
1557
- const now = this.audioContext.currentTime;
1558
- const freqVibLFO = note.voiceParams.freqVibLFO;
1559
- note.vibratoLFO.frequency
1560
- .cancelScheduledValues(now)
1561
- .setValueAtTime(freqVibLFO * channel.state.vibratoRate, now);
1553
+ this.setFreqVibLFO(channel, note);
1562
1554
  }
1563
1555
  },
1564
1556
  };
@@ -1602,7 +1594,7 @@ export class Midy {
1602
1594
  this.setPortamentoStartFilterEnvelope(channel, note);
1603
1595
  }
1604
1596
  else {
1605
- this.setFilterEnvelope(channel, note);
1597
+ this.setFilterEnvelope(channel, note, 0);
1606
1598
  }
1607
1599
  this.setPitchEnvelope(note);
1608
1600
  }
@@ -1616,7 +1608,7 @@ export class Midy {
1616
1608
  if (key in voiceParams)
1617
1609
  noteVoiceParams[key] = voiceParams[key];
1618
1610
  }
1619
- this.setVolumeEnvelope(channel, note);
1611
+ this.setVolumeEnvelope(channel, note, 0);
1620
1612
  }
1621
1613
  }
1622
1614
  }
@@ -1837,7 +1829,7 @@ export class Midy {
1837
1829
  continue;
1838
1830
  if (note.startTime < now)
1839
1831
  continue;
1840
- this.setVolumeEnvelope(channel, note);
1832
+ this.setVolumeEnvelope(channel, note, 0);
1841
1833
  }
1842
1834
  });
1843
1835
  }
@@ -1849,7 +1841,12 @@ export class Midy {
1849
1841
  const note = noteList[i];
1850
1842
  if (!note)
1851
1843
  continue;
1852
- this.setFilterEnvelope(channel, note);
1844
+ if (note.portamento) {
1845
+ this.setPortamentoStartFilterEnvelope(channel, note);
1846
+ }
1847
+ else {
1848
+ this.setFilterEnvelope(channel, note, 0);
1849
+ }
1853
1850
  }
1854
1851
  });
1855
1852
  }
@@ -1861,7 +1858,7 @@ export class Midy {
1861
1858
  const note = noteList[i];
1862
1859
  if (!note)
1863
1860
  continue;
1864
- this.setVolumeEnvelope(channel, note);
1861
+ this.setVolumeEnvelope(channel, note, 0);
1865
1862
  }
1866
1863
  });
1867
1864
  }
@@ -1870,21 +1867,53 @@ export class Midy {
1870
1867
  channel.state.vibratoRate = vibratoRate / 64;
1871
1868
  if (channel.vibratoDepth <= 0)
1872
1869
  return;
1873
- const now = this.audioContext.currentTime;
1874
- const activeNotes = this.getActiveNotes(channel, now);
1875
- activeNotes.forEach((activeNote) => {
1876
- activeNote.vibratoLFO.frequency
1877
- .cancelScheduledValues(now)
1878
- .setValueAtTime(channel.state.vibratoRate, now);
1870
+ channel.scheduledNotes.forEach((noteList) => {
1871
+ for (let i = 0; i < noteList.length; i++) {
1872
+ const note = noteList[i];
1873
+ if (!note)
1874
+ continue;
1875
+ this.setVibLfoToPitch(channel, note);
1876
+ }
1879
1877
  });
1880
1878
  }
1881
1879
  setVibratoDepth(channelNumber, vibratoDepth) {
1882
1880
  const channel = this.channels[channelNumber];
1881
+ const prev = channel.state.vibratoDepth;
1883
1882
  channel.state.vibratoDepth = vibratoDepth / 64;
1883
+ if (0 < prev) {
1884
+ channel.scheduledNotes.forEach((noteList) => {
1885
+ for (let i = 0; i < noteList.length; i++) {
1886
+ const note = noteList[i];
1887
+ if (!note)
1888
+ continue;
1889
+ this.setFreqVibLFO(channel, note);
1890
+ }
1891
+ });
1892
+ }
1893
+ else {
1894
+ channel.scheduledNotes.forEach((noteList) => {
1895
+ for (let i = 0; i < noteList.length; i++) {
1896
+ const note = noteList[i];
1897
+ if (!note)
1898
+ continue;
1899
+ this.startVibrato(channel, note, note.startTime);
1900
+ }
1901
+ });
1902
+ }
1884
1903
  }
1885
1904
  setVibratoDelay(channelNumber, vibratoDelay) {
1886
1905
  const channel = this.channels[channelNumber];
1887
1906
  channel.state.vibratoDelay = vibratoDelay / 64;
1907
+ if (0 < channel.state.vibratoDepth) {
1908
+ channel.scheduledNotes.forEach((noteList) => {
1909
+ for (let i = 0; i < noteList.length; i++) {
1910
+ const note = noteList[i];
1911
+ if (!note)
1912
+ continue;
1913
+ this.startVibrato(channel, note, note.startTime);
1914
+ }
1915
+ });
1916
+ }
1888
1917
  }
1889
1918
  setReverbSendLevel(channelNumber, reverbSendLevel) {
1890
1919
  const channel = this.channels[channelNumber];
@@ -2049,7 +2078,7 @@ export class Midy {
2049
2078
  const next = value / 128;
2050
2079
  state.pitchWheelSensitivity = next;
2051
2080
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
2052
- this.updateDetune(channel);
2081
+ this.updateChannelDetune(channel);
2053
2082
  this.applyVoiceParams(channel, 16);
2054
2083
  }
2055
2084
  handleFineTuningRPN(channelNumber) {
@@ -2064,7 +2093,7 @@ export class Midy {
2064
2093
  const next = (value - 8192) / 8.192; // cent
2065
2094
  channel.fineTuning = next;
2066
2095
  channel.detune += next - prev;
2067
- this.updateDetune(channel);
2096
+ this.updateChannelDetune(channel);
2068
2097
  }
2069
2098
  handleCoarseTuningRPN(channelNumber) {
2070
2099
  const channel = this.channels[channelNumber];
@@ -2078,7 +2107,7 @@ export class Midy {
2078
2107
  const next = (value - 64) * 100; // cent
2079
2108
  channel.coarseTuning = next;
2080
2109
  channel.detune += next - prev;
2081
- this.updateDetune(channel);
2110
+ this.updateChannelDetune(channel);
2082
2111
  }
2083
2112
  handleModulationDepthRangeRPN(channelNumber) {
2084
2113
  const channel = this.channels[channelNumber];
@@ -2109,7 +2138,7 @@ export class Midy {
2109
2138
  const state = channel.state;
2110
2139
  for (let i = 0; i < stateTypes.length; i++) {
2111
2140
  const type = stateTypes[i];
2112
- state[type] = defaultControllerState[type];
2141
+ state[type] = defaultControllerState[type].defaultValue;
2113
2142
  }
2114
2143
  const settingTypes = [
2115
2144
  "rpnMSB",
@@ -2212,7 +2241,9 @@ export class Midy {
2212
2241
  case 9:
2213
2242
  switch (data[3]) {
2214
2243
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2215
- return this.handleChannelPressureSysEx(data);
2244
+ return this.handlePressureSysEx(data, "channelPressureTable");
2245
+ case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2246
+ return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
2216
2247
  case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2217
2248
  return this.handleControlChangeSysEx(data);
2218
2249
  default:
@@ -2254,7 +2285,7 @@ export class Midy {
2254
2285
  const next = (value - 8192) / 8.192; // cent
2255
2286
  this.masterFineTuning = next;
2256
2287
  channel.detune += next - prev;
2257
- this.updateDetune(channel);
2288
+ this.updateChannelDetune(channel);
2258
2289
  }
2259
2290
  handleMasterCoarseTuningSysEx(data) {
2260
2291
  const coarseTuning = data[4];
@@ -2265,7 +2296,7 @@ export class Midy {
2265
2296
  const next = (value - 64) * 100; // cent
2266
2297
  this.masterCoarseTuning = next;
2267
2298
  channel.detune += next - prev;
2268
- this.updateDetune(channel);
2299
+ this.updateChannelDetune(channel);
2269
2300
  }
2270
2301
  handleGlobalParameterControlSysEx(data) {
2271
2302
  if (data[7] === 1) {
@@ -2489,29 +2520,63 @@ export class Midy {
2489
2520
  }
2490
2521
  applyDestinationSettings(channel, note, table) {
2491
2522
  if (table[0] !== 64) {
2492
- this.updateDetune(channel);
2523
+ const polyphonicKeyPressure = (0 < note.pressure)
2524
+ ? channel.polyphonicKeyPressureTable[0] * note.pressure
2525
+ : 0;
2526
+ const pressure = (polyphonicKeyPressure - 64) / 37.5; // 2400 / 64;
2527
+ this.updateDetune(channel, note, pressure);
2493
2528
  }
2494
2529
  if (!note.portamento) {
2495
2530
  if (table[1] !== 64) {
2496
- this.setFilterEnvelope(channel, note);
2531
+ const channelPressure = channel.channelPressureTable[1] *
2532
+ channel.state.channelPressure;
2533
+ const polyphonicKeyPressure = (0 < note.pressure)
2534
+ ? channel.polyphonicKeyPressureTable[1] * note.pressure
2535
+ : 0;
2536
+ const pressure = (channelPressure + polyphonicKeyPressure - 128) * 15;
2537
+ this.setFilterEnvelope(channel, note, pressure);
2497
2538
  }
2498
2539
  if (table[2] !== 64) {
2499
- this.setVolumeEnvelope(channel, note);
2540
+ const channelPressure = channel.channelPressureTable[2] *
2541
+ channel.state.channelPressure;
2542
+ const polyphonicKeyPressure = (0 < note.pressure)
2543
+ ? channel.polyphonicKeyPressureTable[2] * note.pressure
2544
+ : 0;
2545
+ const pressure = (channelPressure + polyphonicKeyPressure) / 128;
2546
+ this.setVolumeEnvelope(channel, note, pressure);
2500
2547
  }
2501
2548
  }
2502
2549
  if (table[3] !== 0) {
2503
- this.setModLfoToPitch(channel, note);
2550
+ const channelPressure = channel.channelPressureTable[3] *
2551
+ channel.state.channelPressure;
2552
+ const polyphonicKeyPressure = (0 < note.pressure)
2553
+ ? channel.polyphonicKeyPressureTable[3] * note.pressure
2554
+ : 0;
2555
+ const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 600;
2556
+ this.setModLfoToPitch(channel, note, pressure);
2504
2557
  }
2505
2558
  if (table[4] !== 0) {
2506
- this.setModLfoToFilterFc(channel, note);
2559
+ const channelPressure = channel.channelPressureTable[4] *
2560
+ channel.state.channelPressure;
2561
+ const polyphonicKeyPressure = (0 < note.pressure)
2562
+ ? channel.polyphonicKeyPressureTable[4] * note.pressure
2563
+ : 0;
2564
+ const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 2400;
2565
+ this.setModLfoToFilterFc(note, pressure);
2507
2566
  }
2508
2567
  if (table[5] !== 0) {
2509
- this.setModLfoToVolume(channel, note);
2568
+ const channelPressure = channel.channelPressureTable[5] *
2569
+ channel.state.channelPressure;
2570
+ const polyphonicKeyPressure = (0 < note.pressure)
2571
+ ? channel.polyphonicKeyPressureTable[5] * note.pressure
2572
+ : 0;
2573
+ const pressure = (channelPressure + polyphonicKeyPressure) / 254;
2574
+ this.setModLfoToVolume(note, pressure);
2510
2575
  }
2511
2576
  }
2512
- handleChannelPressureSysEx(data) {
2577
+ handleChannelPressureSysEx(data, tableName) {
2513
2578
  const channelNumber = data[4];
2514
- const table = this.channels[channelNumber].pressureTable;
2579
+ const table = this.channels[channelNumber][tableName];
2515
2580
  for (let i = 5; i < data.length - 1; i += 2) {
2516
2581
  const pp = data[i];
2517
2582
  const rr = data[i + 1];
@@ -2602,7 +2667,8 @@ Object.defineProperty(Midy, "channelSettings", {
2602
2667
  currentBufferSource: null,
2603
2668
  detune: 0,
2604
2669
  scaleOctaveTuningTable: new Array(12).fill(0), // cent
2605
- pressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2670
+ channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2671
+ polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2606
2672
  keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
2607
2673
  program: 0,
2608
2674
  bank: 121 * 128,
@@ -2617,16 +2683,3 @@ Object.defineProperty(Midy, "channelSettings", {
2617
2683
  modulationDepthRange: 50, // cent
2618
2684
  }
2619
2685
  });
2620
- Object.defineProperty(Midy, "controllerDestinationSettings", {
2621
- enumerable: true,
2622
- configurable: true,
2623
- writable: true,
2624
- value: {
2625
- pitchControl: 0,
2626
- filterCutoffControl: 0,
2627
- amplitudeControl: 1,
2628
- lfoPitchDepth: 0,
2629
- lfoFilterDepth: 0,
2630
- lfoAmplitudeDepth: 0,
2631
- }
2632
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
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",
@@ -97,7 +97,8 @@ export class MidyGM1 {
97
97
  centToRate(cent: any): number;
98
98
  centToHz(cent: any): number;
99
99
  calcChannelDetune(channel: any): any;
100
- updateDetune(channel: any): void;
100
+ updateChannelDetune(channel: any): void;
101
+ updateDetune(channel: any, note: any): void;
101
102
  setVolumeEnvelope(note: any): void;
102
103
  setPitchEnvelope(note: any): void;
103
104
  clampCutoffFrequency(frequency: any): number;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqFA;IAoBE;;;;;;;;;;;;MAYE;IAEF,+BAQC;IAzCD,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;IAClB,iCAA8B;IAiB5B,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,iCAWC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MA4CC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EAWC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,8CAKC;IAED,oDAOC;IAED,gDAKC;IAED,sDAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAjzCD;IAUE,0FAMC;IAfD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqFA;IAoBE;;;;;;;;;;;;MAYE;IAEF,+BAQC;IAzCD,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;IAClB,iCAA8B;IAiB5B,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MA4CC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EAWC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,8CAKC;IAED,oDAOC;IAED,gDAKC;IAED,sDAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AArzCD;IAUE,0FAMC;IAfD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}