@marmooo/midy 0.0.9 → 0.1.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/esm/midy-GM1.d.ts +0 -2
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +5 -9
- package/esm/midy-GM2.d.ts +46 -24
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +301 -92
- package/esm/midy-GMLite.js +5 -5
- package/esm/midy.d.ts +46 -24
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +300 -91
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +0 -2
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +5 -9
- package/script/midy-GM2.d.ts +46 -24
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +301 -92
- package/script/midy-GMLite.js +5 -5
- package/script/midy.d.ts +46 -24
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +300 -91
package/script/midy-GM2.js
CHANGED
|
@@ -67,12 +67,6 @@ class MidyGM2 {
|
|
|
67
67
|
writable: true,
|
|
68
68
|
value: 0
|
|
69
69
|
});
|
|
70
|
-
Object.defineProperty(this, "reverbFactor", {
|
|
71
|
-
enumerable: true,
|
|
72
|
-
configurable: true,
|
|
73
|
-
writable: true,
|
|
74
|
-
value: 0.1
|
|
75
|
-
});
|
|
76
70
|
Object.defineProperty(this, "masterFineTuning", {
|
|
77
71
|
enumerable: true,
|
|
78
72
|
configurable: true,
|
|
@@ -85,6 +79,27 @@ class MidyGM2 {
|
|
|
85
79
|
writable: true,
|
|
86
80
|
value: 0
|
|
87
81
|
}); // cb
|
|
82
|
+
Object.defineProperty(this, "reverb", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
configurable: true,
|
|
85
|
+
writable: true,
|
|
86
|
+
value: {
|
|
87
|
+
time: this.getReverbTime(64),
|
|
88
|
+
feedback: 0.25,
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
Object.defineProperty(this, "chorus", {
|
|
92
|
+
enumerable: true,
|
|
93
|
+
configurable: true,
|
|
94
|
+
writable: true,
|
|
95
|
+
value: {
|
|
96
|
+
modRate: this.getChorusModRate(3),
|
|
97
|
+
modDepth: this.getChorusModDepth(19),
|
|
98
|
+
feedback: this.getChorusFeedback(8),
|
|
99
|
+
sendToReverb: this.getChorusSendToReverb(0),
|
|
100
|
+
delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
|
|
101
|
+
}
|
|
102
|
+
});
|
|
88
103
|
Object.defineProperty(this, "mono", {
|
|
89
104
|
enumerable: true,
|
|
90
105
|
configurable: true,
|
|
@@ -193,8 +208,19 @@ class MidyGM2 {
|
|
|
193
208
|
writable: true,
|
|
194
209
|
value: {
|
|
195
210
|
reverbAlgorithm: (audioContext) => {
|
|
196
|
-
|
|
197
|
-
|
|
211
|
+
const { time: rt60, feedback } = this.reverb;
|
|
212
|
+
// const delay = this.calcDelay(rt60, feedback);
|
|
213
|
+
// const impulse = this.createConvolutionReverbImpulse(
|
|
214
|
+
// audioContext,
|
|
215
|
+
// rt60,
|
|
216
|
+
// delay,
|
|
217
|
+
// );
|
|
218
|
+
// return this.createConvolutionReverb(audioContext, impulse);
|
|
219
|
+
const combFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
220
|
+
const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
221
|
+
const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
222
|
+
const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
223
|
+
return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
|
|
198
224
|
},
|
|
199
225
|
}
|
|
200
226
|
});
|
|
@@ -593,12 +619,7 @@ class MidyGM2 {
|
|
|
593
619
|
}
|
|
594
620
|
return noteList[0];
|
|
595
621
|
}
|
|
596
|
-
|
|
597
|
-
const { decay = 0.8, preDecay = 0, } = options;
|
|
598
|
-
const input = new GainNode(audioContext);
|
|
599
|
-
const output = new GainNode(audioContext);
|
|
600
|
-
const dryGain = new GainNode(audioContext);
|
|
601
|
-
const wetGain = new GainNode(audioContext);
|
|
622
|
+
createConvolutionReverbImpulse(audioContext, decay, preDecay) {
|
|
602
623
|
const sampleRate = audioContext.sampleRate;
|
|
603
624
|
const length = sampleRate * decay;
|
|
604
625
|
const impulse = new AudioBuffer({
|
|
@@ -618,18 +639,17 @@ class MidyGM2 {
|
|
|
618
639
|
channelData[i] = (Math.random() * 2 - 1) * attenuation;
|
|
619
640
|
}
|
|
620
641
|
}
|
|
642
|
+
return impulse;
|
|
643
|
+
}
|
|
644
|
+
createConvolutionReverb(audioContext, impulse) {
|
|
645
|
+
const output = new GainNode(audioContext);
|
|
621
646
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
622
647
|
buffer: impulse,
|
|
623
648
|
});
|
|
624
|
-
|
|
625
|
-
convolverNode.connect(wetGain);
|
|
626
|
-
wetGain.connect(output);
|
|
627
|
-
dryGain.connect(output);
|
|
649
|
+
convolverNode.connect(output);
|
|
628
650
|
return {
|
|
629
|
-
input,
|
|
651
|
+
input: convolverNode,
|
|
630
652
|
output,
|
|
631
|
-
dryGain,
|
|
632
|
-
wetGain,
|
|
633
653
|
convolverNode,
|
|
634
654
|
};
|
|
635
655
|
}
|
|
@@ -657,17 +677,22 @@ class MidyGM2 {
|
|
|
657
677
|
delayNode.connect(passGain);
|
|
658
678
|
return passGain;
|
|
659
679
|
}
|
|
680
|
+
generateDistributedArray(center, count, varianceRatio = 0.1, randomness = 0.05) {
|
|
681
|
+
const variance = center * varianceRatio;
|
|
682
|
+
const array = new Array(count);
|
|
683
|
+
for (let i = 0; i < count; i++) {
|
|
684
|
+
const fraction = i / (count - 1 || 1);
|
|
685
|
+
const value = center - variance + fraction * 2 * variance;
|
|
686
|
+
array[i] = value * (1 - (Math.random() * 2 - 1) * randomness);
|
|
687
|
+
}
|
|
688
|
+
return array;
|
|
689
|
+
}
|
|
660
690
|
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
661
691
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
662
|
-
createSchroederReverb(audioContext,
|
|
663
|
-
const { combDelays = [0.31, 0.34, 0.37, 0.40], combFeedbacks = [0.86, 0.87, 0.88, 0.89], allpassDelays = [0.02, 0.05], allpassFeedbacks = [0.7, 0.7], mix = 0.5, } = options;
|
|
692
|
+
createSchroederReverb(audioContext, combDelays, combFeedbacks, allpassDelays, allpassFeedbacks) {
|
|
664
693
|
const input = new GainNode(audioContext);
|
|
665
694
|
const output = new GainNode(audioContext);
|
|
666
|
-
const mergerGain = new GainNode(audioContext
|
|
667
|
-
gain: 1 / (combDelays.length * 2),
|
|
668
|
-
});
|
|
669
|
-
const dryGain = new GainNode(audioContext, { gain: 1 - mix });
|
|
670
|
-
const wetGain = new GainNode(audioContext, { gain: mix });
|
|
695
|
+
const mergerGain = new GainNode(audioContext);
|
|
671
696
|
for (let i = 0; i < combDelays.length; i++) {
|
|
672
697
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
673
698
|
comb.connect(mergerGain);
|
|
@@ -677,68 +702,65 @@ class MidyGM2 {
|
|
|
677
702
|
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
678
703
|
allpasses.push(allpass);
|
|
679
704
|
}
|
|
680
|
-
allpasses.at(-1).connect(
|
|
681
|
-
input
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
createChorusEffect(audioContext, options = {}) {
|
|
687
|
-
const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
|
|
688
|
-
const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
|
|
689
|
-
const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
|
|
705
|
+
allpasses.at(-1).connect(output);
|
|
706
|
+
return { input, output };
|
|
707
|
+
}
|
|
708
|
+
createChorusEffect(audioContext) {
|
|
709
|
+
const input = new GainNode(audioContext);
|
|
690
710
|
const output = new GainNode(audioContext);
|
|
691
|
-
const
|
|
711
|
+
const sendGain = new GainNode(audioContext);
|
|
712
|
+
const lfo = new OscillatorNode(audioContext, {
|
|
713
|
+
frequency: this.chorus.modRate,
|
|
714
|
+
});
|
|
715
|
+
const lfoGain = new GainNode(audioContext, {
|
|
716
|
+
gain: this.chorus.modDepth / 2,
|
|
717
|
+
});
|
|
718
|
+
const delayTimes = this.chorus.delayTimes;
|
|
692
719
|
const delayNodes = [];
|
|
693
|
-
const
|
|
694
|
-
for (let i = 0; i <
|
|
695
|
-
const
|
|
696
|
-
const delayTime = (i + 1) * delay + randomDelayFactor;
|
|
720
|
+
const feedbackGains = [];
|
|
721
|
+
for (let i = 0; i < delayTimes.length; i++) {
|
|
722
|
+
const delayTime = delayTimes[i];
|
|
697
723
|
const delayNode = new DelayNode(audioContext, {
|
|
698
|
-
maxDelayTime: delayTime
|
|
724
|
+
maxDelayTime: 0.1, // generally, 5ms < delayTime < 50ms
|
|
725
|
+
delayTime,
|
|
726
|
+
});
|
|
727
|
+
const feedbackGain = new GainNode(audioContext, {
|
|
728
|
+
gain: this.chorus.feedback,
|
|
699
729
|
});
|
|
700
|
-
const chorusGain = new GainNode(audioContext, { gain: baseGain });
|
|
701
730
|
delayNodes.push(delayNode);
|
|
702
|
-
|
|
731
|
+
feedbackGains.push(feedbackGain);
|
|
732
|
+
input.connect(delayNode);
|
|
703
733
|
lfoGain.connect(delayNode.delayTime);
|
|
704
|
-
delayNode.connect(
|
|
705
|
-
|
|
734
|
+
delayNode.connect(feedbackGain);
|
|
735
|
+
feedbackGain.connect(delayNode);
|
|
736
|
+
delayNode.connect(output);
|
|
706
737
|
}
|
|
738
|
+
output.connect(sendGain);
|
|
707
739
|
lfo.connect(lfoGain);
|
|
708
740
|
lfo.start();
|
|
709
741
|
return {
|
|
742
|
+
input,
|
|
743
|
+
output,
|
|
744
|
+
sendGain,
|
|
710
745
|
lfo,
|
|
711
746
|
lfoGain,
|
|
712
747
|
delayNodes,
|
|
713
|
-
|
|
714
|
-
output,
|
|
748
|
+
feedbackGains,
|
|
715
749
|
};
|
|
716
750
|
}
|
|
717
751
|
connectEffects(channel, gainNode) {
|
|
718
752
|
gainNode.connect(channel.merger);
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
else { // chorus
|
|
724
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
725
|
-
channel.merger.connect(delayNode);
|
|
726
|
-
});
|
|
727
|
-
channel.chorusEffect.output.connect(this.masterGain);
|
|
728
|
-
}
|
|
753
|
+
channel.merger.connect(this.masterGain);
|
|
754
|
+
if (0 < channel.reverbSendLevel) {
|
|
755
|
+
channel.merger.connect(channel.reverbEffect.input);
|
|
756
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
729
757
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
737
|
-
channel.merger.connect(delayNode);
|
|
738
|
-
});
|
|
739
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
740
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
741
|
-
}
|
|
758
|
+
if (0 < channel.chorusSendLevel) {
|
|
759
|
+
channel.merger.connect(channel.chorusEffect.input);
|
|
760
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
761
|
+
}
|
|
762
|
+
if (0 < this.chorus.sendToReverb) {
|
|
763
|
+
channel.chorusEffect.sendGain.connect(channel.reverbEffect.input);
|
|
742
764
|
}
|
|
743
765
|
}
|
|
744
766
|
cbToRatio(cb) {
|
|
@@ -1139,23 +1161,25 @@ class MidyGM2 {
|
|
|
1139
1161
|
this.releaseSustainPedal(channelNumber, value);
|
|
1140
1162
|
}
|
|
1141
1163
|
}
|
|
1164
|
+
// TODO
|
|
1142
1165
|
setPortamento(channelNumber, value) {
|
|
1143
1166
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1144
1167
|
}
|
|
1145
|
-
setReverbSendLevel(channelNumber,
|
|
1168
|
+
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1146
1169
|
const now = this.audioContext.currentTime;
|
|
1147
1170
|
const channel = this.channels[channelNumber];
|
|
1148
1171
|
const reverbEffect = channel.reverbEffect;
|
|
1149
|
-
channel.
|
|
1150
|
-
reverbEffect.
|
|
1151
|
-
reverbEffect.
|
|
1152
|
-
reverbEffect.wetGain.gain.cancelScheduledValues(now);
|
|
1153
|
-
reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
|
|
1172
|
+
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1173
|
+
reverbEffect.output.gain.cancelScheduledValues(now);
|
|
1174
|
+
reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1154
1175
|
}
|
|
1155
|
-
setChorusSendLevel(channelNumber,
|
|
1176
|
+
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1177
|
+
const now = this.audioContext.currentTime;
|
|
1156
1178
|
const channel = this.channels[channelNumber];
|
|
1157
|
-
|
|
1158
|
-
channel.
|
|
1179
|
+
const chorusEffect = channel.chorusEffect;
|
|
1180
|
+
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1181
|
+
chorusEffect.output.gain.cancelScheduledValues(now);
|
|
1182
|
+
chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1159
1183
|
}
|
|
1160
1184
|
setSostenutoPedal(channelNumber, value) {
|
|
1161
1185
|
const isOn = value >= 64;
|
|
@@ -1351,11 +1375,11 @@ class MidyGM2 {
|
|
|
1351
1375
|
this.GM2SystemOn();
|
|
1352
1376
|
break;
|
|
1353
1377
|
default:
|
|
1354
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1378
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1355
1379
|
}
|
|
1356
1380
|
break;
|
|
1357
1381
|
default:
|
|
1358
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1382
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1359
1383
|
}
|
|
1360
1384
|
}
|
|
1361
1385
|
GM1SystemOn() {
|
|
@@ -1386,9 +1410,10 @@ class MidyGM2 {
|
|
|
1386
1410
|
return this.handleMasterFineTuningSysEx(data);
|
|
1387
1411
|
case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1388
1412
|
return this.handleMasterCoarseTuningSysEx(data);
|
|
1389
|
-
|
|
1413
|
+
case 5:
|
|
1414
|
+
return this.handleGlobalParameterControlSysEx(data);
|
|
1390
1415
|
default:
|
|
1391
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1416
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1392
1417
|
}
|
|
1393
1418
|
break;
|
|
1394
1419
|
case 8:
|
|
@@ -1397,7 +1422,7 @@ class MidyGM2 {
|
|
|
1397
1422
|
// // TODO
|
|
1398
1423
|
// return this.handleScaleOctaveTuning1ByteFormat();
|
|
1399
1424
|
default:
|
|
1400
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1425
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1401
1426
|
}
|
|
1402
1427
|
break;
|
|
1403
1428
|
case 9:
|
|
@@ -1409,7 +1434,7 @@ class MidyGM2 {
|
|
|
1409
1434
|
// // TODO
|
|
1410
1435
|
// return this.setControlChange();
|
|
1411
1436
|
default:
|
|
1412
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1437
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1413
1438
|
}
|
|
1414
1439
|
break;
|
|
1415
1440
|
case 10:
|
|
@@ -1418,11 +1443,11 @@ class MidyGM2 {
|
|
|
1418
1443
|
// // TODO
|
|
1419
1444
|
// return this.handleKeyBasedInstrumentControl();
|
|
1420
1445
|
default:
|
|
1421
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1446
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1422
1447
|
}
|
|
1423
1448
|
break;
|
|
1424
1449
|
default:
|
|
1425
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1450
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1426
1451
|
}
|
|
1427
1452
|
}
|
|
1428
1453
|
handleMasterVolumeSysEx(data) {
|
|
@@ -1463,8 +1488,192 @@ class MidyGM2 {
|
|
|
1463
1488
|
this.masterCoarseTuning = coarseTuning - 64;
|
|
1464
1489
|
}
|
|
1465
1490
|
}
|
|
1491
|
+
handleGlobalParameterControlSysEx(data) {
|
|
1492
|
+
if (data[7] === 1) {
|
|
1493
|
+
switch (data[8]) {
|
|
1494
|
+
case 1:
|
|
1495
|
+
return this.handleReverbParameterSysEx(data);
|
|
1496
|
+
case 2:
|
|
1497
|
+
return this.handleChorusParameterSysEx(data);
|
|
1498
|
+
default:
|
|
1499
|
+
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
handleReverbParameterSysEx(data) {
|
|
1507
|
+
switch (data[9]) {
|
|
1508
|
+
case 0:
|
|
1509
|
+
return this.setReverbType(data[10]);
|
|
1510
|
+
case 1:
|
|
1511
|
+
return this.setReverbTime(data[10]);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
setReverbType(type) {
|
|
1515
|
+
this.reverb.time = this.getReverbTimeFromType(type);
|
|
1516
|
+
this.reverb.feedback = (type === 8) ? 0.1 : 0.2;
|
|
1517
|
+
const { audioContext, channels, options } = this;
|
|
1518
|
+
for (let i = 0; i < channels.length; i++) {
|
|
1519
|
+
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
getReverbTimeFromType(type) {
|
|
1523
|
+
switch (type) {
|
|
1524
|
+
case 0:
|
|
1525
|
+
return this.getReverbTime(44);
|
|
1526
|
+
case 1:
|
|
1527
|
+
return this.getReverbTime(50);
|
|
1528
|
+
case 2:
|
|
1529
|
+
return this.getReverbTime(56);
|
|
1530
|
+
case 3:
|
|
1531
|
+
return this.getReverbTime(64);
|
|
1532
|
+
case 4:
|
|
1533
|
+
return this.getReverbTime(64);
|
|
1534
|
+
case 8:
|
|
1535
|
+
return this.getReverbTime(50);
|
|
1536
|
+
default:
|
|
1537
|
+
console.warn(`Unsupported Reverb Time: ${type}`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
setReverbTime(value) {
|
|
1541
|
+
this.reverb.time = this.getReverbTime(value);
|
|
1542
|
+
const { audioContext, channels, options } = this;
|
|
1543
|
+
for (let i = 0; i < channels.length; i++) {
|
|
1544
|
+
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
getReverbTime(value) {
|
|
1548
|
+
return Math.pow(Math.E, (value - 40) * 0.025);
|
|
1549
|
+
}
|
|
1550
|
+
// mean free path equation
|
|
1551
|
+
// https://repository.dl.itc.u-tokyo.ac.jp/record/8550/files/A31912.pdf
|
|
1552
|
+
// 江田和司, 拡散性制御に基づく室内音響設計に向けた音場解析に関する研究, 2015
|
|
1553
|
+
// V: room size (m^3)
|
|
1554
|
+
// S: room surface area (m^2)
|
|
1555
|
+
// meanFreePath = 4V / S (m)
|
|
1556
|
+
// delay estimation using mean free path
|
|
1557
|
+
// t: degree Celsius, generally used 20
|
|
1558
|
+
// c: speed of sound = 331.5 + 0.61t = 331.5 * 0.61 * 20 = 343.7 (m/s)
|
|
1559
|
+
// delay = meanFreePath / c (s)
|
|
1560
|
+
// feedback equation
|
|
1561
|
+
// RT60 means that the energy is reduced to Math.pow(10, -6).
|
|
1562
|
+
// Since energy is proportional to the square of the amplitude,
|
|
1563
|
+
// the amplitude is reduced to Math.pow(10, -3).
|
|
1564
|
+
// When this is done through n feedbacks,
|
|
1565
|
+
// Math.pow(feedback, n) = Math.pow(10, -3)
|
|
1566
|
+
// Math.pow(feedback, RT60 / delay) = Math.pow(10, -3)
|
|
1567
|
+
// RT60 / delay * Math.log10(feedback) = -3
|
|
1568
|
+
// RT60 = -3 * delay / Math.log10(feedback)
|
|
1569
|
+
// feedback = Math.pow(10, -3 * delay / RT60)
|
|
1570
|
+
// delay estimation using ideal feedback
|
|
1571
|
+
// The structure of a concert hall is complex,
|
|
1572
|
+
// so estimates based on mean free path are unstable.
|
|
1573
|
+
// It is easier to determine the delay based on ideal feedback.
|
|
1574
|
+
// The average sound absorption coefficient
|
|
1575
|
+
// suitable for playing musical instruments is 0.18 to 0.28.
|
|
1576
|
+
// delay = -RT60 * Math.log10(feedback) / 3
|
|
1577
|
+
calcDelay(rt60, feedback) {
|
|
1578
|
+
return -rt60 * Math.log10(feedback) / 3;
|
|
1579
|
+
}
|
|
1580
|
+
handleChorusParameterSysEx(data) {
|
|
1581
|
+
switch (data[9]) {
|
|
1582
|
+
case 0:
|
|
1583
|
+
return this.setChorusType(data[10]);
|
|
1584
|
+
case 1:
|
|
1585
|
+
return this.setChorusModRate(data[10]);
|
|
1586
|
+
case 2:
|
|
1587
|
+
return this.setChorusModDepth(data[10]);
|
|
1588
|
+
case 3:
|
|
1589
|
+
return this.setChorusFeedback(data[10]);
|
|
1590
|
+
case 4:
|
|
1591
|
+
return this.setChorusSendToReverb(data[10]);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
setChorusType(type) {
|
|
1595
|
+
switch (type) {
|
|
1596
|
+
case 0:
|
|
1597
|
+
return this.setChorusParameter(3, 5, 0, 0);
|
|
1598
|
+
case 1:
|
|
1599
|
+
return this.setChorusParameter(9, 19, 5, 0);
|
|
1600
|
+
case 2:
|
|
1601
|
+
return this.setChorusParameter(3, 19, 8, 0);
|
|
1602
|
+
case 3:
|
|
1603
|
+
return this.setChorusParameter(9, 16, 16, 0);
|
|
1604
|
+
case 4:
|
|
1605
|
+
return this.setChorusParameter(2, 24, 64, 0);
|
|
1606
|
+
case 5:
|
|
1607
|
+
return this.setChorusParameter(1, 5, 112, 0);
|
|
1608
|
+
default:
|
|
1609
|
+
console.warn(`Unsupported Chorus Type: ${type}`);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
setChorusParameter(modRate, modDepth, feedback, sendToReverb) {
|
|
1613
|
+
this.setChorusModRate(modRate);
|
|
1614
|
+
this.setChorusModDepth(modDepth);
|
|
1615
|
+
this.setChorusFeedback(feedback);
|
|
1616
|
+
this.setChorusSendToReverb(sendToReverb);
|
|
1617
|
+
}
|
|
1618
|
+
setChorusModRate(value) {
|
|
1619
|
+
const now = this.audioContext.currentTime;
|
|
1620
|
+
const modRate = this.getChorusModRate(value);
|
|
1621
|
+
this.chorus.modRate = modRate;
|
|
1622
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1623
|
+
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1624
|
+
lfo.frequency.setValueAtTime(modRate, now);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
getChorusModRate(value) {
|
|
1628
|
+
return value * 0.122; // Hz
|
|
1629
|
+
}
|
|
1630
|
+
setChorusModDepth(value) {
|
|
1631
|
+
const now = this.audioContext.currentTime;
|
|
1632
|
+
const modDepth = this.getChorusModDepth(value);
|
|
1633
|
+
this.chorus.modDepth = modDepth;
|
|
1634
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1635
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1636
|
+
chorusEffect.lfoGain.gain
|
|
1637
|
+
.cancelScheduledValues(now)
|
|
1638
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
getChorusModDepth(value) {
|
|
1642
|
+
return (value + 1) / 3200; // second
|
|
1643
|
+
}
|
|
1644
|
+
setChorusFeedback(value) {
|
|
1645
|
+
const now = this.audioContext.currentTime;
|
|
1646
|
+
const feedback = this.getChorusFeedback(value);
|
|
1647
|
+
this.chorus.feedback = feedback;
|
|
1648
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1649
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1650
|
+
for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
|
|
1651
|
+
const feedbackGain = chorusEffect.feedbackGains[j];
|
|
1652
|
+
feedbackGain.gain
|
|
1653
|
+
.cancelScheduledValues(now)
|
|
1654
|
+
.setValueAtTime(feedback, now);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
getChorusFeedback(value) {
|
|
1659
|
+
return value * 0.00763;
|
|
1660
|
+
}
|
|
1661
|
+
setChorusSendToReverb(value) {
|
|
1662
|
+
const now = this.audioContext.currentTime;
|
|
1663
|
+
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1664
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1665
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1666
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1667
|
+
chorusEffect.sendGain.gain
|
|
1668
|
+
.cancelScheduledValues(now)
|
|
1669
|
+
.setValueAtTime(sendToReverb, now);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
getChorusSendToReverb(value) {
|
|
1673
|
+
return value * 0.00787;
|
|
1674
|
+
}
|
|
1466
1675
|
handleExclusiveMessage(data) {
|
|
1467
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1676
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1468
1677
|
}
|
|
1469
1678
|
handleSysEx(data) {
|
|
1470
1679
|
switch (data[0]) {
|
|
@@ -1498,8 +1707,8 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1498
1707
|
volume: 100 / 127,
|
|
1499
1708
|
pan: 64,
|
|
1500
1709
|
portamentoTime: 0,
|
|
1501
|
-
|
|
1502
|
-
|
|
1710
|
+
reverbSendLevel: 0,
|
|
1711
|
+
chorusSendLevel: 0,
|
|
1503
1712
|
bank: 121 * 128,
|
|
1504
1713
|
bankMSB: 121,
|
|
1505
1714
|
bankLSB: 0,
|
package/script/midy-GMLite.js
CHANGED
|
@@ -896,11 +896,11 @@ class MidyGMLite {
|
|
|
896
896
|
case 2: // GM System Off
|
|
897
897
|
break;
|
|
898
898
|
default:
|
|
899
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
899
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
900
900
|
}
|
|
901
901
|
break;
|
|
902
902
|
default:
|
|
903
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
903
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
GM1SystemOn() {
|
|
@@ -919,11 +919,11 @@ class MidyGMLite {
|
|
|
919
919
|
case 1:
|
|
920
920
|
return this.handleMasterVolumeSysEx(data);
|
|
921
921
|
default:
|
|
922
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
922
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
923
923
|
}
|
|
924
924
|
break;
|
|
925
925
|
default:
|
|
926
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
926
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
927
927
|
}
|
|
928
928
|
}
|
|
929
929
|
handleMasterVolumeSysEx(data) {
|
|
@@ -941,7 +941,7 @@ class MidyGMLite {
|
|
|
941
941
|
}
|
|
942
942
|
}
|
|
943
943
|
handleExclusiveMessage(data) {
|
|
944
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
944
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
945
945
|
}
|
|
946
946
|
handleSysEx(data) {
|
|
947
947
|
switch (data[0]) {
|