@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/script/midy.js
CHANGED
|
@@ -89,6 +89,12 @@ class Note {
|
|
|
89
89
|
writable: true,
|
|
90
90
|
value: void 0
|
|
91
91
|
});
|
|
92
|
+
Object.defineProperty(this, "pressure", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true,
|
|
95
|
+
writable: true,
|
|
96
|
+
value: 0
|
|
97
|
+
});
|
|
92
98
|
this.noteNumber = noteNumber;
|
|
93
99
|
this.velocity = velocity;
|
|
94
100
|
this.startTime = startTime;
|
|
@@ -100,7 +106,7 @@ class Note {
|
|
|
100
106
|
const defaultControllerState = {
|
|
101
107
|
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
102
108
|
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
103
|
-
|
|
109
|
+
polyphonicKeyPressure: { type: 10, defaultValue: 0 },
|
|
104
110
|
channelPressure: { type: 13, defaultValue: 0 },
|
|
105
111
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
106
112
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
@@ -362,15 +368,15 @@ class Midy {
|
|
|
362
368
|
});
|
|
363
369
|
this.audioContext = audioContext;
|
|
364
370
|
this.options = { ...this.defaultOptions, ...options };
|
|
365
|
-
this.
|
|
371
|
+
this.masterVolume = new GainNode(audioContext);
|
|
366
372
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
367
373
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
368
374
|
this.channels = this.createChannels(audioContext);
|
|
369
375
|
this.reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
370
376
|
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
371
|
-
this.chorusEffect.output.connect(this.
|
|
372
|
-
this.reverbEffect.output.connect(this.
|
|
373
|
-
this.
|
|
377
|
+
this.chorusEffect.output.connect(this.masterVolume);
|
|
378
|
+
this.reverbEffect.output.connect(this.masterVolume);
|
|
379
|
+
this.masterVolume.connect(audioContext.destination);
|
|
374
380
|
this.GM2SystemOn();
|
|
375
381
|
}
|
|
376
382
|
initSoundFontTable() {
|
|
@@ -416,7 +422,7 @@ class Midy {
|
|
|
416
422
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
417
423
|
gainL.connect(merger, 0, 0);
|
|
418
424
|
gainR.connect(merger, 0, 1);
|
|
419
|
-
merger.connect(this.
|
|
425
|
+
merger.connect(this.masterVolume);
|
|
420
426
|
return {
|
|
421
427
|
gainL,
|
|
422
428
|
gainR,
|
|
@@ -428,15 +434,10 @@ class Midy {
|
|
|
428
434
|
return {
|
|
429
435
|
...this.constructor.channelSettings,
|
|
430
436
|
state: new ControllerState(),
|
|
437
|
+
controlTable: this.initControlTable(),
|
|
431
438
|
...this.setChannelAudioNodes(audioContext),
|
|
432
439
|
scheduledNotes: new Map(),
|
|
433
440
|
sostenutoNotes: new Map(),
|
|
434
|
-
polyphonicKeyPressure: {
|
|
435
|
-
...this.constructor.controllerDestinationSettings,
|
|
436
|
-
},
|
|
437
|
-
channelPressure: {
|
|
438
|
-
...this.constructor.controllerDestinationSettings,
|
|
439
|
-
},
|
|
440
441
|
};
|
|
441
442
|
});
|
|
442
443
|
return channels;
|
|
@@ -941,28 +942,31 @@ class Midy {
|
|
|
941
942
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
942
943
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
943
944
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
944
|
-
const pressureDepth = (channel.
|
|
945
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
945
946
|
const pressure = pressureDepth * channel.state.channelPressure;
|
|
946
947
|
return tuning + pitch + pressure;
|
|
947
948
|
}
|
|
948
949
|
calcNoteDetune(channel, note) {
|
|
949
950
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
950
951
|
}
|
|
951
|
-
|
|
952
|
-
const now = this.audioContext.currentTime;
|
|
952
|
+
updateChannelDetune(channel) {
|
|
953
953
|
channel.scheduledNotes.forEach((noteList) => {
|
|
954
954
|
for (let i = 0; i < noteList.length; i++) {
|
|
955
955
|
const note = noteList[i];
|
|
956
956
|
if (!note)
|
|
957
957
|
continue;
|
|
958
|
-
|
|
959
|
-
const detune = channel.detune + noteDetune;
|
|
960
|
-
note.bufferSource.detune
|
|
961
|
-
.cancelScheduledValues(now)
|
|
962
|
-
.setValueAtTime(detune, now);
|
|
958
|
+
this.updateDetune(channel, note, 0);
|
|
963
959
|
}
|
|
964
960
|
});
|
|
965
961
|
}
|
|
962
|
+
updateDetune(channel, note, pressure) {
|
|
963
|
+
const now = this.audioContext.currentTime;
|
|
964
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
965
|
+
const detune = channel.detune + noteDetune + pressure;
|
|
966
|
+
note.bufferSource.detune
|
|
967
|
+
.cancelScheduledValues(now)
|
|
968
|
+
.setValueAtTime(detune, now);
|
|
969
|
+
}
|
|
966
970
|
getPortamentoTime(channel) {
|
|
967
971
|
const factor = 5 * Math.log(10) / 127;
|
|
968
972
|
const time = channel.state.portamentoTime;
|
|
@@ -980,14 +984,12 @@ class Midy {
|
|
|
980
984
|
.setValueAtTime(0, volDelay)
|
|
981
985
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
982
986
|
}
|
|
983
|
-
setVolumeEnvelope(channel, note) {
|
|
987
|
+
setVolumeEnvelope(channel, note, pressure) {
|
|
984
988
|
const now = this.audioContext.currentTime;
|
|
985
989
|
const state = channel.state;
|
|
986
990
|
const { voiceParams, startTime } = note;
|
|
987
|
-
const pressureDepth = channel.pressureTable[2] / 64;
|
|
988
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
989
991
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
990
|
-
pressure;
|
|
992
|
+
(1 + pressure);
|
|
991
993
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
992
994
|
const volDelay = startTime + voiceParams.volDelay;
|
|
993
995
|
const volAttack = volDelay + voiceParams.volAttack * state.attackTime * 2;
|
|
@@ -1035,10 +1037,8 @@ class Midy {
|
|
|
1035
1037
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1036
1038
|
const softPedalFactor = 1 -
|
|
1037
1039
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1041
|
-
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1040
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
1041
|
+
softPedalFactor *
|
|
1042
1042
|
state.brightness * 2;
|
|
1043
1043
|
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1044
1044
|
const sustainFreq = baseFreq +
|
|
@@ -1053,15 +1053,17 @@ class Midy {
|
|
|
1053
1053
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1054
1054
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1055
1055
|
}
|
|
1056
|
-
setFilterEnvelope(channel, note) {
|
|
1056
|
+
setFilterEnvelope(channel, note, pressure) {
|
|
1057
1057
|
const now = this.audioContext.currentTime;
|
|
1058
1058
|
const state = channel.state;
|
|
1059
1059
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1060
1060
|
const softPedalFactor = 1 -
|
|
1061
1061
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1062
|
-
const
|
|
1062
|
+
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1063
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1064
|
+
state.brightness * 2;
|
|
1065
|
+
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1063
1066
|
softPedalFactor * state.brightness * 2;
|
|
1064
|
-
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1065
1067
|
const sustainFreq = baseFreq +
|
|
1066
1068
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1067
1069
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1088,9 +1090,9 @@ class Midy {
|
|
|
1088
1090
|
gain: voiceParams.modLfoToFilterFc,
|
|
1089
1091
|
});
|
|
1090
1092
|
note.modulationDepth = new GainNode(this.audioContext);
|
|
1091
|
-
this.setModLfoToPitch(channel, note);
|
|
1093
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1092
1094
|
note.volumeDepth = new GainNode(this.audioContext);
|
|
1093
|
-
this.setModLfoToVolume(
|
|
1095
|
+
this.setModLfoToVolume(note, 0);
|
|
1094
1096
|
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
1095
1097
|
note.modulationLFO.connect(note.filterDepth);
|
|
1096
1098
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
@@ -1103,8 +1105,7 @@ class Midy {
|
|
|
1103
1105
|
const { voiceParams } = note;
|
|
1104
1106
|
const state = channel.state;
|
|
1105
1107
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1106
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1107
|
-
state.vibratoRate,
|
|
1108
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
|
|
1108
1109
|
});
|
|
1109
1110
|
note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
|
|
1110
1111
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
@@ -1133,8 +1134,8 @@ class Midy {
|
|
|
1133
1134
|
}
|
|
1134
1135
|
else {
|
|
1135
1136
|
note.portamento = false;
|
|
1136
|
-
this.setVolumeEnvelope(channel, note);
|
|
1137
|
-
this.setFilterEnvelope(channel, note);
|
|
1137
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1138
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1138
1139
|
}
|
|
1139
1140
|
if (0 < state.vibratoDepth) {
|
|
1140
1141
|
this.startVibrato(channel, note, startTime);
|
|
@@ -1349,16 +1350,12 @@ class Midy {
|
|
|
1349
1350
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
1350
1351
|
const now = this.audioContext.currentTime;
|
|
1351
1352
|
const channel = this.channels[channelNumber];
|
|
1352
|
-
pressure
|
|
1353
|
+
channel.state.polyphonicKeyPressure = pressure / 127;
|
|
1354
|
+
const table = channel.polyphonicKeyPressureTable;
|
|
1353
1355
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1354
|
-
if (
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
const gain = activeNote.gainL.gain.value;
|
|
1358
|
-
activeNote.volumeNode.gain
|
|
1359
|
-
.cancelScheduledValues(now)
|
|
1360
|
-
.setValueAtTime(gain * pressure, now);
|
|
1361
|
-
}
|
|
1356
|
+
if (activeNotes.has(noteNumber)) {
|
|
1357
|
+
const note = activeNotes.get(noteNumber);
|
|
1358
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1362
1359
|
}
|
|
1363
1360
|
// this.applyVoiceParams(channel, 10);
|
|
1364
1361
|
}
|
|
@@ -1372,40 +1369,21 @@ class Midy {
|
|
|
1372
1369
|
const prev = channel.state.channelPressure;
|
|
1373
1370
|
const next = value / 127;
|
|
1374
1371
|
channel.state.channelPressure = next;
|
|
1375
|
-
if (channel.
|
|
1376
|
-
const pressureDepth = (channel.
|
|
1372
|
+
if (channel.channelPressureTable[0] !== 64) {
|
|
1373
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
1377
1374
|
channel.detune += pressureDepth * (next - prev);
|
|
1378
1375
|
}
|
|
1376
|
+
const table = channel.channelPressureTable;
|
|
1379
1377
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1380
1378
|
for (let i = 0; i < noteList.length; i++) {
|
|
1381
1379
|
const note = noteList[i];
|
|
1382
1380
|
if (!note)
|
|
1383
1381
|
continue;
|
|
1384
|
-
this.
|
|
1382
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1385
1383
|
}
|
|
1386
1384
|
});
|
|
1387
1385
|
// this.applyVoiceParams(channel, 13);
|
|
1388
1386
|
}
|
|
1389
|
-
setChannelPressure(channel, note) {
|
|
1390
|
-
if (channel.pressureTable[0] !== 64) {
|
|
1391
|
-
this.updateDetune(channel);
|
|
1392
|
-
}
|
|
1393
|
-
if (channel.pressureTable[1] !== 64 && !note.portamento) {
|
|
1394
|
-
this.setFilterEnvelope(channel, note);
|
|
1395
|
-
}
|
|
1396
|
-
if (channel.pressureTable[2] !== 64 && !note.portamento) {
|
|
1397
|
-
this.setVolumeEnvelope(channel, note);
|
|
1398
|
-
}
|
|
1399
|
-
if (channel.pressureTable[3] !== 0) {
|
|
1400
|
-
this.setModLfoToPitch(channel, note);
|
|
1401
|
-
}
|
|
1402
|
-
if (channel.pressureTable[4] !== 0) {
|
|
1403
|
-
this.setModLfoToFilterFc(channel, note);
|
|
1404
|
-
}
|
|
1405
|
-
if (channel.pressureTable[5] !== 0) {
|
|
1406
|
-
this.setModLfoToVolume(channel, note);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
1387
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1410
1388
|
const pitchBend = msb * 128 + lsb;
|
|
1411
1389
|
this.setPitchBend(channelNumber, pitchBend);
|
|
@@ -1417,16 +1395,13 @@ class Midy {
|
|
|
1417
1395
|
const next = (value - 8192) / 8192;
|
|
1418
1396
|
state.pitchWheel = value / 16383;
|
|
1419
1397
|
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1420
|
-
this.
|
|
1398
|
+
this.updateChannelDetune(channel);
|
|
1421
1399
|
this.applyVoiceParams(channel, 14);
|
|
1422
1400
|
}
|
|
1423
|
-
setModLfoToPitch(channel, note) {
|
|
1401
|
+
setModLfoToPitch(channel, note, pressure) {
|
|
1424
1402
|
const now = this.audioContext.currentTime;
|
|
1425
|
-
const pressureDepth = channel.pressureTable[3] / 127 * 600;
|
|
1426
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1427
1403
|
const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
|
|
1428
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1429
|
-
channel.state.modulationDepth;
|
|
1404
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1430
1405
|
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1431
1406
|
note.modulationDepth.gain
|
|
1432
1407
|
.cancelScheduledValues(now)
|
|
@@ -1442,22 +1417,18 @@ class Midy {
|
|
|
1442
1417
|
.cancelScheduledValues(now)
|
|
1443
1418
|
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1444
1419
|
}
|
|
1445
|
-
setModLfoToFilterFc(
|
|
1420
|
+
setModLfoToFilterFc(note, pressure) {
|
|
1446
1421
|
const now = this.audioContext.currentTime;
|
|
1447
|
-
const pressureDepth = channel.pressureTable[4] / 127 * 2400;
|
|
1448
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1449
1422
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
|
|
1450
1423
|
note.filterDepth.gain
|
|
1451
1424
|
.cancelScheduledValues(now)
|
|
1452
1425
|
.setValueAtTime(modLfoToFilterFc, now);
|
|
1453
1426
|
}
|
|
1454
|
-
setModLfoToVolume(
|
|
1427
|
+
setModLfoToVolume(note, pressure) {
|
|
1455
1428
|
const now = this.audioContext.currentTime;
|
|
1456
1429
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1457
1430
|
const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1458
|
-
const
|
|
1459
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
1460
|
-
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
|
|
1431
|
+
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
|
|
1461
1432
|
note.volumeDepth.gain
|
|
1462
1433
|
.cancelScheduledValues(now)
|
|
1463
1434
|
.setValueAtTime(volumeDepth, now);
|
|
@@ -1530,11 +1501,18 @@ class Midy {
|
|
|
1530
1501
|
.cancelScheduledValues(now)
|
|
1531
1502
|
.setValueAtTime(freqModLFO, now);
|
|
1532
1503
|
}
|
|
1504
|
+
setFreqVibLFO(channel, note) {
|
|
1505
|
+
const now = this.audioContext.currentTime;
|
|
1506
|
+
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1507
|
+
note.vibratoLFO.frequency
|
|
1508
|
+
.cancelScheduledValues(now)
|
|
1509
|
+
.setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
|
|
1510
|
+
}
|
|
1533
1511
|
createVoiceParamsHandlers() {
|
|
1534
1512
|
return {
|
|
1535
1513
|
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1536
1514
|
if (0 < channel.state.modulationDepth) {
|
|
1537
|
-
this.setModLfoToPitch(channel, note);
|
|
1515
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1538
1516
|
}
|
|
1539
1517
|
},
|
|
1540
1518
|
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
@@ -1544,12 +1522,12 @@ class Midy {
|
|
|
1544
1522
|
},
|
|
1545
1523
|
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1546
1524
|
if (0 < channel.state.modulationDepth) {
|
|
1547
|
-
this.setModLfoToFilterFc(
|
|
1525
|
+
this.setModLfoToFilterFc(note, 0);
|
|
1548
1526
|
}
|
|
1549
1527
|
},
|
|
1550
|
-
modLfoToVolume: (channel, note) => {
|
|
1528
|
+
modLfoToVolume: (channel, note, _prevValue) => {
|
|
1551
1529
|
if (0 < channel.state.modulationDepth) {
|
|
1552
|
-
this.setModLfoToVolume(
|
|
1530
|
+
this.setModLfoToVolume(note, 0);
|
|
1553
1531
|
}
|
|
1554
1532
|
},
|
|
1555
1533
|
chorusEffectsSend: (channel, note, prevValue) => {
|
|
@@ -1563,22 +1541,19 @@ class Midy {
|
|
|
1563
1541
|
delayVibLFO: (channel, note, prevValue) => {
|
|
1564
1542
|
if (0 < channel.state.vibratoDepth) {
|
|
1565
1543
|
const now = this.audioContext.currentTime;
|
|
1566
|
-
const
|
|
1567
|
-
|
|
1544
|
+
const vibratoDelay = channel.state.vibratoDelay * 2;
|
|
1545
|
+
const prevStartTime = note.startTime + prevValue * vibratoDelay;
|
|
1568
1546
|
if (now < prevStartTime)
|
|
1569
1547
|
return;
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1548
|
+
const value = note.voiceParams.delayVibLFO;
|
|
1549
|
+
const startTime = note.startTime + value * vibratoDelay;
|
|
1572
1550
|
note.vibratoLFO.stop(now);
|
|
1573
1551
|
note.vibratoLFO.start(startTime);
|
|
1574
1552
|
}
|
|
1575
1553
|
},
|
|
1576
1554
|
freqVibLFO: (channel, note, _prevValue) => {
|
|
1577
1555
|
if (0 < channel.state.vibratoDepth) {
|
|
1578
|
-
|
|
1579
|
-
note.vibratoLFO.frequency
|
|
1580
|
-
.cancelScheduledValues(now)
|
|
1581
|
-
.setValueAtTime(value * sate.vibratoRate, now);
|
|
1556
|
+
this.setFreqVibLFO(channel, note);
|
|
1582
1557
|
}
|
|
1583
1558
|
},
|
|
1584
1559
|
};
|
|
@@ -1622,7 +1597,7 @@ class Midy {
|
|
|
1622
1597
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1623
1598
|
}
|
|
1624
1599
|
else {
|
|
1625
|
-
this.setFilterEnvelope(channel, note);
|
|
1600
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1626
1601
|
}
|
|
1627
1602
|
this.setPitchEnvelope(note);
|
|
1628
1603
|
}
|
|
@@ -1636,7 +1611,7 @@ class Midy {
|
|
|
1636
1611
|
if (key in voiceParams)
|
|
1637
1612
|
noteVoiceParams[key] = voiceParams[key];
|
|
1638
1613
|
}
|
|
1639
|
-
this.setVolumeEnvelope(channel, note);
|
|
1614
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1640
1615
|
}
|
|
1641
1616
|
}
|
|
1642
1617
|
}
|
|
@@ -1685,8 +1660,8 @@ class Midy {
|
|
|
1685
1660
|
if (handler) {
|
|
1686
1661
|
handler.call(this, channelNumber, value);
|
|
1687
1662
|
const channel = this.channels[channelNumber];
|
|
1688
|
-
|
|
1689
|
-
this.
|
|
1663
|
+
this.applyVoiceParams(channel, controllerType + 128);
|
|
1664
|
+
this.applyControlTable(channel, controllerType);
|
|
1690
1665
|
}
|
|
1691
1666
|
else {
|
|
1692
1667
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1697,13 +1672,14 @@ class Midy {
|
|
|
1697
1672
|
}
|
|
1698
1673
|
updateModulation(channel) {
|
|
1699
1674
|
const now = this.audioContext.currentTime;
|
|
1675
|
+
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1700
1676
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1701
1677
|
for (let i = 0; i < noteList.length; i++) {
|
|
1702
1678
|
const note = noteList[i];
|
|
1703
1679
|
if (!note)
|
|
1704
1680
|
continue;
|
|
1705
1681
|
if (note.modulationDepth) {
|
|
1706
|
-
note.modulationDepth.gain.setValueAtTime(
|
|
1682
|
+
note.modulationDepth.gain.setValueAtTime(depth, now);
|
|
1707
1683
|
}
|
|
1708
1684
|
else {
|
|
1709
1685
|
this.setPitchEnvelope(note);
|
|
@@ -1714,8 +1690,7 @@ class Midy {
|
|
|
1714
1690
|
}
|
|
1715
1691
|
setModulationDepth(channelNumber, modulation) {
|
|
1716
1692
|
const channel = this.channels[channelNumber];
|
|
1717
|
-
channel.state.modulationDepth =
|
|
1718
|
-
channel.modulationDepthRange;
|
|
1693
|
+
channel.state.modulationDepth = modulation / 127;
|
|
1719
1694
|
this.updateModulation(channel);
|
|
1720
1695
|
}
|
|
1721
1696
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
@@ -1857,7 +1832,7 @@ class Midy {
|
|
|
1857
1832
|
continue;
|
|
1858
1833
|
if (note.startTime < now)
|
|
1859
1834
|
continue;
|
|
1860
|
-
this.setVolumeEnvelope(channel, note);
|
|
1835
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1861
1836
|
}
|
|
1862
1837
|
});
|
|
1863
1838
|
}
|
|
@@ -1869,7 +1844,12 @@ class Midy {
|
|
|
1869
1844
|
const note = noteList[i];
|
|
1870
1845
|
if (!note)
|
|
1871
1846
|
continue;
|
|
1872
|
-
|
|
1847
|
+
if (note.portamento) {
|
|
1848
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1849
|
+
}
|
|
1850
|
+
else {
|
|
1851
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1852
|
+
}
|
|
1873
1853
|
}
|
|
1874
1854
|
});
|
|
1875
1855
|
}
|
|
@@ -1881,7 +1861,7 @@ class Midy {
|
|
|
1881
1861
|
const note = noteList[i];
|
|
1882
1862
|
if (!note)
|
|
1883
1863
|
continue;
|
|
1884
|
-
this.setVolumeEnvelope(channel, note);
|
|
1864
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1885
1865
|
}
|
|
1886
1866
|
});
|
|
1887
1867
|
}
|
|
@@ -1890,21 +1870,53 @@ class Midy {
|
|
|
1890
1870
|
channel.state.vibratoRate = vibratoRate / 64;
|
|
1891
1871
|
if (channel.vibratoDepth <= 0)
|
|
1892
1872
|
return;
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
.
|
|
1873
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1874
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1875
|
+
const note = noteList[i];
|
|
1876
|
+
if (!note)
|
|
1877
|
+
continue;
|
|
1878
|
+
this.setVibLfoToPitch(channel, note);
|
|
1879
|
+
}
|
|
1899
1880
|
});
|
|
1900
1881
|
}
|
|
1901
1882
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1902
1883
|
const channel = this.channels[channelNumber];
|
|
1884
|
+
const prev = channel.state.vibratoDepth;
|
|
1903
1885
|
channel.state.vibratoDepth = vibratoDepth / 64;
|
|
1886
|
+
if (0 < prev) {
|
|
1887
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1888
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1889
|
+
const note = noteList[i];
|
|
1890
|
+
if (!note)
|
|
1891
|
+
continue;
|
|
1892
|
+
this.setFreqVibLFO(channel, note);
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
else {
|
|
1897
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1898
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1899
|
+
const note = noteList[i];
|
|
1900
|
+
if (!note)
|
|
1901
|
+
continue;
|
|
1902
|
+
this.startVibrato(channel, note, note.startTime);
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1904
1906
|
}
|
|
1905
1907
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1906
1908
|
const channel = this.channels[channelNumber];
|
|
1907
1909
|
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1910
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1911
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1912
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1913
|
+
const note = noteList[i];
|
|
1914
|
+
if (!note)
|
|
1915
|
+
continue;
|
|
1916
|
+
this.startVibrato(channel, note, note.startTime);
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1908
1920
|
}
|
|
1909
1921
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1910
1922
|
const channel = this.channels[channelNumber];
|
|
@@ -2069,7 +2081,7 @@ class Midy {
|
|
|
2069
2081
|
const next = value / 128;
|
|
2070
2082
|
state.pitchWheelSensitivity = next;
|
|
2071
2083
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2072
|
-
this.
|
|
2084
|
+
this.updateChannelDetune(channel);
|
|
2073
2085
|
this.applyVoiceParams(channel, 16);
|
|
2074
2086
|
}
|
|
2075
2087
|
handleFineTuningRPN(channelNumber) {
|
|
@@ -2084,7 +2096,7 @@ class Midy {
|
|
|
2084
2096
|
const next = (value - 8192) / 8.192; // cent
|
|
2085
2097
|
channel.fineTuning = next;
|
|
2086
2098
|
channel.detune += next - prev;
|
|
2087
|
-
this.
|
|
2099
|
+
this.updateChannelDetune(channel);
|
|
2088
2100
|
}
|
|
2089
2101
|
handleCoarseTuningRPN(channelNumber) {
|
|
2090
2102
|
const channel = this.channels[channelNumber];
|
|
@@ -2098,7 +2110,7 @@ class Midy {
|
|
|
2098
2110
|
const next = (value - 64) * 100; // cent
|
|
2099
2111
|
channel.coarseTuning = next;
|
|
2100
2112
|
channel.detune += next - prev;
|
|
2101
|
-
this.
|
|
2113
|
+
this.updateChannelDetune(channel);
|
|
2102
2114
|
}
|
|
2103
2115
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
2104
2116
|
const channel = this.channels[channelNumber];
|
|
@@ -2109,7 +2121,6 @@ class Midy {
|
|
|
2109
2121
|
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
2110
2122
|
const channel = this.channels[channelNumber];
|
|
2111
2123
|
channel.modulationDepthRange = modulationDepthRange;
|
|
2112
|
-
channel.modulationDepth = (modulation / 127) * modulationDepthRange;
|
|
2113
2124
|
this.updateModulation(channel);
|
|
2114
2125
|
}
|
|
2115
2126
|
allSoundOff(channelNumber) {
|
|
@@ -2130,7 +2141,7 @@ class Midy {
|
|
|
2130
2141
|
const state = channel.state;
|
|
2131
2142
|
for (let i = 0; i < stateTypes.length; i++) {
|
|
2132
2143
|
const type = stateTypes[i];
|
|
2133
|
-
state[type] = defaultControllerState[type];
|
|
2144
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
2134
2145
|
}
|
|
2135
2146
|
const settingTypes = [
|
|
2136
2147
|
"rpnMSB",
|
|
@@ -2233,10 +2244,11 @@ class Midy {
|
|
|
2233
2244
|
case 9:
|
|
2234
2245
|
switch (data[3]) {
|
|
2235
2246
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2236
|
-
return this.
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
//
|
|
2247
|
+
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2248
|
+
case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2249
|
+
return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
|
|
2250
|
+
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2251
|
+
return this.handleControlChangeSysEx(data);
|
|
2240
2252
|
default:
|
|
2241
2253
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2242
2254
|
}
|
|
@@ -2263,8 +2275,8 @@ class Midy {
|
|
|
2263
2275
|
}
|
|
2264
2276
|
else {
|
|
2265
2277
|
const now = this.audioContext.currentTime;
|
|
2266
|
-
this.
|
|
2267
|
-
this.
|
|
2278
|
+
this.masterVolume.gain.cancelScheduledValues(now);
|
|
2279
|
+
this.masterVolume.gain.setValueAtTime(volume * volume, now);
|
|
2268
2280
|
}
|
|
2269
2281
|
}
|
|
2270
2282
|
handleMasterFineTuningSysEx(data) {
|
|
@@ -2276,7 +2288,7 @@ class Midy {
|
|
|
2276
2288
|
const next = (value - 8192) / 8.192; // cent
|
|
2277
2289
|
this.masterFineTuning = next;
|
|
2278
2290
|
channel.detune += next - prev;
|
|
2279
|
-
this.
|
|
2291
|
+
this.updateChannelDetune(channel);
|
|
2280
2292
|
}
|
|
2281
2293
|
handleMasterCoarseTuningSysEx(data) {
|
|
2282
2294
|
const coarseTuning = data[4];
|
|
@@ -2287,7 +2299,7 @@ class Midy {
|
|
|
2287
2299
|
const next = (value - 64) * 100; // cent
|
|
2288
2300
|
this.masterCoarseTuning = next;
|
|
2289
2301
|
channel.detune += next - prev;
|
|
2290
|
-
this.
|
|
2302
|
+
this.updateChannelDetune(channel);
|
|
2291
2303
|
}
|
|
2292
2304
|
handleGlobalParameterControlSysEx(data) {
|
|
2293
2305
|
if (data[7] === 1) {
|
|
@@ -2509,15 +2521,105 @@ class Midy {
|
|
|
2509
2521
|
}
|
|
2510
2522
|
}
|
|
2511
2523
|
}
|
|
2512
|
-
|
|
2524
|
+
applyDestinationSettings(channel, note, table) {
|
|
2525
|
+
if (table[0] !== 64) {
|
|
2526
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2527
|
+
? channel.polyphonicKeyPressureTable[0] * note.pressure
|
|
2528
|
+
: 0;
|
|
2529
|
+
const pressure = (polyphonicKeyPressure - 64) / 37.5; // 2400 / 64;
|
|
2530
|
+
this.updateDetune(channel, note, pressure);
|
|
2531
|
+
}
|
|
2532
|
+
if (!note.portamento) {
|
|
2533
|
+
if (table[1] !== 64) {
|
|
2534
|
+
const channelPressure = channel.channelPressureTable[1] *
|
|
2535
|
+
channel.state.channelPressure;
|
|
2536
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2537
|
+
? channel.polyphonicKeyPressureTable[1] * note.pressure
|
|
2538
|
+
: 0;
|
|
2539
|
+
const pressure = (channelPressure + polyphonicKeyPressure - 128) * 15;
|
|
2540
|
+
this.setFilterEnvelope(channel, note, pressure);
|
|
2541
|
+
}
|
|
2542
|
+
if (table[2] !== 64) {
|
|
2543
|
+
const channelPressure = channel.channelPressureTable[2] *
|
|
2544
|
+
channel.state.channelPressure;
|
|
2545
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2546
|
+
? channel.polyphonicKeyPressureTable[2] * note.pressure
|
|
2547
|
+
: 0;
|
|
2548
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 128;
|
|
2549
|
+
this.setVolumeEnvelope(channel, note, pressure);
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
if (table[3] !== 0) {
|
|
2553
|
+
const channelPressure = channel.channelPressureTable[3] *
|
|
2554
|
+
channel.state.channelPressure;
|
|
2555
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2556
|
+
? channel.polyphonicKeyPressureTable[3] * note.pressure
|
|
2557
|
+
: 0;
|
|
2558
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 600;
|
|
2559
|
+
this.setModLfoToPitch(channel, note, pressure);
|
|
2560
|
+
}
|
|
2561
|
+
if (table[4] !== 0) {
|
|
2562
|
+
const channelPressure = channel.channelPressureTable[4] *
|
|
2563
|
+
channel.state.channelPressure;
|
|
2564
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2565
|
+
? channel.polyphonicKeyPressureTable[4] * note.pressure
|
|
2566
|
+
: 0;
|
|
2567
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 2400;
|
|
2568
|
+
this.setModLfoToFilterFc(note, pressure);
|
|
2569
|
+
}
|
|
2570
|
+
if (table[5] !== 0) {
|
|
2571
|
+
const channelPressure = channel.channelPressureTable[5] *
|
|
2572
|
+
channel.state.channelPressure;
|
|
2573
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2574
|
+
? channel.polyphonicKeyPressureTable[5] * note.pressure
|
|
2575
|
+
: 0;
|
|
2576
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254;
|
|
2577
|
+
this.setModLfoToVolume(note, pressure);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
handleChannelPressureSysEx(data, tableName) {
|
|
2513
2581
|
const channelNumber = data[4];
|
|
2514
|
-
const table = this.channels[channelNumber]
|
|
2582
|
+
const table = this.channels[channelNumber][tableName];
|
|
2515
2583
|
for (let i = 5; i < data.length - 1; i += 2) {
|
|
2516
2584
|
const pp = data[i];
|
|
2517
2585
|
const rr = data[i + 1];
|
|
2518
2586
|
table[pp] = rr;
|
|
2519
2587
|
}
|
|
2520
2588
|
}
|
|
2589
|
+
initControlTable() {
|
|
2590
|
+
const channelCount = 128;
|
|
2591
|
+
const slotSize = 6;
|
|
2592
|
+
const defaultValues = [64, 64, 64, 0, 0, 0];
|
|
2593
|
+
const table = new Uint8Array(channelCount * slotSize);
|
|
2594
|
+
for (let ch = 0; ch < channelCount; ch++) {
|
|
2595
|
+
const offset = ch * slotSize;
|
|
2596
|
+
table.set(defaultValues, offset);
|
|
2597
|
+
}
|
|
2598
|
+
return table;
|
|
2599
|
+
}
|
|
2600
|
+
applyControlTable(channel, controllerType) {
|
|
2601
|
+
const slotSize = 6;
|
|
2602
|
+
const offset = controllerType * slotSize;
|
|
2603
|
+
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2604
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
2605
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
2606
|
+
const note = noteList[i];
|
|
2607
|
+
if (!note)
|
|
2608
|
+
continue;
|
|
2609
|
+
this.applyDestinationSettings(channel, note, table);
|
|
2610
|
+
}
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
handleControlChangeSysEx(data) {
|
|
2614
|
+
const channelNumber = data[4];
|
|
2615
|
+
const controllerType = data[5];
|
|
2616
|
+
const table = this.channels[channelNumber].controlTable[controllerType];
|
|
2617
|
+
for (let i = 6; i < data.length - 1; i += 2) {
|
|
2618
|
+
const pp = data[i];
|
|
2619
|
+
const rr = data[i + 1];
|
|
2620
|
+
table[pp] = rr;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2521
2623
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
2522
2624
|
const index = keyNumber * 128 + controllerType;
|
|
2523
2625
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
@@ -2569,7 +2671,8 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2569
2671
|
currentBufferSource: null,
|
|
2570
2672
|
detune: 0,
|
|
2571
2673
|
scaleOctaveTuningTable: new Array(12).fill(0), // cent
|
|
2572
|
-
|
|
2674
|
+
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2675
|
+
polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2573
2676
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2574
2677
|
program: 0,
|
|
2575
2678
|
bank: 121 * 128,
|
|
@@ -2584,16 +2687,3 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2584
2687
|
modulationDepthRange: 50, // cent
|
|
2585
2688
|
}
|
|
2586
2689
|
});
|
|
2587
|
-
Object.defineProperty(Midy, "controllerDestinationSettings", {
|
|
2588
|
-
enumerable: true,
|
|
2589
|
-
configurable: true,
|
|
2590
|
-
writable: true,
|
|
2591
|
-
value: {
|
|
2592
|
-
pitchControl: 0,
|
|
2593
|
-
filterCutoffControl: 0,
|
|
2594
|
-
amplitudeControl: 1,
|
|
2595
|
-
lfoPitchDepth: 0,
|
|
2596
|
-
lfoFilterDepth: 0,
|
|
2597
|
-
lfoAmplitudeDepth: 0,
|
|
2598
|
-
}
|
|
2599
|
-
});
|