@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.
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.4 → soundfont-parser@0.0.6}/+esm.d.ts +16 -20
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +1 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +180 -0
- package/esm/midy-GM1.d.ts +32 -3
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +93 -87
- package/esm/midy-GM2.d.ts +56 -3
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +142 -126
- package/esm/midy-GMLite.d.ts +32 -3
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +91 -85
- package/esm/midy.d.ts +87 -4
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +234 -140
- package/package.json +1 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.4 → soundfont-parser@0.0.6}/+esm.d.ts +16 -20
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +1 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +190 -0
- package/script/midy-GM1.d.ts +32 -3
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +93 -87
- package/script/midy-GM2.d.ts +56 -3
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +142 -126
- package/script/midy-GMLite.d.ts +32 -3
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +91 -85
- package/script/midy.d.ts +87 -4
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +234 -140
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +0 -1
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js +0 -162
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +0 -1
- 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.
|
|
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.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
-
|
|
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 =
|
|
827
|
-
const adjustedPeekFreq =
|
|
828
|
-
const adjustedSustainFreq =
|
|
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.
|
|
892
|
-
this.
|
|
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,
|
|
959
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
|
|
951
960
|
const channel = this.channels[channelNumber];
|
|
952
|
-
if (stopPedal
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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 *
|
|
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.
|
|
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
|
-
|
|
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.
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
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
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
|
1255
|
-
|
|
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
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
|
1268
|
-
|
|
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
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
.
|
|
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"}
|