@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/esm/midy.js
CHANGED
|
@@ -64,12 +64,6 @@ export class Midy {
|
|
|
64
64
|
writable: true,
|
|
65
65
|
value: 0
|
|
66
66
|
});
|
|
67
|
-
Object.defineProperty(this, "reverbFactor", {
|
|
68
|
-
enumerable: true,
|
|
69
|
-
configurable: true,
|
|
70
|
-
writable: true,
|
|
71
|
-
value: 0.1
|
|
72
|
-
});
|
|
73
67
|
Object.defineProperty(this, "masterFineTuning", {
|
|
74
68
|
enumerable: true,
|
|
75
69
|
configurable: true,
|
|
@@ -82,6 +76,27 @@ export class Midy {
|
|
|
82
76
|
writable: true,
|
|
83
77
|
value: 0
|
|
84
78
|
}); // cb
|
|
79
|
+
Object.defineProperty(this, "reverb", {
|
|
80
|
+
enumerable: true,
|
|
81
|
+
configurable: true,
|
|
82
|
+
writable: true,
|
|
83
|
+
value: {
|
|
84
|
+
time: this.getReverbTime(64),
|
|
85
|
+
feedback: 0.25,
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(this, "chorus", {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
configurable: true,
|
|
91
|
+
writable: true,
|
|
92
|
+
value: {
|
|
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),
|
|
98
|
+
}
|
|
99
|
+
});
|
|
85
100
|
Object.defineProperty(this, "mono", {
|
|
86
101
|
enumerable: true,
|
|
87
102
|
configurable: true,
|
|
@@ -190,8 +205,19 @@ export class Midy {
|
|
|
190
205
|
writable: true,
|
|
191
206
|
value: {
|
|
192
207
|
reverbAlgorithm: (audioContext) => {
|
|
193
|
-
|
|
194
|
-
|
|
208
|
+
const { time: rt60, feedback } = this.reverb;
|
|
209
|
+
// const delay = this.calcDelay(rt60, feedback);
|
|
210
|
+
// const impulse = this.createConvolutionReverbImpulse(
|
|
211
|
+
// audioContext,
|
|
212
|
+
// rt60,
|
|
213
|
+
// delay,
|
|
214
|
+
// );
|
|
215
|
+
// return this.createConvolutionReverb(audioContext, impulse);
|
|
216
|
+
const combFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
217
|
+
const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
218
|
+
const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
219
|
+
const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
220
|
+
return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
|
|
195
221
|
},
|
|
196
222
|
}
|
|
197
223
|
});
|
|
@@ -596,12 +622,7 @@ export class Midy {
|
|
|
596
622
|
}
|
|
597
623
|
return noteList[0];
|
|
598
624
|
}
|
|
599
|
-
|
|
600
|
-
const { decay = 0.8, preDecay = 0, } = options;
|
|
601
|
-
const input = new GainNode(audioContext);
|
|
602
|
-
const output = new GainNode(audioContext);
|
|
603
|
-
const dryGain = new GainNode(audioContext);
|
|
604
|
-
const wetGain = new GainNode(audioContext);
|
|
625
|
+
createConvolutionReverbImpulse(audioContext, decay, preDecay) {
|
|
605
626
|
const sampleRate = audioContext.sampleRate;
|
|
606
627
|
const length = sampleRate * decay;
|
|
607
628
|
const impulse = new AudioBuffer({
|
|
@@ -621,18 +642,17 @@ export class Midy {
|
|
|
621
642
|
channelData[i] = (Math.random() * 2 - 1) * attenuation;
|
|
622
643
|
}
|
|
623
644
|
}
|
|
645
|
+
return impulse;
|
|
646
|
+
}
|
|
647
|
+
createConvolutionReverb(audioContext, impulse) {
|
|
648
|
+
const output = new GainNode(audioContext);
|
|
624
649
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
625
650
|
buffer: impulse,
|
|
626
651
|
});
|
|
627
|
-
|
|
628
|
-
convolverNode.connect(wetGain);
|
|
629
|
-
wetGain.connect(output);
|
|
630
|
-
dryGain.connect(output);
|
|
652
|
+
convolverNode.connect(output);
|
|
631
653
|
return {
|
|
632
|
-
input,
|
|
654
|
+
input: convolverNode,
|
|
633
655
|
output,
|
|
634
|
-
dryGain,
|
|
635
|
-
wetGain,
|
|
636
656
|
convolverNode,
|
|
637
657
|
};
|
|
638
658
|
}
|
|
@@ -660,17 +680,22 @@ export class Midy {
|
|
|
660
680
|
delayNode.connect(passGain);
|
|
661
681
|
return passGain;
|
|
662
682
|
}
|
|
683
|
+
generateDistributedArray(center, count, varianceRatio = 0.1, randomness = 0.05) {
|
|
684
|
+
const variance = center * varianceRatio;
|
|
685
|
+
const array = new Array(count);
|
|
686
|
+
for (let i = 0; i < count; i++) {
|
|
687
|
+
const fraction = i / (count - 1 || 1);
|
|
688
|
+
const value = center - variance + fraction * 2 * variance;
|
|
689
|
+
array[i] = value * (1 - (Math.random() * 2 - 1) * randomness);
|
|
690
|
+
}
|
|
691
|
+
return array;
|
|
692
|
+
}
|
|
663
693
|
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
664
694
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
665
|
-
createSchroederReverb(audioContext,
|
|
666
|
-
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;
|
|
695
|
+
createSchroederReverb(audioContext, combDelays, combFeedbacks, allpassDelays, allpassFeedbacks) {
|
|
667
696
|
const input = new GainNode(audioContext);
|
|
668
697
|
const output = new GainNode(audioContext);
|
|
669
|
-
const mergerGain = new GainNode(audioContext
|
|
670
|
-
gain: 1 / (combDelays.length * 2),
|
|
671
|
-
});
|
|
672
|
-
const dryGain = new GainNode(audioContext, { gain: 1 - mix });
|
|
673
|
-
const wetGain = new GainNode(audioContext, { gain: mix });
|
|
698
|
+
const mergerGain = new GainNode(audioContext);
|
|
674
699
|
for (let i = 0; i < combDelays.length; i++) {
|
|
675
700
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
676
701
|
comb.connect(mergerGain);
|
|
@@ -680,68 +705,65 @@ export class Midy {
|
|
|
680
705
|
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
681
706
|
allpasses.push(allpass);
|
|
682
707
|
}
|
|
683
|
-
allpasses.at(-1).connect(
|
|
684
|
-
input
|
|
685
|
-
dryGain.connect(output);
|
|
686
|
-
wetGain.connect(output);
|
|
687
|
-
return { input, output, dryGain, wetGain };
|
|
708
|
+
allpasses.at(-1).connect(output);
|
|
709
|
+
return { input, output };
|
|
688
710
|
}
|
|
689
|
-
createChorusEffect(audioContext
|
|
690
|
-
const
|
|
691
|
-
const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
|
|
692
|
-
const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
|
|
711
|
+
createChorusEffect(audioContext) {
|
|
712
|
+
const input = new GainNode(audioContext);
|
|
693
713
|
const output = new GainNode(audioContext);
|
|
694
|
-
const
|
|
714
|
+
const sendGain = new GainNode(audioContext);
|
|
715
|
+
const lfo = new OscillatorNode(audioContext, {
|
|
716
|
+
frequency: this.chorus.modRate,
|
|
717
|
+
});
|
|
718
|
+
const lfoGain = new GainNode(audioContext, {
|
|
719
|
+
gain: this.chorus.modDepth / 2,
|
|
720
|
+
});
|
|
721
|
+
const delayTimes = this.chorus.delayTimes;
|
|
695
722
|
const delayNodes = [];
|
|
696
|
-
const
|
|
697
|
-
for (let i = 0; i <
|
|
698
|
-
const
|
|
699
|
-
const delayTime = (i + 1) * delay + randomDelayFactor;
|
|
723
|
+
const feedbackGains = [];
|
|
724
|
+
for (let i = 0; i < delayTimes.length; i++) {
|
|
725
|
+
const delayTime = delayTimes[i];
|
|
700
726
|
const delayNode = new DelayNode(audioContext, {
|
|
701
|
-
maxDelayTime: delayTime
|
|
727
|
+
maxDelayTime: 0.1, // generally, 5ms < delayTime < 50ms
|
|
728
|
+
delayTime,
|
|
729
|
+
});
|
|
730
|
+
const feedbackGain = new GainNode(audioContext, {
|
|
731
|
+
gain: this.chorus.feedback,
|
|
702
732
|
});
|
|
703
|
-
const chorusGain = new GainNode(audioContext, { gain: baseGain });
|
|
704
733
|
delayNodes.push(delayNode);
|
|
705
|
-
|
|
734
|
+
feedbackGains.push(feedbackGain);
|
|
735
|
+
input.connect(delayNode);
|
|
706
736
|
lfoGain.connect(delayNode.delayTime);
|
|
707
|
-
delayNode.connect(
|
|
708
|
-
|
|
737
|
+
delayNode.connect(feedbackGain);
|
|
738
|
+
feedbackGain.connect(delayNode);
|
|
739
|
+
delayNode.connect(output);
|
|
709
740
|
}
|
|
741
|
+
output.connect(sendGain);
|
|
710
742
|
lfo.connect(lfoGain);
|
|
711
743
|
lfo.start();
|
|
712
744
|
return {
|
|
745
|
+
input,
|
|
746
|
+
output,
|
|
747
|
+
sendGain,
|
|
713
748
|
lfo,
|
|
714
749
|
lfoGain,
|
|
715
750
|
delayNodes,
|
|
716
|
-
|
|
717
|
-
output,
|
|
751
|
+
feedbackGains,
|
|
718
752
|
};
|
|
719
753
|
}
|
|
720
754
|
connectEffects(channel, gainNode) {
|
|
721
755
|
gainNode.connect(channel.merger);
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
else { // chorus
|
|
727
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
728
|
-
channel.merger.connect(delayNode);
|
|
729
|
-
});
|
|
730
|
-
channel.chorusEffect.output.connect(this.masterGain);
|
|
731
|
-
}
|
|
756
|
+
channel.merger.connect(this.masterGain);
|
|
757
|
+
if (0 < channel.reverbSendLevel) {
|
|
758
|
+
channel.merger.connect(channel.reverbEffect.input);
|
|
759
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
732
760
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
740
|
-
channel.merger.connect(delayNode);
|
|
741
|
-
});
|
|
742
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
743
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
744
|
-
}
|
|
761
|
+
if (0 < channel.chorusSendLevel) {
|
|
762
|
+
channel.merger.connect(channel.chorusEffect.input);
|
|
763
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
764
|
+
}
|
|
765
|
+
if (0 < this.chorus.sendToReverb) {
|
|
766
|
+
channel.chorusEffect.sendGain.connect(channel.reverbEffect.input);
|
|
745
767
|
}
|
|
746
768
|
}
|
|
747
769
|
cbToRatio(cb) {
|
|
@@ -1187,23 +1209,25 @@ export class Midy {
|
|
|
1187
1209
|
this.releaseSustainPedal(channelNumber, value);
|
|
1188
1210
|
}
|
|
1189
1211
|
}
|
|
1212
|
+
// TODO
|
|
1190
1213
|
setPortamento(channelNumber, value) {
|
|
1191
1214
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1192
1215
|
}
|
|
1193
|
-
setReverbSendLevel(channelNumber,
|
|
1216
|
+
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1194
1217
|
const now = this.audioContext.currentTime;
|
|
1195
1218
|
const channel = this.channels[channelNumber];
|
|
1196
1219
|
const reverbEffect = channel.reverbEffect;
|
|
1197
|
-
channel.
|
|
1198
|
-
reverbEffect.
|
|
1199
|
-
reverbEffect.
|
|
1200
|
-
reverbEffect.wetGain.gain.cancelScheduledValues(now);
|
|
1201
|
-
reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
|
|
1220
|
+
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1221
|
+
reverbEffect.output.gain.cancelScheduledValues(now);
|
|
1222
|
+
reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1202
1223
|
}
|
|
1203
|
-
setChorusSendLevel(channelNumber,
|
|
1224
|
+
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1225
|
+
const now = this.audioContext.currentTime;
|
|
1204
1226
|
const channel = this.channels[channelNumber];
|
|
1205
|
-
|
|
1206
|
-
channel.
|
|
1227
|
+
const chorusEffect = channel.chorusEffect;
|
|
1228
|
+
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1229
|
+
chorusEffect.output.gain.cancelScheduledValues(now);
|
|
1230
|
+
chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1207
1231
|
}
|
|
1208
1232
|
setSostenutoPedal(channelNumber, value) {
|
|
1209
1233
|
const isOn = value >= 64;
|
|
@@ -1427,11 +1451,11 @@ export class Midy {
|
|
|
1427
1451
|
this.GM2SystemOn();
|
|
1428
1452
|
break;
|
|
1429
1453
|
default:
|
|
1430
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1454
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1431
1455
|
}
|
|
1432
1456
|
break;
|
|
1433
1457
|
default:
|
|
1434
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1458
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1435
1459
|
}
|
|
1436
1460
|
}
|
|
1437
1461
|
GM1SystemOn() {
|
|
@@ -1462,9 +1486,10 @@ export class Midy {
|
|
|
1462
1486
|
return this.handleMasterFineTuningSysEx(data);
|
|
1463
1487
|
case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1464
1488
|
return this.handleMasterCoarseTuningSysEx(data);
|
|
1465
|
-
|
|
1489
|
+
case 5:
|
|
1490
|
+
return this.handleGlobalParameterControlSysEx(data);
|
|
1466
1491
|
default:
|
|
1467
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1492
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1468
1493
|
}
|
|
1469
1494
|
break;
|
|
1470
1495
|
case 8:
|
|
@@ -1473,7 +1498,7 @@ export class Midy {
|
|
|
1473
1498
|
// // TODO
|
|
1474
1499
|
// return this.handleScaleOctaveTuning1ByteFormat();
|
|
1475
1500
|
default:
|
|
1476
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1501
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1477
1502
|
}
|
|
1478
1503
|
break;
|
|
1479
1504
|
case 9:
|
|
@@ -1485,7 +1510,7 @@ export class Midy {
|
|
|
1485
1510
|
// // TODO
|
|
1486
1511
|
// return this.setControlChange();
|
|
1487
1512
|
default:
|
|
1488
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1513
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1489
1514
|
}
|
|
1490
1515
|
break;
|
|
1491
1516
|
case 10:
|
|
@@ -1494,11 +1519,11 @@ export class Midy {
|
|
|
1494
1519
|
// // TODO
|
|
1495
1520
|
// return this.handleKeyBasedInstrumentControl();
|
|
1496
1521
|
default:
|
|
1497
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1522
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1498
1523
|
}
|
|
1499
1524
|
break;
|
|
1500
1525
|
default:
|
|
1501
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1526
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1502
1527
|
}
|
|
1503
1528
|
}
|
|
1504
1529
|
handleMasterVolumeSysEx(data) {
|
|
@@ -1539,8 +1564,192 @@ export class Midy {
|
|
|
1539
1564
|
this.masterCoarseTuning = coarseTuning - 64;
|
|
1540
1565
|
}
|
|
1541
1566
|
}
|
|
1567
|
+
handleGlobalParameterControlSysEx(data) {
|
|
1568
|
+
if (data[7] === 1) {
|
|
1569
|
+
switch (data[8]) {
|
|
1570
|
+
case 1:
|
|
1571
|
+
return this.handleReverbParameterSysEx(data);
|
|
1572
|
+
case 2:
|
|
1573
|
+
return this.handleChorusParameterSysEx(data);
|
|
1574
|
+
default:
|
|
1575
|
+
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
else {
|
|
1579
|
+
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
handleReverbParameterSysEx(data) {
|
|
1583
|
+
switch (data[9]) {
|
|
1584
|
+
case 0:
|
|
1585
|
+
return this.setReverbType(data[10]);
|
|
1586
|
+
case 1:
|
|
1587
|
+
return this.setReverbTime(data[10]);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
setReverbType(type) {
|
|
1591
|
+
this.reverb.time = this.getReverbTimeFromType(type);
|
|
1592
|
+
this.reverb.feedback = (type === 8) ? 0.1 : 0.2;
|
|
1593
|
+
const { audioContext, channels, options } = this;
|
|
1594
|
+
for (let i = 0; i < channels.length; i++) {
|
|
1595
|
+
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
getReverbTimeFromType(type) {
|
|
1599
|
+
switch (type) {
|
|
1600
|
+
case 0:
|
|
1601
|
+
return this.getReverbTime(44);
|
|
1602
|
+
case 1:
|
|
1603
|
+
return this.getReverbTime(50);
|
|
1604
|
+
case 2:
|
|
1605
|
+
return this.getReverbTime(56);
|
|
1606
|
+
case 3:
|
|
1607
|
+
return this.getReverbTime(64);
|
|
1608
|
+
case 4:
|
|
1609
|
+
return this.getReverbTime(64);
|
|
1610
|
+
case 8:
|
|
1611
|
+
return this.getReverbTime(50);
|
|
1612
|
+
default:
|
|
1613
|
+
console.warn(`Unsupported Reverb Time: ${type}`);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
setReverbTime(value) {
|
|
1617
|
+
this.reverb.time = this.getReverbTime(value);
|
|
1618
|
+
const { audioContext, channels, options } = this;
|
|
1619
|
+
for (let i = 0; i < channels.length; i++) {
|
|
1620
|
+
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
getReverbTime(value) {
|
|
1624
|
+
return Math.pow(Math.E, (value - 40) * 0.025);
|
|
1625
|
+
}
|
|
1626
|
+
// mean free path equation
|
|
1627
|
+
// https://repository.dl.itc.u-tokyo.ac.jp/record/8550/files/A31912.pdf
|
|
1628
|
+
// 江田和司, 拡散性制御に基づく室内音響設計に向けた音場解析に関する研究, 2015
|
|
1629
|
+
// V: room size (m^3)
|
|
1630
|
+
// S: room surface area (m^2)
|
|
1631
|
+
// meanFreePath = 4V / S (m)
|
|
1632
|
+
// delay estimation using mean free path
|
|
1633
|
+
// t: degree Celsius, generally used 20
|
|
1634
|
+
// c: speed of sound = 331.5 + 0.61t = 331.5 * 0.61 * 20 = 343.7 (m/s)
|
|
1635
|
+
// delay = meanFreePath / c (s)
|
|
1636
|
+
// feedback equation
|
|
1637
|
+
// RT60 means that the energy is reduced to Math.pow(10, -6).
|
|
1638
|
+
// Since energy is proportional to the square of the amplitude,
|
|
1639
|
+
// the amplitude is reduced to Math.pow(10, -3).
|
|
1640
|
+
// When this is done through n feedbacks,
|
|
1641
|
+
// Math.pow(feedback, n) = Math.pow(10, -3)
|
|
1642
|
+
// Math.pow(feedback, RT60 / delay) = Math.pow(10, -3)
|
|
1643
|
+
// RT60 / delay * Math.log10(feedback) = -3
|
|
1644
|
+
// RT60 = -3 * delay / Math.log10(feedback)
|
|
1645
|
+
// feedback = Math.pow(10, -3 * delay / RT60)
|
|
1646
|
+
// delay estimation using ideal feedback
|
|
1647
|
+
// The structure of a concert hall is complex,
|
|
1648
|
+
// so estimates based on mean free path are unstable.
|
|
1649
|
+
// It is easier to determine the delay based on ideal feedback.
|
|
1650
|
+
// The average sound absorption coefficient
|
|
1651
|
+
// suitable for playing musical instruments is 0.18 to 0.28.
|
|
1652
|
+
// delay = -RT60 * Math.log10(feedback) / 3
|
|
1653
|
+
calcDelay(rt60, feedback) {
|
|
1654
|
+
return -rt60 * Math.log10(feedback) / 3;
|
|
1655
|
+
}
|
|
1656
|
+
handleChorusParameterSysEx(data) {
|
|
1657
|
+
switch (data[9]) {
|
|
1658
|
+
case 0:
|
|
1659
|
+
return this.setChorusType(data[10]);
|
|
1660
|
+
case 1:
|
|
1661
|
+
return this.setChorusModRate(data[10]);
|
|
1662
|
+
case 2:
|
|
1663
|
+
return this.setChorusModDepth(data[10]);
|
|
1664
|
+
case 3:
|
|
1665
|
+
return this.setChorusFeedback(data[10]);
|
|
1666
|
+
case 4:
|
|
1667
|
+
return this.setChorusSendToReverb(data[10]);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
setChorusType(type) {
|
|
1671
|
+
switch (type) {
|
|
1672
|
+
case 0:
|
|
1673
|
+
return this.setChorusParameter(3, 5, 0, 0);
|
|
1674
|
+
case 1:
|
|
1675
|
+
return this.setChorusParameter(9, 19, 5, 0);
|
|
1676
|
+
case 2:
|
|
1677
|
+
return this.setChorusParameter(3, 19, 8, 0);
|
|
1678
|
+
case 3:
|
|
1679
|
+
return this.setChorusParameter(9, 16, 16, 0);
|
|
1680
|
+
case 4:
|
|
1681
|
+
return this.setChorusParameter(2, 24, 64, 0);
|
|
1682
|
+
case 5:
|
|
1683
|
+
return this.setChorusParameter(1, 5, 112, 0);
|
|
1684
|
+
default:
|
|
1685
|
+
console.warn(`Unsupported Chorus Type: ${type}`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
setChorusParameter(modRate, modDepth, feedback, sendToReverb) {
|
|
1689
|
+
this.setChorusModRate(modRate);
|
|
1690
|
+
this.setChorusModDepth(modDepth);
|
|
1691
|
+
this.setChorusFeedback(feedback);
|
|
1692
|
+
this.setChorusSendToReverb(sendToReverb);
|
|
1693
|
+
}
|
|
1694
|
+
setChorusModRate(value) {
|
|
1695
|
+
const now = this.audioContext.currentTime;
|
|
1696
|
+
const modRate = this.getChorusModRate(value);
|
|
1697
|
+
this.chorus.modRate = modRate;
|
|
1698
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1699
|
+
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1700
|
+
lfo.frequency.setValueAtTime(modRate, now);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
getChorusModRate(value) {
|
|
1704
|
+
return value * 0.122; // Hz
|
|
1705
|
+
}
|
|
1706
|
+
setChorusModDepth(value) {
|
|
1707
|
+
const now = this.audioContext.currentTime;
|
|
1708
|
+
const modDepth = this.getChorusModDepth(value);
|
|
1709
|
+
this.chorus.modDepth = modDepth;
|
|
1710
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1711
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1712
|
+
chorusEffect.lfoGain.gain
|
|
1713
|
+
.cancelScheduledValues(now)
|
|
1714
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
getChorusModDepth(value) {
|
|
1718
|
+
return (value + 1) / 3200; // second
|
|
1719
|
+
}
|
|
1720
|
+
setChorusFeedback(value) {
|
|
1721
|
+
const now = this.audioContext.currentTime;
|
|
1722
|
+
const feedback = this.getChorusFeedback(value);
|
|
1723
|
+
this.chorus.feedback = feedback;
|
|
1724
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1725
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1726
|
+
for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
|
|
1727
|
+
const feedbackGain = chorusEffect.feedbackGains[j];
|
|
1728
|
+
feedbackGain.gain
|
|
1729
|
+
.cancelScheduledValues(now)
|
|
1730
|
+
.setValueAtTime(feedback, now);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
getChorusFeedback(value) {
|
|
1735
|
+
return value * 0.00763;
|
|
1736
|
+
}
|
|
1737
|
+
setChorusSendToReverb(value) {
|
|
1738
|
+
const now = this.audioContext.currentTime;
|
|
1739
|
+
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1740
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1741
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1742
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1743
|
+
chorusEffect.sendGain.gain
|
|
1744
|
+
.cancelScheduledValues(now)
|
|
1745
|
+
.setValueAtTime(sendToReverb, now);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
getChorusSendToReverb(value) {
|
|
1749
|
+
return value * 0.00787;
|
|
1750
|
+
}
|
|
1542
1751
|
handleExclusiveMessage(data) {
|
|
1543
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1752
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1544
1753
|
}
|
|
1545
1754
|
handleSysEx(data) {
|
|
1546
1755
|
switch (data[0]) {
|
|
@@ -1573,8 +1782,8 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1573
1782
|
volume: 100 / 127,
|
|
1574
1783
|
pan: 64,
|
|
1575
1784
|
portamentoTime: 0,
|
|
1576
|
-
|
|
1577
|
-
|
|
1785
|
+
reverbSendLevel: 0,
|
|
1786
|
+
chorusSendLevel: 0,
|
|
1578
1787
|
vibratoRate: 5,
|
|
1579
1788
|
vibratoDepth: 0.5,
|
|
1580
1789
|
vibratoDelay: 2.5,
|
package/package.json
CHANGED
package/script/midy-GM1.d.ts
CHANGED
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;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED
|
|
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;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAcC;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,kDAGC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,iDAiBC;IAED,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;AAnjCD;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
|
@@ -192,14 +192,10 @@ 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
|
-
const reverbEffect = this.createConvolutionReverb(audioContext);
|
|
196
|
-
const chorusEffect = this.createChorusEffect(audioContext);
|
|
197
195
|
return {
|
|
198
196
|
gainL,
|
|
199
197
|
gainR,
|
|
200
198
|
merger,
|
|
201
|
-
reverbEffect,
|
|
202
|
-
chorusEffect,
|
|
203
199
|
};
|
|
204
200
|
}
|
|
205
201
|
createChannels(audioContext) {
|
|
@@ -959,11 +955,11 @@ class MidyGM1 {
|
|
|
959
955
|
case 2: // GM System Off
|
|
960
956
|
break;
|
|
961
957
|
default:
|
|
962
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
958
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
963
959
|
}
|
|
964
960
|
break;
|
|
965
961
|
default:
|
|
966
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
962
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
967
963
|
}
|
|
968
964
|
}
|
|
969
965
|
GM1SystemOn() {
|
|
@@ -982,11 +978,11 @@ class MidyGM1 {
|
|
|
982
978
|
case 1:
|
|
983
979
|
return this.handleMasterVolumeSysEx(data);
|
|
984
980
|
default:
|
|
985
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
981
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
986
982
|
}
|
|
987
983
|
break;
|
|
988
984
|
default:
|
|
989
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
985
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
990
986
|
}
|
|
991
987
|
}
|
|
992
988
|
handleMasterVolumeSysEx(data) {
|
|
@@ -1004,7 +1000,7 @@ class MidyGM1 {
|
|
|
1004
1000
|
}
|
|
1005
1001
|
}
|
|
1006
1002
|
handleExclusiveMessage(data) {
|
|
1007
|
-
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1003
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1008
1004
|
}
|
|
1009
1005
|
handleSysEx(data) {
|
|
1010
1006
|
switch (data[0]) {
|