@marmooo/midy 0.1.3 → 0.1.5

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.
Files changed (35) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.4 → soundfont-parser@0.0.6}/+esm.d.ts +16 -20
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +180 -0
  4. package/esm/midy-GM1.d.ts +32 -3
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +93 -87
  7. package/esm/midy-GM2.d.ts +56 -3
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +142 -126
  10. package/esm/midy-GMLite.d.ts +32 -3
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +91 -85
  13. package/esm/midy.d.ts +87 -4
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +234 -140
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.4 → soundfont-parser@0.0.6}/+esm.d.ts +16 -20
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +190 -0
  20. package/script/midy-GM1.d.ts +32 -3
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +93 -87
  23. package/script/midy-GM2.d.ts +56 -3
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +142 -126
  26. package/script/midy-GMLite.d.ts +32 -3
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +91 -85
  29. package/script/midy.d.ts +87 -4
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +234 -140
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +0 -1
  33. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js +0 -162
  34. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +0 -1
  35. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js +0 -169
package/script/midy.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Midy = void 0;
4
4
  const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js");
5
+ const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js");
6
6
  class Note {
7
7
  constructor(noteNumber, velocity, startTime, instrumentKey) {
8
8
  Object.defineProperty(this, "bufferSource", {
@@ -233,6 +233,7 @@ class Midy {
233
233
  this.audioContext = audioContext;
234
234
  this.options = { ...this.defaultOptions, ...options };
235
235
  this.masterGain = new GainNode(audioContext);
236
+ this.controlChangeHandlers = this.createControlChangeHandlers();
236
237
  this.channels = this.createChannels(audioContext);
237
238
  this.reverbEffect = this.options.reverbAlgorithm(audioContext);
238
239
  this.chorusEffect = this.createChorusEffect(audioContext);
@@ -554,12 +555,13 @@ class Midy {
554
555
  const now = this.audioContext.currentTime;
555
556
  const channel = this.channels[channelNumber];
556
557
  channel.scheduledNotes.forEach((noteList) => {
557
- noteList.forEach((note) => {
558
- if (note) {
559
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
560
- this.notePromises.push(promise);
561
- }
562
- });
558
+ for (let i = 0; i < noteList.length; i++) {
559
+ const note = noteList[i];
560
+ if (!note)
561
+ continue;
562
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
563
+ this.notePromises.push(promise);
564
+ }
563
565
  });
564
566
  channel.scheduledNotes.clear();
565
567
  await Promise.all(this.notePromises);
@@ -780,16 +782,17 @@ class Midy {
780
782
  return instrumentKey.playbackRate(noteNumber) *
781
783
  Math.pow(2, semitoneOffset / 12);
782
784
  }
783
- setVolumeEnvelope(note) {
785
+ setVolumeEnvelope(channel, note) {
784
786
  const { instrumentKey, startTime } = note;
785
- note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
786
787
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
787
788
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
788
789
  const volDelay = startTime + instrumentKey.volDelay;
789
- const volAttack = volDelay + instrumentKey.volAttack;
790
+ const volAttack = volDelay + instrumentKey.volAttack * channel.attackTime;
790
791
  const volHold = volAttack + instrumentKey.volHold;
791
- const volDecay = volHold + instrumentKey.volDecay;
792
+ const volDecay = volHold + instrumentKey.volDecay * channel.decayTime;
792
793
  note.volumeNode.gain
794
+ .cancelScheduledValues(startTime)
795
+ .setValueAtTime(0, startTime)
793
796
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
794
797
  .exponentialRampToValueAtTime(attackVolume, volAttack)
795
798
  .setValueAtTime(attackVolume, volHold)
@@ -813,29 +816,30 @@ class Midy {
813
816
  .setValueAtTime(peekPitch, modHold)
814
817
  .linearRampToValueAtTime(basePitch, modDecay);
815
818
  }
816
- setFilterNode(channel, note) {
819
+ clampCutoffFrequency(frequency) {
820
+ const minFrequency = 20; // min Hz of initialFilterFc
821
+ const maxFrequency = 20000; // max Hz of initialFilterFc
822
+ return Math.max(minFrequency, Math.min(frequency, maxFrequency));
823
+ }
824
+ setFilterEnvelope(channel, note) {
817
825
  const { instrumentKey, noteNumber, startTime } = note;
818
826
  const softPedalFactor = 1 -
819
827
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
820
- const maxFreq = this.audioContext.sampleRate / 2;
821
828
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
822
- softPedalFactor;
823
- const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
829
+ softPedalFactor * channel.brightness;
830
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
824
831
  const sustainFreq = baseFreq +
825
832
  (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
826
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
827
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
828
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
833
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
834
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
835
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
829
836
  const modDelay = startTime + instrumentKey.modDelay;
830
837
  const modAttack = modDelay + instrumentKey.modAttack;
831
838
  const modHold = modAttack + instrumentKey.modHold;
832
839
  const modDecay = modHold + instrumentKey.modDecay;
833
- note.filterNode = new BiquadFilterNode(this.audioContext, {
834
- type: "lowpass",
835
- Q: instrumentKey.initialFilterQ / 10, // dB
836
- frequency: adjustedBaseFreq,
837
- });
838
840
  note.filterNode.frequency
841
+ .cancelScheduledValues(startTime)
842
+ .setValueAtTime(adjustedBaseFreq, startTime)
839
843
  .setValueAtTime(adjustedBaseFreq, modDelay)
840
844
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
841
845
  .setValueAtTime(adjustedPeekFreq, modHold)
@@ -888,8 +892,13 @@ class Midy {
888
892
  const semitoneOffset = this.calcSemitoneOffset(channel);
889
893
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
890
894
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
891
- this.setFilterNode(channel, note);
892
- this.setVolumeEnvelope(note);
895
+ note.volumeNode = new GainNode(this.audioContext);
896
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
897
+ type: "lowpass",
898
+ Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
899
+ });
900
+ this.setVolumeEnvelope(channel, note);
901
+ this.setFilterEnvelope(channel, note);
893
902
  if (0 < channel.vibratoDepth) {
894
903
  this.startVibrato(channel, note, startTime);
895
904
  }
@@ -926,7 +935,7 @@ class Midy {
926
935
  return;
927
936
  const soundFont = this.soundFonts[soundFontIndex];
928
937
  const isSF3 = soundFont.parsed.info.version.major === 3;
929
- const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
938
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
930
939
  if (!instrumentKey)
931
940
  return;
932
941
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
@@ -947,12 +956,14 @@ class Midy {
947
956
  const now = this.audioContext.currentTime;
948
957
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
949
958
  }
950
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
959
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
951
960
  const channel = this.channels[channelNumber];
952
- if (stopPedal && channel.sustainPedal)
953
- return;
954
- if (stopPedal && channel.sostenutoNotes.has(noteNumber))
955
- return;
961
+ if (stopPedal) {
962
+ if (channel.sustainPedal)
963
+ return;
964
+ if (channel.sostenutoNotes.has(noteNumber))
965
+ return;
966
+ }
956
967
  if (!channel.scheduledNotes.has(noteNumber))
957
968
  return;
958
969
  const scheduledNotes = channel.scheduledNotes.get(noteNumber);
@@ -962,14 +973,12 @@ class Midy {
962
973
  continue;
963
974
  if (note.ending)
964
975
  continue;
965
- const velocityRate = (velocity + 127) / 127;
966
976
  const volEndTime = stopTime +
967
- note.instrumentKey.volRelease * velocityRate;
977
+ note.instrumentKey.volRelease * channel.releaseTime;
968
978
  note.volumeNode.gain
969
979
  .cancelScheduledValues(stopTime)
970
980
  .linearRampToValueAtTime(0, volEndTime);
971
- const modRelease = stopTime +
972
- note.instrumentKey.modRelease * velocityRate;
981
+ const modRelease = stopTime + note.instrumentKey.modRelease;
973
982
  note.filterNode.frequency
974
983
  .cancelScheduledValues(stopTime)
975
984
  .linearRampToValueAtTime(0, modRelease);
@@ -983,16 +992,15 @@ class Midy {
983
992
  note.bufferSource.disconnect();
984
993
  note.volumeNode.disconnect();
985
994
  note.filterNode.disconnect();
986
- if (note.volumeDepth)
995
+ if (note.modulationDepth) {
987
996
  note.volumeDepth.disconnect();
988
- if (note.modulationDepth)
989
997
  note.modulationDepth.disconnect();
990
- if (note.modulationLFO)
991
998
  note.modulationLFO.stop();
992
- if (note.vibratoDepth)
999
+ }
1000
+ if (note.vibratoDepth) {
993
1001
  note.vibratoDepth.disconnect();
994
- if (note.vibratoLFO)
995
1002
  note.vibratoLFO.stop();
1003
+ }
996
1004
  resolve();
997
1005
  };
998
1006
  note.bufferSource.stop(volEndTime);
@@ -1009,13 +1017,14 @@ class Midy {
1009
1017
  const promises = [];
1010
1018
  channel.sustainPedal = false;
1011
1019
  channel.scheduledNotes.forEach((noteList) => {
1012
- noteList.forEach((note) => {
1013
- if (note) {
1014
- const { noteNumber } = note;
1015
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1016
- promises.push(promise);
1017
- }
1018
- });
1020
+ for (let i = 0; i < noteList.length; i++) {
1021
+ const note = noteList[i];
1022
+ if (!note)
1023
+ continue;
1024
+ const { noteNumber } = note;
1025
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1026
+ promises.push(promise);
1027
+ }
1019
1028
  });
1020
1029
  return promises;
1021
1030
  }
@@ -1101,69 +1110,51 @@ class Midy {
1101
1110
  channel.pitchBendRange * 100;
1102
1111
  this.updateDetune(channel, detuneChange);
1103
1112
  }
1113
+ createControlChangeHandlers() {
1114
+ return {
1115
+ 0: this.setBankMSB,
1116
+ 1: this.setModulationDepth,
1117
+ 5: this.setPortamentoTime,
1118
+ 6: this.dataEntryMSB,
1119
+ 7: this.setVolume,
1120
+ 10: this.setPan,
1121
+ 11: this.setExpression,
1122
+ 32: this.setBankLSB,
1123
+ 38: this.dataEntryLSB,
1124
+ 64: this.setSustainPedal,
1125
+ 65: this.setPortamento,
1126
+ 66: this.setSostenutoPedal,
1127
+ 67: this.setSoftPedal,
1128
+ 71: this.setFilterResonance,
1129
+ 72: this.setReleaseTime,
1130
+ 73: this.setAttackTime,
1131
+ 74: this.setBrightness,
1132
+ 75: this.setDecayTime,
1133
+ 76: this.setVibratoRate,
1134
+ 77: this.setVibratoDepth,
1135
+ 78: this.setVibratoDelay,
1136
+ 91: this.setReverbSendLevel,
1137
+ 93: this.setChorusSendLevel,
1138
+ 96: this.dataIncrement,
1139
+ 97: this.dataDecrement,
1140
+ 100: this.setRPNLSB,
1141
+ 101: this.setRPNMSB,
1142
+ 120: this.allSoundOff,
1143
+ 121: this.resetAllControllers,
1144
+ 123: this.allNotesOff,
1145
+ 124: this.omniOff,
1146
+ 125: this.omniOn,
1147
+ 126: this.monoOn,
1148
+ 127: this.polyOn,
1149
+ };
1150
+ }
1104
1151
  handleControlChange(channelNumber, controller, value) {
1105
- switch (controller) {
1106
- case 0:
1107
- return this.setBankMSB(channelNumber, value);
1108
- case 1:
1109
- return this.setModulationDepth(channelNumber, value);
1110
- case 5:
1111
- return this.setPortamentoTime(channelNumber, value);
1112
- case 6:
1113
- return this.dataEntryMSB(channelNumber, value);
1114
- case 7:
1115
- return this.setVolume(channelNumber, value);
1116
- case 10:
1117
- return this.setPan(channelNumber, value);
1118
- case 11:
1119
- return this.setExpression(channelNumber, value);
1120
- case 32:
1121
- return this.setBankLSB(channelNumber, value);
1122
- case 38:
1123
- return this.dataEntryLSB(channelNumber, value);
1124
- case 64:
1125
- return this.setSustainPedal(channelNumber, value);
1126
- case 65:
1127
- return this.setPortamento(channelNumber, value);
1128
- case 66:
1129
- return this.setSostenutoPedal(channelNumber, value);
1130
- case 67:
1131
- return this.setSoftPedal(channelNumber, value);
1132
- // TODO: 71-75
1133
- case 76:
1134
- return this.setVibratoRate(channelNumber, value);
1135
- case 77:
1136
- return this.setVibratoDepth(channelNumber, value);
1137
- case 78:
1138
- return this.setVibratoDelay(channelNumber, value);
1139
- case 91:
1140
- return this.setReverbSendLevel(channelNumber, value);
1141
- case 93:
1142
- return this.setChorusSendLevel(channelNumber, value);
1143
- case 96: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1144
- return this.dataIncrement(channelNumber);
1145
- case 97: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1146
- return this.dataDecrement(channelNumber);
1147
- case 100:
1148
- return this.setRPNLSB(channelNumber, value);
1149
- case 101:
1150
- return this.setRPNMSB(channelNumber, value);
1151
- case 120:
1152
- return this.allSoundOff(channelNumber);
1153
- case 121:
1154
- return this.resetAllControllers(channelNumber);
1155
- case 123:
1156
- return this.allNotesOff(channelNumber);
1157
- case 124:
1158
- return this.omniOff();
1159
- case 125:
1160
- return this.omniOn();
1161
- case 126:
1162
- return this.monoOn();
1163
- case 127:
1164
- return this.polyOn();
1165
- default:
1166
- console.warn(`Unsupported Control change: controller=${controller} value=${value}`);
1152
+ const handler = this.controlChangeHandlers[controller];
1153
+ if (handler) {
1154
+ handler.call(this, channelNumber, value);
1155
+ }
1156
+ else {
1157
+ console.warn(`Unsupported Control change: controller=${controller} value=${value}`);
1167
1158
  }
1168
1159
  }
1169
1160
  setBankMSB(channelNumber, msb) {
@@ -1171,15 +1162,19 @@ class Midy {
1171
1162
  }
1172
1163
  updateModulation(channel) {
1173
1164
  const now = this.audioContext.currentTime;
1174
- const activeNotes = this.getActiveNotes(channel, now);
1175
- activeNotes.forEach((activeNote) => {
1176
- if (activeNote.modulationDepth) {
1177
- activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1178
- }
1179
- else {
1180
- const semitoneOffset = this.calcSemitoneOffset(channel);
1181
- this.setPitch(activeNote, semitoneOffset);
1182
- this.startModulation(channel, activeNote, now);
1165
+ channel.scheduledNotes.forEach((noteList) => {
1166
+ for (let i = 0; i < noteList.length; i++) {
1167
+ const note = noteList[i];
1168
+ if (!note)
1169
+ continue;
1170
+ if (note.modulationDepth) {
1171
+ note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1172
+ }
1173
+ else {
1174
+ const semitoneOffset = this.calcSemitoneOffset(channel);
1175
+ this.setPitch(note, semitoneOffset);
1176
+ this.startModulation(channel, note, now);
1177
+ }
1183
1178
  }
1184
1179
  });
1185
1180
  }
@@ -1244,28 +1239,49 @@ class Midy {
1244
1239
  }
1245
1240
  setReverbSendLevel(channelNumber, reverbSendLevel) {
1246
1241
  const channel = this.channels[channelNumber];
1247
- const reverbEffect = this.reverbEffect;
1248
- if (0 < reverbSendLevel) {
1249
- const now = this.audioContext.currentTime;
1250
- channel.reverbSendLevel = reverbSendLevel / 127;
1251
- reverbEffect.output.gain.cancelScheduledValues(now);
1252
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1242
+ if (0 < channel.reverbSendLevel) {
1243
+ if (0 < reverbSendLevel) {
1244
+ const now = this.audioContext.currentTime;
1245
+ channel.reverbSendLevel = reverbSendLevel / 127;
1246
+ reverbEffect.output.gain.cancelScheduledValues(now);
1247
+ reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1248
+ }
1249
+ else {
1250
+ channel.merger.disconnect(reverbEffect.input);
1251
+ }
1253
1252
  }
1254
- else if (channel.reverbSendLevel !== 0) {
1255
- channel.merger.disconnect(reverbEffect.input);
1253
+ else {
1254
+ if (0 < reverbSendLevel) {
1255
+ channel.merger.connect(reverbEffect.input);
1256
+ const now = this.audioContext.currentTime;
1257
+ channel.reverbSendLevel = reverbSendLevel / 127;
1258
+ reverbEffect.output.gain.cancelScheduledValues(now);
1259
+ reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1260
+ }
1256
1261
  }
1257
1262
  }
1258
1263
  setChorusSendLevel(channelNumber, chorusSendLevel) {
1259
1264
  const channel = this.channels[channelNumber];
1260
1265
  const chorusEffect = this.chorusEffect;
1261
- if (0 < chorusSendLevel) {
1262
- const now = this.audioContext.currentTime;
1263
- channel.chorusSendLevel = chorusSendLevel / 127;
1264
- chorusEffect.output.gain.cancelScheduledValues(now);
1265
- chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1266
+ if (0 < channel.chorusSendLevel) {
1267
+ if (0 < chorusSendLevel) {
1268
+ const now = this.audioContext.currentTime;
1269
+ channel.chorusSendLevel = chorusSendLevel / 127;
1270
+ chorusEffect.output.gain.cancelScheduledValues(now);
1271
+ chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1272
+ }
1273
+ else {
1274
+ channel.merger.disconnect(chorusEffect.input);
1275
+ }
1266
1276
  }
1267
- else if (channel.chorusSendLevel !== 0) {
1268
- channel.merger.disconnect(chorusEffect.input);
1277
+ else {
1278
+ if (0 < chorusSendLevel) {
1279
+ channel.merger.connect(chorusEffect.input);
1280
+ const now = this.audioContext.currentTime;
1281
+ channel.chorusSendLevel = chorusSendLevel / 127;
1282
+ chorusEffect.output.gain.cancelScheduledValues(now);
1283
+ chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1284
+ }
1269
1285
  }
1270
1286
  }
1271
1287
  setSostenutoPedal(channelNumber, value) {
@@ -1285,9 +1301,76 @@ class Midy {
1285
1301
  const channel = this.channels[channelNumber];
1286
1302
  channel.softPedal = softPedal / 127;
1287
1303
  }
1304
+ setFilterResonance(channelNumber, filterResonance) {
1305
+ const now = this.audioContext.currentTime;
1306
+ const channel = this.channels[channelNumber];
1307
+ channel.filterResonance = filterResonance / 64;
1308
+ channel.scheduledNotes.forEach((noteList) => {
1309
+ for (let i = 0; i < noteList.length; i++) {
1310
+ const note = noteList[i];
1311
+ if (!note)
1312
+ continue;
1313
+ const Q = note.instrumentKey.initialFilterQ / 10 *
1314
+ channel.filterResonance;
1315
+ note.filterNode.Q.setValueAtTime(Q, now);
1316
+ }
1317
+ });
1318
+ }
1319
+ setReleaseTime(channelNumber, releaseTime) {
1320
+ const channel = this.channels[channelNumber];
1321
+ channel.releaseTime = releaseTime / 64;
1322
+ }
1323
+ setAttackTime(channelNumber, attackTime) {
1324
+ const now = this.audioContext.currentTime;
1325
+ const channel = this.channels[channelNumber];
1326
+ channel.attackTime = attackTime / 64;
1327
+ channel.scheduledNotes.forEach((noteList) => {
1328
+ for (let i = 0; i < noteList.length; i++) {
1329
+ const note = noteList[i];
1330
+ if (!note)
1331
+ continue;
1332
+ if (note.startTime < now)
1333
+ continue;
1334
+ this.setVolumeEnvelope(channel, note);
1335
+ }
1336
+ });
1337
+ }
1338
+ setBrightness(channelNumber, brightness) {
1339
+ const channel = this.channels[channelNumber];
1340
+ channel.brightness = brightness / 64;
1341
+ channel.scheduledNotes.forEach((noteList) => {
1342
+ for (let i = 0; i < noteList.length; i++) {
1343
+ const note = noteList[i];
1344
+ if (!note)
1345
+ continue;
1346
+ this.setFilterEnvelope(channel, note);
1347
+ }
1348
+ });
1349
+ }
1350
+ setDecayTime(channelNumber, dacayTime) {
1351
+ const channel = this.channels[channelNumber];
1352
+ channel.decayTime = dacayTime / 64;
1353
+ channel.scheduledNotes.forEach((noteList) => {
1354
+ for (let i = 0; i < noteList.length; i++) {
1355
+ const note = noteList[i];
1356
+ if (!note)
1357
+ continue;
1358
+ this.setVolumeEnvelope(channel, note);
1359
+ }
1360
+ });
1361
+ }
1288
1362
  setVibratoRate(channelNumber, vibratoRate) {
1289
1363
  const channel = this.channels[channelNumber];
1290
1364
  channel.vibratoRate = vibratoRate / 64;
1365
+ if (channel.vibratoDepth <= 0)
1366
+ return;
1367
+ const now = this.audioContext.currentTime;
1368
+ const activeNotes = this.getActiveNotes(channel, now);
1369
+ activeNotes.forEach((activeNote) => {
1370
+ activeNote.vibratoLFO.frequency
1371
+ .cancelScheduledValues(now)
1372
+ .setValueAtTime(channel.vibratoRate, now);
1373
+ });
1291
1374
  }
1292
1375
  setVibratoDepth(channelNumber, vibratoDepth) {
1293
1376
  const channel = this.channels[channelNumber];
@@ -1347,9 +1430,11 @@ class Midy {
1347
1430
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1348
1431
  }
1349
1432
  }
1433
+ // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1350
1434
  dataIncrement(channelNumber) {
1351
1435
  this.handleRPN(channelNumber, 1);
1352
1436
  }
1437
+ // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1353
1438
  dataDecrement(channelNumber) {
1354
1439
  this.handleRPN(channelNumber, -1);
1355
1440
  }
@@ -1365,13 +1450,17 @@ class Midy {
1365
1450
  }
1366
1451
  updateDetune(channel, detuneChange) {
1367
1452
  const now = this.audioContext.currentTime;
1368
- const activeNotes = this.getActiveNotes(channel, now);
1369
- activeNotes.forEach((activeNote) => {
1370
- const { bufferSource } = activeNote;
1371
- const detune = bufferSource.detune.value + detuneChange;
1372
- bufferSource.detune
1373
- .cancelScheduledValues(now)
1374
- .setValueAtTime(detune, now);
1453
+ channel.scheduledNotes.forEach((noteList) => {
1454
+ for (let i = 0; i < noteList.length; i++) {
1455
+ const note = noteList[i];
1456
+ if (!note)
1457
+ continue;
1458
+ const { bufferSource } = note;
1459
+ const detune = bufferSource.detune.value + detuneChange;
1460
+ bufferSource.detune
1461
+ .cancelScheduledValues(now)
1462
+ .setValueAtTime(detune, now);
1463
+ }
1375
1464
  });
1376
1465
  }
1377
1466
  handlePitchBendRangeRPN(channelNumber) {
@@ -1792,6 +1881,11 @@ Object.defineProperty(Midy, "channelSettings", {
1792
1881
  volume: 100 / 127,
1793
1882
  pan: 64,
1794
1883
  portamentoTime: 0,
1884
+ filterResonance: 1,
1885
+ releaseTime: 1,
1886
+ attackTime: 1,
1887
+ brightness: 1,
1888
+ decayTime: 1,
1795
1889
  reverbSendLevel: 0,
1796
1890
  chorusSendLevel: 0,
1797
1891
  vibratoRate: 1,
@@ -1 +0,0 @@
1
- {"version":3,"file":"+esm.d.ts","sourceRoot":"","sources":["../../../../../../src/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js"],"names":[],"mappings":";;AAMi9Z;IAAQ,oBAA2H;IAAd,YAAa;IAAC,qDAAoJ;IAAA,mCAAoP;IAAA,uCAAwQ;IAAA,+DAAqP;IAAA,gDAAuX;IAAA,mCAAiL;IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAA03E;IAAA,qBAA0I;CAAC;AAAj6L,+BAAmL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAniE;;;;;;;;;;;;;;;;EAA27B;AAAs1N,mCAAwC;AAA3sQ;IAAQ,oCAAyU;IAAnC,SAAU;IAAC,WAAY;IAAC,SAAU;IAAC,gBAAgE;CAAC;AAApyH;IAAqQ,wBAAwD;IAArT,4BAAyN;IAApB,QAAS;IAAC,QAAS;IAAC,oBAAoC;CAAyD;AAAllG;IAAwjC,gCAA8d;CAAC"}