@marmooo/midy 0.2.2 → 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.
@@ -100,7 +100,6 @@ class Note {
100
100
  const defaultControllerState = {
101
101
  noteOnVelocity: { type: 2, defaultValue: 0 },
102
102
  noteOnKeyNumber: { type: 3, defaultValue: 0 },
103
- polyPressure: { type: 10, defaultValue: 0 },
104
103
  channelPressure: { type: 13, defaultValue: 0 },
105
104
  pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
106
105
  pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
@@ -362,15 +361,15 @@ class MidyGM2 {
362
361
  });
363
362
  this.audioContext = audioContext;
364
363
  this.options = { ...this.defaultOptions, ...options };
365
- this.masterGain = new GainNode(audioContext);
364
+ this.masterVolume = new GainNode(audioContext);
366
365
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
367
366
  this.controlChangeHandlers = this.createControlChangeHandlers();
368
367
  this.channels = this.createChannels(audioContext);
369
368
  this.reverbEffect = this.options.reverbAlgorithm(audioContext);
370
369
  this.chorusEffect = this.createChorusEffect(audioContext);
371
- this.chorusEffect.output.connect(this.masterGain);
372
- this.reverbEffect.output.connect(this.masterGain);
373
- this.masterGain.connect(audioContext.destination);
370
+ this.chorusEffect.output.connect(this.masterVolume);
371
+ this.reverbEffect.output.connect(this.masterVolume);
372
+ this.masterVolume.connect(audioContext.destination);
374
373
  this.GM2SystemOn();
375
374
  }
376
375
  initSoundFontTable() {
@@ -416,7 +415,7 @@ class MidyGM2 {
416
415
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
417
416
  gainL.connect(merger, 0, 0);
418
417
  gainR.connect(merger, 0, 1);
419
- merger.connect(this.masterGain);
418
+ merger.connect(this.masterVolume);
420
419
  return {
421
420
  gainL,
422
421
  gainR,
@@ -428,12 +427,10 @@ class MidyGM2 {
428
427
  return {
429
428
  ...this.constructor.channelSettings,
430
429
  state: new ControllerState(),
430
+ controlTable: this.initControlTable(),
431
431
  ...this.setChannelAudioNodes(audioContext),
432
432
  scheduledNotes: new Map(),
433
433
  sostenutoNotes: new Map(),
434
- channelPressure: {
435
- ...this.constructor.controllerDestinationSettings,
436
- },
437
434
  };
438
435
  });
439
436
  return channels;
@@ -935,28 +932,31 @@ class MidyGM2 {
935
932
  const pitchWheel = channel.state.pitchWheel * 2 - 1;
936
933
  const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
937
934
  const pitch = pitchWheel * pitchWheelSensitivity;
938
- const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
935
+ const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
939
936
  const pressure = pressureDepth * channel.state.channelPressure;
940
937
  return tuning + pitch + pressure;
941
938
  }
942
939
  calcNoteDetune(channel, note) {
943
940
  return channel.scaleOctaveTuningTable[note.noteNumber % 12];
944
941
  }
945
- updateDetune(channel) {
946
- const now = this.audioContext.currentTime;
942
+ updateChannelDetune(channel) {
947
943
  channel.scheduledNotes.forEach((noteList) => {
948
944
  for (let i = 0; i < noteList.length; i++) {
949
945
  const note = noteList[i];
950
946
  if (!note)
951
947
  continue;
952
- const noteDetune = this.calcNoteDetune(channel, note);
953
- const detune = channel.detune + noteDetune;
954
- note.bufferSource.detune
955
- .cancelScheduledValues(now)
956
- .setValueAtTime(detune, now);
948
+ this.updateDetune(channel, note, 0);
957
949
  }
958
950
  });
959
951
  }
952
+ updateDetune(channel, note, pressure) {
953
+ const now = this.audioContext.currentTime;
954
+ const noteDetune = this.calcNoteDetune(channel, note);
955
+ const detune = channel.detune + noteDetune + pressure;
956
+ note.bufferSource.detune
957
+ .cancelScheduledValues(now)
958
+ .setValueAtTime(detune, now);
959
+ }
960
960
  getPortamentoTime(channel) {
961
961
  const factor = 5 * Math.log(10) / 127;
962
962
  const time = channel.state.portamentoTime;
@@ -974,14 +974,11 @@ class MidyGM2 {
974
974
  .setValueAtTime(0, volDelay)
975
975
  .linearRampToValueAtTime(sustainVolume, portamentoTime);
976
976
  }
977
- setVolumeEnvelope(channel, note) {
977
+ setVolumeEnvelope(note, pressure) {
978
978
  const now = this.audioContext.currentTime;
979
- const state = channel.state;
980
979
  const { voiceParams, startTime } = note;
981
- const pressureDepth = channel.pressureTable[2] / 64;
982
- const pressure = 1 + pressureDepth * channel.state.channelPressure;
983
980
  const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
984
- pressure;
981
+ (1 + pressure);
985
982
  const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
986
983
  const volDelay = startTime + voiceParams.volDelay;
987
984
  const volAttack = volDelay + voiceParams.volAttack;
@@ -1029,10 +1026,8 @@ class MidyGM2 {
1029
1026
  const { voiceParams, noteNumber, startTime } = note;
1030
1027
  const softPedalFactor = 1 -
1031
1028
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1032
- const pressureDepth = (channel.pressureTable[1] - 64) * 15;
1033
- const pressure = pressureDepth * channel.state.channelPressure;
1034
- const baseCent = voiceParams.initialFilterFc + pressure;
1035
- const baseFreq = this.centToHz(baseCent) * softPedalFactor;
1029
+ const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
1030
+ softPedalFactor;
1036
1031
  const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor;
1037
1032
  const sustainFreq = baseFreq +
1038
1033
  (peekFreq - baseFreq) * (1 - voiceParams.modSustain);
@@ -1046,15 +1041,16 @@ class MidyGM2 {
1046
1041
  .setValueAtTime(adjustedBaseFreq, modDelay)
1047
1042
  .linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
1048
1043
  }
1049
- setFilterEnvelope(channel, note) {
1044
+ setFilterEnvelope(channel, note, pressure) {
1050
1045
  const now = this.audioContext.currentTime;
1051
1046
  const state = channel.state;
1052
1047
  const { voiceParams, noteNumber, startTime } = note;
1053
1048
  const softPedalFactor = 1 -
1054
1049
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1055
- const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
1050
+ const baseCent = voiceParams.initialFilterFc + pressure;
1051
+ const baseFreq = this.centToHz(baseCent) * softPedalFactor;
1052
+ const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
1056
1053
  softPedalFactor;
1057
- const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor;
1058
1054
  const sustainFreq = baseFreq +
1059
1055
  (peekFreq - baseFreq) * (1 - voiceParams.modSustain);
1060
1056
  const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
@@ -1081,9 +1077,9 @@ class MidyGM2 {
1081
1077
  gain: voiceParams.modLfoToFilterFc,
1082
1078
  });
1083
1079
  note.modulationDepth = new GainNode(this.audioContext);
1084
- this.setModLfoToPitch(channel, note);
1080
+ this.setModLfoToPitch(channel, note, 0);
1085
1081
  note.volumeDepth = new GainNode(this.audioContext);
1086
- this.setModLfoToVolume(channel, note);
1082
+ this.setModLfoToVolume(note, 0);
1087
1083
  note.modulationLFO.start(startTime + voiceParams.delayModLFO);
1088
1084
  note.modulationLFO.connect(note.filterDepth);
1089
1085
  note.filterDepth.connect(note.filterNode.frequency);
@@ -1096,8 +1092,7 @@ class MidyGM2 {
1096
1092
  const { voiceParams } = note;
1097
1093
  const state = channel.state;
1098
1094
  note.vibratoLFO = new OscillatorNode(this.audioContext, {
1099
- frequency: this.centToHz(voiceParams.freqVibLFO) *
1100
- state.vibratoRate,
1095
+ frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
1101
1096
  });
1102
1097
  note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
1103
1098
  note.vibratoDepth = new GainNode(this.audioContext);
@@ -1126,8 +1121,8 @@ class MidyGM2 {
1126
1121
  }
1127
1122
  else {
1128
1123
  note.portamento = false;
1129
- this.setVolumeEnvelope(channel, note);
1130
- this.setFilterEnvelope(channel, note);
1124
+ this.setVolumeEnvelope(note, 0);
1125
+ this.setFilterEnvelope(channel, note, 0);
1131
1126
  }
1132
1127
  if (0 < state.vibratoDepth) {
1133
1128
  this.startVibrato(channel, note, startTime);
@@ -1346,40 +1341,21 @@ class MidyGM2 {
1346
1341
  const prev = channel.state.channelPressure;
1347
1342
  const next = value / 127;
1348
1343
  channel.state.channelPressure = next;
1349
- if (channel.pressureTable[0] !== 64) {
1350
- const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
1344
+ if (channel.channelPressureTable[0] !== 64) {
1345
+ const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
1351
1346
  channel.detune += pressureDepth * (next - prev);
1352
1347
  }
1348
+ const table = channel.channelPressureTable;
1353
1349
  channel.scheduledNotes.forEach((noteList) => {
1354
1350
  for (let i = 0; i < noteList.length; i++) {
1355
1351
  const note = noteList[i];
1356
1352
  if (!note)
1357
1353
  continue;
1358
- this.setChannelPressure(channel, note);
1354
+ this.applyDestinationSettings(channel, note, table);
1359
1355
  }
1360
1356
  });
1361
1357
  // this.applyVoiceParams(channel, 13);
1362
1358
  }
1363
- setChannelPressure(channel, note) {
1364
- if (channel.pressureTable[0] !== 64) {
1365
- this.updateDetune(channel);
1366
- }
1367
- if (channel.pressureTable[1] !== 64 && !note.portamento) {
1368
- this.setFilterEnvelope(channel, note);
1369
- }
1370
- if (channel.pressureTable[2] !== 64 && !note.portamento) {
1371
- this.setVolumeEnvelope(channel, note);
1372
- }
1373
- if (channel.pressureTable[3] !== 0) {
1374
- this.setModLfoToPitch(channel, note);
1375
- }
1376
- if (channel.pressureTable[4] !== 0) {
1377
- this.setModLfoToFilterFc(channel, note);
1378
- }
1379
- if (channel.pressureTable[5] !== 0) {
1380
- this.setModLfoToVolume(channel, note);
1381
- }
1382
- }
1383
1359
  handlePitchBendMessage(channelNumber, lsb, msb) {
1384
1360
  const pitchBend = msb * 128 + lsb;
1385
1361
  this.setPitchBend(channelNumber, pitchBend);
@@ -1391,16 +1367,13 @@ class MidyGM2 {
1391
1367
  const next = (value - 8192) / 8192;
1392
1368
  state.pitchWheel = value / 16383;
1393
1369
  channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
1394
- this.updateDetune(channel);
1370
+ this.updateChannelDetune(channel);
1395
1371
  this.applyVoiceParams(channel, 14);
1396
1372
  }
1397
- setModLfoToPitch(channel, note) {
1373
+ setModLfoToPitch(channel, note, pressure) {
1398
1374
  const now = this.audioContext.currentTime;
1399
- const pressureDepth = channel.pressureTable[3] / 127 * 600;
1400
- const pressure = pressureDepth * channel.state.channelPressure;
1401
1375
  const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
1402
- const baseDepth = Math.abs(modLfoToPitch) +
1403
- channel.state.modulationDepth;
1376
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1404
1377
  const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1405
1378
  note.modulationDepth.gain
1406
1379
  .cancelScheduledValues(now)
@@ -1416,22 +1389,18 @@ class MidyGM2 {
1416
1389
  .cancelScheduledValues(now)
1417
1390
  .setValueAtTime(vibratoDepth * vibratoDepthSign, now);
1418
1391
  }
1419
- setModLfoToFilterFc(channel, note) {
1392
+ setModLfoToFilterFc(note, pressure) {
1420
1393
  const now = this.audioContext.currentTime;
1421
- const pressureDepth = channel.pressureTable[4] / 127 * 2400;
1422
- const pressure = pressureDepth * channel.state.channelPressure;
1423
1394
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
1424
1395
  note.filterDepth.gain
1425
1396
  .cancelScheduledValues(now)
1426
1397
  .setValueAtTime(modLfoToFilterFc, now);
1427
1398
  }
1428
- setModLfoToVolume(channel, note) {
1399
+ setModLfoToVolume(note, pressure) {
1429
1400
  const now = this.audioContext.currentTime;
1430
1401
  const modLfoToVolume = note.voiceParams.modLfoToVolume;
1431
1402
  const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
1432
- const pressureDepth = channel.pressureTable[5] / 127;
1433
- const pressure = 1 + pressureDepth * channel.state.channelPressure;
1434
- const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
1403
+ const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
1435
1404
  note.volumeDepth.gain
1436
1405
  .cancelScheduledValues(now)
1437
1406
  .setValueAtTime(volumeDepth, now);
@@ -1504,11 +1473,18 @@ class MidyGM2 {
1504
1473
  .cancelScheduledValues(now)
1505
1474
  .setValueAtTime(freqModLFO, now);
1506
1475
  }
1476
+ setFreqVibLFO(channel, note) {
1477
+ const now = this.audioContext.currentTime;
1478
+ const freqVibLFO = note.voiceParams.freqVibLFO;
1479
+ note.vibratoLFO.frequency
1480
+ .cancelScheduledValues(now)
1481
+ .setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
1482
+ }
1507
1483
  createVoiceParamsHandlers() {
1508
1484
  return {
1509
1485
  modLfoToPitch: (channel, note, _prevValue) => {
1510
1486
  if (0 < channel.state.modulationDepth) {
1511
- this.setModLfoToPitch(channel, note);
1487
+ this.setModLfoToPitch(channel, note, 0);
1512
1488
  }
1513
1489
  },
1514
1490
  vibLfoToPitch: (channel, note, _prevValue) => {
@@ -1518,12 +1494,12 @@ class MidyGM2 {
1518
1494
  },
1519
1495
  modLfoToFilterFc: (channel, note, _prevValue) => {
1520
1496
  if (0 < channel.state.modulationDepth) {
1521
- this.setModLfoToFilterFc(channel, note);
1497
+ this.setModLfoToFilterFc(note, 0);
1522
1498
  }
1523
1499
  },
1524
- modLfoToVolume: (channel, note) => {
1500
+ modLfoToVolume: (channel, note, _prevValue) => {
1525
1501
  if (0 < channel.state.modulationDepth) {
1526
- this.setModLfoToVolume(channel, note);
1502
+ this.setModLfoToVolume(note, 0);
1527
1503
  }
1528
1504
  },
1529
1505
  chorusEffectsSend: (channel, note, prevValue) => {
@@ -1537,22 +1513,19 @@ class MidyGM2 {
1537
1513
  delayVibLFO: (channel, note, prevValue) => {
1538
1514
  if (0 < channel.state.vibratoDepth) {
1539
1515
  const now = this.audioContext.currentTime;
1540
- const prevStartTime = note.startTime +
1541
- prevValue * channel.state.vibratoDelay * 2;
1516
+ const vibratoDelay = channel.state.vibratoDelay * 2;
1517
+ const prevStartTime = note.startTime + prevValue * vibratoDelay;
1542
1518
  if (now < prevStartTime)
1543
1519
  return;
1544
- const startTime = note.startTime +
1545
- value * channel.state.vibratoDelay * 2;
1520
+ const value = note.voiceParams.delayVibLFO;
1521
+ const startTime = note.startTime + value * vibratoDelay;
1546
1522
  note.vibratoLFO.stop(now);
1547
1523
  note.vibratoLFO.start(startTime);
1548
1524
  }
1549
1525
  },
1550
1526
  freqVibLFO: (channel, note, _prevValue) => {
1551
1527
  if (0 < channel.state.vibratoDepth) {
1552
- const now = this.audioContext.currentTime;
1553
- note.vibratoLFO.frequency
1554
- .cancelScheduledValues(now)
1555
- .setValueAtTime(value * sate.vibratoRate, now);
1528
+ this.setFreqVibLFO(channel, note);
1556
1529
  }
1557
1530
  },
1558
1531
  };
@@ -1596,7 +1569,7 @@ class MidyGM2 {
1596
1569
  this.setPortamentoStartFilterEnvelope(channel, note);
1597
1570
  }
1598
1571
  else {
1599
- this.setFilterEnvelope(channel, note);
1572
+ this.setFilterEnvelope(channel, note, 0);
1600
1573
  }
1601
1574
  this.setPitchEnvelope(note);
1602
1575
  }
@@ -1610,7 +1583,7 @@ class MidyGM2 {
1610
1583
  if (key in voiceParams)
1611
1584
  noteVoiceParams[key] = voiceParams[key];
1612
1585
  }
1613
- this.setVolumeEnvelope(channel, note);
1586
+ this.setVolumeEnvelope(note, 0);
1614
1587
  }
1615
1588
  }
1616
1589
  }
@@ -1649,8 +1622,8 @@ class MidyGM2 {
1649
1622
  if (handler) {
1650
1623
  handler.call(this, channelNumber, value);
1651
1624
  const channel = this.channels[channelNumber];
1652
- const controller = 128 + controllerType;
1653
- this.applyVoiceParams(channel, controller);
1625
+ this.applyVoiceParams(channel, controllerType + 128);
1626
+ this.applyControlTable(channel, controllerType);
1654
1627
  }
1655
1628
  else {
1656
1629
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1661,13 +1634,14 @@ class MidyGM2 {
1661
1634
  }
1662
1635
  updateModulation(channel) {
1663
1636
  const now = this.audioContext.currentTime;
1637
+ const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1664
1638
  channel.scheduledNotes.forEach((noteList) => {
1665
1639
  for (let i = 0; i < noteList.length; i++) {
1666
1640
  const note = noteList[i];
1667
1641
  if (!note)
1668
1642
  continue;
1669
1643
  if (note.modulationDepth) {
1670
- note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
1644
+ note.modulationDepth.gain.setValueAtTime(depth, now);
1671
1645
  }
1672
1646
  else {
1673
1647
  this.setPitchEnvelope(note);
@@ -1678,8 +1652,7 @@ class MidyGM2 {
1678
1652
  }
1679
1653
  setModulationDepth(channelNumber, modulation) {
1680
1654
  const channel = this.channels[channelNumber];
1681
- channel.state.modulationDepth = (modulation / 127) *
1682
- channel.modulationDepthRange;
1655
+ channel.state.modulationDepth = modulation / 127;
1683
1656
  this.updateModulation(channel);
1684
1657
  }
1685
1658
  setPortamentoTime(channelNumber, portamentoTime) {
@@ -1942,7 +1915,7 @@ class MidyGM2 {
1942
1915
  const next = value / 128;
1943
1916
  state.pitchWheelSensitivity = next;
1944
1917
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
1945
- this.updateDetune(channel);
1918
+ this.updateChannelDetune(channel);
1946
1919
  this.applyVoiceParams(channel, 16);
1947
1920
  }
1948
1921
  handleFineTuningRPN(channelNumber) {
@@ -1957,7 +1930,7 @@ class MidyGM2 {
1957
1930
  const next = (value - 8192) / 8.192; // cent
1958
1931
  channel.fineTuning = next;
1959
1932
  channel.detune += next - prev;
1960
- this.updateDetune(channel);
1933
+ this.updateChannelDetune(channel);
1961
1934
  }
1962
1935
  handleCoarseTuningRPN(channelNumber) {
1963
1936
  const channel = this.channels[channelNumber];
@@ -1971,7 +1944,7 @@ class MidyGM2 {
1971
1944
  const next = (value - 64) * 100; // cent
1972
1945
  channel.coarseTuning = next;
1973
1946
  channel.detune += next - prev;
1974
- this.updateDetune(channel);
1947
+ this.updateChannelDetune(channel);
1975
1948
  }
1976
1949
  handleModulationDepthRangeRPN(channelNumber) {
1977
1950
  const channel = this.channels[channelNumber];
@@ -1982,7 +1955,6 @@ class MidyGM2 {
1982
1955
  setModulationDepthRange(channelNumber, modulationDepthRange) {
1983
1956
  const channel = this.channels[channelNumber];
1984
1957
  channel.modulationDepthRange = modulationDepthRange;
1985
- channel.modulationDepth = (modulation / 127) * modulationDepthRange;
1986
1958
  this.updateModulation(channel);
1987
1959
  }
1988
1960
  allSoundOff(channelNumber) {
@@ -2003,7 +1975,7 @@ class MidyGM2 {
2003
1975
  const state = channel.state;
2004
1976
  for (let i = 0; i < stateTypes.length; i++) {
2005
1977
  const type = stateTypes[i];
2006
- state[type] = defaultControllerState[type];
1978
+ state[type] = defaultControllerState[type].defaultValue;
2007
1979
  }
2008
1980
  const settingTypes = [
2009
1981
  "rpnMSB",
@@ -2097,10 +2069,9 @@ class MidyGM2 {
2097
2069
  case 9:
2098
2070
  switch (data[3]) {
2099
2071
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2100
- return this.handleChannelPressureSysEx(data);
2101
- // case 3:
2102
- // // TODO
2103
- // return this.setControlChange();
2072
+ return this.handlePressureSysEx(data, "channelPressureTable");
2073
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2074
+ return this.handleControlChangeSysEx(data);
2104
2075
  default:
2105
2076
  console.warn(`Unsupported Exclusive Message: ${data}`);
2106
2077
  }
@@ -2127,8 +2098,8 @@ class MidyGM2 {
2127
2098
  }
2128
2099
  else {
2129
2100
  const now = this.audioContext.currentTime;
2130
- this.masterGain.gain.cancelScheduledValues(now);
2131
- this.masterGain.gain.setValueAtTime(volume * volume, now);
2101
+ this.masterVolume.gain.cancelScheduledValues(now);
2102
+ this.masterVolume.gain.setValueAtTime(volume * volume, now);
2132
2103
  }
2133
2104
  }
2134
2105
  handleMasterFineTuningSysEx(data) {
@@ -2140,7 +2111,7 @@ class MidyGM2 {
2140
2111
  const next = (value - 8192) / 8.192; // cent
2141
2112
  this.masterFineTuning = next;
2142
2113
  channel.detune += next - prev;
2143
- this.updateDetune(channel);
2114
+ this.updateChannelDetune(channel);
2144
2115
  }
2145
2116
  handleMasterCoarseTuningSysEx(data) {
2146
2117
  const coarseTuning = data[4];
@@ -2151,7 +2122,7 @@ class MidyGM2 {
2151
2122
  const next = (value - 64) * 100; // cent
2152
2123
  this.masterCoarseTuning = next;
2153
2124
  channel.detune += next - prev;
2154
- this.updateDetune(channel);
2125
+ this.updateChannelDetune(channel);
2155
2126
  }
2156
2127
  handleGlobalParameterControlSysEx(data) {
2157
2128
  if (data[7] === 1) {
@@ -2373,15 +2344,86 @@ class MidyGM2 {
2373
2344
  }
2374
2345
  }
2375
2346
  }
2376
- handleChannelPressureSysEx(data) {
2347
+ applyDestinationSettings(channel, note, table) {
2348
+ if (table[0] !== 64) {
2349
+ this.updateDetune(channel, note, 0);
2350
+ }
2351
+ if (!note.portamento) {
2352
+ if (table[1] !== 64) {
2353
+ const channelPressure = channel.channelPressureTable[1] *
2354
+ channel.state.channelPressure;
2355
+ const pressure = (channelPressure - 64) * 15;
2356
+ this.setFilterEnvelope(channel, note, pressure);
2357
+ }
2358
+ if (table[2] !== 64) {
2359
+ const channelPressure = channel.channelPressureTable[2] *
2360
+ channel.state.channelPressure;
2361
+ const pressure = channelPressure / 64;
2362
+ this.setVolumeEnvelope(note, pressure);
2363
+ }
2364
+ }
2365
+ if (table[3] !== 0) {
2366
+ const channelPressure = channel.channelPressureTable[3] *
2367
+ channel.state.channelPressure;
2368
+ const pressure = channelPressure / 127 * 600;
2369
+ this.setModLfoToPitch(channel, note, pressure);
2370
+ }
2371
+ if (table[4] !== 0) {
2372
+ const channelPressure = channel.channelPressureTable[4] *
2373
+ channel.state.channelPressure;
2374
+ const pressure = channelPressure / 127 * 2400;
2375
+ this.setModLfoToFilterFc(note, pressure);
2376
+ }
2377
+ if (table[5] !== 0) {
2378
+ const channelPressure = channel.channelPressureTable[5] *
2379
+ channel.state.channelPressure;
2380
+ const pressure = channelPressure / 127;
2381
+ this.setModLfoToVolume(note, pressure);
2382
+ }
2383
+ }
2384
+ handleChannelPressureSysEx(data, tableName) {
2377
2385
  const channelNumber = data[4];
2378
- const table = this.channels[channelNumber].pressureTable;
2386
+ const table = this.channels[channelNumber][tableName];
2379
2387
  for (let i = 5; i < data.length - 1; i += 2) {
2380
2388
  const pp = data[i];
2381
2389
  const rr = data[i + 1];
2382
2390
  table[pp] = rr;
2383
2391
  }
2384
2392
  }
2393
+ initControlTable() {
2394
+ const channelCount = 128;
2395
+ const slotSize = 6;
2396
+ const defaultValues = [64, 64, 64, 0, 0, 0];
2397
+ const table = new Uint8Array(channelCount * slotSize);
2398
+ for (let ch = 0; ch < channelCount; ch++) {
2399
+ const offset = ch * slotSize;
2400
+ table.set(defaultValues, offset);
2401
+ }
2402
+ return table;
2403
+ }
2404
+ applyControlTable(channel, controllerType) {
2405
+ const slotSize = 6;
2406
+ const offset = controllerType * slotSize;
2407
+ const table = channel.controlTable.subarray(offset, offset + slotSize);
2408
+ channel.scheduledNotes.forEach((noteList) => {
2409
+ for (let i = 0; i < noteList.length; i++) {
2410
+ const note = noteList[i];
2411
+ if (!note)
2412
+ continue;
2413
+ this.applyDestinationSettings(channel, note, table);
2414
+ }
2415
+ });
2416
+ }
2417
+ handleControlChangeSysEx(data) {
2418
+ const channelNumber = data[4];
2419
+ const controllerType = data[5];
2420
+ const table = this.channels[channelNumber].controlTable[controllerType];
2421
+ for (let i = 6; i < data.length - 1; i += 2) {
2422
+ const pp = data[i];
2423
+ const rr = data[i + 1];
2424
+ table[pp] = rr;
2425
+ }
2426
+ }
2385
2427
  getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
2386
2428
  const index = keyNumber * 128 + controllerType;
2387
2429
  const controlValue = channel.keyBasedInstrumentControlTable[index];
@@ -2433,7 +2475,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2433
2475
  currentBufferSource: null,
2434
2476
  detune: 0,
2435
2477
  scaleOctaveTuningTable: new Array(12).fill(0), // cent
2436
- pressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2478
+ channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2437
2479
  keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
2438
2480
  program: 0,
2439
2481
  bank: 121 * 128,
@@ -2448,16 +2490,3 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2448
2490
  modulationDepthRange: 50, // cent
2449
2491
  }
2450
2492
  });
2451
- Object.defineProperty(MidyGM2, "controllerDestinationSettings", {
2452
- enumerable: true,
2453
- configurable: true,
2454
- writable: true,
2455
- value: {
2456
- pitchControl: 0,
2457
- filterCutoffControl: 0,
2458
- amplitudeControl: 1,
2459
- lfoPitchDepth: 0,
2460
- lfoFilterDepth: 0,
2461
- lfoAmplitudeDepth: 0,
2462
- }
2463
- });
@@ -29,12 +29,12 @@ export class MidyGMLite {
29
29
  notePromises: any[];
30
30
  exclusiveClassMap: Map<any, any>;
31
31
  audioContext: any;
32
- masterGain: any;
32
+ masterVolume: any;
33
33
  voiceParamsHandlers: {
34
34
  modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
35
35
  vibLfoToPitch: (_channel: any, _note: any, _prevValue: any) => void;
36
36
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
37
- modLfoToVolume: (channel: any, note: any) => void;
37
+ modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
38
38
  chorusEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
39
39
  reverbEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
40
40
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
@@ -94,7 +94,8 @@ export class MidyGMLite {
94
94
  centToRate(cent: any): number;
95
95
  centToHz(cent: any): number;
96
96
  calcChannelDetune(channel: any): number;
97
- updateDetune(channel: any): void;
97
+ updateChannelDetune(channel: any): void;
98
+ updateDetune(channel: any, note: any): void;
98
99
  setVolumeEnvelope(note: any): void;
99
100
  setPitchEnvelope(note: any): void;
100
101
  clampCutoffFrequency(frequency: any): number;
@@ -120,7 +121,7 @@ export class MidyGMLite {
120
121
  modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
121
122
  vibLfoToPitch: (_channel: any, _note: any, _prevValue: any) => void;
122
123
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
123
- modLfoToVolume: (channel: any, note: any) => void;
124
+ modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
124
125
  chorusEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
125
126
  reverbEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
126
127
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
@@ -163,7 +164,7 @@ export class MidyGMLite {
163
164
  setRPNLSB(channelNumber: any, value: any): void;
164
165
  dataEntryMSB(channelNumber: any, value: any): void;
165
166
  handlePitchBendRangeRPN(channelNumber: any): void;
166
- setPitchBendRange(channelNumber: any, pitchWheelSensitivity: any): void;
167
+ setPitchBendRange(channelNumber: any, value: any): void;
167
168
  allSoundOff(channelNumber: any): Promise<void>;
168
169
  resetAllControllers(channelNumber: any): void;
169
170
  allNotesOff(channelNumber: any): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmFA;IAoBE;;;;;;;;;MASE;IAEF,+BAQC;IAtCD,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;IAc5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;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,wCAIC;IAED,iCAWC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EASC;IAED,qCAiBC;IAED,8DAKC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wEAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AArtCD;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmFA;IAoBE;;;;;;;;;MASE;IAEF,+BAQC;IAtCD,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;IAc5B,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,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;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,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA1tCD;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}