@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.
package/esm/midy-GM2.js CHANGED
@@ -97,7 +97,6 @@ class Note {
97
97
  const defaultControllerState = {
98
98
  noteOnVelocity: { type: 2, defaultValue: 0 },
99
99
  noteOnKeyNumber: { type: 3, defaultValue: 0 },
100
- polyPressure: { type: 10, defaultValue: 0 },
101
100
  channelPressure: { type: 13, defaultValue: 0 },
102
101
  pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
103
102
  pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
@@ -359,15 +358,15 @@ export class MidyGM2 {
359
358
  });
360
359
  this.audioContext = audioContext;
361
360
  this.options = { ...this.defaultOptions, ...options };
362
- this.masterGain = new GainNode(audioContext);
361
+ this.masterVolume = new GainNode(audioContext);
363
362
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
364
363
  this.controlChangeHandlers = this.createControlChangeHandlers();
365
364
  this.channels = this.createChannels(audioContext);
366
365
  this.reverbEffect = this.options.reverbAlgorithm(audioContext);
367
366
  this.chorusEffect = this.createChorusEffect(audioContext);
368
- this.chorusEffect.output.connect(this.masterGain);
369
- this.reverbEffect.output.connect(this.masterGain);
370
- this.masterGain.connect(audioContext.destination);
367
+ this.chorusEffect.output.connect(this.masterVolume);
368
+ this.reverbEffect.output.connect(this.masterVolume);
369
+ this.masterVolume.connect(audioContext.destination);
371
370
  this.GM2SystemOn();
372
371
  }
373
372
  initSoundFontTable() {
@@ -413,7 +412,7 @@ export class MidyGM2 {
413
412
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
414
413
  gainL.connect(merger, 0, 0);
415
414
  gainR.connect(merger, 0, 1);
416
- merger.connect(this.masterGain);
415
+ merger.connect(this.masterVolume);
417
416
  return {
418
417
  gainL,
419
418
  gainR,
@@ -425,12 +424,10 @@ export class MidyGM2 {
425
424
  return {
426
425
  ...this.constructor.channelSettings,
427
426
  state: new ControllerState(),
427
+ controlTable: this.initControlTable(),
428
428
  ...this.setChannelAudioNodes(audioContext),
429
429
  scheduledNotes: new Map(),
430
430
  sostenutoNotes: new Map(),
431
- channelPressure: {
432
- ...this.constructor.controllerDestinationSettings,
433
- },
434
431
  };
435
432
  });
436
433
  return channels;
@@ -932,28 +929,31 @@ export class MidyGM2 {
932
929
  const pitchWheel = channel.state.pitchWheel * 2 - 1;
933
930
  const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
934
931
  const pitch = pitchWheel * pitchWheelSensitivity;
935
- const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
932
+ const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
936
933
  const pressure = pressureDepth * channel.state.channelPressure;
937
934
  return tuning + pitch + pressure;
938
935
  }
939
936
  calcNoteDetune(channel, note) {
940
937
  return channel.scaleOctaveTuningTable[note.noteNumber % 12];
941
938
  }
942
- updateDetune(channel) {
943
- const now = this.audioContext.currentTime;
939
+ updateChannelDetune(channel) {
944
940
  channel.scheduledNotes.forEach((noteList) => {
945
941
  for (let i = 0; i < noteList.length; i++) {
946
942
  const note = noteList[i];
947
943
  if (!note)
948
944
  continue;
949
- const noteDetune = this.calcNoteDetune(channel, note);
950
- const detune = channel.detune + noteDetune;
951
- note.bufferSource.detune
952
- .cancelScheduledValues(now)
953
- .setValueAtTime(detune, now);
945
+ this.updateDetune(channel, note, 0);
954
946
  }
955
947
  });
956
948
  }
949
+ updateDetune(channel, note, pressure) {
950
+ const now = this.audioContext.currentTime;
951
+ const noteDetune = this.calcNoteDetune(channel, note);
952
+ const detune = channel.detune + noteDetune + pressure;
953
+ note.bufferSource.detune
954
+ .cancelScheduledValues(now)
955
+ .setValueAtTime(detune, now);
956
+ }
957
957
  getPortamentoTime(channel) {
958
958
  const factor = 5 * Math.log(10) / 127;
959
959
  const time = channel.state.portamentoTime;
@@ -971,14 +971,11 @@ export class MidyGM2 {
971
971
  .setValueAtTime(0, volDelay)
972
972
  .linearRampToValueAtTime(sustainVolume, portamentoTime);
973
973
  }
974
- setVolumeEnvelope(channel, note) {
974
+ setVolumeEnvelope(note, pressure) {
975
975
  const now = this.audioContext.currentTime;
976
- const state = channel.state;
977
976
  const { voiceParams, startTime } = note;
978
- const pressureDepth = channel.pressureTable[2] / 64;
979
- const pressure = 1 + pressureDepth * channel.state.channelPressure;
980
977
  const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
981
- pressure;
978
+ (1 + pressure);
982
979
  const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
983
980
  const volDelay = startTime + voiceParams.volDelay;
984
981
  const volAttack = volDelay + voiceParams.volAttack;
@@ -1026,10 +1023,8 @@ export class MidyGM2 {
1026
1023
  const { voiceParams, noteNumber, startTime } = note;
1027
1024
  const softPedalFactor = 1 -
1028
1025
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1029
- const pressureDepth = (channel.pressureTable[1] - 64) * 15;
1030
- const pressure = pressureDepth * channel.state.channelPressure;
1031
- const baseCent = voiceParams.initialFilterFc + pressure;
1032
- const baseFreq = this.centToHz(baseCent) * softPedalFactor;
1026
+ const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
1027
+ softPedalFactor;
1033
1028
  const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor;
1034
1029
  const sustainFreq = baseFreq +
1035
1030
  (peekFreq - baseFreq) * (1 - voiceParams.modSustain);
@@ -1043,15 +1038,16 @@ export class MidyGM2 {
1043
1038
  .setValueAtTime(adjustedBaseFreq, modDelay)
1044
1039
  .linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
1045
1040
  }
1046
- setFilterEnvelope(channel, note) {
1041
+ setFilterEnvelope(channel, note, pressure) {
1047
1042
  const now = this.audioContext.currentTime;
1048
1043
  const state = channel.state;
1049
1044
  const { voiceParams, noteNumber, startTime } = note;
1050
1045
  const softPedalFactor = 1 -
1051
1046
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1052
- const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
1047
+ const baseCent = voiceParams.initialFilterFc + pressure;
1048
+ const baseFreq = this.centToHz(baseCent) * softPedalFactor;
1049
+ const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
1053
1050
  softPedalFactor;
1054
- const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor;
1055
1051
  const sustainFreq = baseFreq +
1056
1052
  (peekFreq - baseFreq) * (1 - voiceParams.modSustain);
1057
1053
  const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
@@ -1078,9 +1074,9 @@ export class MidyGM2 {
1078
1074
  gain: voiceParams.modLfoToFilterFc,
1079
1075
  });
1080
1076
  note.modulationDepth = new GainNode(this.audioContext);
1081
- this.setModLfoToPitch(channel, note);
1077
+ this.setModLfoToPitch(channel, note, 0);
1082
1078
  note.volumeDepth = new GainNode(this.audioContext);
1083
- this.setModLfoToVolume(channel, note);
1079
+ this.setModLfoToVolume(note, 0);
1084
1080
  note.modulationLFO.start(startTime + voiceParams.delayModLFO);
1085
1081
  note.modulationLFO.connect(note.filterDepth);
1086
1082
  note.filterDepth.connect(note.filterNode.frequency);
@@ -1093,8 +1089,7 @@ export class MidyGM2 {
1093
1089
  const { voiceParams } = note;
1094
1090
  const state = channel.state;
1095
1091
  note.vibratoLFO = new OscillatorNode(this.audioContext, {
1096
- frequency: this.centToHz(voiceParams.freqVibLFO) *
1097
- state.vibratoRate,
1092
+ frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
1098
1093
  });
1099
1094
  note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
1100
1095
  note.vibratoDepth = new GainNode(this.audioContext);
@@ -1123,8 +1118,8 @@ export class MidyGM2 {
1123
1118
  }
1124
1119
  else {
1125
1120
  note.portamento = false;
1126
- this.setVolumeEnvelope(channel, note);
1127
- this.setFilterEnvelope(channel, note);
1121
+ this.setVolumeEnvelope(note, 0);
1122
+ this.setFilterEnvelope(channel, note, 0);
1128
1123
  }
1129
1124
  if (0 < state.vibratoDepth) {
1130
1125
  this.startVibrato(channel, note, startTime);
@@ -1343,40 +1338,21 @@ export class MidyGM2 {
1343
1338
  const prev = channel.state.channelPressure;
1344
1339
  const next = value / 127;
1345
1340
  channel.state.channelPressure = next;
1346
- if (channel.pressureTable[0] !== 64) {
1347
- const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
1341
+ if (channel.channelPressureTable[0] !== 64) {
1342
+ const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
1348
1343
  channel.detune += pressureDepth * (next - prev);
1349
1344
  }
1345
+ const table = channel.channelPressureTable;
1350
1346
  channel.scheduledNotes.forEach((noteList) => {
1351
1347
  for (let i = 0; i < noteList.length; i++) {
1352
1348
  const note = noteList[i];
1353
1349
  if (!note)
1354
1350
  continue;
1355
- this.setChannelPressure(channel, note);
1351
+ this.applyDestinationSettings(channel, note, table);
1356
1352
  }
1357
1353
  });
1358
1354
  // this.applyVoiceParams(channel, 13);
1359
1355
  }
1360
- setChannelPressure(channel, note) {
1361
- if (channel.pressureTable[0] !== 64) {
1362
- this.updateDetune(channel);
1363
- }
1364
- if (channel.pressureTable[1] !== 64 && !note.portamento) {
1365
- this.setFilterEnvelope(channel, note);
1366
- }
1367
- if (channel.pressureTable[2] !== 64 && !note.portamento) {
1368
- this.setVolumeEnvelope(channel, note);
1369
- }
1370
- if (channel.pressureTable[3] !== 0) {
1371
- this.setModLfoToPitch(channel, note);
1372
- }
1373
- if (channel.pressureTable[4] !== 0) {
1374
- this.setModLfoToFilterFc(channel, note);
1375
- }
1376
- if (channel.pressureTable[5] !== 0) {
1377
- this.setModLfoToVolume(channel, note);
1378
- }
1379
- }
1380
1356
  handlePitchBendMessage(channelNumber, lsb, msb) {
1381
1357
  const pitchBend = msb * 128 + lsb;
1382
1358
  this.setPitchBend(channelNumber, pitchBend);
@@ -1388,16 +1364,13 @@ export class MidyGM2 {
1388
1364
  const next = (value - 8192) / 8192;
1389
1365
  state.pitchWheel = value / 16383;
1390
1366
  channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
1391
- this.updateDetune(channel);
1367
+ this.updateChannelDetune(channel);
1392
1368
  this.applyVoiceParams(channel, 14);
1393
1369
  }
1394
- setModLfoToPitch(channel, note) {
1370
+ setModLfoToPitch(channel, note, pressure) {
1395
1371
  const now = this.audioContext.currentTime;
1396
- const pressureDepth = channel.pressureTable[3] / 127 * 600;
1397
- const pressure = pressureDepth * channel.state.channelPressure;
1398
1372
  const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
1399
- const baseDepth = Math.abs(modLfoToPitch) +
1400
- channel.state.modulationDepth;
1373
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1401
1374
  const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1402
1375
  note.modulationDepth.gain
1403
1376
  .cancelScheduledValues(now)
@@ -1413,22 +1386,18 @@ export class MidyGM2 {
1413
1386
  .cancelScheduledValues(now)
1414
1387
  .setValueAtTime(vibratoDepth * vibratoDepthSign, now);
1415
1388
  }
1416
- setModLfoToFilterFc(channel, note) {
1389
+ setModLfoToFilterFc(note, pressure) {
1417
1390
  const now = this.audioContext.currentTime;
1418
- const pressureDepth = channel.pressureTable[4] / 127 * 2400;
1419
- const pressure = pressureDepth * channel.state.channelPressure;
1420
1391
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
1421
1392
  note.filterDepth.gain
1422
1393
  .cancelScheduledValues(now)
1423
1394
  .setValueAtTime(modLfoToFilterFc, now);
1424
1395
  }
1425
- setModLfoToVolume(channel, note) {
1396
+ setModLfoToVolume(note, pressure) {
1426
1397
  const now = this.audioContext.currentTime;
1427
1398
  const modLfoToVolume = note.voiceParams.modLfoToVolume;
1428
1399
  const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
1429
- const pressureDepth = channel.pressureTable[5] / 127;
1430
- const pressure = 1 + pressureDepth * channel.state.channelPressure;
1431
- const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
1400
+ const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
1432
1401
  note.volumeDepth.gain
1433
1402
  .cancelScheduledValues(now)
1434
1403
  .setValueAtTime(volumeDepth, now);
@@ -1501,11 +1470,18 @@ export class MidyGM2 {
1501
1470
  .cancelScheduledValues(now)
1502
1471
  .setValueAtTime(freqModLFO, now);
1503
1472
  }
1473
+ setFreqVibLFO(channel, note) {
1474
+ const now = this.audioContext.currentTime;
1475
+ const freqVibLFO = note.voiceParams.freqVibLFO;
1476
+ note.vibratoLFO.frequency
1477
+ .cancelScheduledValues(now)
1478
+ .setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
1479
+ }
1504
1480
  createVoiceParamsHandlers() {
1505
1481
  return {
1506
1482
  modLfoToPitch: (channel, note, _prevValue) => {
1507
1483
  if (0 < channel.state.modulationDepth) {
1508
- this.setModLfoToPitch(channel, note);
1484
+ this.setModLfoToPitch(channel, note, 0);
1509
1485
  }
1510
1486
  },
1511
1487
  vibLfoToPitch: (channel, note, _prevValue) => {
@@ -1515,12 +1491,12 @@ export class MidyGM2 {
1515
1491
  },
1516
1492
  modLfoToFilterFc: (channel, note, _prevValue) => {
1517
1493
  if (0 < channel.state.modulationDepth) {
1518
- this.setModLfoToFilterFc(channel, note);
1494
+ this.setModLfoToFilterFc(note, 0);
1519
1495
  }
1520
1496
  },
1521
- modLfoToVolume: (channel, note) => {
1497
+ modLfoToVolume: (channel, note, _prevValue) => {
1522
1498
  if (0 < channel.state.modulationDepth) {
1523
- this.setModLfoToVolume(channel, note);
1499
+ this.setModLfoToVolume(note, 0);
1524
1500
  }
1525
1501
  },
1526
1502
  chorusEffectsSend: (channel, note, prevValue) => {
@@ -1534,22 +1510,19 @@ export class MidyGM2 {
1534
1510
  delayVibLFO: (channel, note, prevValue) => {
1535
1511
  if (0 < channel.state.vibratoDepth) {
1536
1512
  const now = this.audioContext.currentTime;
1537
- const prevStartTime = note.startTime +
1538
- prevValue * channel.state.vibratoDelay * 2;
1513
+ const vibratoDelay = channel.state.vibratoDelay * 2;
1514
+ const prevStartTime = note.startTime + prevValue * vibratoDelay;
1539
1515
  if (now < prevStartTime)
1540
1516
  return;
1541
- const startTime = note.startTime +
1542
- value * channel.state.vibratoDelay * 2;
1517
+ const value = note.voiceParams.delayVibLFO;
1518
+ const startTime = note.startTime + value * vibratoDelay;
1543
1519
  note.vibratoLFO.stop(now);
1544
1520
  note.vibratoLFO.start(startTime);
1545
1521
  }
1546
1522
  },
1547
1523
  freqVibLFO: (channel, note, _prevValue) => {
1548
1524
  if (0 < channel.state.vibratoDepth) {
1549
- const now = this.audioContext.currentTime;
1550
- note.vibratoLFO.frequency
1551
- .cancelScheduledValues(now)
1552
- .setValueAtTime(value * sate.vibratoRate, now);
1525
+ this.setFreqVibLFO(channel, note);
1553
1526
  }
1554
1527
  },
1555
1528
  };
@@ -1593,7 +1566,7 @@ export class MidyGM2 {
1593
1566
  this.setPortamentoStartFilterEnvelope(channel, note);
1594
1567
  }
1595
1568
  else {
1596
- this.setFilterEnvelope(channel, note);
1569
+ this.setFilterEnvelope(channel, note, 0);
1597
1570
  }
1598
1571
  this.setPitchEnvelope(note);
1599
1572
  }
@@ -1607,7 +1580,7 @@ export class MidyGM2 {
1607
1580
  if (key in voiceParams)
1608
1581
  noteVoiceParams[key] = voiceParams[key];
1609
1582
  }
1610
- this.setVolumeEnvelope(channel, note);
1583
+ this.setVolumeEnvelope(note, 0);
1611
1584
  }
1612
1585
  }
1613
1586
  }
@@ -1646,8 +1619,8 @@ export class MidyGM2 {
1646
1619
  if (handler) {
1647
1620
  handler.call(this, channelNumber, value);
1648
1621
  const channel = this.channels[channelNumber];
1649
- const controller = 128 + controllerType;
1650
- this.applyVoiceParams(channel, controller);
1622
+ this.applyVoiceParams(channel, controllerType + 128);
1623
+ this.applyControlTable(channel, controllerType);
1651
1624
  }
1652
1625
  else {
1653
1626
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1658,13 +1631,14 @@ export class MidyGM2 {
1658
1631
  }
1659
1632
  updateModulation(channel) {
1660
1633
  const now = this.audioContext.currentTime;
1634
+ const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1661
1635
  channel.scheduledNotes.forEach((noteList) => {
1662
1636
  for (let i = 0; i < noteList.length; i++) {
1663
1637
  const note = noteList[i];
1664
1638
  if (!note)
1665
1639
  continue;
1666
1640
  if (note.modulationDepth) {
1667
- note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
1641
+ note.modulationDepth.gain.setValueAtTime(depth, now);
1668
1642
  }
1669
1643
  else {
1670
1644
  this.setPitchEnvelope(note);
@@ -1675,8 +1649,7 @@ export class MidyGM2 {
1675
1649
  }
1676
1650
  setModulationDepth(channelNumber, modulation) {
1677
1651
  const channel = this.channels[channelNumber];
1678
- channel.state.modulationDepth = (modulation / 127) *
1679
- channel.modulationDepthRange;
1652
+ channel.state.modulationDepth = modulation / 127;
1680
1653
  this.updateModulation(channel);
1681
1654
  }
1682
1655
  setPortamentoTime(channelNumber, portamentoTime) {
@@ -1939,7 +1912,7 @@ export class MidyGM2 {
1939
1912
  const next = value / 128;
1940
1913
  state.pitchWheelSensitivity = next;
1941
1914
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
1942
- this.updateDetune(channel);
1915
+ this.updateChannelDetune(channel);
1943
1916
  this.applyVoiceParams(channel, 16);
1944
1917
  }
1945
1918
  handleFineTuningRPN(channelNumber) {
@@ -1954,7 +1927,7 @@ export class MidyGM2 {
1954
1927
  const next = (value - 8192) / 8.192; // cent
1955
1928
  channel.fineTuning = next;
1956
1929
  channel.detune += next - prev;
1957
- this.updateDetune(channel);
1930
+ this.updateChannelDetune(channel);
1958
1931
  }
1959
1932
  handleCoarseTuningRPN(channelNumber) {
1960
1933
  const channel = this.channels[channelNumber];
@@ -1968,7 +1941,7 @@ export class MidyGM2 {
1968
1941
  const next = (value - 64) * 100; // cent
1969
1942
  channel.coarseTuning = next;
1970
1943
  channel.detune += next - prev;
1971
- this.updateDetune(channel);
1944
+ this.updateChannelDetune(channel);
1972
1945
  }
1973
1946
  handleModulationDepthRangeRPN(channelNumber) {
1974
1947
  const channel = this.channels[channelNumber];
@@ -1979,7 +1952,6 @@ export class MidyGM2 {
1979
1952
  setModulationDepthRange(channelNumber, modulationDepthRange) {
1980
1953
  const channel = this.channels[channelNumber];
1981
1954
  channel.modulationDepthRange = modulationDepthRange;
1982
- channel.modulationDepth = (modulation / 127) * modulationDepthRange;
1983
1955
  this.updateModulation(channel);
1984
1956
  }
1985
1957
  allSoundOff(channelNumber) {
@@ -2000,7 +1972,7 @@ export class MidyGM2 {
2000
1972
  const state = channel.state;
2001
1973
  for (let i = 0; i < stateTypes.length; i++) {
2002
1974
  const type = stateTypes[i];
2003
- state[type] = defaultControllerState[type];
1975
+ state[type] = defaultControllerState[type].defaultValue;
2004
1976
  }
2005
1977
  const settingTypes = [
2006
1978
  "rpnMSB",
@@ -2094,10 +2066,9 @@ export class MidyGM2 {
2094
2066
  case 9:
2095
2067
  switch (data[3]) {
2096
2068
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2097
- return this.handleChannelPressureSysEx(data);
2098
- // case 3:
2099
- // // TODO
2100
- // return this.setControlChange();
2069
+ return this.handlePressureSysEx(data, "channelPressureTable");
2070
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2071
+ return this.handleControlChangeSysEx(data);
2101
2072
  default:
2102
2073
  console.warn(`Unsupported Exclusive Message: ${data}`);
2103
2074
  }
@@ -2124,8 +2095,8 @@ export class MidyGM2 {
2124
2095
  }
2125
2096
  else {
2126
2097
  const now = this.audioContext.currentTime;
2127
- this.masterGain.gain.cancelScheduledValues(now);
2128
- this.masterGain.gain.setValueAtTime(volume * volume, now);
2098
+ this.masterVolume.gain.cancelScheduledValues(now);
2099
+ this.masterVolume.gain.setValueAtTime(volume * volume, now);
2129
2100
  }
2130
2101
  }
2131
2102
  handleMasterFineTuningSysEx(data) {
@@ -2137,7 +2108,7 @@ export class MidyGM2 {
2137
2108
  const next = (value - 8192) / 8.192; // cent
2138
2109
  this.masterFineTuning = next;
2139
2110
  channel.detune += next - prev;
2140
- this.updateDetune(channel);
2111
+ this.updateChannelDetune(channel);
2141
2112
  }
2142
2113
  handleMasterCoarseTuningSysEx(data) {
2143
2114
  const coarseTuning = data[4];
@@ -2148,7 +2119,7 @@ export class MidyGM2 {
2148
2119
  const next = (value - 64) * 100; // cent
2149
2120
  this.masterCoarseTuning = next;
2150
2121
  channel.detune += next - prev;
2151
- this.updateDetune(channel);
2122
+ this.updateChannelDetune(channel);
2152
2123
  }
2153
2124
  handleGlobalParameterControlSysEx(data) {
2154
2125
  if (data[7] === 1) {
@@ -2370,15 +2341,86 @@ export class MidyGM2 {
2370
2341
  }
2371
2342
  }
2372
2343
  }
2373
- handleChannelPressureSysEx(data) {
2344
+ applyDestinationSettings(channel, note, table) {
2345
+ if (table[0] !== 64) {
2346
+ this.updateDetune(channel, note, 0);
2347
+ }
2348
+ if (!note.portamento) {
2349
+ if (table[1] !== 64) {
2350
+ const channelPressure = channel.channelPressureTable[1] *
2351
+ channel.state.channelPressure;
2352
+ const pressure = (channelPressure - 64) * 15;
2353
+ this.setFilterEnvelope(channel, note, pressure);
2354
+ }
2355
+ if (table[2] !== 64) {
2356
+ const channelPressure = channel.channelPressureTable[2] *
2357
+ channel.state.channelPressure;
2358
+ const pressure = channelPressure / 64;
2359
+ this.setVolumeEnvelope(note, pressure);
2360
+ }
2361
+ }
2362
+ if (table[3] !== 0) {
2363
+ const channelPressure = channel.channelPressureTable[3] *
2364
+ channel.state.channelPressure;
2365
+ const pressure = channelPressure / 127 * 600;
2366
+ this.setModLfoToPitch(channel, note, pressure);
2367
+ }
2368
+ if (table[4] !== 0) {
2369
+ const channelPressure = channel.channelPressureTable[4] *
2370
+ channel.state.channelPressure;
2371
+ const pressure = channelPressure / 127 * 2400;
2372
+ this.setModLfoToFilterFc(note, pressure);
2373
+ }
2374
+ if (table[5] !== 0) {
2375
+ const channelPressure = channel.channelPressureTable[5] *
2376
+ channel.state.channelPressure;
2377
+ const pressure = channelPressure / 127;
2378
+ this.setModLfoToVolume(note, pressure);
2379
+ }
2380
+ }
2381
+ handleChannelPressureSysEx(data, tableName) {
2374
2382
  const channelNumber = data[4];
2375
- const table = this.channels[channelNumber].pressureTable;
2383
+ const table = this.channels[channelNumber][tableName];
2376
2384
  for (let i = 5; i < data.length - 1; i += 2) {
2377
2385
  const pp = data[i];
2378
2386
  const rr = data[i + 1];
2379
2387
  table[pp] = rr;
2380
2388
  }
2381
2389
  }
2390
+ initControlTable() {
2391
+ const channelCount = 128;
2392
+ const slotSize = 6;
2393
+ const defaultValues = [64, 64, 64, 0, 0, 0];
2394
+ const table = new Uint8Array(channelCount * slotSize);
2395
+ for (let ch = 0; ch < channelCount; ch++) {
2396
+ const offset = ch * slotSize;
2397
+ table.set(defaultValues, offset);
2398
+ }
2399
+ return table;
2400
+ }
2401
+ applyControlTable(channel, controllerType) {
2402
+ const slotSize = 6;
2403
+ const offset = controllerType * slotSize;
2404
+ const table = channel.controlTable.subarray(offset, offset + slotSize);
2405
+ channel.scheduledNotes.forEach((noteList) => {
2406
+ for (let i = 0; i < noteList.length; i++) {
2407
+ const note = noteList[i];
2408
+ if (!note)
2409
+ continue;
2410
+ this.applyDestinationSettings(channel, note, table);
2411
+ }
2412
+ });
2413
+ }
2414
+ handleControlChangeSysEx(data) {
2415
+ const channelNumber = data[4];
2416
+ const controllerType = data[5];
2417
+ const table = this.channels[channelNumber].controlTable[controllerType];
2418
+ for (let i = 6; i < data.length - 1; i += 2) {
2419
+ const pp = data[i];
2420
+ const rr = data[i + 1];
2421
+ table[pp] = rr;
2422
+ }
2423
+ }
2382
2424
  getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
2383
2425
  const index = keyNumber * 128 + controllerType;
2384
2426
  const controlValue = channel.keyBasedInstrumentControlTable[index];
@@ -2429,7 +2471,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2429
2471
  currentBufferSource: null,
2430
2472
  detune: 0,
2431
2473
  scaleOctaveTuningTable: new Array(12).fill(0), // cent
2432
- pressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2474
+ channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2433
2475
  keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
2434
2476
  program: 0,
2435
2477
  bank: 121 * 128,
@@ -2444,16 +2486,3 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2444
2486
  modulationDepthRange: 50, // cent
2445
2487
  }
2446
2488
  });
2447
- Object.defineProperty(MidyGM2, "controllerDestinationSettings", {
2448
- enumerable: true,
2449
- configurable: true,
2450
- writable: true,
2451
- value: {
2452
- pitchControl: 0,
2453
- filterCutoffControl: 0,
2454
- amplitudeControl: 1,
2455
- lfoPitchDepth: 0,
2456
- lfoFilterDepth: 0,
2457
- lfoAmplitudeDepth: 0,
2458
- }
2459
- });
@@ -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"}