@marmooo/midy 0.4.0 → 0.4.1

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
@@ -13,6 +13,7 @@ export class Midy {
13
13
  modulationDepthRange: number;
14
14
  fineTuning: number;
15
15
  coarseTuning: number;
16
+ portamentoControl: boolean;
16
17
  };
17
18
  constructor(audioContext: any);
18
19
  mode: string;
@@ -182,11 +183,11 @@ export class Midy {
182
183
  noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
183
184
  disconnectNote(note: any): void;
184
185
  releaseNote(channel: any, note: any, endTime: any): Promise<any>;
185
- noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): void;
186
+ noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): Promise<any> | undefined;
186
187
  setNoteIndex(channel: any, index: any): void;
187
188
  findNoteOffIndex(channel: any, noteNumber: any): any;
188
- releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
189
- releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
189
+ releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
190
+ releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
190
191
  createMessageHandlers(): any[];
191
192
  handleMessage(data: any, scheduleTime: any): void;
192
193
  activeSensing(): void;
@@ -223,16 +224,16 @@ export class Midy {
223
224
  setControlChange(channelNumber: any, controllerType: any, value: any, scheduleTime: any): void;
224
225
  setBankMSB(channelNumber: any, msb: any): void;
225
226
  updateModulation(channel: any, scheduleTime: any): void;
226
- setModulationDepth(channelNumber: any, modulation: any, scheduleTime: any): void;
227
+ setModulationDepth(channelNumber: any, value: any, scheduleTime: any): void;
227
228
  updatePortamento(channel: any, scheduleTime: any): void;
228
- setPortamentoTime(channelNumber: any, portamentoTime: any, scheduleTime: any): void;
229
- setVolume(channelNumber: any, volume: any, scheduleTime: any): void;
229
+ setPortamentoTime(channelNumber: any, value: any, scheduleTime: any): void;
230
+ setVolume(channelNumber: any, value: any, scheduleTime: any): void;
230
231
  panToGain(pan: any): {
231
232
  gainLeft: number;
232
233
  gainRight: number;
233
234
  };
234
- setPan(channelNumber: any, pan: any, scheduleTime: any): void;
235
- setExpression(channelNumber: any, expression: any, scheduleTime: any): void;
235
+ setPan(channelNumber: any, value: any, scheduleTime: any): void;
236
+ setExpression(channelNumber: any, value: any, scheduleTime: any): void;
236
237
  setBankLSB(channelNumber: any, lsb: any): void;
237
238
  dataEntryLSB(channelNumber: any, value: any, scheduleTime: any): void;
238
239
  updateChannelVolume(channel: any, scheduleTime: any): void;
@@ -252,6 +253,7 @@ export class Midy {
252
253
  setVibratoRate(channelNumber: any, vibratoRate: any, scheduleTime: any): void;
253
254
  setVibratoDepth(channelNumber: any, vibratoDepth: any, scheduleTime: any): void;
254
255
  setVibratoDelay(channelNumber: any, vibratoDelay: any, scheduleTime: any): void;
256
+ setPortamentoNoteNumber(channelNumber: any, value: any, scheduleTime: any): void;
255
257
  setReverbSendLevel(channelNumber: any, reverbSendLevel: any, scheduleTime: any): void;
256
258
  setChorusSendLevel(channelNumber: any, chorusSendLevel: any, scheduleTime: any): void;
257
259
  limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
@@ -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
@@ -161,14 +161,19 @@ const defaultControllerState = {
161
161
  pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
162
162
  link: { type: 127, defaultValue: 0 },
163
163
  // bankMSB: { type: 128 + 0, defaultValue: 121, },
164
- modulationDepth: { type: 128 + 1, defaultValue: 0 },
165
- portamentoTime: { type: 128 + 5, defaultValue: 0 },
164
+ modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
165
+ portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
166
166
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
167
- volume: { type: 128 + 7, defaultValue: 100 / 127 },
168
- pan: { type: 128 + 10, defaultValue: 64 / 127 },
169
- expression: { type: 128 + 11, defaultValue: 1 },
167
+ volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
168
+ panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
169
+ expressionMSB: { type: 128 + 11, defaultValue: 1 },
170
170
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
171
+ modulationDepthLSB: { type: 128 + 33, defaultValue: 0 },
172
+ portamentoTimeLSB: { type: 128 + 37, defaultValue: 0 },
171
173
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
174
+ volumeLSB: { type: 128 + 39, defaultValue: 0 },
175
+ panLSB: { type: 128 + 42, defaultValue: 0 },
176
+ expressionLSB: { type: 128 + 43, defaultValue: 0 },
172
177
  sustainPedal: { type: 128 + 64, defaultValue: 0 },
173
178
  portamento: { type: 128 + 65, defaultValue: 0 },
174
179
  sostenutoPedal: { type: 128 + 66, defaultValue: 0 },
@@ -181,6 +186,7 @@ const defaultControllerState = {
181
186
  vibratoRate: { type: 128 + 76, defaultValue: 64 / 127 },
182
187
  vibratoDepth: { type: 128 + 77, defaultValue: 64 / 127 },
183
188
  vibratoDelay: { type: 128 + 78, defaultValue: 64 / 127 },
189
+ portamentoNoteNumber: { type: 128 + 84, defaultValue: 0 },
184
190
  reverbSendLevel: { type: 128 + 91, defaultValue: 0 },
185
191
  chorusSendLevel: { type: 128 + 93, defaultValue: 0 },
186
192
  // dataIncrement: { type: 128 + 96, defaultValue: 0 },
@@ -564,7 +570,7 @@ class Midy {
564
570
  return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
565
571
  }
566
572
  createChannelAudioNodes(audioContext) {
567
- const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
573
+ const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
568
574
  const gainL = new GainNode(audioContext, { gain: gainLeft });
569
575
  const gainR = new GainNode(audioContext, { gain: gainRight });
570
576
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
@@ -607,10 +613,9 @@ class Midy {
607
613
  return channels;
608
614
  }
609
615
  async createAudioBuffer(voiceParams) {
610
- const sample = voiceParams.sample;
611
- const sampleStart = voiceParams.start;
612
- const sampleEnd = sample.data.length + voiceParams.end;
613
- const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
616
+ const { sample, start, end } = voiceParams;
617
+ const sampleEnd = sample.data.length + end;
618
+ const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
614
619
  return audioBuffer;
615
620
  }
616
621
  isLoopDrum(channel, noteNumber) {
@@ -645,9 +650,7 @@ class Midy {
645
650
  await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
646
651
  break;
647
652
  case "noteOff": {
648
- const notePromise = this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
649
- if (notePromise)
650
- this.notePromises.push(notePromise);
653
+ this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
651
654
  break;
652
655
  }
653
656
  case "noteAftertouch":
@@ -754,6 +757,12 @@ class Midy {
754
757
  const waitTime = now + this.noteCheckInterval;
755
758
  await this.scheduleTask(() => { }, waitTime);
756
759
  }
760
+ if (this.timeline.length <= queueIndex) {
761
+ const now = this.audioContext.currentTime;
762
+ await this.stopNotes(0, true, now);
763
+ await this.audioContext.suspend();
764
+ finished = true;
765
+ }
757
766
  if (finished) {
758
767
  this.notePromises = [];
759
768
  this.resetAllStates();
@@ -1146,6 +1155,13 @@ class Midy {
1146
1155
  const noteDetune = this.calcNoteDetune(channel, note);
1147
1156
  const pitchControl = this.getPitchControl(channel, note);
1148
1157
  const detune = channel.detune + noteDetune + pitchControl;
1158
+ if (channel.portamentoControl) {
1159
+ const state = channel.state;
1160
+ const portamentoNoteNumber = Math.ceil(state.portamentoNoteNumber * 127);
1161
+ note.portamentoNoteNumber = portamentoNoteNumber;
1162
+ channel.portamentoControl = false;
1163
+ state.portamentoNoteNumber = 0;
1164
+ }
1149
1165
  if (this.isPortamento(channel, note)) {
1150
1166
  const startTime = note.startTime;
1151
1167
  const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
@@ -1162,8 +1178,10 @@ class Midy {
1162
1178
  }
1163
1179
  }
1164
1180
  getPortamentoTime(channel, note) {
1181
+ const { portamentoTimeMSB, portamentoTimeLSB } = channel.state;
1182
+ const portamentoTime = portamentoTimeMSB + portamentoTimeLSB / 128;
1165
1183
  const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
1166
- const value = Math.ceil(channel.state.portamentoTime * 127);
1184
+ const value = Math.ceil(portamentoTime * 128);
1167
1185
  return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
1168
1186
  }
1169
1187
  getPitchIncrementSpeed(value) {
@@ -1427,7 +1445,7 @@ class Midy {
1427
1445
  if (0 < state.vibratoDepth) {
1428
1446
  this.startVibrato(channel, note, now);
1429
1447
  }
1430
- if (0 < state.modulationDepth) {
1448
+ if (0 < state.modulationDepthMSB + state.modulationDepthLSB) {
1431
1449
  this.startModulation(channel, note, now);
1432
1450
  }
1433
1451
  if (channel.mono && channel.currentBufferSource) {
@@ -1438,7 +1456,13 @@ class Midy {
1438
1456
  note.filterNode.connect(note.volumeEnvelopeNode);
1439
1457
  this.setChorusSend(channel, note, now);
1440
1458
  this.setReverbSend(channel, note, now);
1441
- note.bufferSource.start(startTime);
1459
+ if (voiceParams.sample.type === "compressed") {
1460
+ const offset = voiceParams.start / audioBuffer.sampleRate;
1461
+ note.bufferSource.start(startTime, offset);
1462
+ }
1463
+ else {
1464
+ note.bufferSource.start(startTime);
1465
+ }
1442
1466
  return note;
1443
1467
  }
1444
1468
  handleExclusiveClass(note, channelNumber, startTime) {
@@ -1597,7 +1621,9 @@ class Midy {
1597
1621
  }
1598
1622
  note.ending = true;
1599
1623
  this.setNoteIndex(channel, index);
1600
- this.releaseNote(channel, note, endTime);
1624
+ const promise = this.releaseNote(channel, note, endTime);
1625
+ this.notePromises.push(promise);
1626
+ return promise;
1601
1627
  }
1602
1628
  setNoteIndex(channel, index) {
1603
1629
  let allEnds = true;
@@ -1750,9 +1776,11 @@ class Midy {
1750
1776
  }
1751
1777
  setModLfoToPitch(channel, note, scheduleTime) {
1752
1778
  if (note.modulationDepth) {
1779
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
1780
+ const modulationDepth = modulationDepthMSB + modulationDepthLSB / 128;
1753
1781
  const modLfoToPitch = note.voiceParams.modLfoToPitch +
1754
1782
  this.getLFOPitchDepth(channel, note);
1755
- const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1783
+ const baseDepth = Math.abs(modLfoToPitch) + modulationDepth;
1756
1784
  const depth = baseDepth * Math.sign(modLfoToPitch);
1757
1785
  note.modulationDepth.gain
1758
1786
  .cancelScheduledValues(scheduleTime)
@@ -1885,7 +1913,8 @@ class Midy {
1885
1913
  createVoiceParamsHandlers() {
1886
1914
  return {
1887
1915
  modLfoToPitch: (channel, note, scheduleTime) => {
1888
- if (0 < channel.state.modulationDepth) {
1916
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
1917
+ if (0 < modulationDepthMSB + modulationDepthLSB) {
1889
1918
  this.setModLfoToPitch(channel, note, scheduleTime);
1890
1919
  }
1891
1920
  },
@@ -1895,12 +1924,14 @@ class Midy {
1895
1924
  }
1896
1925
  },
1897
1926
  modLfoToFilterFc: (channel, note, scheduleTime) => {
1898
- if (0 < channel.state.modulationDepth) {
1927
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
1928
+ if (0 < modulationDepthMSB + modulationDepthLSB) {
1899
1929
  this.setModLfoToFilterFc(channel, note, scheduleTime);
1900
1930
  }
1901
1931
  },
1902
1932
  modLfoToVolume: (channel, note, scheduleTime) => {
1903
- if (0 < channel.state.modulationDepth) {
1933
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
1934
+ if (0 < modulationDepthMSB + modulationDepthLSB) {
1904
1935
  this.setModLfoToVolume(channel, note, scheduleTime);
1905
1936
  }
1906
1937
  },
@@ -1911,12 +1942,14 @@ class Midy {
1911
1942
  this.setReverbSend(channel, note, scheduleTime);
1912
1943
  },
1913
1944
  delayModLFO: (_channel, note, _scheduleTime) => {
1914
- if (0 < channel.state.modulationDepth) {
1945
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
1946
+ if (0 < modulationDepthMSB + modulationDepthLSB) {
1915
1947
  this.setDelayModLFO(note);
1916
1948
  }
1917
1949
  },
1918
1950
  freqModLFO: (_channel, note, scheduleTime) => {
1919
- if (0 < channel.state.modulationDepth) {
1951
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
1952
+ if (0 < modulationDepthMSB + modulationDepthLSB) {
1920
1953
  this.setFreqModLFO(note, scheduleTime);
1921
1954
  }
1922
1955
  },
@@ -1985,7 +2018,12 @@ class Midy {
1985
2018
  handlers[10] = this.setPan;
1986
2019
  handlers[11] = this.setExpression;
1987
2020
  handlers[32] = this.setBankLSB;
2021
+ handlers[33] = this.setModulationDepth;
2022
+ handlers[37] = this.setPortamentoTime;
1988
2023
  handlers[38] = this.dataEntryLSB;
2024
+ handlers[39] = this.setVolume;
2025
+ handlers[42] = this.setPan;
2026
+ handlers[43] = this.setExpression;
1989
2027
  handlers[64] = this.setSustainPedal;
1990
2028
  handlers[65] = this.setPortamento;
1991
2029
  handlers[66] = this.setSostenutoPedal;
@@ -1998,6 +2036,7 @@ class Midy {
1998
2036
  handlers[76] = this.setVibratoRate;
1999
2037
  handlers[77] = this.setVibratoDepth;
2000
2038
  handlers[78] = this.setVibratoDelay;
2039
+ handlers[84] = this.setPortamentoNoteNumber;
2001
2040
  handlers[91] = this.setReverbSendLevel;
2002
2041
  handlers[93] = this.setChorusSendLevel;
2003
2042
  handlers[96] = this.dataIncrement;
@@ -2029,7 +2068,9 @@ class Midy {
2029
2068
  this.channels[channelNumber].bankMSB = msb;
2030
2069
  }
2031
2070
  updateModulation(channel, scheduleTime) {
2032
- const depth = channel.state.modulationDepth * channel.modulationDepthRange;
2071
+ const { modulationDepthMSB, modulationDepthLSB } = channel.state;
2072
+ const modulationDepth = modulationDepthMSB + modulationDepthLSB / 128;
2073
+ const depth = modulationDepth * channel.modulationDepthRange;
2033
2074
  this.processScheduledNotes(channel, (note) => {
2034
2075
  if (note.modulationDepth) {
2035
2076
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
@@ -2039,12 +2080,15 @@ class Midy {
2039
2080
  }
2040
2081
  });
2041
2082
  }
2042
- setModulationDepth(channelNumber, modulation, scheduleTime) {
2083
+ setModulationDepth(channelNumber, value, scheduleTime) {
2043
2084
  const channel = this.channels[channelNumber];
2044
2085
  if (channel.isDrum)
2045
2086
  return;
2046
2087
  scheduleTime ??= this.audioContext.currentTime;
2047
- channel.state.modulationDepth = modulation / 127;
2088
+ const state = channel.state;
2089
+ const intPart = Math.trunc(value);
2090
+ state.modulationDepthMSB = intPart / 127;
2091
+ state.modulationDepthLSB = value - intPart;
2048
2092
  this.updateModulation(channel, scheduleTime);
2049
2093
  }
2050
2094
  updatePortamento(channel, scheduleTime) {
@@ -2065,18 +2109,24 @@ class Midy {
2065
2109
  }
2066
2110
  });
2067
2111
  }
2068
- setPortamentoTime(channelNumber, portamentoTime, scheduleTime) {
2069
- const channel = this.channels[channelNumber];
2112
+ setPortamentoTime(channelNumber, value, scheduleTime) {
2070
2113
  scheduleTime ??= this.audioContext.currentTime;
2071
- channel.state.portamentoTime = portamentoTime / 127;
2114
+ const channel = this.channels[channelNumber];
2115
+ const state = channel.state;
2116
+ const intPart = Math.trunc(value);
2117
+ state.portamentoTimeMSB = intPart / 127;
2118
+ state.portamentoTimeLSB = value - 127;
2072
2119
  if (channel.isDrum)
2073
2120
  return;
2074
2121
  this.updatePortamento(channel, scheduleTime);
2075
2122
  }
2076
- setVolume(channelNumber, volume, scheduleTime) {
2123
+ setVolume(channelNumber, value, scheduleTime) {
2077
2124
  scheduleTime ??= this.audioContext.currentTime;
2078
2125
  const channel = this.channels[channelNumber];
2079
- channel.state.volume = volume / 127;
2126
+ const state = channel.state;
2127
+ const intPart = Math.trunc(value);
2128
+ state.volumeMSB = intPart / 127;
2129
+ state.volumeLSB = value - intPart;
2080
2130
  if (channel.isDrum) {
2081
2131
  for (let i = 0; i < 128; i++) {
2082
2132
  this.updateKeyBasedVolume(channel, i, scheduleTime);
@@ -2087,16 +2137,19 @@ class Midy {
2087
2137
  }
2088
2138
  }
2089
2139
  panToGain(pan) {
2090
- const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
2140
+ const theta = Math.PI / 2 * Math.max(pan * 127 - 1) / 126;
2091
2141
  return {
2092
2142
  gainLeft: Math.cos(theta),
2093
2143
  gainRight: Math.sin(theta),
2094
2144
  };
2095
2145
  }
2096
- setPan(channelNumber, pan, scheduleTime) {
2146
+ setPan(channelNumber, value, scheduleTime) {
2097
2147
  scheduleTime ??= this.audioContext.currentTime;
2098
2148
  const channel = this.channels[channelNumber];
2099
- channel.state.pan = pan / 127;
2149
+ const state = channel.state;
2150
+ const intPart = Math.trunc(value);
2151
+ state.panMSB = intPart / 127;
2152
+ state.panLSB = value - intPart;
2100
2153
  if (channel.isDrum) {
2101
2154
  for (let i = 0; i < 128; i++) {
2102
2155
  this.updateKeyBasedVolume(channel, i, scheduleTime);
@@ -2106,10 +2159,13 @@ class Midy {
2106
2159
  this.updateChannelVolume(channel, scheduleTime);
2107
2160
  }
2108
2161
  }
2109
- setExpression(channelNumber, expression, scheduleTime) {
2162
+ setExpression(channelNumber, value, scheduleTime) {
2110
2163
  scheduleTime ??= this.audioContext.currentTime;
2111
2164
  const channel = this.channels[channelNumber];
2112
- channel.state.expression = expression / 127;
2165
+ const state = channel.state;
2166
+ const intPart = Math.trunc(value);
2167
+ state.expressionMSB = intPart / 127;
2168
+ state.expressionLSB = value - intPart;
2113
2169
  this.updateChannelVolume(channel, scheduleTime);
2114
2170
  }
2115
2171
  setBankLSB(channelNumber, lsb) {
@@ -2120,37 +2176,42 @@ class Midy {
2120
2176
  this.handleRPN(channelNumber, 0, scheduleTime);
2121
2177
  }
2122
2178
  updateChannelVolume(channel, scheduleTime) {
2123
- const state = channel.state;
2124
- const volume = state.volume * state.expression;
2125
- const { gainLeft, gainRight } = this.panToGain(state.pan);
2179
+ const { expressionMSB, expressionLSB, volumeMSB, volumeLSB, panMSB, panLSB, } = channel.state;
2180
+ const volume = volumeMSB + volumeLSB / 128;
2181
+ const expression = expressionMSB + expressionLSB / 128;
2182
+ const pan = panMSB + panLSB / 128;
2183
+ const gain = volume * expression;
2184
+ const { gainLeft, gainRight } = this.panToGain(pan);
2126
2185
  channel.gainL.gain
2127
2186
  .cancelScheduledValues(scheduleTime)
2128
- .setValueAtTime(volume * gainLeft, scheduleTime);
2187
+ .setValueAtTime(gain * gainLeft, scheduleTime);
2129
2188
  channel.gainR.gain
2130
2189
  .cancelScheduledValues(scheduleTime)
2131
- .setValueAtTime(volume * gainRight, scheduleTime);
2190
+ .setValueAtTime(gain * gainRight, scheduleTime);
2132
2191
  }
2133
2192
  updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
2134
2193
  const gainL = channel.keyBasedGainLs[keyNumber];
2135
2194
  if (!gainL)
2136
2195
  return;
2137
2196
  const gainR = channel.keyBasedGainRs[keyNumber];
2138
- const state = channel.state;
2139
- const defaultVolume = state.volume * state.expression;
2140
- const defaultPan = state.pan;
2197
+ const { expressionMSB, expressionLSB, volumeMSB, volumeLSB, panMSB, panLSB, } = channel.state;
2198
+ const volume = volumeMSB + volumeLSB / 128;
2199
+ const expression = expressionMSB + expressionLSB / 128;
2200
+ const defaultGain = volume * expression;
2201
+ const defaultPan = panMSB + panLSB / 128;
2141
2202
  const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
2142
- const volume = (0 <= keyBasedVolume)
2143
- ? defaultVolume * keyBasedVolume / 64
2144
- : defaultVolume;
2203
+ const gain = (0 <= keyBasedVolume)
2204
+ ? defaultGain * keyBasedVolume / 64
2205
+ : defaultGain;
2145
2206
  const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
2146
2207
  const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
2147
2208
  const { gainLeft, gainRight } = this.panToGain(pan);
2148
2209
  gainL.gain
2149
2210
  .cancelScheduledValues(scheduleTime)
2150
- .setValueAtTime(volume * gainLeft, scheduleTime);
2211
+ .setValueAtTime(gain * gainLeft, scheduleTime);
2151
2212
  gainR.gain
2152
2213
  .cancelScheduledValues(scheduleTime)
2153
- .setValueAtTime(volume * gainRight, scheduleTime);
2214
+ .setValueAtTime(gain * gainRight, scheduleTime);
2154
2215
  }
2155
2216
  setSustainPedal(channelNumber, value, scheduleTime) {
2156
2217
  const channel = this.channels[channelNumber];
@@ -2223,8 +2284,8 @@ class Midy {
2223
2284
  scheduleTime ??= this.audioContext.currentTime;
2224
2285
  const state = channel.state;
2225
2286
  state.filterResonance = ccValue / 127;
2226
- const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
2227
2287
  this.processScheduledNotes(channel, (note) => {
2288
+ const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
2228
2289
  const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
2229
2290
  note.filterNode.Q.setValueAtTime(Q, scheduleTime);
2230
2291
  });
@@ -2324,6 +2385,12 @@ class Midy {
2324
2385
  });
2325
2386
  }
2326
2387
  }
2388
+ setPortamentoNoteNumber(channelNumber, value, scheduleTime) {
2389
+ scheduleTime ??= this.audioContext.currentTime;
2390
+ const channel = this.channels[channelNumber];
2391
+ channel.portamentoControl = true;
2392
+ channel.state.portamentoNoteNumber = value / 127;
2393
+ }
2327
2394
  setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
2328
2395
  scheduleTime ??= this.audioContext.currentTime;
2329
2396
  const channel = this.channels[channelNumber];
@@ -2511,8 +2578,10 @@ class Midy {
2511
2578
  "polyphonicKeyPressure",
2512
2579
  "channelPressure",
2513
2580
  "pitchWheel",
2514
- "expression",
2515
- "modulationDepth",
2581
+ "expressionMSB",
2582
+ "expressionLSB",
2583
+ "modulationDepthMSB",
2584
+ "modulationDepthLSB",
2516
2585
  "sustainPedal",
2517
2586
  "portamento",
2518
2587
  "sostenutoPedal",
@@ -3222,5 +3291,6 @@ Object.defineProperty(Midy, "channelSettings", {
3222
3291
  modulationDepthRange: 50, // cent
3223
3292
  fineTuning: 0, // cent
3224
3293
  coarseTuning: 0, // cent
3294
+ portamentoControl: false,
3225
3295
  }
3226
3296
  });