@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-GM2.js
CHANGED
|
@@ -82,7 +82,7 @@ export class MidyGM2 {
|
|
|
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 MidyGM2 {
|
|
|
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 MidyGM2 {
|
|
|
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 MidyGM2 {
|
|
|
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) {
|
|
@@ -685,12 +687,10 @@ export class MidyGM2 {
|
|
|
685
687
|
}
|
|
686
688
|
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
687
689
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
688
|
-
createSchroederReverb(audioContext, combDelays,
|
|
690
|
+
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
689
691
|
const input = new GainNode(audioContext);
|
|
690
692
|
const output = new GainNode(audioContext);
|
|
691
|
-
const mergerGain = new GainNode(audioContext
|
|
692
|
-
gain: 1 / (combDelays.length * 2),
|
|
693
|
-
});
|
|
693
|
+
const mergerGain = new GainNode(audioContext);
|
|
694
694
|
for (let i = 0; i < combDelays.length; i++) {
|
|
695
695
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
696
696
|
comb.connect(mergerGain);
|
|
@@ -703,62 +703,49 @@ export class MidyGM2 {
|
|
|
703
703
|
allpasses.at(-1).connect(output);
|
|
704
704
|
return { input, output };
|
|
705
705
|
}
|
|
706
|
-
createChorusEffect(audioContext
|
|
707
|
-
const
|
|
708
|
-
const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
|
|
709
|
-
const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
|
|
706
|
+
createChorusEffect(audioContext) {
|
|
707
|
+
const input = new GainNode(audioContext);
|
|
710
708
|
const output = new GainNode(audioContext);
|
|
711
|
-
const
|
|
709
|
+
const sendGain = new GainNode(audioContext);
|
|
710
|
+
const lfo = new OscillatorNode(audioContext, {
|
|
711
|
+
frequency: this.chorus.modRate,
|
|
712
|
+
});
|
|
713
|
+
const lfoGain = new GainNode(audioContext, {
|
|
714
|
+
gain: this.chorus.modDepth / 2,
|
|
715
|
+
});
|
|
716
|
+
const delayTimes = this.chorus.delayTimes;
|
|
712
717
|
const delayNodes = [];
|
|
713
|
-
const
|
|
714
|
-
for (let i = 0; i <
|
|
715
|
-
const
|
|
716
|
-
const delayTime = (i + 1) * delay + randomDelayFactor;
|
|
718
|
+
const feedbackGains = [];
|
|
719
|
+
for (let i = 0; i < delayTimes.length; i++) {
|
|
720
|
+
const delayTime = delayTimes[i];
|
|
717
721
|
const delayNode = new DelayNode(audioContext, {
|
|
718
|
-
maxDelayTime: delayTime
|
|
722
|
+
maxDelayTime: 0.1, // generally, 5ms < delayTime < 50ms
|
|
723
|
+
delayTime,
|
|
724
|
+
});
|
|
725
|
+
const feedbackGain = new GainNode(audioContext, {
|
|
726
|
+
gain: this.chorus.feedback,
|
|
719
727
|
});
|
|
720
|
-
const chorusGain = new GainNode(audioContext, { gain: baseGain });
|
|
721
728
|
delayNodes.push(delayNode);
|
|
722
|
-
|
|
729
|
+
feedbackGains.push(feedbackGain);
|
|
730
|
+
input.connect(delayNode);
|
|
723
731
|
lfoGain.connect(delayNode.delayTime);
|
|
724
|
-
delayNode.connect(
|
|
725
|
-
|
|
732
|
+
delayNode.connect(feedbackGain);
|
|
733
|
+
feedbackGain.connect(delayNode);
|
|
734
|
+
delayNode.connect(output);
|
|
726
735
|
}
|
|
736
|
+
output.connect(sendGain);
|
|
727
737
|
lfo.connect(lfoGain);
|
|
728
738
|
lfo.start();
|
|
729
739
|
return {
|
|
740
|
+
input,
|
|
741
|
+
output,
|
|
742
|
+
sendGain,
|
|
730
743
|
lfo,
|
|
731
744
|
lfoGain,
|
|
732
745
|
delayNodes,
|
|
733
|
-
|
|
734
|
-
output,
|
|
746
|
+
feedbackGains,
|
|
735
747
|
};
|
|
736
748
|
}
|
|
737
|
-
connectEffects(channel, gainNode) {
|
|
738
|
-
gainNode.connect(channel.merger);
|
|
739
|
-
channel.merger.connect(this.masterGain);
|
|
740
|
-
if (channel.reverbSendLevel === 0) {
|
|
741
|
-
if (channel.chorusSendLevel !== 0) { // chorus
|
|
742
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
743
|
-
channel.merger.connect(delayNode);
|
|
744
|
-
});
|
|
745
|
-
channel.chorusEffect.output.connect(this.masterGain);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
else {
|
|
749
|
-
if (channel.chorusSendLevel === 0) { // reverb
|
|
750
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
751
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
752
|
-
}
|
|
753
|
-
else { // reverb + chorus
|
|
754
|
-
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
755
|
-
channel.merger.connect(delayNode);
|
|
756
|
-
});
|
|
757
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
758
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
749
|
cbToRatio(cb) {
|
|
763
750
|
return Math.pow(10, cb / 200);
|
|
764
751
|
}
|
|
@@ -775,14 +762,10 @@ export class MidyGM2 {
|
|
|
775
762
|
return instrumentKey.playbackRate(noteNumber) *
|
|
776
763
|
Math.pow(2, semitoneOffset / 12);
|
|
777
764
|
}
|
|
778
|
-
setVolumeEnvelope(
|
|
779
|
-
const { instrumentKey, startTime
|
|
765
|
+
setVolumeEnvelope(note) {
|
|
766
|
+
const { instrumentKey, startTime } = note;
|
|
780
767
|
note.gainNode = new GainNode(this.audioContext, { gain: 0 });
|
|
781
|
-
|
|
782
|
-
if (volume === 0)
|
|
783
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
784
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
785
|
-
volume;
|
|
768
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
786
769
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
787
770
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
788
771
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
@@ -842,7 +825,7 @@ export class MidyGM2 {
|
|
|
842
825
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
843
826
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
844
827
|
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
845
|
-
this.setVolumeEnvelope(
|
|
828
|
+
this.setVolumeEnvelope(note);
|
|
846
829
|
this.setFilterEnvelope(channel, note);
|
|
847
830
|
if (channel.modulation > 0) {
|
|
848
831
|
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
@@ -878,7 +861,8 @@ export class MidyGM2 {
|
|
|
878
861
|
if (!instrumentKey)
|
|
879
862
|
return;
|
|
880
863
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
881
|
-
|
|
864
|
+
note.gainNode.connect(channel.gainL);
|
|
865
|
+
note.gainNode.connect(channel.gainR);
|
|
882
866
|
if (channel.sostenutoPedal) {
|
|
883
867
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
884
868
|
}
|
|
@@ -1021,13 +1005,13 @@ export class MidyGM2 {
|
|
|
1021
1005
|
}
|
|
1022
1006
|
}
|
|
1023
1007
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1024
|
-
const pitchBend = msb * 128 + lsb;
|
|
1008
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
1025
1009
|
this.setPitchBend(channelNumber, pitchBend);
|
|
1026
1010
|
}
|
|
1027
1011
|
setPitchBend(channelNumber, pitchBend) {
|
|
1028
1012
|
const channel = this.channels[channelNumber];
|
|
1029
1013
|
const prevPitchBend = channel.pitchBend;
|
|
1030
|
-
channel.pitchBend =
|
|
1014
|
+
channel.pitchBend = pitchBend / 8192;
|
|
1031
1015
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
1032
1016
|
channel.pitchBendRange * 100;
|
|
1033
1017
|
this.updateDetune(channel, detuneChange);
|
|
@@ -1162,17 +1146,30 @@ export class MidyGM2 {
|
|
|
1162
1146
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1163
1147
|
}
|
|
1164
1148
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1165
|
-
const now = this.audioContext.currentTime;
|
|
1166
1149
|
const channel = this.channels[channelNumber];
|
|
1167
|
-
const reverbEffect =
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1150
|
+
const reverbEffect = this.reverbEffect;
|
|
1151
|
+
if (0 < reverbSendLevel) {
|
|
1152
|
+
const now = this.audioContext.currentTime;
|
|
1153
|
+
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1154
|
+
reverbEffect.output.gain.cancelScheduledValues(now);
|
|
1155
|
+
reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1156
|
+
}
|
|
1157
|
+
else if (channel.reverbSendLevel !== 0) {
|
|
1158
|
+
channel.merger.disconnect(reverbEffect.input);
|
|
1159
|
+
}
|
|
1171
1160
|
}
|
|
1172
1161
|
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1173
1162
|
const channel = this.channels[channelNumber];
|
|
1174
|
-
|
|
1175
|
-
|
|
1163
|
+
const chorusEffect = this.chorusEffect;
|
|
1164
|
+
if (0 < chorusSendLevel) {
|
|
1165
|
+
const now = this.audioContext.currentTime;
|
|
1166
|
+
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1167
|
+
chorusEffect.output.gain.cancelScheduledValues(now);
|
|
1168
|
+
chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1169
|
+
}
|
|
1170
|
+
else if (channel.chorusSendLevel !== 0) {
|
|
1171
|
+
channel.merger.disconnect(chorusEffect.input);
|
|
1172
|
+
}
|
|
1176
1173
|
}
|
|
1177
1174
|
setSostenutoPedal(channelNumber, value) {
|
|
1178
1175
|
const isOn = value >= 64;
|
|
@@ -1337,7 +1334,7 @@ export class MidyGM2 {
|
|
|
1337
1334
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1338
1335
|
const activeNote = this.getActiveNote(noteList, now);
|
|
1339
1336
|
if (activeNote) {
|
|
1340
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1337
|
+
const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
|
|
1341
1338
|
promises.push(notePromise);
|
|
1342
1339
|
}
|
|
1343
1340
|
});
|
|
@@ -1404,7 +1401,7 @@ export class MidyGM2 {
|
|
|
1404
1401
|
case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1405
1402
|
return this.handleMasterCoarseTuningSysEx(data);
|
|
1406
1403
|
case 5:
|
|
1407
|
-
return this.
|
|
1404
|
+
return this.handleGlobalParameterControlSysEx(data);
|
|
1408
1405
|
default:
|
|
1409
1406
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1410
1407
|
}
|
|
@@ -1481,13 +1478,13 @@ export class MidyGM2 {
|
|
|
1481
1478
|
this.masterCoarseTuning = coarseTuning - 64;
|
|
1482
1479
|
}
|
|
1483
1480
|
}
|
|
1484
|
-
|
|
1485
|
-
if (data[
|
|
1486
|
-
switch (data[
|
|
1481
|
+
handleGlobalParameterControlSysEx(data) {
|
|
1482
|
+
if (data[7] === 1) {
|
|
1483
|
+
switch (data[8]) {
|
|
1487
1484
|
case 1:
|
|
1488
|
-
return this.
|
|
1485
|
+
return this.handleReverbParameterSysEx(data);
|
|
1489
1486
|
case 2:
|
|
1490
|
-
return this.
|
|
1487
|
+
return this.handleChorusParameterSysEx(data);
|
|
1491
1488
|
default:
|
|
1492
1489
|
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1493
1490
|
}
|
|
@@ -1496,21 +1493,19 @@ export class MidyGM2 {
|
|
|
1496
1493
|
console.warn(`Unsupported Global Parameter Control Message: ${data}`);
|
|
1497
1494
|
}
|
|
1498
1495
|
}
|
|
1499
|
-
|
|
1500
|
-
switch (data[
|
|
1496
|
+
handleReverbParameterSysEx(data) {
|
|
1497
|
+
switch (data[9]) {
|
|
1501
1498
|
case 0:
|
|
1502
|
-
return this.setReverbType(data[
|
|
1499
|
+
return this.setReverbType(data[10]);
|
|
1503
1500
|
case 1:
|
|
1504
|
-
return this.setReverbTime(data[
|
|
1501
|
+
return this.setReverbTime(data[10]);
|
|
1505
1502
|
}
|
|
1506
1503
|
}
|
|
1507
1504
|
setReverbType(type) {
|
|
1508
1505
|
this.reverb.time = this.getReverbTimeFromType(type);
|
|
1509
|
-
this.reverb.feedback = (type === 8) ? 0.
|
|
1510
|
-
const { audioContext,
|
|
1511
|
-
|
|
1512
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1513
|
-
}
|
|
1506
|
+
this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
|
|
1507
|
+
const { audioContext, options } = this;
|
|
1508
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1514
1509
|
}
|
|
1515
1510
|
getReverbTimeFromType(type) {
|
|
1516
1511
|
switch (type) {
|
|
@@ -1561,41 +1556,111 @@ export class MidyGM2 {
|
|
|
1561
1556
|
// RT60 = -3 * delay / Math.log10(feedback)
|
|
1562
1557
|
// feedback = Math.pow(10, -3 * delay / RT60)
|
|
1563
1558
|
// delay estimation using ideal feedback
|
|
1564
|
-
//
|
|
1565
|
-
//
|
|
1566
|
-
// It
|
|
1559
|
+
// The structure of a concert hall is complex,
|
|
1560
|
+
// so estimates based on mean free path are unstable.
|
|
1561
|
+
// It is easier to determine the delay based on ideal feedback.
|
|
1562
|
+
// The average sound absorption coefficient
|
|
1563
|
+
// suitable for playing musical instruments is 0.18 to 0.28.
|
|
1567
1564
|
// delay = -RT60 * Math.log10(feedback) / 3
|
|
1568
1565
|
calcDelay(rt60, feedback) {
|
|
1569
1566
|
return -rt60 * Math.log10(feedback) / 3;
|
|
1570
1567
|
}
|
|
1571
|
-
|
|
1572
|
-
switch (data[
|
|
1568
|
+
handleChorusParameterSysEx(data) {
|
|
1569
|
+
switch (data[9]) {
|
|
1573
1570
|
case 0:
|
|
1574
|
-
return this.setChorusType(data[
|
|
1571
|
+
return this.setChorusType(data[10]);
|
|
1575
1572
|
case 1:
|
|
1576
|
-
return this.setChorusModRate(data[
|
|
1573
|
+
return this.setChorusModRate(data[10]);
|
|
1577
1574
|
case 2:
|
|
1578
|
-
return this.setChorusModDepth(data[
|
|
1575
|
+
return this.setChorusModDepth(data[10]);
|
|
1579
1576
|
case 3:
|
|
1580
|
-
return this.setChorusFeedback(data[
|
|
1577
|
+
return this.setChorusFeedback(data[10]);
|
|
1581
1578
|
case 4:
|
|
1582
|
-
return this.setChorusSendToReverb(data[
|
|
1579
|
+
return this.setChorusSendToReverb(data[10]);
|
|
1583
1580
|
}
|
|
1584
1581
|
}
|
|
1585
1582
|
setChorusType(type) {
|
|
1586
|
-
|
|
1583
|
+
switch (type) {
|
|
1584
|
+
case 0:
|
|
1585
|
+
return this.setChorusParameter(3, 5, 0, 0);
|
|
1586
|
+
case 1:
|
|
1587
|
+
return this.setChorusParameter(9, 19, 5, 0);
|
|
1588
|
+
case 2:
|
|
1589
|
+
return this.setChorusParameter(3, 19, 8, 0);
|
|
1590
|
+
case 3:
|
|
1591
|
+
return this.setChorusParameter(9, 16, 16, 0);
|
|
1592
|
+
case 4:
|
|
1593
|
+
return this.setChorusParameter(2, 24, 64, 0);
|
|
1594
|
+
case 5:
|
|
1595
|
+
return this.setChorusParameter(1, 5, 112, 0);
|
|
1596
|
+
default:
|
|
1597
|
+
console.warn(`Unsupported Chorus Type: ${type}`);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
setChorusParameter(modRate, modDepth, feedback, sendToReverb) {
|
|
1601
|
+
this.setChorusModRate(modRate);
|
|
1602
|
+
this.setChorusModDepth(modDepth);
|
|
1603
|
+
this.setChorusFeedback(feedback);
|
|
1604
|
+
this.setChorusSendToReverb(sendToReverb);
|
|
1587
1605
|
}
|
|
1588
1606
|
setChorusModRate(value) {
|
|
1589
|
-
|
|
1607
|
+
const now = this.audioContext.currentTime;
|
|
1608
|
+
const modRate = this.getChorusModRate(value);
|
|
1609
|
+
this.chorus.modRate = modRate;
|
|
1610
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1611
|
+
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1612
|
+
lfo.frequency.setValueAtTime(modRate, now);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
getChorusModRate(value) {
|
|
1616
|
+
return value * 0.122; // Hz
|
|
1590
1617
|
}
|
|
1591
1618
|
setChorusModDepth(value) {
|
|
1592
|
-
|
|
1619
|
+
const now = this.audioContext.currentTime;
|
|
1620
|
+
const modDepth = this.getChorusModDepth(value);
|
|
1621
|
+
this.chorus.modDepth = modDepth;
|
|
1622
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1623
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1624
|
+
chorusEffect.lfoGain.gain
|
|
1625
|
+
.cancelScheduledValues(now)
|
|
1626
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
getChorusModDepth(value) {
|
|
1630
|
+
return (value + 1) / 3200; // second
|
|
1593
1631
|
}
|
|
1594
1632
|
setChorusFeedback(value) {
|
|
1595
|
-
|
|
1633
|
+
const now = this.audioContext.currentTime;
|
|
1634
|
+
const feedback = this.getChorusFeedback(value);
|
|
1635
|
+
this.chorus.feedback = feedback;
|
|
1636
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1637
|
+
const chorusEffect = this.channels[i].chorusEffect;
|
|
1638
|
+
for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
|
|
1639
|
+
const feedbackGain = chorusEffect.feedbackGains[j];
|
|
1640
|
+
feedbackGain.gain
|
|
1641
|
+
.cancelScheduledValues(now)
|
|
1642
|
+
.setValueAtTime(feedback, now);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
getChorusFeedback(value) {
|
|
1647
|
+
return value * 0.00763;
|
|
1596
1648
|
}
|
|
1597
1649
|
setChorusSendToReverb(value) {
|
|
1598
|
-
|
|
1650
|
+
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1651
|
+
if (0 < sendToReverb) {
|
|
1652
|
+
const now = this.audioContext.currentTime;
|
|
1653
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1654
|
+
this.chorusEffect.sendGain.gain
|
|
1655
|
+
.cancelScheduledValues(now)
|
|
1656
|
+
.setValueAtTime(sendToReverb, now);
|
|
1657
|
+
}
|
|
1658
|
+
else if (this.chorus.sendToReverb !== 0) {
|
|
1659
|
+
this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
getChorusSendToReverb(value) {
|
|
1663
|
+
return value * 0.00787;
|
|
1599
1664
|
}
|
|
1600
1665
|
handleExclusiveMessage(data) {
|
|
1601
1666
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -70,12 +70,11 @@ export class MidyGMLite {
|
|
|
70
70
|
currentTime(): number;
|
|
71
71
|
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
72
72
|
getActiveNote(noteList: any, time: any): any;
|
|
73
|
-
connectEffects(channel: any, gainNode: any): void;
|
|
74
73
|
cbToRatio(cb: any): number;
|
|
75
74
|
centToHz(cent: any): number;
|
|
76
75
|
calcSemitoneOffset(channel: any): number;
|
|
77
76
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
78
|
-
setVolumeEnvelope(
|
|
77
|
+
setVolumeEnvelope(note: any): void;
|
|
79
78
|
setFilterEnvelope(channel: any, note: any): void;
|
|
80
79
|
startModulation(channel: any, note: any, time: any): void;
|
|
81
80
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,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;IAuBhB,kBAAgC;IAChC,gBAA4C;
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,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;IAuBhB,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,yCAEC;IAED,mFAGC;IAED,mCAcC;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,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5+BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|
package/esm/midy-GMLite.js
CHANGED
|
@@ -144,8 +144,8 @@ export class MidyGMLite {
|
|
|
144
144
|
});
|
|
145
145
|
this.audioContext = audioContext;
|
|
146
146
|
this.masterGain = new GainNode(audioContext);
|
|
147
|
-
this.masterGain.connect(audioContext.destination);
|
|
148
147
|
this.channels = this.createChannels(audioContext);
|
|
148
|
+
this.masterGain.connect(audioContext.destination);
|
|
149
149
|
this.GM1SystemOn();
|
|
150
150
|
}
|
|
151
151
|
initSoundFontTable() {
|
|
@@ -189,6 +189,7 @@ export class MidyGMLite {
|
|
|
189
189
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
190
190
|
gainL.connect(merger, 0, 0);
|
|
191
191
|
gainR.connect(merger, 0, 1);
|
|
192
|
+
merger.connect(this.masterGain);
|
|
192
193
|
return {
|
|
193
194
|
gainL,
|
|
194
195
|
gainR,
|
|
@@ -491,10 +492,6 @@ export class MidyGMLite {
|
|
|
491
492
|
}
|
|
492
493
|
return noteList[0];
|
|
493
494
|
}
|
|
494
|
-
connectEffects(channel, gainNode) {
|
|
495
|
-
gainNode.connect(channel.merger);
|
|
496
|
-
merger.connect(this.masterGain);
|
|
497
|
-
}
|
|
498
495
|
cbToRatio(cb) {
|
|
499
496
|
return Math.pow(10, cb / 200);
|
|
500
497
|
}
|
|
@@ -508,14 +505,10 @@ export class MidyGMLite {
|
|
|
508
505
|
return instrumentKey.playbackRate(noteNumber) *
|
|
509
506
|
Math.pow(2, semitoneOffset / 12);
|
|
510
507
|
}
|
|
511
|
-
setVolumeEnvelope(
|
|
512
|
-
const { instrumentKey, startTime
|
|
508
|
+
setVolumeEnvelope(note) {
|
|
509
|
+
const { instrumentKey, startTime } = note;
|
|
513
510
|
note.gainNode = new GainNode(this.audioContext, { gain: 0 });
|
|
514
|
-
|
|
515
|
-
if (volume === 0)
|
|
516
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
517
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
518
|
-
volume;
|
|
511
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
519
512
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
520
513
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
521
514
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
@@ -575,7 +568,7 @@ export class MidyGMLite {
|
|
|
575
568
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
576
569
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
577
570
|
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
578
|
-
this.setVolumeEnvelope(
|
|
571
|
+
this.setVolumeEnvelope(note);
|
|
579
572
|
this.setFilterEnvelope(channel, note);
|
|
580
573
|
if (channel.modulation > 0) {
|
|
581
574
|
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
@@ -598,7 +591,8 @@ export class MidyGMLite {
|
|
|
598
591
|
if (!instrumentKey)
|
|
599
592
|
return;
|
|
600
593
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
601
|
-
|
|
594
|
+
note.gainNode.connect(channel.gainL);
|
|
595
|
+
note.gainNode.connect(channel.gainR);
|
|
602
596
|
const scheduledNotes = channel.scheduledNotes;
|
|
603
597
|
if (scheduledNotes.has(noteNumber)) {
|
|
604
598
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -701,13 +695,13 @@ export class MidyGMLite {
|
|
|
701
695
|
channel.program = program;
|
|
702
696
|
}
|
|
703
697
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
704
|
-
const pitchBend = msb * 128 + lsb;
|
|
698
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
705
699
|
this.setPitchBend(channelNumber, pitchBend);
|
|
706
700
|
}
|
|
707
701
|
setPitchBend(channelNumber, pitchBend) {
|
|
708
702
|
const channel = this.channels[channelNumber];
|
|
709
703
|
const prevPitchBend = channel.pitchBend;
|
|
710
|
-
channel.pitchBend =
|
|
704
|
+
channel.pitchBend = pitchBend / 8192;
|
|
711
705
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
712
706
|
channel.pitchBendRange * 100;
|
|
713
707
|
this.updateDetune(channel, detuneChange);
|
|
@@ -877,7 +871,7 @@ export class MidyGMLite {
|
|
|
877
871
|
channel.scheduledNotes.forEach((noteList) => {
|
|
878
872
|
const activeNote = this.getActiveNote(noteList, now);
|
|
879
873
|
if (activeNote) {
|
|
880
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
874
|
+
const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
|
|
881
875
|
promises.push(notePromise);
|
|
882
876
|
}
|
|
883
877
|
});
|
package/esm/midy.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ export class Midy {
|
|
|
59
59
|
modDepth: number;
|
|
60
60
|
feedback: number;
|
|
61
61
|
sendToReverb: number;
|
|
62
|
+
delayTimes: any[];
|
|
62
63
|
};
|
|
63
64
|
mono: boolean;
|
|
64
65
|
omni: boolean;
|
|
@@ -92,6 +93,19 @@ export class Midy {
|
|
|
92
93
|
};
|
|
93
94
|
masterGain: any;
|
|
94
95
|
channels: any[];
|
|
96
|
+
reverbEffect: {
|
|
97
|
+
input: any;
|
|
98
|
+
output: any;
|
|
99
|
+
};
|
|
100
|
+
chorusEffect: {
|
|
101
|
+
input: any;
|
|
102
|
+
output: any;
|
|
103
|
+
sendGain: any;
|
|
104
|
+
lfo: any;
|
|
105
|
+
lfoGain: any;
|
|
106
|
+
delayNodes: any[];
|
|
107
|
+
feedbackGains: any[];
|
|
108
|
+
};
|
|
95
109
|
initSoundFontTable(): any[];
|
|
96
110
|
addSoundFont(soundFont: any): void;
|
|
97
111
|
loadSoundFont(soundFontUrl: any): Promise<void>;
|
|
@@ -100,17 +114,6 @@ export class Midy {
|
|
|
100
114
|
gainL: any;
|
|
101
115
|
gainR: any;
|
|
102
116
|
merger: any;
|
|
103
|
-
reverbEffect: {
|
|
104
|
-
input: any;
|
|
105
|
-
output: any;
|
|
106
|
-
};
|
|
107
|
-
chorusEffect: {
|
|
108
|
-
lfo: any;
|
|
109
|
-
lfoGain: any;
|
|
110
|
-
delayNodes: any[];
|
|
111
|
-
chorusGains: any[];
|
|
112
|
-
output: any;
|
|
113
|
-
};
|
|
114
117
|
};
|
|
115
118
|
createChannels(audioContext: any): any[];
|
|
116
119
|
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
@@ -144,23 +147,24 @@ export class Midy {
|
|
|
144
147
|
createCombFilter(audioContext: any, input: any, delay: any, feedback: any): any;
|
|
145
148
|
createAllpassFilter(audioContext: any, input: any, delay: any, feedback: any): any;
|
|
146
149
|
generateDistributedArray(center: any, count: any, varianceRatio?: number, randomness?: number): any[];
|
|
147
|
-
createSchroederReverb(audioContext: any,
|
|
150
|
+
createSchroederReverb(audioContext: any, combFeedbacks: any, combDelays: any, allpassFeedbacks: any, allpassDelays: any): {
|
|
148
151
|
input: any;
|
|
149
152
|
output: any;
|
|
150
153
|
};
|
|
151
|
-
createChorusEffect(audioContext: any
|
|
154
|
+
createChorusEffect(audioContext: any): {
|
|
155
|
+
input: any;
|
|
156
|
+
output: any;
|
|
157
|
+
sendGain: any;
|
|
152
158
|
lfo: any;
|
|
153
159
|
lfoGain: any;
|
|
154
160
|
delayNodes: any[];
|
|
155
|
-
|
|
156
|
-
output: any;
|
|
161
|
+
feedbackGains: any[];
|
|
157
162
|
};
|
|
158
|
-
connectEffects(channel: any, gainNode: any): void;
|
|
159
163
|
cbToRatio(cb: any): number;
|
|
160
164
|
centToHz(cent: any): number;
|
|
161
165
|
calcSemitoneOffset(channel: any): any;
|
|
162
166
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
163
|
-
setVolumeEnvelope(
|
|
167
|
+
setVolumeEnvelope(note: any): void;
|
|
164
168
|
setFilterEnvelope(channel: any, note: any): void;
|
|
165
169
|
startModulation(channel: any, note: any, time: any): void;
|
|
166
170
|
startVibrato(channel: any, note: any, time: any): void;
|
|
@@ -236,19 +240,24 @@ export class Midy {
|
|
|
236
240
|
setMasterFineTuning(fineTuning: any): void;
|
|
237
241
|
handleMasterCoarseTuningSysEx(data: any): void;
|
|
238
242
|
setMasterCoarseTuning(coarseTuning: any): void;
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
handleGlobalParameterControlSysEx(data: any): void;
|
|
244
|
+
handleReverbParameterSysEx(data: any): void;
|
|
241
245
|
setReverbType(type: any): void;
|
|
242
246
|
getReverbTimeFromType(type: any): number | undefined;
|
|
243
247
|
setReverbTime(value: any): void;
|
|
244
248
|
getReverbTime(value: any): number;
|
|
245
249
|
calcDelay(rt60: any, feedback: any): number;
|
|
246
|
-
|
|
250
|
+
handleChorusParameterSysEx(data: any): void;
|
|
247
251
|
setChorusType(type: any): void;
|
|
252
|
+
setChorusParameter(modRate: any, modDepth: any, feedback: any, sendToReverb: any): void;
|
|
248
253
|
setChorusModRate(value: any): void;
|
|
254
|
+
getChorusModRate(value: any): number;
|
|
249
255
|
setChorusModDepth(value: any): void;
|
|
256
|
+
getChorusModDepth(value: any): number;
|
|
250
257
|
setChorusFeedback(value: any): void;
|
|
258
|
+
getChorusFeedback(value: any): number;
|
|
251
259
|
setChorusSendToReverb(value: any): void;
|
|
260
|
+
getChorusSendToReverb(value: any): number;
|
|
252
261
|
handleExclusiveMessage(data: any): void;
|
|
253
262
|
handleSysEx(data: any): void;
|
|
254
263
|
scheduleTask(callback: any, startTime: any): Promise<any>;
|
package/esm/midy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;
|
|
1
|
+
{"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
|