@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/esm/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;
package/esm/midy.d.ts.map CHANGED
@@ -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/esm/midy.js CHANGED
@@ -68,13 +68,13 @@ class Note {
68
68
  writable: true,
69
69
  value: void 0
70
70
  });
71
- Object.defineProperty(this, "reverbEffectsSend", {
71
+ Object.defineProperty(this, "reverbSend", {
72
72
  enumerable: true,
73
73
  configurable: true,
74
74
  writable: true,
75
75
  value: void 0
76
76
  });
77
- Object.defineProperty(this, "chorusEffectsSend", {
77
+ Object.defineProperty(this, "chorusSend", {
78
78
  enumerable: true,
79
79
  configurable: true,
80
80
  writable: true,
@@ -377,17 +377,17 @@ export class Midy {
377
377
  writable: true,
378
378
  value: []
379
379
  });
380
- Object.defineProperty(this, "instruments", {
380
+ Object.defineProperty(this, "notePromises", {
381
381
  enumerable: true,
382
382
  configurable: true,
383
383
  writable: true,
384
384
  value: []
385
385
  });
386
- Object.defineProperty(this, "notePromises", {
386
+ Object.defineProperty(this, "instruments", {
387
387
  enumerable: true,
388
388
  configurable: true,
389
389
  writable: true,
390
- value: []
390
+ value: new Set()
391
391
  });
392
392
  Object.defineProperty(this, "exclusiveClassNotes", {
393
393
  enumerable: true,
@@ -519,7 +519,7 @@ export class Midy {
519
519
  const soundFont = this.soundFonts[soundFontIndex];
520
520
  const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
521
521
  const { instrument, sampleID } = voice.generators;
522
- return `${soundFontIndex}:${instrument}:${sampleID}`;
522
+ return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
523
523
  }
524
524
  createChannelAudioNodes(audioContext) {
525
525
  const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
@@ -924,13 +924,11 @@ export class Midy {
924
924
  return impulse;
925
925
  }
926
926
  createConvolutionReverb(audioContext, impulse) {
927
- const input = new GainNode(audioContext);
928
927
  const convolverNode = new ConvolverNode(audioContext, {
929
928
  buffer: impulse,
930
929
  });
931
- input.connect(convolverNode);
932
930
  return {
933
- input,
931
+ input: convolverNode,
934
932
  output: convolverNode,
935
933
  convolverNode,
936
934
  };
@@ -1367,12 +1365,8 @@ export class Midy {
1367
1365
  }
1368
1366
  note.bufferSource.connect(note.filterNode);
1369
1367
  note.filterNode.connect(note.volumeEnvelopeNode);
1370
- if (0 < state.chorusSendLevel) {
1371
- this.setChorusEffectsSend(channel, note, 0, now);
1372
- }
1373
- if (0 < state.reverbSendLevel) {
1374
- this.setReverbEffectsSend(channel, note, 0, now);
1375
- }
1368
+ this.setChorusSend(channel, note, now);
1369
+ this.setReverbSend(channel, note, now);
1376
1370
  note.bufferSource.start(startTime);
1377
1371
  return note;
1378
1372
  }
@@ -1438,10 +1432,14 @@ export class Midy {
1438
1432
  return;
1439
1433
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
1440
1434
  if (channel.isDrum) {
1441
- const audioContext = this.audioContext;
1442
- const { gainL, gainR } = this.createChannelAudioNodes(audioContext);
1443
- channel.keyBasedGainLs[noteNumber] = gainL;
1444
- channel.keyBasedGainRs[noteNumber] = gainR;
1435
+ const { keyBasedGainLs, keyBasedGainRs } = channel;
1436
+ let gainL = keyBasedGainLs[noteNumber];
1437
+ let gainR = keyBasedGainRs[noteNumber];
1438
+ if (!gainL) {
1439
+ const audioNodes = this.createChannelAudioNodes(this.audioContext);
1440
+ gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
1441
+ gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
1442
+ }
1445
1443
  note.volumeEnvelopeNode.connect(gainL);
1446
1444
  note.volumeEnvelopeNode.connect(gainR);
1447
1445
  }
@@ -1475,11 +1473,11 @@ export class Midy {
1475
1473
  note.vibratoDepth.disconnect();
1476
1474
  note.vibratoLFO.stop();
1477
1475
  }
1478
- if (note.reverbEffectsSend) {
1479
- note.reverbEffectsSend.disconnect();
1476
+ if (note.reverbSend) {
1477
+ note.reverbSend.disconnect();
1480
1478
  }
1481
- if (note.chorusEffectsSend) {
1482
- note.chorusEffectsSend.disconnect();
1479
+ if (note.chorusSend) {
1480
+ note.chorusSend.disconnect();
1483
1481
  }
1484
1482
  }
1485
1483
  releaseNote(channel, note, endTime) {
@@ -1610,7 +1608,7 @@ export class Midy {
1610
1608
  this.processActiveNotes(channel, scheduleTime, (note) => {
1611
1609
  if (note.noteNumber === noteNumber) {
1612
1610
  note.pressure = pressure;
1613
- this.setControllerParameters(channel, note, table, scheduleTime);
1611
+ this.setEffects(channel, note, table, scheduleTime);
1614
1612
  }
1615
1613
  });
1616
1614
  this.applyVoiceParams(channel, 10);
@@ -1644,7 +1642,7 @@ export class Midy {
1644
1642
  }
1645
1643
  const table = channel.channelPressureTable;
1646
1644
  this.processActiveNotes(channel, scheduleTime, (note) => {
1647
- this.setControllerParameters(channel, note, table, scheduleTime);
1645
+ this.setEffects(channel, note, table, scheduleTime);
1648
1646
  });
1649
1647
  this.applyVoiceParams(channel, 13);
1650
1648
  }
@@ -1666,13 +1664,18 @@ export class Midy {
1666
1664
  this.applyVoiceParams(channel, 14, scheduleTime);
1667
1665
  }
1668
1666
  setModLfoToPitch(channel, note, scheduleTime) {
1669
- const modLfoToPitch = note.voiceParams.modLfoToPitch +
1670
- this.getLFOPitchDepth(channel, note);
1671
- const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1672
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1673
- note.modulationDepth.gain
1674
- .cancelScheduledValues(scheduleTime)
1675
- .setValueAtTime(modulationDepth, scheduleTime);
1667
+ if (note.modulationDepth) {
1668
+ const modLfoToPitch = note.voiceParams.modLfoToPitch +
1669
+ this.getLFOPitchDepth(channel, note);
1670
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1671
+ const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1672
+ note.modulationDepth.gain
1673
+ .cancelScheduledValues(scheduleTime)
1674
+ .setValueAtTime(modulationDepth, scheduleTime);
1675
+ }
1676
+ else {
1677
+ this.startModulation(channel, note, scheduleTime);
1678
+ }
1676
1679
  }
1677
1680
  setVibLfoToPitch(channel, note, scheduleTime) {
1678
1681
  const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
@@ -1699,63 +1702,63 @@ export class Midy {
1699
1702
  .cancelScheduledValues(scheduleTime)
1700
1703
  .setValueAtTime(volumeDepth, scheduleTime);
1701
1704
  }
1702
- setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
1703
- let value = note.voiceParams.reverbEffectsSend;
1705
+ setReverbSend(channel, note, scheduleTime) {
1706
+ let value = note.voiceParams.reverbEffectsSend *
1707
+ channel.state.reverbSendLevel;
1704
1708
  if (channel.isDrum) {
1705
1709
  const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 91);
1706
- if (0 <= keyBasedValue) {
1707
- value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
1708
- }
1710
+ if (0 <= keyBasedValue)
1711
+ value = keyBasedValue / 127;
1709
1712
  }
1710
- if (0 < prevValue) {
1713
+ if (!note.reverbSend) {
1711
1714
  if (0 < value) {
1712
- note.reverbEffectsSend.gain
1713
- .cancelScheduledValues(scheduleTime)
1714
- .setValueAtTime(value, scheduleTime);
1715
- }
1716
- else {
1717
- note.reverbEffectsSend.disconnect();
1715
+ note.reverbSend = new GainNode(this.audioContext, { gain: value });
1716
+ note.volumeEnvelopeNode.connect(note.reverbSend);
1717
+ note.reverbSend.connect(this.reverbEffect.input);
1718
1718
  }
1719
1719
  }
1720
1720
  else {
1721
+ note.reverbSend.gain
1722
+ .cancelScheduledValues(scheduleTime)
1723
+ .setValueAtTime(value, scheduleTime);
1721
1724
  if (0 < value) {
1722
- if (!note.reverbEffectsSend) {
1723
- note.reverbEffectsSend = new GainNode(this.audioContext, {
1724
- gain: value,
1725
- });
1726
- note.volumeEnvelopeNode.connect(note.reverbEffectsSend);
1725
+ note.volumeEnvelopeNode.connect(note.reverbSend);
1726
+ }
1727
+ else {
1728
+ try {
1729
+ note.volumeEnvelopeNode.disconnect(note.reverbSend);
1727
1730
  }
1728
- note.reverbEffectsSend.connect(this.reverbEffect.input);
1731
+ catch { /* empty */ }
1729
1732
  }
1730
1733
  }
1731
1734
  }
1732
- setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
1733
- let value = note.voiceParams.chorusEffectsSend;
1735
+ setChorusSend(channel, note, scheduleTime) {
1736
+ let value = note.voiceParams.chorusEffectsSend *
1737
+ channel.state.chorusSendLevel;
1734
1738
  if (channel.isDrum) {
1735
1739
  const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 93);
1736
- if (0 <= keyBasedValue) {
1737
- value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
1738
- }
1740
+ if (0 <= keyBasedValue)
1741
+ value = keyBasedValue / 127;
1739
1742
  }
1740
- if (0 < prevValue) {
1743
+ if (!note.chorusSend) {
1741
1744
  if (0 < value) {
1742
- note.chorusEffectsSend.gain
1743
- .cancelScheduledValues(scheduleTime)
1744
- .setValueAtTime(value, scheduleTime);
1745
- }
1746
- else {
1747
- note.chorusEffectsSend.disconnect();
1745
+ note.chorusSend = new GainNode(this.audioContext, { gain: value });
1746
+ note.volumeEnvelopeNode.connect(note.chorusSend);
1747
+ note.chorusSend.connect(this.chorusEffect.input);
1748
1748
  }
1749
1749
  }
1750
1750
  else {
1751
+ note.chorusSend.gain
1752
+ .cancelScheduledValues(scheduleTime)
1753
+ .setValueAtTime(value, scheduleTime);
1751
1754
  if (0 < value) {
1752
- if (!note.chorusEffectsSend) {
1753
- note.chorusEffectsSend = new GainNode(this.audioContext, {
1754
- gain: value,
1755
- });
1756
- note.volumeEnvelopeNode.connect(note.chorusEffectsSend);
1755
+ note.volumeEnvelopeNode.connect(note.chorusSend);
1756
+ }
1757
+ else {
1758
+ try {
1759
+ note.volumeEnvelopeNode.disconnect(note.chorusSend);
1757
1760
  }
1758
- note.chorusEffectsSend.connect(this.chorusEffect.input);
1761
+ catch { /* empty */ }
1759
1762
  }
1760
1763
  }
1761
1764
  }
@@ -1801,11 +1804,11 @@ export class Midy {
1801
1804
  this.setModLfoToVolume(channel, note, scheduleTime);
1802
1805
  }
1803
1806
  },
1804
- chorusEffectsSend: (channel, note, prevValue, scheduleTime) => {
1805
- this.setChorusEffectsSend(channel, note, prevValue, scheduleTime);
1807
+ chorusEffectsSend: (channel, note, _prevValue, scheduleTime) => {
1808
+ this.setChorusSend(channel, note, scheduleTime);
1806
1809
  },
1807
- reverbEffectsSend: (channel, note, prevValue, scheduleTime) => {
1808
- this.setReverbEffectsSend(channel, note, prevValue, scheduleTime);
1810
+ reverbEffectsSend: (channel, note, _prevValue, scheduleTime) => {
1811
+ this.setReverbSend(channel, note, scheduleTime);
1809
1812
  },
1810
1813
  delayModLFO: (_channel, note, _prevValue, scheduleTime) => this.setDelayModLFO(note, scheduleTime),
1811
1814
  freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
@@ -1915,7 +1918,7 @@ export class Midy {
1915
1918
  handler.call(this, channelNumber, value, scheduleTime);
1916
1919
  const channel = this.channels[channelNumber];
1917
1920
  this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
1918
- this.applyControlTable(channel, controllerType, scheduleTime);
1921
+ this.setControlChangeEffects(channel, controllerType, scheduleTime);
1919
1922
  }
1920
1923
  else {
1921
1924
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1931,7 +1934,6 @@ export class Midy {
1931
1934
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1932
1935
  }
1933
1936
  else {
1934
- this.setPitchEnvelope(note, scheduleTime);
1935
1937
  this.startModulation(channel, note, scheduleTime);
1936
1938
  }
1937
1939
  });
@@ -1976,8 +1978,14 @@ export class Midy {
1976
1978
  scheduleTime ??= this.audioContext.currentTime;
1977
1979
  const channel = this.channels[channelNumber];
1978
1980
  channel.state.volume = volume / 127;
1979
- this.updateChannelVolume(channel, scheduleTime);
1980
- this.updateKeyBasedVolume(channel, scheduleTime);
1981
+ if (channel.isDrum) {
1982
+ for (let i = 0; i < 128; i++) {
1983
+ this.updateKeyBasedVolume(channel, i, scheduleTime);
1984
+ }
1985
+ }
1986
+ else {
1987
+ this.updateChannelVolume(channel, scheduleTime);
1988
+ }
1981
1989
  }
1982
1990
  panToGain(pan) {
1983
1991
  const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
@@ -1990,8 +1998,14 @@ export class Midy {
1990
1998
  scheduleTime ??= this.audioContext.currentTime;
1991
1999
  const channel = this.channels[channelNumber];
1992
2000
  channel.state.pan = pan / 127;
1993
- this.updateChannelVolume(channel, scheduleTime);
1994
- this.updateKeyBasedVolume(channel, scheduleTime);
2001
+ if (channel.isDrum) {
2002
+ for (let i = 0; i < 128; i++) {
2003
+ this.updateKeyBasedVolume(channel, i, scheduleTime);
2004
+ }
2005
+ }
2006
+ else {
2007
+ this.updateChannelVolume(channel, scheduleTime);
2008
+ }
1995
2009
  }
1996
2010
  setExpression(channelNumber, expression, scheduleTime) {
1997
2011
  scheduleTime ??= this.audioContext.currentTime;
@@ -2017,33 +2031,27 @@ export class Midy {
2017
2031
  .cancelScheduledValues(scheduleTime)
2018
2032
  .setValueAtTime(volume * gainRight, scheduleTime);
2019
2033
  }
2020
- updateKeyBasedVolume(channel, scheduleTime) {
2021
- if (!channel.isDrum)
2022
- return;
2034
+ updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
2023
2035
  const state = channel.state;
2024
2036
  const defaultVolume = state.volume * state.expression;
2025
2037
  const defaultPan = state.pan;
2026
- for (let i = 0; i < 128; i++) {
2027
- const gainL = channel.keyBasedGainLs[i];
2028
- const gainR = channel.keyBasedGainLs[i];
2029
- if (!gainL)
2030
- continue;
2031
- if (!gainR)
2032
- continue;
2033
- const keyBasedVolume = this.getKeyBasedValue(channel, i, 7);
2034
- const volume = (0 <= keyBasedVolume)
2035
- ? defaultVolume * keyBasedVolume / 64
2036
- : defaultVolume;
2037
- const keyBasedPan = this.getKeyBasedValue(channel, i, 10);
2038
- const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
2039
- const { gainLeft, gainRight } = this.panToGain(pan);
2040
- gainL.gain
2041
- .cancelScheduledValues(scheduleTime)
2042
- .setValueAtTime(volume * gainLeft, scheduleTime);
2043
- gainR.gain
2044
- .cancelScheduledValues(scheduleTime)
2045
- .setValueAtTime(volume * gainRight, scheduleTime);
2046
- }
2038
+ const gainL = channel.keyBasedGainLs[keyNumber];
2039
+ const gainR = channel.keyBasedGainRs[keyNumber];
2040
+ if (!gainL)
2041
+ return;
2042
+ const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
2043
+ const volume = (0 <= keyBasedVolume)
2044
+ ? defaultVolume * keyBasedVolume / 64
2045
+ : defaultVolume;
2046
+ const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
2047
+ const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
2048
+ const { gainLeft, gainRight } = this.panToGain(pan);
2049
+ gainL.gain
2050
+ .cancelScheduledValues(scheduleTime)
2051
+ .setValueAtTime(volume * gainLeft, scheduleTime);
2052
+ gainR.gain
2053
+ .cancelScheduledValues(scheduleTime)
2054
+ .setValueAtTime(volume * gainRight, scheduleTime);
2047
2055
  }
2048
2056
  setSustainPedal(channelNumber, value, scheduleTime) {
2049
2057
  const channel = this.channels[channelNumber];
@@ -2209,67 +2217,19 @@ export class Midy {
2209
2217
  scheduleTime ??= this.audioContext.currentTime;
2210
2218
  const channel = this.channels[channelNumber];
2211
2219
  const state = channel.state;
2212
- const reverbEffect = this.reverbEffect;
2213
- if (0 < state.reverbSendLevel) {
2214
- if (0 < reverbSendLevel) {
2215
- state.reverbSendLevel = reverbSendLevel / 127;
2216
- reverbEffect.input.gain
2217
- .cancelScheduledValues(scheduleTime)
2218
- .setValueAtTime(state.reverbSendLevel, scheduleTime);
2219
- }
2220
- else {
2221
- this.processScheduledNotes(channel, (note) => {
2222
- if (note.voiceParams.reverbEffectsSend <= 0)
2223
- return false;
2224
- if (note.reverbEffectsSend)
2225
- note.reverbEffectsSend.disconnect();
2226
- });
2227
- }
2228
- }
2229
- else {
2230
- if (0 < reverbSendLevel) {
2231
- this.processScheduledNotes(channel, (note) => {
2232
- this.setReverbEffectsSend(channel, note, 0, scheduleTime);
2233
- });
2234
- state.reverbSendLevel = reverbSendLevel / 127;
2235
- reverbEffect.input.gain
2236
- .cancelScheduledValues(scheduleTime)
2237
- .setValueAtTime(state.reverbSendLevel, scheduleTime);
2238
- }
2239
- }
2220
+ state.reverbSendLevel = reverbSendLevel / 127;
2221
+ this.processScheduledNotes(channel, (note) => {
2222
+ this.setReverbSend(channel, note, scheduleTime);
2223
+ });
2240
2224
  }
2241
2225
  setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
2242
2226
  scheduleTime ??= this.audioContext.currentTime;
2243
2227
  const channel = this.channels[channelNumber];
2244
2228
  const state = channel.state;
2245
- const chorusEffect = this.chorusEffect;
2246
- if (0 < state.chorusSendLevel) {
2247
- if (0 < chorusSendLevel) {
2248
- state.chorusSendLevel = chorusSendLevel / 127;
2249
- chorusEffect.input.gain
2250
- .cancelScheduledValues(scheduleTime)
2251
- .setValueAtTime(state.chorusSendLevel, scheduleTime);
2252
- }
2253
- else {
2254
- this.processScheduledNotes(channel, (note) => {
2255
- if (note.voiceParams.chorusEffectsSend <= 0)
2256
- return false;
2257
- if (note.chorusEffectsSend)
2258
- note.chorusEffectsSend.disconnect();
2259
- });
2260
- }
2261
- }
2262
- else {
2263
- if (0 < chorusSendLevel) {
2264
- this.processScheduledNotes(channel, (note) => {
2265
- this.setChorusEffectsSend(channel, note, 0, scheduleTime);
2266
- });
2267
- state.chorusSendLevel = chorusSendLevel / 127;
2268
- chorusEffect.input.gain
2269
- .cancelScheduledValues(scheduleTime)
2270
- .setValueAtTime(state.chorusSendLevel, scheduleTime);
2271
- }
2272
- }
2229
+ state.chorusSendLevel = chorusSendLevel / 127;
2230
+ this.processScheduledNotes(channel, (note) => {
2231
+ this.setChorusSend(channel, note, scheduleTime);
2232
+ });
2273
2233
  }
2274
2234
  limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
2275
2235
  if (maxLSB < channel.dataLSB) {
@@ -2580,11 +2540,11 @@ export class Midy {
2580
2540
  case 9:
2581
2541
  switch (data[3]) {
2582
2542
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2583
- return this.handlePressureSysEx(data, "channelPressureTable");
2543
+ return this.handlePressureSysEx(data, "channelPressureTable", scheduleTime);
2584
2544
  case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2585
- return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
2545
+ return this.handlePressureSysEx(data, "polyphonicKeyPressureTable", scheduleTime);
2586
2546
  case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2587
- return this.handleControlChangeSysEx(data);
2547
+ return this.handleControlChangeSysEx(data, scheduleTime);
2588
2548
  default:
2589
2549
  console.warn(`Unsupported Exclusive Message: ${data}`);
2590
2550
  }
@@ -2955,9 +2915,9 @@ export class Midy {
2955
2915
  : 0;
2956
2916
  return (channelPressure + polyphonicKeyPressure) / 254;
2957
2917
  }
2958
- setControllerParameters(channel, note, table, scheduleTime) {
2918
+ setEffects(channel, note, table, scheduleTime) {
2959
2919
  if (0 <= table[0])
2960
- this.updateDetune(channel, note, scueduleTime);
2920
+ this.updateDetune(channel, note, scheduleTime);
2961
2921
  if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
2962
2922
  if (0 <= table[1]) {
2963
2923
  this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
@@ -2979,7 +2939,7 @@ export class Midy {
2979
2939
  if (0 <= table[5])
2980
2940
  this.setModLfoToVolume(channel, note, scheduleTime);
2981
2941
  }
2982
- handlePressureSysEx(data, tableName) {
2942
+ handlePressureSysEx(data, tableName, scheduleTime) {
2983
2943
  const channelNumber = data[4];
2984
2944
  const channel = this.channels[channelNumber];
2985
2945
  if (channel.isDrum)
@@ -2990,32 +2950,38 @@ export class Midy {
2990
2950
  const rr = data[i + 1];
2991
2951
  table[pp] = rr;
2992
2952
  }
2953
+ this.processActiveNotes(channel, scheduleTime, (note) => {
2954
+ this.setEffects(channel, note, table, scheduleTime);
2955
+ });
2993
2956
  }
2994
2957
  initControlTable() {
2995
2958
  const ccCount = 128;
2996
2959
  const slotSize = 6;
2997
2960
  return new Int8Array(ccCount * slotSize).fill(-1);
2998
2961
  }
2999
- applyControlTable(channel, controllerType, scheduleTime) {
2962
+ setControlChangeEffects(channel, controllerType, scheduleTime) {
3000
2963
  const slotSize = 6;
3001
2964
  const offset = controllerType * slotSize;
3002
2965
  const table = channel.controlTable.subarray(offset, offset + slotSize);
3003
2966
  this.processScheduledNotes(channel, (note) => {
3004
- this.setControllerParameters(channel, note, table, scheduleTime);
2967
+ this.setEffects(channel, note, table, scheduleTime);
3005
2968
  });
3006
2969
  }
3007
- handleControlChangeSysEx(data) {
2970
+ handleControlChangeSysEx(data, scheduleTime) {
3008
2971
  const channelNumber = data[4];
3009
2972
  const channel = this.channels[channelNumber];
3010
2973
  if (channel.isDrum)
3011
2974
  return;
2975
+ const slotSize = 6;
3012
2976
  const controllerType = data[5];
3013
- const table = channel.controlTable[controllerType];
3014
- for (let i = 6; i < data.length - 1; i += 2) {
2977
+ const offset = controllerType * slotSize;
2978
+ const table = channel.controlTable;
2979
+ for (let i = 6; i < data.length; i += 2) {
3015
2980
  const pp = data[i];
3016
2981
  const rr = data[i + 1];
3017
- table[pp] = rr;
2982
+ table[offset + pp] = rr;
3018
2983
  }
2984
+ this.setControlChangeEffects(channel, controllerType, scheduleTime);
3019
2985
  }
3020
2986
  getKeyBasedValue(channel, keyNumber, controllerType) {
3021
2987
  const index = keyNumber * 128 + controllerType;
@@ -3029,13 +2995,20 @@ export class Midy {
3029
2995
  return;
3030
2996
  const keyNumber = data[5];
3031
2997
  const table = channel.keyBasedInstrumentControlTable;
3032
- for (let i = 6; i < data.length - 1; i += 2) {
2998
+ for (let i = 6; i < data.length; i += 2) {
3033
2999
  const controllerType = data[i];
3034
3000
  const value = data[i + 1];
3035
3001
  const index = keyNumber * 128 + controllerType;
3036
3002
  table[index] = value;
3003
+ switch (controllerType) {
3004
+ case 7:
3005
+ case 10:
3006
+ this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
3007
+ break;
3008
+ default: // TODO
3009
+ this.setControlChange(channelNumber, controllerType, value, scheduleTime);
3010
+ }
3037
3011
  }
3038
- this.setChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
3039
3012
  }
3040
3013
  handleSysEx(data, scheduleTime) {
3041
3014
  switch (data[0]) {