@marmooo/midy 0.3.5 → 0.3.6

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/script/midy.d.ts CHANGED
@@ -49,8 +49,8 @@ export class Midy {
49
49
  isStopping: boolean;
50
50
  isSeeking: boolean;
51
51
  timeline: any[];
52
- instruments: any[];
53
52
  notePromises: any[];
53
+ instruments: Set<any>;
54
54
  exclusiveClassNotes: any[];
55
55
  drumExclusiveClassNotes: any[];
56
56
  audioContext: any;
@@ -62,8 +62,8 @@ export class Midy {
62
62
  vibLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
63
63
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
64
64
  modLfoToVolume: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
65
- chorusEffectsSend: (channel: any, note: any, prevValue: any, scheduleTime: any) => void;
66
- reverbEffectsSend: (channel: any, note: any, prevValue: any, scheduleTime: any) => void;
65
+ chorusEffectsSend: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
66
+ reverbEffectsSend: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
67
67
  delayModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
68
68
  freqModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
69
69
  delayVibLFO: (channel: any, note: any, prevValue: any, scheduleTime: any) => void;
@@ -90,7 +90,7 @@ export class Midy {
90
90
  loadSoundFont(input: any): Promise<void>;
91
91
  loadMIDI(input: any): Promise<void>;
92
92
  cacheVoiceIds(): void;
93
- getVoiceId(channel: any, noteNumber: any, velocity: any): string | undefined;
93
+ getVoiceId(channel: any, noteNumber: any, velocity: any): any;
94
94
  createChannelAudioNodes(audioContext: any): {
95
95
  gainL: any;
96
96
  gainR: any;
@@ -192,8 +192,8 @@ export class Midy {
192
192
  setVibLfoToPitch(channel: any, note: any, scheduleTime: any): void;
193
193
  setModLfoToFilterFc(channel: any, note: any, scheduleTime: any): void;
194
194
  setModLfoToVolume(channel: any, note: any, scheduleTime: any): void;
195
- setReverbEffectsSend(channel: any, note: any, prevValue: any, scheduleTime: any): void;
196
- setChorusEffectsSend(channel: any, note: any, prevValue: any, scheduleTime: any): void;
195
+ setReverbSend(channel: any, note: any, scheduleTime: any): void;
196
+ setChorusSend(channel: any, note: any, scheduleTime: any): void;
197
197
  setDelayModLFO(note: any, scheduleTime: any): void;
198
198
  setFreqModLFO(note: any, scheduleTime: any): void;
199
199
  setFreqVibLFO(channel: any, note: any, scheduleTime: any): void;
@@ -202,8 +202,8 @@ export class Midy {
202
202
  vibLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
203
203
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
204
204
  modLfoToVolume: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
205
- chorusEffectsSend: (channel: any, note: any, prevValue: any, scheduleTime: any) => void;
206
- reverbEffectsSend: (channel: any, note: any, prevValue: any, scheduleTime: any) => void;
205
+ chorusEffectsSend: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
206
+ reverbEffectsSend: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
207
207
  delayModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
208
208
  freqModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
209
209
  delayVibLFO: (channel: any, note: any, prevValue: any, scheduleTime: any) => void;
@@ -228,7 +228,7 @@ export class Midy {
228
228
  setBankLSB(channelNumber: any, lsb: any): void;
229
229
  dataEntryLSB(channelNumber: any, value: any, scheduleTime: any): void;
230
230
  updateChannelVolume(channel: any, scheduleTime: any): void;
231
- updateKeyBasedVolume(channel: any, scheduleTime: any): void;
231
+ updateKeyBasedVolume(channel: any, keyNumber: any, scheduleTime: any): void;
232
232
  setSustainPedal(channelNumber: any, value: any, scheduleTime: any): void;
233
233
  setPortamento(channelNumber: any, value: any, scheduleTime: any): void;
234
234
  setSostenutoPedal(channelNumber: any, value: any, scheduleTime: any): void;
@@ -305,11 +305,11 @@ export class Midy {
305
305
  getLFOPitchDepth(channel: any, note: any): number;
306
306
  getLFOFilterDepth(channel: any, note: any): number;
307
307
  getLFOAmplitudeDepth(channel: any, note: any): number;
308
- setControllerParameters(channel: any, note: any, table: any, scheduleTime: any): void;
309
- handlePressureSysEx(data: any, tableName: any): void;
308
+ setEffects(channel: any, note: any, table: any, scheduleTime: any): void;
309
+ handlePressureSysEx(data: any, tableName: any, scheduleTime: any): void;
310
310
  initControlTable(): Int8Array<ArrayBuffer>;
311
- applyControlTable(channel: any, controllerType: any, scheduleTime: any): void;
312
- handleControlChangeSysEx(data: any): void;
311
+ setControlChangeEffects(channel: any, controllerType: any, scheduleTime: any): void;
312
+ handleControlChangeSysEx(data: any, scheduleTime: any): void;
313
313
  getKeyBasedValue(channel: any, keyNumber: any, controllerType: any): any;
314
314
  handleKeyBasedInstrumentControlSysEx(data: any, scheduleTime: any): void;
315
315
  handleSysEx(data: any, scheduleTime: any): void;
@@ -328,8 +328,8 @@ declare class Note {
328
328
  modulationDepth: any;
329
329
  vibratoLFO: any;
330
330
  vibratoDepth: any;
331
- reverbEffectsSend: any;
332
- chorusEffectsSend: any;
331
+ reverbSend: any;
332
+ chorusSend: any;
333
333
  portamentoNoteNumber: number;
334
334
  pressure: number;
335
335
  noteNumber: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
package/script/midy.js CHANGED
@@ -71,13 +71,13 @@ class Note {
71
71
  writable: true,
72
72
  value: void 0
73
73
  });
74
- Object.defineProperty(this, "reverbEffectsSend", {
74
+ Object.defineProperty(this, "reverbSend", {
75
75
  enumerable: true,
76
76
  configurable: true,
77
77
  writable: true,
78
78
  value: void 0
79
79
  });
80
- Object.defineProperty(this, "chorusEffectsSend", {
80
+ Object.defineProperty(this, "chorusSend", {
81
81
  enumerable: true,
82
82
  configurable: true,
83
83
  writable: true,
@@ -380,17 +380,17 @@ class Midy {
380
380
  writable: true,
381
381
  value: []
382
382
  });
383
- Object.defineProperty(this, "instruments", {
383
+ Object.defineProperty(this, "notePromises", {
384
384
  enumerable: true,
385
385
  configurable: true,
386
386
  writable: true,
387
387
  value: []
388
388
  });
389
- Object.defineProperty(this, "notePromises", {
389
+ Object.defineProperty(this, "instruments", {
390
390
  enumerable: true,
391
391
  configurable: true,
392
392
  writable: true,
393
- value: []
393
+ value: new Set()
394
394
  });
395
395
  Object.defineProperty(this, "exclusiveClassNotes", {
396
396
  enumerable: true,
@@ -522,7 +522,7 @@ class Midy {
522
522
  const soundFont = this.soundFonts[soundFontIndex];
523
523
  const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
524
524
  const { instrument, sampleID } = voice.generators;
525
- return `${soundFontIndex}:${instrument}:${sampleID}`;
525
+ return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
526
526
  }
527
527
  createChannelAudioNodes(audioContext) {
528
528
  const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
@@ -927,13 +927,11 @@ class Midy {
927
927
  return impulse;
928
928
  }
929
929
  createConvolutionReverb(audioContext, impulse) {
930
- const input = new GainNode(audioContext);
931
930
  const convolverNode = new ConvolverNode(audioContext, {
932
931
  buffer: impulse,
933
932
  });
934
- input.connect(convolverNode);
935
933
  return {
936
- input,
934
+ input: convolverNode,
937
935
  output: convolverNode,
938
936
  convolverNode,
939
937
  };
@@ -1370,12 +1368,8 @@ class Midy {
1370
1368
  }
1371
1369
  note.bufferSource.connect(note.filterNode);
1372
1370
  note.filterNode.connect(note.volumeEnvelopeNode);
1373
- if (0 < state.chorusSendLevel) {
1374
- this.setChorusEffectsSend(channel, note, 0, now);
1375
- }
1376
- if (0 < state.reverbSendLevel) {
1377
- this.setReverbEffectsSend(channel, note, 0, now);
1378
- }
1371
+ this.setChorusSend(channel, note, now);
1372
+ this.setReverbSend(channel, note, now);
1379
1373
  note.bufferSource.start(startTime);
1380
1374
  return note;
1381
1375
  }
@@ -1441,10 +1435,14 @@ class Midy {
1441
1435
  return;
1442
1436
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
1443
1437
  if (channel.isDrum) {
1444
- const audioContext = this.audioContext;
1445
- const { gainL, gainR } = this.createChannelAudioNodes(audioContext);
1446
- channel.keyBasedGainLs[noteNumber] = gainL;
1447
- channel.keyBasedGainRs[noteNumber] = gainR;
1438
+ const { keyBasedGainLs, keyBasedGainRs } = channel;
1439
+ let gainL = keyBasedGainLs[noteNumber];
1440
+ let gainR = keyBasedGainRs[noteNumber];
1441
+ if (!gainL) {
1442
+ const audioNodes = this.createChannelAudioNodes(this.audioContext);
1443
+ gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
1444
+ gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
1445
+ }
1448
1446
  note.volumeEnvelopeNode.connect(gainL);
1449
1447
  note.volumeEnvelopeNode.connect(gainR);
1450
1448
  }
@@ -1478,11 +1476,11 @@ class Midy {
1478
1476
  note.vibratoDepth.disconnect();
1479
1477
  note.vibratoLFO.stop();
1480
1478
  }
1481
- if (note.reverbEffectsSend) {
1482
- note.reverbEffectsSend.disconnect();
1479
+ if (note.reverbSend) {
1480
+ note.reverbSend.disconnect();
1483
1481
  }
1484
- if (note.chorusEffectsSend) {
1485
- note.chorusEffectsSend.disconnect();
1482
+ if (note.chorusSend) {
1483
+ note.chorusSend.disconnect();
1486
1484
  }
1487
1485
  }
1488
1486
  releaseNote(channel, note, endTime) {
@@ -1613,7 +1611,7 @@ class Midy {
1613
1611
  this.processActiveNotes(channel, scheduleTime, (note) => {
1614
1612
  if (note.noteNumber === noteNumber) {
1615
1613
  note.pressure = pressure;
1616
- this.setControllerParameters(channel, note, table, scheduleTime);
1614
+ this.setEffects(channel, note, table, scheduleTime);
1617
1615
  }
1618
1616
  });
1619
1617
  this.applyVoiceParams(channel, 10);
@@ -1647,7 +1645,7 @@ class Midy {
1647
1645
  }
1648
1646
  const table = channel.channelPressureTable;
1649
1647
  this.processActiveNotes(channel, scheduleTime, (note) => {
1650
- this.setControllerParameters(channel, note, table, scheduleTime);
1648
+ this.setEffects(channel, note, table, scheduleTime);
1651
1649
  });
1652
1650
  this.applyVoiceParams(channel, 13);
1653
1651
  }
@@ -1669,13 +1667,18 @@ class Midy {
1669
1667
  this.applyVoiceParams(channel, 14, scheduleTime);
1670
1668
  }
1671
1669
  setModLfoToPitch(channel, note, scheduleTime) {
1672
- const modLfoToPitch = note.voiceParams.modLfoToPitch +
1673
- this.getLFOPitchDepth(channel, note);
1674
- const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1675
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1676
- note.modulationDepth.gain
1677
- .cancelScheduledValues(scheduleTime)
1678
- .setValueAtTime(modulationDepth, scheduleTime);
1670
+ if (note.modulationDepth) {
1671
+ const modLfoToPitch = note.voiceParams.modLfoToPitch +
1672
+ this.getLFOPitchDepth(channel, note);
1673
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1674
+ const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1675
+ note.modulationDepth.gain
1676
+ .cancelScheduledValues(scheduleTime)
1677
+ .setValueAtTime(modulationDepth, scheduleTime);
1678
+ }
1679
+ else {
1680
+ this.startModulation(channel, note, scheduleTime);
1681
+ }
1679
1682
  }
1680
1683
  setVibLfoToPitch(channel, note, scheduleTime) {
1681
1684
  const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
@@ -1702,63 +1705,63 @@ class Midy {
1702
1705
  .cancelScheduledValues(scheduleTime)
1703
1706
  .setValueAtTime(volumeDepth, scheduleTime);
1704
1707
  }
1705
- setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
1706
- let value = note.voiceParams.reverbEffectsSend;
1708
+ setReverbSend(channel, note, scheduleTime) {
1709
+ let value = note.voiceParams.reverbEffectsSend *
1710
+ channel.state.reverbSendLevel;
1707
1711
  if (channel.isDrum) {
1708
1712
  const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 91);
1709
- if (0 <= keyBasedValue) {
1710
- value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
1711
- }
1713
+ if (0 <= keyBasedValue)
1714
+ value = keyBasedValue / 127;
1712
1715
  }
1713
- if (0 < prevValue) {
1716
+ if (!note.reverbSend) {
1714
1717
  if (0 < value) {
1715
- note.reverbEffectsSend.gain
1716
- .cancelScheduledValues(scheduleTime)
1717
- .setValueAtTime(value, scheduleTime);
1718
- }
1719
- else {
1720
- note.reverbEffectsSend.disconnect();
1718
+ note.reverbSend = new GainNode(this.audioContext, { gain: value });
1719
+ note.volumeEnvelopeNode.connect(note.reverbSend);
1720
+ note.reverbSend.connect(this.reverbEffect.input);
1721
1721
  }
1722
1722
  }
1723
1723
  else {
1724
+ note.reverbSend.gain
1725
+ .cancelScheduledValues(scheduleTime)
1726
+ .setValueAtTime(value, scheduleTime);
1724
1727
  if (0 < value) {
1725
- if (!note.reverbEffectsSend) {
1726
- note.reverbEffectsSend = new GainNode(this.audioContext, {
1727
- gain: value,
1728
- });
1729
- note.volumeEnvelopeNode.connect(note.reverbEffectsSend);
1728
+ note.volumeEnvelopeNode.connect(note.reverbSend);
1729
+ }
1730
+ else {
1731
+ try {
1732
+ note.volumeEnvelopeNode.disconnect(note.reverbSend);
1730
1733
  }
1731
- note.reverbEffectsSend.connect(this.reverbEffect.input);
1734
+ catch { /* empty */ }
1732
1735
  }
1733
1736
  }
1734
1737
  }
1735
- setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
1736
- let value = note.voiceParams.chorusEffectsSend;
1738
+ setChorusSend(channel, note, scheduleTime) {
1739
+ let value = note.voiceParams.chorusEffectsSend *
1740
+ channel.state.chorusSendLevel;
1737
1741
  if (channel.isDrum) {
1738
1742
  const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 93);
1739
- if (0 <= keyBasedValue) {
1740
- value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
1741
- }
1743
+ if (0 <= keyBasedValue)
1744
+ value = keyBasedValue / 127;
1742
1745
  }
1743
- if (0 < prevValue) {
1746
+ if (!note.chorusSend) {
1744
1747
  if (0 < value) {
1745
- note.chorusEffectsSend.gain
1746
- .cancelScheduledValues(scheduleTime)
1747
- .setValueAtTime(value, scheduleTime);
1748
- }
1749
- else {
1750
- note.chorusEffectsSend.disconnect();
1748
+ note.chorusSend = new GainNode(this.audioContext, { gain: value });
1749
+ note.volumeEnvelopeNode.connect(note.chorusSend);
1750
+ note.chorusSend.connect(this.chorusEffect.input);
1751
1751
  }
1752
1752
  }
1753
1753
  else {
1754
+ note.chorusSend.gain
1755
+ .cancelScheduledValues(scheduleTime)
1756
+ .setValueAtTime(value, scheduleTime);
1754
1757
  if (0 < value) {
1755
- if (!note.chorusEffectsSend) {
1756
- note.chorusEffectsSend = new GainNode(this.audioContext, {
1757
- gain: value,
1758
- });
1759
- note.volumeEnvelopeNode.connect(note.chorusEffectsSend);
1758
+ note.volumeEnvelopeNode.connect(note.chorusSend);
1759
+ }
1760
+ else {
1761
+ try {
1762
+ note.volumeEnvelopeNode.disconnect(note.chorusSend);
1760
1763
  }
1761
- note.chorusEffectsSend.connect(this.chorusEffect.input);
1764
+ catch { /* empty */ }
1762
1765
  }
1763
1766
  }
1764
1767
  }
@@ -1804,11 +1807,11 @@ class Midy {
1804
1807
  this.setModLfoToVolume(channel, note, scheduleTime);
1805
1808
  }
1806
1809
  },
1807
- chorusEffectsSend: (channel, note, prevValue, scheduleTime) => {
1808
- this.setChorusEffectsSend(channel, note, prevValue, scheduleTime);
1810
+ chorusEffectsSend: (channel, note, _prevValue, scheduleTime) => {
1811
+ this.setChorusSend(channel, note, scheduleTime);
1809
1812
  },
1810
- reverbEffectsSend: (channel, note, prevValue, scheduleTime) => {
1811
- this.setReverbEffectsSend(channel, note, prevValue, scheduleTime);
1813
+ reverbEffectsSend: (channel, note, _prevValue, scheduleTime) => {
1814
+ this.setReverbSend(channel, note, scheduleTime);
1812
1815
  },
1813
1816
  delayModLFO: (_channel, note, _prevValue, scheduleTime) => this.setDelayModLFO(note, scheduleTime),
1814
1817
  freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
@@ -1918,7 +1921,7 @@ class Midy {
1918
1921
  handler.call(this, channelNumber, value, scheduleTime);
1919
1922
  const channel = this.channels[channelNumber];
1920
1923
  this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
1921
- this.applyControlTable(channel, controllerType, scheduleTime);
1924
+ this.setControlChangeEffects(channel, controllerType, scheduleTime);
1922
1925
  }
1923
1926
  else {
1924
1927
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1934,7 +1937,6 @@ class Midy {
1934
1937
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1935
1938
  }
1936
1939
  else {
1937
- this.setPitchEnvelope(note, scheduleTime);
1938
1940
  this.startModulation(channel, note, scheduleTime);
1939
1941
  }
1940
1942
  });
@@ -1979,8 +1981,14 @@ class Midy {
1979
1981
  scheduleTime ??= this.audioContext.currentTime;
1980
1982
  const channel = this.channels[channelNumber];
1981
1983
  channel.state.volume = volume / 127;
1982
- this.updateChannelVolume(channel, scheduleTime);
1983
- this.updateKeyBasedVolume(channel, scheduleTime);
1984
+ if (channel.isDrum) {
1985
+ for (let i = 0; i < 128; i++) {
1986
+ this.updateKeyBasedVolume(channel, i, scheduleTime);
1987
+ }
1988
+ }
1989
+ else {
1990
+ this.updateChannelVolume(channel, scheduleTime);
1991
+ }
1984
1992
  }
1985
1993
  panToGain(pan) {
1986
1994
  const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
@@ -1993,8 +2001,14 @@ class Midy {
1993
2001
  scheduleTime ??= this.audioContext.currentTime;
1994
2002
  const channel = this.channels[channelNumber];
1995
2003
  channel.state.pan = pan / 127;
1996
- this.updateChannelVolume(channel, scheduleTime);
1997
- this.updateKeyBasedVolume(channel, scheduleTime);
2004
+ if (channel.isDrum) {
2005
+ for (let i = 0; i < 128; i++) {
2006
+ this.updateKeyBasedVolume(channel, i, scheduleTime);
2007
+ }
2008
+ }
2009
+ else {
2010
+ this.updateChannelVolume(channel, scheduleTime);
2011
+ }
1998
2012
  }
1999
2013
  setExpression(channelNumber, expression, scheduleTime) {
2000
2014
  scheduleTime ??= this.audioContext.currentTime;
@@ -2020,33 +2034,27 @@ class Midy {
2020
2034
  .cancelScheduledValues(scheduleTime)
2021
2035
  .setValueAtTime(volume * gainRight, scheduleTime);
2022
2036
  }
2023
- updateKeyBasedVolume(channel, scheduleTime) {
2024
- if (!channel.isDrum)
2025
- return;
2037
+ updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
2026
2038
  const state = channel.state;
2027
2039
  const defaultVolume = state.volume * state.expression;
2028
2040
  const defaultPan = state.pan;
2029
- for (let i = 0; i < 128; i++) {
2030
- const gainL = channel.keyBasedGainLs[i];
2031
- const gainR = channel.keyBasedGainLs[i];
2032
- if (!gainL)
2033
- continue;
2034
- if (!gainR)
2035
- continue;
2036
- const keyBasedVolume = this.getKeyBasedValue(channel, i, 7);
2037
- const volume = (0 <= keyBasedVolume)
2038
- ? defaultVolume * keyBasedVolume / 64
2039
- : defaultVolume;
2040
- const keyBasedPan = this.getKeyBasedValue(channel, i, 10);
2041
- const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
2042
- const { gainLeft, gainRight } = this.panToGain(pan);
2043
- gainL.gain
2044
- .cancelScheduledValues(scheduleTime)
2045
- .setValueAtTime(volume * gainLeft, scheduleTime);
2046
- gainR.gain
2047
- .cancelScheduledValues(scheduleTime)
2048
- .setValueAtTime(volume * gainRight, scheduleTime);
2049
- }
2041
+ const gainL = channel.keyBasedGainLs[keyNumber];
2042
+ const gainR = channel.keyBasedGainRs[keyNumber];
2043
+ if (!gainL)
2044
+ return;
2045
+ const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
2046
+ const volume = (0 <= keyBasedVolume)
2047
+ ? defaultVolume * keyBasedVolume / 64
2048
+ : defaultVolume;
2049
+ const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
2050
+ const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
2051
+ const { gainLeft, gainRight } = this.panToGain(pan);
2052
+ gainL.gain
2053
+ .cancelScheduledValues(scheduleTime)
2054
+ .setValueAtTime(volume * gainLeft, scheduleTime);
2055
+ gainR.gain
2056
+ .cancelScheduledValues(scheduleTime)
2057
+ .setValueAtTime(volume * gainRight, scheduleTime);
2050
2058
  }
2051
2059
  setSustainPedal(channelNumber, value, scheduleTime) {
2052
2060
  const channel = this.channels[channelNumber];
@@ -2212,67 +2220,19 @@ class Midy {
2212
2220
  scheduleTime ??= this.audioContext.currentTime;
2213
2221
  const channel = this.channels[channelNumber];
2214
2222
  const state = channel.state;
2215
- const reverbEffect = this.reverbEffect;
2216
- if (0 < state.reverbSendLevel) {
2217
- if (0 < reverbSendLevel) {
2218
- state.reverbSendLevel = reverbSendLevel / 127;
2219
- reverbEffect.input.gain
2220
- .cancelScheduledValues(scheduleTime)
2221
- .setValueAtTime(state.reverbSendLevel, scheduleTime);
2222
- }
2223
- else {
2224
- this.processScheduledNotes(channel, (note) => {
2225
- if (note.voiceParams.reverbEffectsSend <= 0)
2226
- return false;
2227
- if (note.reverbEffectsSend)
2228
- note.reverbEffectsSend.disconnect();
2229
- });
2230
- }
2231
- }
2232
- else {
2233
- if (0 < reverbSendLevel) {
2234
- this.processScheduledNotes(channel, (note) => {
2235
- this.setReverbEffectsSend(channel, note, 0, scheduleTime);
2236
- });
2237
- state.reverbSendLevel = reverbSendLevel / 127;
2238
- reverbEffect.input.gain
2239
- .cancelScheduledValues(scheduleTime)
2240
- .setValueAtTime(state.reverbSendLevel, scheduleTime);
2241
- }
2242
- }
2223
+ state.reverbSendLevel = reverbSendLevel / 127;
2224
+ this.processScheduledNotes(channel, (note) => {
2225
+ this.setReverbSend(channel, note, scheduleTime);
2226
+ });
2243
2227
  }
2244
2228
  setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
2245
2229
  scheduleTime ??= this.audioContext.currentTime;
2246
2230
  const channel = this.channels[channelNumber];
2247
2231
  const state = channel.state;
2248
- const chorusEffect = this.chorusEffect;
2249
- if (0 < state.chorusSendLevel) {
2250
- if (0 < chorusSendLevel) {
2251
- state.chorusSendLevel = chorusSendLevel / 127;
2252
- chorusEffect.input.gain
2253
- .cancelScheduledValues(scheduleTime)
2254
- .setValueAtTime(state.chorusSendLevel, scheduleTime);
2255
- }
2256
- else {
2257
- this.processScheduledNotes(channel, (note) => {
2258
- if (note.voiceParams.chorusEffectsSend <= 0)
2259
- return false;
2260
- if (note.chorusEffectsSend)
2261
- note.chorusEffectsSend.disconnect();
2262
- });
2263
- }
2264
- }
2265
- else {
2266
- if (0 < chorusSendLevel) {
2267
- this.processScheduledNotes(channel, (note) => {
2268
- this.setChorusEffectsSend(channel, note, 0, scheduleTime);
2269
- });
2270
- state.chorusSendLevel = chorusSendLevel / 127;
2271
- chorusEffect.input.gain
2272
- .cancelScheduledValues(scheduleTime)
2273
- .setValueAtTime(state.chorusSendLevel, scheduleTime);
2274
- }
2275
- }
2232
+ state.chorusSendLevel = chorusSendLevel / 127;
2233
+ this.processScheduledNotes(channel, (note) => {
2234
+ this.setChorusSend(channel, note, scheduleTime);
2235
+ });
2276
2236
  }
2277
2237
  limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
2278
2238
  if (maxLSB < channel.dataLSB) {
@@ -2583,11 +2543,11 @@ class Midy {
2583
2543
  case 9:
2584
2544
  switch (data[3]) {
2585
2545
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2586
- return this.handlePressureSysEx(data, "channelPressureTable");
2546
+ return this.handlePressureSysEx(data, "channelPressureTable", scheduleTime);
2587
2547
  case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2588
- return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
2548
+ return this.handlePressureSysEx(data, "polyphonicKeyPressureTable", scheduleTime);
2589
2549
  case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2590
- return this.handleControlChangeSysEx(data);
2550
+ return this.handleControlChangeSysEx(data, scheduleTime);
2591
2551
  default:
2592
2552
  console.warn(`Unsupported Exclusive Message: ${data}`);
2593
2553
  }
@@ -2958,9 +2918,9 @@ class Midy {
2958
2918
  : 0;
2959
2919
  return (channelPressure + polyphonicKeyPressure) / 254;
2960
2920
  }
2961
- setControllerParameters(channel, note, table, scheduleTime) {
2921
+ setEffects(channel, note, table, scheduleTime) {
2962
2922
  if (0 <= table[0])
2963
- this.updateDetune(channel, note, scueduleTime);
2923
+ this.updateDetune(channel, note, scheduleTime);
2964
2924
  if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
2965
2925
  if (0 <= table[1]) {
2966
2926
  this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
@@ -2982,7 +2942,7 @@ class Midy {
2982
2942
  if (0 <= table[5])
2983
2943
  this.setModLfoToVolume(channel, note, scheduleTime);
2984
2944
  }
2985
- handlePressureSysEx(data, tableName) {
2945
+ handlePressureSysEx(data, tableName, scheduleTime) {
2986
2946
  const channelNumber = data[4];
2987
2947
  const channel = this.channels[channelNumber];
2988
2948
  if (channel.isDrum)
@@ -2993,32 +2953,38 @@ class Midy {
2993
2953
  const rr = data[i + 1];
2994
2954
  table[pp] = rr;
2995
2955
  }
2956
+ this.processActiveNotes(channel, scheduleTime, (note) => {
2957
+ this.setEffects(channel, note, table, scheduleTime);
2958
+ });
2996
2959
  }
2997
2960
  initControlTable() {
2998
2961
  const ccCount = 128;
2999
2962
  const slotSize = 6;
3000
2963
  return new Int8Array(ccCount * slotSize).fill(-1);
3001
2964
  }
3002
- applyControlTable(channel, controllerType, scheduleTime) {
2965
+ setControlChangeEffects(channel, controllerType, scheduleTime) {
3003
2966
  const slotSize = 6;
3004
2967
  const offset = controllerType * slotSize;
3005
2968
  const table = channel.controlTable.subarray(offset, offset + slotSize);
3006
2969
  this.processScheduledNotes(channel, (note) => {
3007
- this.setControllerParameters(channel, note, table, scheduleTime);
2970
+ this.setEffects(channel, note, table, scheduleTime);
3008
2971
  });
3009
2972
  }
3010
- handleControlChangeSysEx(data) {
2973
+ handleControlChangeSysEx(data, scheduleTime) {
3011
2974
  const channelNumber = data[4];
3012
2975
  const channel = this.channels[channelNumber];
3013
2976
  if (channel.isDrum)
3014
2977
  return;
2978
+ const slotSize = 6;
3015
2979
  const controllerType = data[5];
3016
- const table = channel.controlTable[controllerType];
3017
- for (let i = 6; i < data.length - 1; i += 2) {
2980
+ const offset = controllerType * slotSize;
2981
+ const table = channel.controlTable;
2982
+ for (let i = 6; i < data.length; i += 2) {
3018
2983
  const pp = data[i];
3019
2984
  const rr = data[i + 1];
3020
- table[pp] = rr;
2985
+ table[offset + pp] = rr;
3021
2986
  }
2987
+ this.setControlChangeEffects(channel, controllerType, scheduleTime);
3022
2988
  }
3023
2989
  getKeyBasedValue(channel, keyNumber, controllerType) {
3024
2990
  const index = keyNumber * 128 + controllerType;
@@ -3032,13 +2998,20 @@ class Midy {
3032
2998
  return;
3033
2999
  const keyNumber = data[5];
3034
3000
  const table = channel.keyBasedInstrumentControlTable;
3035
- for (let i = 6; i < data.length - 1; i += 2) {
3001
+ for (let i = 6; i < data.length; i += 2) {
3036
3002
  const controllerType = data[i];
3037
3003
  const value = data[i + 1];
3038
3004
  const index = keyNumber * 128 + controllerType;
3039
3005
  table[index] = value;
3006
+ switch (controllerType) {
3007
+ case 7:
3008
+ case 10:
3009
+ this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
3010
+ break;
3011
+ default: // TODO
3012
+ this.setControlChange(channelNumber, controllerType, value, scheduleTime);
3013
+ }
3040
3014
  }
3041
- this.setChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
3042
3015
  }
3043
3016
  handleSysEx(data, scheduleTime) {
3044
3017
  switch (data[0]) {