@marmooo/midy 0.1.0 → 0.1.2
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-GM1.d.ts +1 -2
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +11 -17
- package/esm/midy-GM2.d.ts +29 -20
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +169 -104
- package/esm/midy-GMLite.d.ts +1 -2
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +11 -17
- package/esm/midy.d.ts +29 -20
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +169 -104
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +1 -2
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +11 -17
- package/script/midy-GM2.d.ts +29 -20
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +169 -104
- package/script/midy-GMLite.d.ts +1 -2
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +11 -17
- package/script/midy.d.ts +29 -20
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +169 -104
package/esm/midy.js
CHANGED
|
@@ -82,7 +82,7 @@ export class Midy {
|
|
|
82
82
|
writable: true,
|
|
83
83
|
value: {
|
|
84
84
|
time: this.getReverbTime(64),
|
|
85
|
-
feedback: 0.
|
|
85
|
+
feedback: 0.8,
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
Object.defineProperty(this, "chorus", {
|
|
@@ -90,10 +90,11 @@ export class Midy {
|
|
|
90
90
|
configurable: true,
|
|
91
91
|
writable: true,
|
|
92
92
|
value: {
|
|
93
|
-
modRate: 3
|
|
94
|
-
modDepth: (
|
|
95
|
-
feedback: 8
|
|
96
|
-
sendToReverb: 0
|
|
93
|
+
modRate: this.getChorusModRate(3),
|
|
94
|
+
modDepth: this.getChorusModDepth(19),
|
|
95
|
+
feedback: this.getChorusFeedback(8),
|
|
96
|
+
sendToReverb: this.getChorusSendToReverb(0),
|
|
97
|
+
delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
|
|
97
98
|
}
|
|
98
99
|
});
|
|
99
100
|
Object.defineProperty(this, "mono", {
|
|
@@ -223,8 +224,12 @@ export class Midy {
|
|
|
223
224
|
this.audioContext = audioContext;
|
|
224
225
|
this.options = { ...this.defaultOptions, ...options };
|
|
225
226
|
this.masterGain = new GainNode(audioContext);
|
|
226
|
-
this.masterGain.connect(audioContext.destination);
|
|
227
227
|
this.channels = this.createChannels(audioContext);
|
|
228
|
+
this.reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
229
|
+
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
230
|
+
this.chorusEffect.output.connect(this.masterGain);
|
|
231
|
+
this.reverbEffect.output.connect(this.masterGain);
|
|
232
|
+
this.masterGain.connect(audioContext.destination);
|
|
228
233
|
this.GM2SystemOn();
|
|
229
234
|
}
|
|
230
235
|
initSoundFontTable() {
|
|
@@ -268,14 +273,11 @@ export class Midy {
|
|
|
268
273
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
269
274
|
gainL.connect(merger, 0, 0);
|
|
270
275
|
gainR.connect(merger, 0, 1);
|
|
271
|
-
|
|
272
|
-
const chorusEffect = this.createChorusEffect(audioContext);
|
|
276
|
+
merger.connect(this.masterGain);
|
|
273
277
|
return {
|
|
274
278
|
gainL,
|
|
275
279
|
gainR,
|
|
276
280
|
merger,
|
|
277
|
-
reverbEffect,
|
|
278
|
-
chorusEffect,
|
|
279
281
|
};
|
|
280
282
|
}
|
|
281
283
|
createChannels(audioContext) {
|
|
@@ -691,12 +693,10 @@ export class Midy {
|
|
|
691
693
|
}
|
|
692
694
|
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
693
695
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
694
|
-
createSchroederReverb(audioContext, combDelays,
|
|
696
|
+
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
695
697
|
const input = new GainNode(audioContext);
|
|
696
698
|
const output = new GainNode(audioContext);
|
|
697
|
-
const mergerGain = new GainNode(audioContext
|
|
698
|
-
gain: 1 / (combDelays.length * 2),
|
|
699
|
-
});
|
|
699
|
+
const mergerGain = new GainNode(audioContext);
|
|
700
700
|
for (let i = 0; i < combDelays.length; i++) {
|
|
701
701
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
702
702
|
comb.connect(mergerGain);
|
|
@@ -709,62 +709,49 @@ export class Midy {
|
|
|
709
709
|
allpasses.at(-1).connect(output);
|
|
710
710
|
return { input, output };
|
|
711
711
|
}
|
|
712
|
-
createChorusEffect(audioContext
|
|
713
|
-
const
|
|
714
|
-
const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
|
|
715
|
-
const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
|
|
712
|
+
createChorusEffect(audioContext) {
|
|
713
|
+
const input = new GainNode(audioContext);
|
|
716
714
|
const output = new GainNode(audioContext);
|
|
717
|
-
const
|
|
715
|
+
const sendGain = new GainNode(audioContext);
|
|
716
|
+
const lfo = new OscillatorNode(audioContext, {
|
|
717
|
+
frequency: this.chorus.modRate,
|
|
718
|
+
});
|
|
719
|
+
const lfoGain = new GainNode(audioContext, {
|
|
720
|
+
gain: this.chorus.modDepth / 2,
|
|
721
|
+
});
|
|
722
|
+
const delayTimes = this.chorus.delayTimes;
|
|
718
723
|
const delayNodes = [];
|
|
719
|
-
const
|
|
720
|
-
for (let i = 0; i <
|
|
721
|
-
const
|
|
722
|
-
const delayTime = (i + 1) * delay + randomDelayFactor;
|
|
724
|
+
const feedbackGains = [];
|
|
725
|
+
for (let i = 0; i < delayTimes.length; i++) {
|
|
726
|
+
const delayTime = delayTimes[i];
|
|
723
727
|
const delayNode = new DelayNode(audioContext, {
|
|
724
|
-
maxDelayTime: delayTime
|
|
728
|
+
maxDelayTime: 0.1, // generally, 5ms < delayTime < 50ms
|
|
729
|
+
delayTime,
|
|
730
|
+
});
|
|
731
|
+
const feedbackGain = new GainNode(audioContext, {
|
|
732
|
+
gain: this.chorus.feedback,
|
|
725
733
|
});
|
|
726
|
-
const chorusGain = new GainNode(audioContext, { gain: baseGain });
|
|
727
734
|
delayNodes.push(delayNode);
|
|
728
|
-
|
|
735
|
+
feedbackGains.push(feedbackGain);
|
|
736
|
+
input.connect(delayNode);
|
|
729
737
|
lfoGain.connect(delayNode.delayTime);
|
|
730
|
-
delayNode.connect(
|
|
731
|
-
|
|
738
|
+
delayNode.connect(feedbackGain);
|
|
739
|
+
feedbackGain.connect(delayNode);
|
|
740
|
+
delayNode.connect(output);
|
|
732
741
|
}
|
|
742
|
+
output.connect(sendGain);
|
|
733
743
|
lfo.connect(lfoGain);
|
|
734
744
|
lfo.start();
|
|
735
745
|
return {
|
|
746
|
+
input,
|
|
747
|
+
output,
|
|
748
|
+
sendGain,
|
|
736
749
|
lfo,
|
|
737
750
|
lfoGain,
|
|
738
751
|
delayNodes,
|
|
739
|
-
|
|
740
|
-
output,
|
|
752
|
+
feedbackGains,
|
|
741
753
|
};
|
|
742
754
|
}
|
|
743
|
-
connectEffects(channel, gainNode) {
|
|
744
|
-
gainNode.connect(channel.merger);
|
|
745
|
-
channel.merger.connect(this.masterGain);
|
|
746
|
-
if (channel.reverbSendLevel === 0) {
|
|
747
|
-
if (channel.chorusSendLevel !== 0) { // chorus
|
|
748
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
749
|
-
channel.merger.connect(delayNode);
|
|
750
|
-
});
|
|
751
|
-
channel.chorusEffect.output.connect(this.masterGain);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
if (channel.chorusSendLevel === 0) { // reverb
|
|
756
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
757
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
758
|
-
}
|
|
759
|
-
else { // reverb + chorus
|
|
760
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
761
|
-
channel.merger.connect(delayNode);
|
|
762
|
-
});
|
|
763
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
764
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
755
|
cbToRatio(cb) {
|
|
769
756
|
return Math.pow(10, cb / 200);
|
|
770
757
|
}
|
|
@@ -781,14 +768,10 @@ export class Midy {
|
|
|
781
768
|
return instrumentKey.playbackRate(noteNumber) *
|
|
782
769
|
Math.pow(2, semitoneOffset / 12);
|
|
783
770
|
}
|
|
784
|
-
setVolumeEnvelope(
|
|
785
|
-
const { instrumentKey, startTime
|
|
771
|
+
setVolumeEnvelope(note) {
|
|
772
|
+
const { instrumentKey, startTime } = note;
|
|
786
773
|
note.gainNode = new GainNode(this.audioContext, { gain: 0 });
|
|
787
|
-
|
|
788
|
-
if (volume === 0)
|
|
789
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
790
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
791
|
-
volume;
|
|
774
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
792
775
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
793
776
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
794
777
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
@@ -861,7 +844,7 @@ export class Midy {
|
|
|
861
844
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
862
845
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
863
846
|
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
864
|
-
this.setVolumeEnvelope(
|
|
847
|
+
this.setVolumeEnvelope(note);
|
|
865
848
|
this.setFilterEnvelope(channel, note);
|
|
866
849
|
if (channel.modulation > 0) {
|
|
867
850
|
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
@@ -901,7 +884,8 @@ export class Midy {
|
|
|
901
884
|
if (!instrumentKey)
|
|
902
885
|
return;
|
|
903
886
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
904
|
-
|
|
887
|
+
note.gainNode.connect(channel.gainL);
|
|
888
|
+
note.gainNode.connect(channel.gainR);
|
|
905
889
|
if (channel.sostenutoPedal) {
|
|
906
890
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
907
891
|
}
|
|
@@ -1061,13 +1045,13 @@ export class Midy {
|
|
|
1061
1045
|
}
|
|
1062
1046
|
}
|
|
1063
1047
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1064
|
-
const pitchBend = msb * 128 + lsb;
|
|
1048
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
1065
1049
|
this.setPitchBend(channelNumber, pitchBend);
|
|
1066
1050
|
}
|
|
1067
1051
|
setPitchBend(channelNumber, pitchBend) {
|
|
1068
1052
|
const channel = this.channels[channelNumber];
|
|
1069
1053
|
const prevPitchBend = channel.pitchBend;
|
|
1070
|
-
channel.pitchBend =
|
|
1054
|
+
channel.pitchBend = pitchBend / 8192;
|
|
1071
1055
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
1072
1056
|
channel.pitchBendRange * 100;
|
|
1073
1057
|
this.updateDetune(channel, detuneChange);
|
|
@@ -1213,17 +1197,30 @@ export class Midy {
|
|
|
1213
1197
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1214
1198
|
}
|
|
1215
1199
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1216
|
-
const now = this.audioContext.currentTime;
|
|
1217
1200
|
const channel = this.channels[channelNumber];
|
|
1218
|
-
const reverbEffect =
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1201
|
+
const reverbEffect = this.reverbEffect;
|
|
1202
|
+
if (0 < reverbSendLevel) {
|
|
1203
|
+
const now = this.audioContext.currentTime;
|
|
1204
|
+
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1205
|
+
reverbEffect.output.gain.cancelScheduledValues(now);
|
|
1206
|
+
reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1207
|
+
}
|
|
1208
|
+
else if (channel.reverbSendLevel !== 0) {
|
|
1209
|
+
channel.merger.disconnect(reverbEffect.input);
|
|
1210
|
+
}
|
|
1222
1211
|
}
|
|
1223
1212
|
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1224
1213
|
const channel = this.channels[channelNumber];
|
|
1225
|
-
|
|
1226
|
-
|
|
1214
|
+
const chorusEffect = this.chorusEffect;
|
|
1215
|
+
if (0 < chorusSendLevel) {
|
|
1216
|
+
const now = this.audioContext.currentTime;
|
|
1217
|
+
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1218
|
+
chorusEffect.output.gain.cancelScheduledValues(now);
|
|
1219
|
+
chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1220
|
+
}
|
|
1221
|
+
else if (channel.chorusSendLevel !== 0) {
|
|
1222
|
+
channel.merger.disconnect(chorusEffect.input);
|
|
1223
|
+
}
|
|
1227
1224
|
}
|
|
1228
1225
|
setSostenutoPedal(channelNumber, value) {
|
|
1229
1226
|
const isOn = value >= 64;
|
|
@@ -1416,7 +1413,7 @@ export class Midy {
|
|
|
1416
1413
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1417
1414
|
const activeNote = this.getActiveNote(noteList, now);
|
|
1418
1415
|
if (activeNote) {
|
|
1419
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1416
|
+
const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
|
|
1420
1417
|
promises.push(notePromise);
|
|
1421
1418
|
}
|
|
1422
1419
|
});
|
|
@@ -1483,7 +1480,7 @@ export class Midy {
|
|
|
1483
1480
|
case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1484
1481
|
return this.handleMasterCoarseTuningSysEx(data);
|
|
1485
1482
|
case 5:
|
|
1486
|
-
return this.
|
|
1483
|
+
return this.handleGlobalParameterControlSysEx(data);
|
|
1487
1484
|
default:
|
|
1488
1485
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1489
1486
|
}
|
|
@@ -1560,13 +1557,13 @@ export class Midy {
|
|
|
1560
1557
|
this.masterCoarseTuning = coarseTuning - 64;
|
|
1561
1558
|
}
|
|
1562
1559
|
}
|
|
1563
|
-
|
|
1564
|
-
if (data[
|
|
1565
|
-
switch (data[
|
|
1560
|
+
handleGlobalParameterControlSysEx(data) {
|
|
1561
|
+
if (data[7] === 1) {
|
|
1562
|
+
switch (data[8]) {
|
|
1566
1563
|
case 1:
|
|
1567
|
-
return this.
|
|
1564
|
+
return this.handleReverbParameterSysEx(data);
|
|
1568
1565
|
case 2:
|
|
1569
|
-
return this.
|
|
1566
|
+
return this.handleChorusParameterSysEx(data);
|
|
1570
1567
|
default:
|
|
1571
1568
|
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1572
1569
|
}
|
|
@@ -1575,21 +1572,19 @@ export class Midy {
|
|
|
1575
1572
|
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1576
1573
|
}
|
|
1577
1574
|
}
|
|
1578
|
-
|
|
1579
|
-
switch (data[
|
|
1575
|
+
handleReverbParameterSysEx(data) {
|
|
1576
|
+
switch (data[9]) {
|
|
1580
1577
|
case 0:
|
|
1581
|
-
return this.setReverbType(data[
|
|
1578
|
+
return this.setReverbType(data[10]);
|
|
1582
1579
|
case 1:
|
|
1583
|
-
return this.setReverbTime(data[
|
|
1580
|
+
return this.setReverbTime(data[10]);
|
|
1584
1581
|
}
|
|
1585
1582
|
}
|
|
1586
1583
|
setReverbType(type) {
|
|
1587
1584
|
this.reverb.time = this.getReverbTimeFromType(type);
|
|
1588
|
-
this.reverb.feedback = (type === 8) ? 0.
|
|
1589
|
-
const { audioContext,
|
|
1590
|
-
|
|
1591
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1592
|
-
}
|
|
1585
|
+
this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
|
|
1586
|
+
const { audioContext, options } = this;
|
|
1587
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1593
1588
|
}
|
|
1594
1589
|
getReverbTimeFromType(type) {
|
|
1595
1590
|
switch (type) {
|
|
@@ -1640,41 +1635,111 @@ export class Midy {
|
|
|
1640
1635
|
// RT60 = -3 * delay / Math.log10(feedback)
|
|
1641
1636
|
// feedback = Math.pow(10, -3 * delay / RT60)
|
|
1642
1637
|
// delay estimation using ideal feedback
|
|
1643
|
-
//
|
|
1644
|
-
//
|
|
1645
|
-
// It
|
|
1638
|
+
// The structure of a concert hall is complex,
|
|
1639
|
+
// so estimates based on mean free path are unstable.
|
|
1640
|
+
// It is easier to determine the delay based on ideal feedback.
|
|
1641
|
+
// The average sound absorption coefficient
|
|
1642
|
+
// suitable for playing musical instruments is 0.18 to 0.28.
|
|
1646
1643
|
// delay = -RT60 * Math.log10(feedback) / 3
|
|
1647
1644
|
calcDelay(rt60, feedback) {
|
|
1648
1645
|
return -rt60 * Math.log10(feedback) / 3;
|
|
1649
1646
|
}
|
|
1650
|
-
|
|
1651
|
-
switch (data[
|
|
1647
|
+
handleChorusParameterSysEx(data) {
|
|
1648
|
+
switch (data[9]) {
|
|
1652
1649
|
case 0:
|
|
1653
|
-
return this.setChorusType(data[
|
|
1650
|
+
return this.setChorusType(data[10]);
|
|
1654
1651
|
case 1:
|
|
1655
|
-
return this.setChorusModRate(data[
|
|
1652
|
+
return this.setChorusModRate(data[10]);
|
|
1656
1653
|
case 2:
|
|
1657
|
-
return this.setChorusModDepth(data[
|
|
1654
|
+
return this.setChorusModDepth(data[10]);
|
|
1658
1655
|
case 3:
|
|
1659
|
-
return this.setChorusFeedback(data[
|
|
1656
|
+
return this.setChorusFeedback(data[10]);
|
|
1660
1657
|
case 4:
|
|
1661
|
-
return this.setChorusSendToReverb(data[
|
|
1658
|
+
return this.setChorusSendToReverb(data[10]);
|
|
1662
1659
|
}
|
|
1663
1660
|
}
|
|
1664
1661
|
setChorusType(type) {
|
|
1665
|
-
|
|
1662
|
+
switch (type) {
|
|
1663
|
+
case 0:
|
|
1664
|
+
return this.setChorusParameter(3, 5, 0, 0);
|
|
1665
|
+
case 1:
|
|
1666
|
+
return this.setChorusParameter(9, 19, 5, 0);
|
|
1667
|
+
case 2:
|
|
1668
|
+
return this.setChorusParameter(3, 19, 8, 0);
|
|
1669
|
+
case 3:
|
|
1670
|
+
return this.setChorusParameter(9, 16, 16, 0);
|
|
1671
|
+
case 4:
|
|
1672
|
+
return this.setChorusParameter(2, 24, 64, 0);
|
|
1673
|
+
case 5:
|
|
1674
|
+
return this.setChorusParameter(1, 5, 112, 0);
|
|
1675
|
+
default:
|
|
1676
|
+
console.warn(`Unsupported Chorus Type: ${type}`);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
setChorusParameter(modRate, modDepth, feedback, sendToReverb) {
|
|
1680
|
+
this.setChorusModRate(modRate);
|
|
1681
|
+
this.setChorusModDepth(modDepth);
|
|
1682
|
+
this.setChorusFeedback(feedback);
|
|
1683
|
+
this.setChorusSendToReverb(sendToReverb);
|
|
1666
1684
|
}
|
|
1667
1685
|
setChorusModRate(value) {
|
|
1668
|
-
|
|
1686
|
+
const now = this.audioContext.currentTime;
|
|
1687
|
+
const modRate = this.getChorusModRate(value);
|
|
1688
|
+
this.chorus.modRate = modRate;
|
|
1689
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1690
|
+
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1691
|
+
lfo.frequency.setValueAtTime(modRate, now);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
getChorusModRate(value) {
|
|
1695
|
+
return value * 0.122; // Hz
|
|
1669
1696
|
}
|
|
1670
1697
|
setChorusModDepth(value) {
|
|
1671
|
-
|
|
1698
|
+
const now = this.audioContext.currentTime;
|
|
1699
|
+
const modDepth = this.getChorusModDepth(value);
|
|
1700
|
+
this.chorus.modDepth = modDepth;
|
|
1701
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1702
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1703
|
+
chorusEffect.lfoGain.gain
|
|
1704
|
+
.cancelScheduledValues(now)
|
|
1705
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
getChorusModDepth(value) {
|
|
1709
|
+
return (value + 1) / 3200; // second
|
|
1672
1710
|
}
|
|
1673
1711
|
setChorusFeedback(value) {
|
|
1674
|
-
|
|
1712
|
+
const now = this.audioContext.currentTime;
|
|
1713
|
+
const feedback = this.getChorusFeedback(value);
|
|
1714
|
+
this.chorus.feedback = feedback;
|
|
1715
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1716
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1717
|
+
for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
|
|
1718
|
+
const feedbackGain = chorusEffect.feedbackGains[j];
|
|
1719
|
+
feedbackGain.gain
|
|
1720
|
+
.cancelScheduledValues(now)
|
|
1721
|
+
.setValueAtTime(feedback, now);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
getChorusFeedback(value) {
|
|
1726
|
+
return value * 0.00763;
|
|
1675
1727
|
}
|
|
1676
1728
|
setChorusSendToReverb(value) {
|
|
1677
|
-
|
|
1729
|
+
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1730
|
+
if (0 < sendToReverb) {
|
|
1731
|
+
const now = this.audioContext.currentTime;
|
|
1732
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1733
|
+
this.chorusEffect.sendGain.gain
|
|
1734
|
+
.cancelScheduledValues(now)
|
|
1735
|
+
.setValueAtTime(sendToReverb, now);
|
|
1736
|
+
}
|
|
1737
|
+
else if (this.chorus.sendToReverb !== 0) {
|
|
1738
|
+
this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
getChorusSendToReverb(value) {
|
|
1742
|
+
return value * 0.00787;
|
|
1678
1743
|
}
|
|
1679
1744
|
handleExclusiveMessage(data) {
|
|
1680
1745
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
package/package.json
CHANGED
package/script/midy-GM1.d.ts
CHANGED
|
@@ -72,12 +72,11 @@ export class MidyGM1 {
|
|
|
72
72
|
currentTime(): number;
|
|
73
73
|
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
74
74
|
getActiveNote(noteList: any, time: any): any;
|
|
75
|
-
connectEffects(channel: any, gainNode: any): void;
|
|
76
75
|
cbToRatio(cb: any): number;
|
|
77
76
|
centToHz(cent: any): number;
|
|
78
77
|
calcSemitoneOffset(channel: any): any;
|
|
79
78
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
80
|
-
setVolumeEnvelope(
|
|
79
|
+
setVolumeEnvelope(note: any): void;
|
|
81
80
|
setFilterEnvelope(channel: any, note: any): void;
|
|
82
81
|
startModulation(channel: any, note: any, time: any): void;
|
|
83
82
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
package/script/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAC5C,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,mCAcC;IACD,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA8CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,mFA+BC;IAED,qCAcC;IAED,yDAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA3iCD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|
package/script/midy-GM1.js
CHANGED
|
@@ -147,8 +147,8 @@ class MidyGM1 {
|
|
|
147
147
|
});
|
|
148
148
|
this.audioContext = audioContext;
|
|
149
149
|
this.masterGain = new GainNode(audioContext);
|
|
150
|
-
this.masterGain.connect(audioContext.destination);
|
|
151
150
|
this.channels = this.createChannels(audioContext);
|
|
151
|
+
this.masterGain.connect(audioContext.destination);
|
|
152
152
|
this.GM1SystemOn();
|
|
153
153
|
}
|
|
154
154
|
initSoundFontTable() {
|
|
@@ -192,6 +192,7 @@ class MidyGM1 {
|
|
|
192
192
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
193
193
|
gainL.connect(merger, 0, 0);
|
|
194
194
|
gainR.connect(merger, 0, 1);
|
|
195
|
+
merger.connect(this.masterGain);
|
|
195
196
|
return {
|
|
196
197
|
gainL,
|
|
197
198
|
gainR,
|
|
@@ -494,10 +495,6 @@ class MidyGM1 {
|
|
|
494
495
|
}
|
|
495
496
|
return noteList[0];
|
|
496
497
|
}
|
|
497
|
-
connectEffects(channel, gainNode) {
|
|
498
|
-
gainNode.connect(channel.merger);
|
|
499
|
-
merger.connect(this.masterGain);
|
|
500
|
-
}
|
|
501
498
|
cbToRatio(cb) {
|
|
502
499
|
return Math.pow(10, cb / 200);
|
|
503
500
|
}
|
|
@@ -512,14 +509,10 @@ class MidyGM1 {
|
|
|
512
509
|
return instrumentKey.playbackRate(noteNumber) *
|
|
513
510
|
Math.pow(2, semitoneOffset / 12);
|
|
514
511
|
}
|
|
515
|
-
setVolumeEnvelope(
|
|
516
|
-
const { instrumentKey, startTime
|
|
512
|
+
setVolumeEnvelope(note) {
|
|
513
|
+
const { instrumentKey, startTime } = note;
|
|
517
514
|
note.gainNode = new GainNode(this.audioContext, { gain: 0 });
|
|
518
|
-
|
|
519
|
-
if (volume === 0)
|
|
520
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
521
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
522
|
-
volume;
|
|
515
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
523
516
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
524
517
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
525
518
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
@@ -579,7 +572,7 @@ class MidyGM1 {
|
|
|
579
572
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
580
573
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
581
574
|
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
582
|
-
this.setVolumeEnvelope(
|
|
575
|
+
this.setVolumeEnvelope(note);
|
|
583
576
|
this.setFilterEnvelope(channel, note);
|
|
584
577
|
if (channel.modulation > 0) {
|
|
585
578
|
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
@@ -602,7 +595,8 @@ class MidyGM1 {
|
|
|
602
595
|
if (!instrumentKey)
|
|
603
596
|
return;
|
|
604
597
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
605
|
-
|
|
598
|
+
note.gainNode.connect(channel.gainL);
|
|
599
|
+
note.gainNode.connect(channel.gainR);
|
|
606
600
|
const scheduledNotes = channel.scheduledNotes;
|
|
607
601
|
if (scheduledNotes.has(noteNumber)) {
|
|
608
602
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -705,13 +699,13 @@ class MidyGM1 {
|
|
|
705
699
|
channel.program = program;
|
|
706
700
|
}
|
|
707
701
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
708
|
-
const pitchBend = msb * 128 + lsb;
|
|
702
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
709
703
|
this.setPitchBend(channelNumber, pitchBend);
|
|
710
704
|
}
|
|
711
705
|
setPitchBend(channelNumber, pitchBend) {
|
|
712
706
|
const channel = this.channels[channelNumber];
|
|
713
707
|
const prevPitchBend = channel.pitchBend;
|
|
714
|
-
channel.pitchBend =
|
|
708
|
+
channel.pitchBend = pitchBend / 8192;
|
|
715
709
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
716
710
|
channel.pitchBendRange * 100;
|
|
717
711
|
this.updateDetune(channel, detuneChange);
|
|
@@ -939,7 +933,7 @@ class MidyGM1 {
|
|
|
939
933
|
channel.scheduledNotes.forEach((noteList) => {
|
|
940
934
|
const activeNote = this.getActiveNote(noteList, now);
|
|
941
935
|
if (activeNote) {
|
|
942
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
936
|
+
const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
|
|
943
937
|
promises.push(notePromise);
|
|
944
938
|
}
|
|
945
939
|
});
|
package/script/midy-GM2.d.ts
CHANGED
|
@@ -56,6 +56,7 @@ export class MidyGM2 {
|
|
|
56
56
|
modDepth: number;
|
|
57
57
|
feedback: number;
|
|
58
58
|
sendToReverb: number;
|
|
59
|
+
delayTimes: any[];
|
|
59
60
|
};
|
|
60
61
|
mono: boolean;
|
|
61
62
|
omni: boolean;
|
|
@@ -89,6 +90,19 @@ export class MidyGM2 {
|
|
|
89
90
|
};
|
|
90
91
|
masterGain: any;
|
|
91
92
|
channels: any[];
|
|
93
|
+
reverbEffect: {
|
|
94
|
+
input: any;
|
|
95
|
+
output: any;
|
|
96
|
+
};
|
|
97
|
+
chorusEffect: {
|
|
98
|
+
input: any;
|
|
99
|
+
output: any;
|
|
100
|
+
sendGain: any;
|
|
101
|
+
lfo: any;
|
|
102
|
+
lfoGain: any;
|
|
103
|
+
delayNodes: any[];
|
|
104
|
+
feedbackGains: any[];
|
|
105
|
+
};
|
|
92
106
|
initSoundFontTable(): any[];
|
|
93
107
|
addSoundFont(soundFont: any): void;
|
|
94
108
|
loadSoundFont(soundFontUrl: any): Promise<void>;
|
|
@@ -97,17 +111,6 @@ export class MidyGM2 {
|
|
|
97
111
|
gainL: any;
|
|
98
112
|
gainR: any;
|
|
99
113
|
merger: any;
|
|
100
|
-
reverbEffect: {
|
|
101
|
-
input: any;
|
|
102
|
-
output: any;
|
|
103
|
-
};
|
|
104
|
-
chorusEffect: {
|
|
105
|
-
lfo: any;
|
|
106
|
-
lfoGain: any;
|
|
107
|
-
delayNodes: any[];
|
|
108
|
-
chorusGains: any[];
|
|
109
|
-
output: any;
|
|
110
|
-
};
|
|
111
114
|
};
|
|
112
115
|
createChannels(audioContext: any): any[];
|
|
113
116
|
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
@@ -141,23 +144,24 @@ export class MidyGM2 {
|
|
|
141
144
|
createCombFilter(audioContext: any, input: any, delay: any, feedback: any): any;
|
|
142
145
|
createAllpassFilter(audioContext: any, input: any, delay: any, feedback: any): any;
|
|
143
146
|
generateDistributedArray(center: any, count: any, varianceRatio?: number, randomness?: number): any[];
|
|
144
|
-
createSchroederReverb(audioContext: any,
|
|
147
|
+
createSchroederReverb(audioContext: any, combFeedbacks: any, combDelays: any, allpassFeedbacks: any, allpassDelays: any): {
|
|
145
148
|
input: any;
|
|
146
149
|
output: any;
|
|
147
150
|
};
|
|
148
|
-
createChorusEffect(audioContext: any
|
|
151
|
+
createChorusEffect(audioContext: any): {
|
|
152
|
+
input: any;
|
|
153
|
+
output: any;
|
|
154
|
+
sendGain: any;
|
|
149
155
|
lfo: any;
|
|
150
156
|
lfoGain: any;
|
|
151
157
|
delayNodes: any[];
|
|
152
|
-
|
|
153
|
-
output: any;
|
|
158
|
+
feedbackGains: any[];
|
|
154
159
|
};
|
|
155
|
-
connectEffects(channel: any, gainNode: any): void;
|
|
156
160
|
cbToRatio(cb: any): number;
|
|
157
161
|
centToHz(cent: any): number;
|
|
158
162
|
calcSemitoneOffset(channel: any): any;
|
|
159
163
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
160
|
-
setVolumeEnvelope(
|
|
164
|
+
setVolumeEnvelope(note: any): void;
|
|
161
165
|
setFilterEnvelope(channel: any, note: any): void;
|
|
162
166
|
startModulation(channel: any, note: any, time: any): void;
|
|
163
167
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
@@ -226,19 +230,24 @@ export class MidyGM2 {
|
|
|
226
230
|
setMasterFineTuning(fineTuning: any): void;
|
|
227
231
|
handleMasterCoarseTuningSysEx(data: any): void;
|
|
228
232
|
setMasterCoarseTuning(coarseTuning: any): void;
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
handleGlobalParameterControlSysEx(data: any): void;
|
|
234
|
+
handleReverbParameterSysEx(data: any): void;
|
|
231
235
|
setReverbType(type: any): void;
|
|
232
236
|
getReverbTimeFromType(type: any): number | undefined;
|
|
233
237
|
setReverbTime(value: any): void;
|
|
234
238
|
getReverbTime(value: any): number;
|
|
235
239
|
calcDelay(rt60: any, feedback: any): number;
|
|
236
|
-
|
|
240
|
+
handleChorusParameterSysEx(data: any): void;
|
|
237
241
|
setChorusType(type: any): void;
|
|
242
|
+
setChorusParameter(modRate: any, modDepth: any, feedback: any, sendToReverb: any): void;
|
|
238
243
|
setChorusModRate(value: any): void;
|
|
244
|
+
getChorusModRate(value: any): number;
|
|
239
245
|
setChorusModDepth(value: any): void;
|
|
246
|
+
getChorusModDepth(value: any): number;
|
|
240
247
|
setChorusFeedback(value: any): void;
|
|
248
|
+
getChorusFeedback(value: any): number;
|
|
241
249
|
setChorusSendToReverb(value: any): void;
|
|
250
|
+
getChorusSendToReverb(value: any): number;
|
|
242
251
|
handleExclusiveMessage(data: any): void;
|
|
243
252
|
handleSysEx(data: any): void;
|
|
244
253
|
scheduleTask(callback: any, startTime: any): Promise<any>;
|