@marmooo/midy 0.2.2 → 0.2.4
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/README.md +3 -5
- package/esm/midy-GM1.d.ts +5 -4
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +31 -25
- package/esm/midy-GM2.d.ts +19 -22
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +148 -119
- package/esm/midy-GMLite.d.ts +6 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +28 -21
- package/esm/midy.d.ts +21 -22
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +229 -139
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +5 -4
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +31 -25
- package/script/midy-GM2.d.ts +19 -22
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +148 -119
- package/script/midy-GMLite.d.ts +6 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +28 -21
- package/script/midy.d.ts +21 -22
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +229 -139
package/esm/midy-GM2.js
CHANGED
|
@@ -97,7 +97,6 @@ class Note {
|
|
|
97
97
|
const defaultControllerState = {
|
|
98
98
|
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
99
99
|
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
100
|
-
polyPressure: { type: 10, defaultValue: 0 },
|
|
101
100
|
channelPressure: { type: 13, defaultValue: 0 },
|
|
102
101
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
103
102
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
@@ -359,15 +358,15 @@ export class MidyGM2 {
|
|
|
359
358
|
});
|
|
360
359
|
this.audioContext = audioContext;
|
|
361
360
|
this.options = { ...this.defaultOptions, ...options };
|
|
362
|
-
this.
|
|
361
|
+
this.masterVolume = new GainNode(audioContext);
|
|
363
362
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
364
363
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
365
364
|
this.channels = this.createChannels(audioContext);
|
|
366
365
|
this.reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
367
366
|
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
368
|
-
this.chorusEffect.output.connect(this.
|
|
369
|
-
this.reverbEffect.output.connect(this.
|
|
370
|
-
this.
|
|
367
|
+
this.chorusEffect.output.connect(this.masterVolume);
|
|
368
|
+
this.reverbEffect.output.connect(this.masterVolume);
|
|
369
|
+
this.masterVolume.connect(audioContext.destination);
|
|
371
370
|
this.GM2SystemOn();
|
|
372
371
|
}
|
|
373
372
|
initSoundFontTable() {
|
|
@@ -413,7 +412,7 @@ export class MidyGM2 {
|
|
|
413
412
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
414
413
|
gainL.connect(merger, 0, 0);
|
|
415
414
|
gainR.connect(merger, 0, 1);
|
|
416
|
-
merger.connect(this.
|
|
415
|
+
merger.connect(this.masterVolume);
|
|
417
416
|
return {
|
|
418
417
|
gainL,
|
|
419
418
|
gainR,
|
|
@@ -425,12 +424,10 @@ export class MidyGM2 {
|
|
|
425
424
|
return {
|
|
426
425
|
...this.constructor.channelSettings,
|
|
427
426
|
state: new ControllerState(),
|
|
427
|
+
controlTable: this.initControlTable(),
|
|
428
428
|
...this.setChannelAudioNodes(audioContext),
|
|
429
429
|
scheduledNotes: new Map(),
|
|
430
430
|
sostenutoNotes: new Map(),
|
|
431
|
-
channelPressure: {
|
|
432
|
-
...this.constructor.controllerDestinationSettings,
|
|
433
|
-
},
|
|
434
431
|
};
|
|
435
432
|
});
|
|
436
433
|
return channels;
|
|
@@ -932,28 +929,31 @@ export class MidyGM2 {
|
|
|
932
929
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
933
930
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
934
931
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
935
|
-
const pressureDepth = (channel.
|
|
932
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
936
933
|
const pressure = pressureDepth * channel.state.channelPressure;
|
|
937
934
|
return tuning + pitch + pressure;
|
|
938
935
|
}
|
|
939
936
|
calcNoteDetune(channel, note) {
|
|
940
937
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
941
938
|
}
|
|
942
|
-
|
|
943
|
-
const now = this.audioContext.currentTime;
|
|
939
|
+
updateChannelDetune(channel) {
|
|
944
940
|
channel.scheduledNotes.forEach((noteList) => {
|
|
945
941
|
for (let i = 0; i < noteList.length; i++) {
|
|
946
942
|
const note = noteList[i];
|
|
947
943
|
if (!note)
|
|
948
944
|
continue;
|
|
949
|
-
|
|
950
|
-
const detune = channel.detune + noteDetune;
|
|
951
|
-
note.bufferSource.detune
|
|
952
|
-
.cancelScheduledValues(now)
|
|
953
|
-
.setValueAtTime(detune, now);
|
|
945
|
+
this.updateDetune(channel, note, 0);
|
|
954
946
|
}
|
|
955
947
|
});
|
|
956
948
|
}
|
|
949
|
+
updateDetune(channel, note, pressure) {
|
|
950
|
+
const now = this.audioContext.currentTime;
|
|
951
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
952
|
+
const detune = channel.detune + noteDetune + pressure;
|
|
953
|
+
note.bufferSource.detune
|
|
954
|
+
.cancelScheduledValues(now)
|
|
955
|
+
.setValueAtTime(detune, now);
|
|
956
|
+
}
|
|
957
957
|
getPortamentoTime(channel) {
|
|
958
958
|
const factor = 5 * Math.log(10) / 127;
|
|
959
959
|
const time = channel.state.portamentoTime;
|
|
@@ -971,14 +971,11 @@ export class MidyGM2 {
|
|
|
971
971
|
.setValueAtTime(0, volDelay)
|
|
972
972
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
973
973
|
}
|
|
974
|
-
setVolumeEnvelope(
|
|
974
|
+
setVolumeEnvelope(note, pressure) {
|
|
975
975
|
const now = this.audioContext.currentTime;
|
|
976
|
-
const state = channel.state;
|
|
977
976
|
const { voiceParams, startTime } = note;
|
|
978
|
-
const pressureDepth = channel.pressureTable[2] / 64;
|
|
979
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
980
977
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
981
|
-
pressure;
|
|
978
|
+
(1 + pressure);
|
|
982
979
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
983
980
|
const volDelay = startTime + voiceParams.volDelay;
|
|
984
981
|
const volAttack = volDelay + voiceParams.volAttack;
|
|
@@ -1026,10 +1023,8 @@ export class MidyGM2 {
|
|
|
1026
1023
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1027
1024
|
const softPedalFactor = 1 -
|
|
1028
1025
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1032
|
-
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
1026
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
1027
|
+
softPedalFactor;
|
|
1033
1028
|
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor;
|
|
1034
1029
|
const sustainFreq = baseFreq +
|
|
1035
1030
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
@@ -1043,15 +1038,16 @@ export class MidyGM2 {
|
|
|
1043
1038
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1044
1039
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1045
1040
|
}
|
|
1046
|
-
setFilterEnvelope(channel, note) {
|
|
1041
|
+
setFilterEnvelope(channel, note, pressure) {
|
|
1047
1042
|
const now = this.audioContext.currentTime;
|
|
1048
1043
|
const state = channel.state;
|
|
1049
1044
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1050
1045
|
const softPedalFactor = 1 -
|
|
1051
1046
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1052
|
-
const
|
|
1047
|
+
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1048
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
1049
|
+
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1053
1050
|
softPedalFactor;
|
|
1054
|
-
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor;
|
|
1055
1051
|
const sustainFreq = baseFreq +
|
|
1056
1052
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1057
1053
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1078,9 +1074,9 @@ export class MidyGM2 {
|
|
|
1078
1074
|
gain: voiceParams.modLfoToFilterFc,
|
|
1079
1075
|
});
|
|
1080
1076
|
note.modulationDepth = new GainNode(this.audioContext);
|
|
1081
|
-
this.setModLfoToPitch(channel, note);
|
|
1077
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1082
1078
|
note.volumeDepth = new GainNode(this.audioContext);
|
|
1083
|
-
this.setModLfoToVolume(
|
|
1079
|
+
this.setModLfoToVolume(note, 0);
|
|
1084
1080
|
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
1085
1081
|
note.modulationLFO.connect(note.filterDepth);
|
|
1086
1082
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
@@ -1093,8 +1089,7 @@ export class MidyGM2 {
|
|
|
1093
1089
|
const { voiceParams } = note;
|
|
1094
1090
|
const state = channel.state;
|
|
1095
1091
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1096
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1097
|
-
state.vibratoRate,
|
|
1092
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
|
|
1098
1093
|
});
|
|
1099
1094
|
note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
|
|
1100
1095
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
@@ -1123,8 +1118,8 @@ export class MidyGM2 {
|
|
|
1123
1118
|
}
|
|
1124
1119
|
else {
|
|
1125
1120
|
note.portamento = false;
|
|
1126
|
-
this.setVolumeEnvelope(
|
|
1127
|
-
this.setFilterEnvelope(channel, note);
|
|
1121
|
+
this.setVolumeEnvelope(note, 0);
|
|
1122
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1128
1123
|
}
|
|
1129
1124
|
if (0 < state.vibratoDepth) {
|
|
1130
1125
|
this.startVibrato(channel, note, startTime);
|
|
@@ -1343,40 +1338,21 @@ export class MidyGM2 {
|
|
|
1343
1338
|
const prev = channel.state.channelPressure;
|
|
1344
1339
|
const next = value / 127;
|
|
1345
1340
|
channel.state.channelPressure = next;
|
|
1346
|
-
if (channel.
|
|
1347
|
-
const pressureDepth = (channel.
|
|
1341
|
+
if (channel.channelPressureTable[0] !== 64) {
|
|
1342
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
1348
1343
|
channel.detune += pressureDepth * (next - prev);
|
|
1349
1344
|
}
|
|
1345
|
+
const table = channel.channelPressureTable;
|
|
1350
1346
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1351
1347
|
for (let i = 0; i < noteList.length; i++) {
|
|
1352
1348
|
const note = noteList[i];
|
|
1353
1349
|
if (!note)
|
|
1354
1350
|
continue;
|
|
1355
|
-
this.
|
|
1351
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1356
1352
|
}
|
|
1357
1353
|
});
|
|
1358
1354
|
// this.applyVoiceParams(channel, 13);
|
|
1359
1355
|
}
|
|
1360
|
-
setChannelPressure(channel, note) {
|
|
1361
|
-
if (channel.pressureTable[0] !== 64) {
|
|
1362
|
-
this.updateDetune(channel);
|
|
1363
|
-
}
|
|
1364
|
-
if (channel.pressureTable[1] !== 64 && !note.portamento) {
|
|
1365
|
-
this.setFilterEnvelope(channel, note);
|
|
1366
|
-
}
|
|
1367
|
-
if (channel.pressureTable[2] !== 64 && !note.portamento) {
|
|
1368
|
-
this.setVolumeEnvelope(channel, note);
|
|
1369
|
-
}
|
|
1370
|
-
if (channel.pressureTable[3] !== 0) {
|
|
1371
|
-
this.setModLfoToPitch(channel, note);
|
|
1372
|
-
}
|
|
1373
|
-
if (channel.pressureTable[4] !== 0) {
|
|
1374
|
-
this.setModLfoToFilterFc(channel, note);
|
|
1375
|
-
}
|
|
1376
|
-
if (channel.pressureTable[5] !== 0) {
|
|
1377
|
-
this.setModLfoToVolume(channel, note);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
1356
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1381
1357
|
const pitchBend = msb * 128 + lsb;
|
|
1382
1358
|
this.setPitchBend(channelNumber, pitchBend);
|
|
@@ -1388,16 +1364,13 @@ export class MidyGM2 {
|
|
|
1388
1364
|
const next = (value - 8192) / 8192;
|
|
1389
1365
|
state.pitchWheel = value / 16383;
|
|
1390
1366
|
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1391
|
-
this.
|
|
1367
|
+
this.updateChannelDetune(channel);
|
|
1392
1368
|
this.applyVoiceParams(channel, 14);
|
|
1393
1369
|
}
|
|
1394
|
-
setModLfoToPitch(channel, note) {
|
|
1370
|
+
setModLfoToPitch(channel, note, pressure) {
|
|
1395
1371
|
const now = this.audioContext.currentTime;
|
|
1396
|
-
const pressureDepth = channel.pressureTable[3] / 127 * 600;
|
|
1397
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1398
1372
|
const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
|
|
1399
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1400
|
-
channel.state.modulationDepth;
|
|
1373
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1401
1374
|
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1402
1375
|
note.modulationDepth.gain
|
|
1403
1376
|
.cancelScheduledValues(now)
|
|
@@ -1413,22 +1386,18 @@ export class MidyGM2 {
|
|
|
1413
1386
|
.cancelScheduledValues(now)
|
|
1414
1387
|
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1415
1388
|
}
|
|
1416
|
-
setModLfoToFilterFc(
|
|
1389
|
+
setModLfoToFilterFc(note, pressure) {
|
|
1417
1390
|
const now = this.audioContext.currentTime;
|
|
1418
|
-
const pressureDepth = channel.pressureTable[4] / 127 * 2400;
|
|
1419
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1420
1391
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
|
|
1421
1392
|
note.filterDepth.gain
|
|
1422
1393
|
.cancelScheduledValues(now)
|
|
1423
1394
|
.setValueAtTime(modLfoToFilterFc, now);
|
|
1424
1395
|
}
|
|
1425
|
-
setModLfoToVolume(
|
|
1396
|
+
setModLfoToVolume(note, pressure) {
|
|
1426
1397
|
const now = this.audioContext.currentTime;
|
|
1427
1398
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1428
1399
|
const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1429
|
-
const
|
|
1430
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
1431
|
-
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
|
|
1400
|
+
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
|
|
1432
1401
|
note.volumeDepth.gain
|
|
1433
1402
|
.cancelScheduledValues(now)
|
|
1434
1403
|
.setValueAtTime(volumeDepth, now);
|
|
@@ -1501,11 +1470,18 @@ export class MidyGM2 {
|
|
|
1501
1470
|
.cancelScheduledValues(now)
|
|
1502
1471
|
.setValueAtTime(freqModLFO, now);
|
|
1503
1472
|
}
|
|
1473
|
+
setFreqVibLFO(channel, note) {
|
|
1474
|
+
const now = this.audioContext.currentTime;
|
|
1475
|
+
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1476
|
+
note.vibratoLFO.frequency
|
|
1477
|
+
.cancelScheduledValues(now)
|
|
1478
|
+
.setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
|
|
1479
|
+
}
|
|
1504
1480
|
createVoiceParamsHandlers() {
|
|
1505
1481
|
return {
|
|
1506
1482
|
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1507
1483
|
if (0 < channel.state.modulationDepth) {
|
|
1508
|
-
this.setModLfoToPitch(channel, note);
|
|
1484
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1509
1485
|
}
|
|
1510
1486
|
},
|
|
1511
1487
|
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
@@ -1515,12 +1491,12 @@ export class MidyGM2 {
|
|
|
1515
1491
|
},
|
|
1516
1492
|
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1517
1493
|
if (0 < channel.state.modulationDepth) {
|
|
1518
|
-
this.setModLfoToFilterFc(
|
|
1494
|
+
this.setModLfoToFilterFc(note, 0);
|
|
1519
1495
|
}
|
|
1520
1496
|
},
|
|
1521
|
-
modLfoToVolume: (channel, note) => {
|
|
1497
|
+
modLfoToVolume: (channel, note, _prevValue) => {
|
|
1522
1498
|
if (0 < channel.state.modulationDepth) {
|
|
1523
|
-
this.setModLfoToVolume(
|
|
1499
|
+
this.setModLfoToVolume(note, 0);
|
|
1524
1500
|
}
|
|
1525
1501
|
},
|
|
1526
1502
|
chorusEffectsSend: (channel, note, prevValue) => {
|
|
@@ -1534,22 +1510,19 @@ export class MidyGM2 {
|
|
|
1534
1510
|
delayVibLFO: (channel, note, prevValue) => {
|
|
1535
1511
|
if (0 < channel.state.vibratoDepth) {
|
|
1536
1512
|
const now = this.audioContext.currentTime;
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1513
|
+
const vibratoDelay = channel.state.vibratoDelay * 2;
|
|
1514
|
+
const prevStartTime = note.startTime + prevValue * vibratoDelay;
|
|
1539
1515
|
if (now < prevStartTime)
|
|
1540
1516
|
return;
|
|
1541
|
-
const
|
|
1542
|
-
|
|
1517
|
+
const value = note.voiceParams.delayVibLFO;
|
|
1518
|
+
const startTime = note.startTime + value * vibratoDelay;
|
|
1543
1519
|
note.vibratoLFO.stop(now);
|
|
1544
1520
|
note.vibratoLFO.start(startTime);
|
|
1545
1521
|
}
|
|
1546
1522
|
},
|
|
1547
1523
|
freqVibLFO: (channel, note, _prevValue) => {
|
|
1548
1524
|
if (0 < channel.state.vibratoDepth) {
|
|
1549
|
-
|
|
1550
|
-
note.vibratoLFO.frequency
|
|
1551
|
-
.cancelScheduledValues(now)
|
|
1552
|
-
.setValueAtTime(value * sate.vibratoRate, now);
|
|
1525
|
+
this.setFreqVibLFO(channel, note);
|
|
1553
1526
|
}
|
|
1554
1527
|
},
|
|
1555
1528
|
};
|
|
@@ -1593,7 +1566,7 @@ export class MidyGM2 {
|
|
|
1593
1566
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1594
1567
|
}
|
|
1595
1568
|
else {
|
|
1596
|
-
this.setFilterEnvelope(channel, note);
|
|
1569
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1597
1570
|
}
|
|
1598
1571
|
this.setPitchEnvelope(note);
|
|
1599
1572
|
}
|
|
@@ -1607,7 +1580,7 @@ export class MidyGM2 {
|
|
|
1607
1580
|
if (key in voiceParams)
|
|
1608
1581
|
noteVoiceParams[key] = voiceParams[key];
|
|
1609
1582
|
}
|
|
1610
|
-
this.setVolumeEnvelope(
|
|
1583
|
+
this.setVolumeEnvelope(note, 0);
|
|
1611
1584
|
}
|
|
1612
1585
|
}
|
|
1613
1586
|
}
|
|
@@ -1646,8 +1619,8 @@ export class MidyGM2 {
|
|
|
1646
1619
|
if (handler) {
|
|
1647
1620
|
handler.call(this, channelNumber, value);
|
|
1648
1621
|
const channel = this.channels[channelNumber];
|
|
1649
|
-
|
|
1650
|
-
this.
|
|
1622
|
+
this.applyVoiceParams(channel, controllerType + 128);
|
|
1623
|
+
this.applyControlTable(channel, controllerType);
|
|
1651
1624
|
}
|
|
1652
1625
|
else {
|
|
1653
1626
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1658,13 +1631,14 @@ export class MidyGM2 {
|
|
|
1658
1631
|
}
|
|
1659
1632
|
updateModulation(channel) {
|
|
1660
1633
|
const now = this.audioContext.currentTime;
|
|
1634
|
+
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1661
1635
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1662
1636
|
for (let i = 0; i < noteList.length; i++) {
|
|
1663
1637
|
const note = noteList[i];
|
|
1664
1638
|
if (!note)
|
|
1665
1639
|
continue;
|
|
1666
1640
|
if (note.modulationDepth) {
|
|
1667
|
-
note.modulationDepth.gain.setValueAtTime(
|
|
1641
|
+
note.modulationDepth.gain.setValueAtTime(depth, now);
|
|
1668
1642
|
}
|
|
1669
1643
|
else {
|
|
1670
1644
|
this.setPitchEnvelope(note);
|
|
@@ -1675,8 +1649,7 @@ export class MidyGM2 {
|
|
|
1675
1649
|
}
|
|
1676
1650
|
setModulationDepth(channelNumber, modulation) {
|
|
1677
1651
|
const channel = this.channels[channelNumber];
|
|
1678
|
-
channel.state.modulationDepth =
|
|
1679
|
-
channel.modulationDepthRange;
|
|
1652
|
+
channel.state.modulationDepth = modulation / 127;
|
|
1680
1653
|
this.updateModulation(channel);
|
|
1681
1654
|
}
|
|
1682
1655
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
@@ -1939,7 +1912,7 @@ export class MidyGM2 {
|
|
|
1939
1912
|
const next = value / 128;
|
|
1940
1913
|
state.pitchWheelSensitivity = next;
|
|
1941
1914
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1942
|
-
this.
|
|
1915
|
+
this.updateChannelDetune(channel);
|
|
1943
1916
|
this.applyVoiceParams(channel, 16);
|
|
1944
1917
|
}
|
|
1945
1918
|
handleFineTuningRPN(channelNumber) {
|
|
@@ -1954,7 +1927,7 @@ export class MidyGM2 {
|
|
|
1954
1927
|
const next = (value - 8192) / 8.192; // cent
|
|
1955
1928
|
channel.fineTuning = next;
|
|
1956
1929
|
channel.detune += next - prev;
|
|
1957
|
-
this.
|
|
1930
|
+
this.updateChannelDetune(channel);
|
|
1958
1931
|
}
|
|
1959
1932
|
handleCoarseTuningRPN(channelNumber) {
|
|
1960
1933
|
const channel = this.channels[channelNumber];
|
|
@@ -1968,7 +1941,7 @@ export class MidyGM2 {
|
|
|
1968
1941
|
const next = (value - 64) * 100; // cent
|
|
1969
1942
|
channel.coarseTuning = next;
|
|
1970
1943
|
channel.detune += next - prev;
|
|
1971
|
-
this.
|
|
1944
|
+
this.updateChannelDetune(channel);
|
|
1972
1945
|
}
|
|
1973
1946
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
1974
1947
|
const channel = this.channels[channelNumber];
|
|
@@ -1979,7 +1952,6 @@ export class MidyGM2 {
|
|
|
1979
1952
|
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
1980
1953
|
const channel = this.channels[channelNumber];
|
|
1981
1954
|
channel.modulationDepthRange = modulationDepthRange;
|
|
1982
|
-
channel.modulationDepth = (modulation / 127) * modulationDepthRange;
|
|
1983
1955
|
this.updateModulation(channel);
|
|
1984
1956
|
}
|
|
1985
1957
|
allSoundOff(channelNumber) {
|
|
@@ -2000,7 +1972,7 @@ export class MidyGM2 {
|
|
|
2000
1972
|
const state = channel.state;
|
|
2001
1973
|
for (let i = 0; i < stateTypes.length; i++) {
|
|
2002
1974
|
const type = stateTypes[i];
|
|
2003
|
-
state[type] = defaultControllerState[type];
|
|
1975
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
2004
1976
|
}
|
|
2005
1977
|
const settingTypes = [
|
|
2006
1978
|
"rpnMSB",
|
|
@@ -2094,10 +2066,9 @@ export class MidyGM2 {
|
|
|
2094
2066
|
case 9:
|
|
2095
2067
|
switch (data[3]) {
|
|
2096
2068
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2097
|
-
return this.
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
// return this.setControlChange();
|
|
2069
|
+
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2070
|
+
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2071
|
+
return this.handleControlChangeSysEx(data);
|
|
2101
2072
|
default:
|
|
2102
2073
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2103
2074
|
}
|
|
@@ -2124,8 +2095,8 @@ export class MidyGM2 {
|
|
|
2124
2095
|
}
|
|
2125
2096
|
else {
|
|
2126
2097
|
const now = this.audioContext.currentTime;
|
|
2127
|
-
this.
|
|
2128
|
-
this.
|
|
2098
|
+
this.masterVolume.gain.cancelScheduledValues(now);
|
|
2099
|
+
this.masterVolume.gain.setValueAtTime(volume * volume, now);
|
|
2129
2100
|
}
|
|
2130
2101
|
}
|
|
2131
2102
|
handleMasterFineTuningSysEx(data) {
|
|
@@ -2137,7 +2108,7 @@ export class MidyGM2 {
|
|
|
2137
2108
|
const next = (value - 8192) / 8.192; // cent
|
|
2138
2109
|
this.masterFineTuning = next;
|
|
2139
2110
|
channel.detune += next - prev;
|
|
2140
|
-
this.
|
|
2111
|
+
this.updateChannelDetune(channel);
|
|
2141
2112
|
}
|
|
2142
2113
|
handleMasterCoarseTuningSysEx(data) {
|
|
2143
2114
|
const coarseTuning = data[4];
|
|
@@ -2148,7 +2119,7 @@ export class MidyGM2 {
|
|
|
2148
2119
|
const next = (value - 64) * 100; // cent
|
|
2149
2120
|
this.masterCoarseTuning = next;
|
|
2150
2121
|
channel.detune += next - prev;
|
|
2151
|
-
this.
|
|
2122
|
+
this.updateChannelDetune(channel);
|
|
2152
2123
|
}
|
|
2153
2124
|
handleGlobalParameterControlSysEx(data) {
|
|
2154
2125
|
if (data[7] === 1) {
|
|
@@ -2370,15 +2341,86 @@ export class MidyGM2 {
|
|
|
2370
2341
|
}
|
|
2371
2342
|
}
|
|
2372
2343
|
}
|
|
2373
|
-
|
|
2344
|
+
applyDestinationSettings(channel, note, table) {
|
|
2345
|
+
if (table[0] !== 64) {
|
|
2346
|
+
this.updateDetune(channel, note, 0);
|
|
2347
|
+
}
|
|
2348
|
+
if (!note.portamento) {
|
|
2349
|
+
if (table[1] !== 64) {
|
|
2350
|
+
const channelPressure = channel.channelPressureTable[1] *
|
|
2351
|
+
channel.state.channelPressure;
|
|
2352
|
+
const pressure = (channelPressure - 64) * 15;
|
|
2353
|
+
this.setFilterEnvelope(channel, note, pressure);
|
|
2354
|
+
}
|
|
2355
|
+
if (table[2] !== 64) {
|
|
2356
|
+
const channelPressure = channel.channelPressureTable[2] *
|
|
2357
|
+
channel.state.channelPressure;
|
|
2358
|
+
const pressure = channelPressure / 64;
|
|
2359
|
+
this.setVolumeEnvelope(note, pressure);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (table[3] !== 0) {
|
|
2363
|
+
const channelPressure = channel.channelPressureTable[3] *
|
|
2364
|
+
channel.state.channelPressure;
|
|
2365
|
+
const pressure = channelPressure / 127 * 600;
|
|
2366
|
+
this.setModLfoToPitch(channel, note, pressure);
|
|
2367
|
+
}
|
|
2368
|
+
if (table[4] !== 0) {
|
|
2369
|
+
const channelPressure = channel.channelPressureTable[4] *
|
|
2370
|
+
channel.state.channelPressure;
|
|
2371
|
+
const pressure = channelPressure / 127 * 2400;
|
|
2372
|
+
this.setModLfoToFilterFc(note, pressure);
|
|
2373
|
+
}
|
|
2374
|
+
if (table[5] !== 0) {
|
|
2375
|
+
const channelPressure = channel.channelPressureTable[5] *
|
|
2376
|
+
channel.state.channelPressure;
|
|
2377
|
+
const pressure = channelPressure / 127;
|
|
2378
|
+
this.setModLfoToVolume(note, pressure);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
handleChannelPressureSysEx(data, tableName) {
|
|
2374
2382
|
const channelNumber = data[4];
|
|
2375
|
-
const table = this.channels[channelNumber]
|
|
2383
|
+
const table = this.channels[channelNumber][tableName];
|
|
2376
2384
|
for (let i = 5; i < data.length - 1; i += 2) {
|
|
2377
2385
|
const pp = data[i];
|
|
2378
2386
|
const rr = data[i + 1];
|
|
2379
2387
|
table[pp] = rr;
|
|
2380
2388
|
}
|
|
2381
2389
|
}
|
|
2390
|
+
initControlTable() {
|
|
2391
|
+
const channelCount = 128;
|
|
2392
|
+
const slotSize = 6;
|
|
2393
|
+
const defaultValues = [64, 64, 64, 0, 0, 0];
|
|
2394
|
+
const table = new Uint8Array(channelCount * slotSize);
|
|
2395
|
+
for (let ch = 0; ch < channelCount; ch++) {
|
|
2396
|
+
const offset = ch * slotSize;
|
|
2397
|
+
table.set(defaultValues, offset);
|
|
2398
|
+
}
|
|
2399
|
+
return table;
|
|
2400
|
+
}
|
|
2401
|
+
applyControlTable(channel, controllerType) {
|
|
2402
|
+
const slotSize = 6;
|
|
2403
|
+
const offset = controllerType * slotSize;
|
|
2404
|
+
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2405
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
2406
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
2407
|
+
const note = noteList[i];
|
|
2408
|
+
if (!note)
|
|
2409
|
+
continue;
|
|
2410
|
+
this.applyDestinationSettings(channel, note, table);
|
|
2411
|
+
}
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
handleControlChangeSysEx(data) {
|
|
2415
|
+
const channelNumber = data[4];
|
|
2416
|
+
const controllerType = data[5];
|
|
2417
|
+
const table = this.channels[channelNumber].controlTable[controllerType];
|
|
2418
|
+
for (let i = 6; i < data.length - 1; i += 2) {
|
|
2419
|
+
const pp = data[i];
|
|
2420
|
+
const rr = data[i + 1];
|
|
2421
|
+
table[pp] = rr;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2382
2424
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
2383
2425
|
const index = keyNumber * 128 + controllerType;
|
|
2384
2426
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
@@ -2429,7 +2471,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2429
2471
|
currentBufferSource: null,
|
|
2430
2472
|
detune: 0,
|
|
2431
2473
|
scaleOctaveTuningTable: new Array(12).fill(0), // cent
|
|
2432
|
-
|
|
2474
|
+
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2433
2475
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2434
2476
|
program: 0,
|
|
2435
2477
|
bank: 121 * 128,
|
|
@@ -2444,16 +2486,3 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2444
2486
|
modulationDepthRange: 50, // cent
|
|
2445
2487
|
}
|
|
2446
2488
|
});
|
|
2447
|
-
Object.defineProperty(MidyGM2, "controllerDestinationSettings", {
|
|
2448
|
-
enumerable: true,
|
|
2449
|
-
configurable: true,
|
|
2450
|
-
writable: true,
|
|
2451
|
-
value: {
|
|
2452
|
-
pitchControl: 0,
|
|
2453
|
-
filterCutoffControl: 0,
|
|
2454
|
-
amplitudeControl: 1,
|
|
2455
|
-
lfoPitchDepth: 0,
|
|
2456
|
-
lfoFilterDepth: 0,
|
|
2457
|
-
lfoAmplitudeDepth: 0,
|
|
2458
|
-
}
|
|
2459
|
-
});
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -29,12 +29,12 @@ export class MidyGMLite {
|
|
|
29
29
|
notePromises: any[];
|
|
30
30
|
exclusiveClassMap: Map<any, any>;
|
|
31
31
|
audioContext: any;
|
|
32
|
-
|
|
32
|
+
masterVolume: any;
|
|
33
33
|
voiceParamsHandlers: {
|
|
34
34
|
modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
|
|
35
35
|
vibLfoToPitch: (_channel: any, _note: any, _prevValue: any) => void;
|
|
36
36
|
modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
|
|
37
|
-
modLfoToVolume: (channel: any, note: any) => void;
|
|
37
|
+
modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
|
|
38
38
|
chorusEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
|
|
39
39
|
reverbEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
|
|
40
40
|
delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
|
|
@@ -94,7 +94,8 @@ export class MidyGMLite {
|
|
|
94
94
|
centToRate(cent: any): number;
|
|
95
95
|
centToHz(cent: any): number;
|
|
96
96
|
calcChannelDetune(channel: any): number;
|
|
97
|
-
|
|
97
|
+
updateChannelDetune(channel: any): void;
|
|
98
|
+
updateDetune(channel: any, note: any): void;
|
|
98
99
|
setVolumeEnvelope(note: any): void;
|
|
99
100
|
setPitchEnvelope(note: any): void;
|
|
100
101
|
clampCutoffFrequency(frequency: any): number;
|
|
@@ -120,7 +121,7 @@ export class MidyGMLite {
|
|
|
120
121
|
modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
|
|
121
122
|
vibLfoToPitch: (_channel: any, _note: any, _prevValue: any) => void;
|
|
122
123
|
modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
|
|
123
|
-
modLfoToVolume: (channel: any, note: any) => void;
|
|
124
|
+
modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
|
|
124
125
|
chorusEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
|
|
125
126
|
reverbEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
|
|
126
127
|
delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
|
|
@@ -163,7 +164,7 @@ export class MidyGMLite {
|
|
|
163
164
|
setRPNLSB(channelNumber: any, value: any): void;
|
|
164
165
|
dataEntryMSB(channelNumber: any, value: any): void;
|
|
165
166
|
handlePitchBendRangeRPN(channelNumber: any): void;
|
|
166
|
-
setPitchBendRange(channelNumber: any,
|
|
167
|
+
setPitchBendRange(channelNumber: any, value: any): void;
|
|
167
168
|
allSoundOff(channelNumber: any): Promise<void>;
|
|
168
169
|
resetAllControllers(channelNumber: any): void;
|
|
169
170
|
allNotesOff(channelNumber: any): Promise<void>;
|
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":"AAmFA;IAoBE;;;;;;;;;MASE;IAEF,+BAQC;IAtCD,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;IAClB,iCAA8B;IAc5B,kBAAgC;IAChC,
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmFA;IAoBE;;;;;;;;;MASE;IAEF,+BAQC;IAtCD,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;IAClB,iCAA8B;IAc5B,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EAWC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA1tCD;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|