@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.js
CHANGED
|
@@ -86,6 +86,12 @@ class Note {
|
|
|
86
86
|
writable: true,
|
|
87
87
|
value: void 0
|
|
88
88
|
});
|
|
89
|
+
Object.defineProperty(this, "pressure", {
|
|
90
|
+
enumerable: true,
|
|
91
|
+
configurable: true,
|
|
92
|
+
writable: true,
|
|
93
|
+
value: 0
|
|
94
|
+
});
|
|
89
95
|
this.noteNumber = noteNumber;
|
|
90
96
|
this.velocity = velocity;
|
|
91
97
|
this.startTime = startTime;
|
|
@@ -97,7 +103,7 @@ class Note {
|
|
|
97
103
|
const defaultControllerState = {
|
|
98
104
|
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
99
105
|
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
100
|
-
|
|
106
|
+
polyphonicKeyPressure: { type: 10, defaultValue: 0 },
|
|
101
107
|
channelPressure: { type: 13, defaultValue: 0 },
|
|
102
108
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
103
109
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
@@ -359,15 +365,15 @@ export class Midy {
|
|
|
359
365
|
});
|
|
360
366
|
this.audioContext = audioContext;
|
|
361
367
|
this.options = { ...this.defaultOptions, ...options };
|
|
362
|
-
this.
|
|
368
|
+
this.masterVolume = new GainNode(audioContext);
|
|
363
369
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
364
370
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
365
371
|
this.channels = this.createChannels(audioContext);
|
|
366
372
|
this.reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
367
373
|
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
368
|
-
this.chorusEffect.output.connect(this.
|
|
369
|
-
this.reverbEffect.output.connect(this.
|
|
370
|
-
this.
|
|
374
|
+
this.chorusEffect.output.connect(this.masterVolume);
|
|
375
|
+
this.reverbEffect.output.connect(this.masterVolume);
|
|
376
|
+
this.masterVolume.connect(audioContext.destination);
|
|
371
377
|
this.GM2SystemOn();
|
|
372
378
|
}
|
|
373
379
|
initSoundFontTable() {
|
|
@@ -413,7 +419,7 @@ export class Midy {
|
|
|
413
419
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
414
420
|
gainL.connect(merger, 0, 0);
|
|
415
421
|
gainR.connect(merger, 0, 1);
|
|
416
|
-
merger.connect(this.
|
|
422
|
+
merger.connect(this.masterVolume);
|
|
417
423
|
return {
|
|
418
424
|
gainL,
|
|
419
425
|
gainR,
|
|
@@ -425,15 +431,10 @@ export class Midy {
|
|
|
425
431
|
return {
|
|
426
432
|
...this.constructor.channelSettings,
|
|
427
433
|
state: new ControllerState(),
|
|
434
|
+
controlTable: this.initControlTable(),
|
|
428
435
|
...this.setChannelAudioNodes(audioContext),
|
|
429
436
|
scheduledNotes: new Map(),
|
|
430
437
|
sostenutoNotes: new Map(),
|
|
431
|
-
polyphonicKeyPressure: {
|
|
432
|
-
...this.constructor.controllerDestinationSettings,
|
|
433
|
-
},
|
|
434
|
-
channelPressure: {
|
|
435
|
-
...this.constructor.controllerDestinationSettings,
|
|
436
|
-
},
|
|
437
438
|
};
|
|
438
439
|
});
|
|
439
440
|
return channels;
|
|
@@ -938,28 +939,31 @@ export class Midy {
|
|
|
938
939
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
939
940
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
940
941
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
941
|
-
const pressureDepth = (channel.
|
|
942
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
942
943
|
const pressure = pressureDepth * channel.state.channelPressure;
|
|
943
944
|
return tuning + pitch + pressure;
|
|
944
945
|
}
|
|
945
946
|
calcNoteDetune(channel, note) {
|
|
946
947
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
947
948
|
}
|
|
948
|
-
|
|
949
|
-
const now = this.audioContext.currentTime;
|
|
949
|
+
updateChannelDetune(channel) {
|
|
950
950
|
channel.scheduledNotes.forEach((noteList) => {
|
|
951
951
|
for (let i = 0; i < noteList.length; i++) {
|
|
952
952
|
const note = noteList[i];
|
|
953
953
|
if (!note)
|
|
954
954
|
continue;
|
|
955
|
-
|
|
956
|
-
const detune = channel.detune + noteDetune;
|
|
957
|
-
note.bufferSource.detune
|
|
958
|
-
.cancelScheduledValues(now)
|
|
959
|
-
.setValueAtTime(detune, now);
|
|
955
|
+
this.updateDetune(channel, note, 0);
|
|
960
956
|
}
|
|
961
957
|
});
|
|
962
958
|
}
|
|
959
|
+
updateDetune(channel, note, pressure) {
|
|
960
|
+
const now = this.audioContext.currentTime;
|
|
961
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
962
|
+
const detune = channel.detune + noteDetune + pressure;
|
|
963
|
+
note.bufferSource.detune
|
|
964
|
+
.cancelScheduledValues(now)
|
|
965
|
+
.setValueAtTime(detune, now);
|
|
966
|
+
}
|
|
963
967
|
getPortamentoTime(channel) {
|
|
964
968
|
const factor = 5 * Math.log(10) / 127;
|
|
965
969
|
const time = channel.state.portamentoTime;
|
|
@@ -977,14 +981,12 @@ export class Midy {
|
|
|
977
981
|
.setValueAtTime(0, volDelay)
|
|
978
982
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
979
983
|
}
|
|
980
|
-
setVolumeEnvelope(channel, note) {
|
|
984
|
+
setVolumeEnvelope(channel, note, pressure) {
|
|
981
985
|
const now = this.audioContext.currentTime;
|
|
982
986
|
const state = channel.state;
|
|
983
987
|
const { voiceParams, startTime } = note;
|
|
984
|
-
const pressureDepth = channel.pressureTable[2] / 64;
|
|
985
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
986
988
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
987
|
-
pressure;
|
|
989
|
+
(1 + pressure);
|
|
988
990
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
989
991
|
const volDelay = startTime + voiceParams.volDelay;
|
|
990
992
|
const volAttack = volDelay + voiceParams.volAttack * state.attackTime * 2;
|
|
@@ -1032,10 +1034,8 @@ export class Midy {
|
|
|
1032
1034
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1033
1035
|
const softPedalFactor = 1 -
|
|
1034
1036
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1038
|
-
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1037
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
1038
|
+
softPedalFactor *
|
|
1039
1039
|
state.brightness * 2;
|
|
1040
1040
|
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1041
1041
|
const sustainFreq = baseFreq +
|
|
@@ -1050,15 +1050,17 @@ export class Midy {
|
|
|
1050
1050
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1051
1051
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1052
1052
|
}
|
|
1053
|
-
setFilterEnvelope(channel, note) {
|
|
1053
|
+
setFilterEnvelope(channel, note, pressure) {
|
|
1054
1054
|
const now = this.audioContext.currentTime;
|
|
1055
1055
|
const state = channel.state;
|
|
1056
1056
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1057
1057
|
const softPedalFactor = 1 -
|
|
1058
1058
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1059
|
-
const
|
|
1059
|
+
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1060
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1061
|
+
state.brightness * 2;
|
|
1062
|
+
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1060
1063
|
softPedalFactor * state.brightness * 2;
|
|
1061
|
-
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1062
1064
|
const sustainFreq = baseFreq +
|
|
1063
1065
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1064
1066
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1085,9 +1087,9 @@ export class Midy {
|
|
|
1085
1087
|
gain: voiceParams.modLfoToFilterFc,
|
|
1086
1088
|
});
|
|
1087
1089
|
note.modulationDepth = new GainNode(this.audioContext);
|
|
1088
|
-
this.setModLfoToPitch(channel, note);
|
|
1090
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1089
1091
|
note.volumeDepth = new GainNode(this.audioContext);
|
|
1090
|
-
this.setModLfoToVolume(
|
|
1092
|
+
this.setModLfoToVolume(note, 0);
|
|
1091
1093
|
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
1092
1094
|
note.modulationLFO.connect(note.filterDepth);
|
|
1093
1095
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
@@ -1100,8 +1102,7 @@ export class Midy {
|
|
|
1100
1102
|
const { voiceParams } = note;
|
|
1101
1103
|
const state = channel.state;
|
|
1102
1104
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1103
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1104
|
-
state.vibratoRate,
|
|
1105
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
|
|
1105
1106
|
});
|
|
1106
1107
|
note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
|
|
1107
1108
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
@@ -1130,8 +1131,8 @@ export class Midy {
|
|
|
1130
1131
|
}
|
|
1131
1132
|
else {
|
|
1132
1133
|
note.portamento = false;
|
|
1133
|
-
this.setVolumeEnvelope(channel, note);
|
|
1134
|
-
this.setFilterEnvelope(channel, note);
|
|
1134
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1135
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1135
1136
|
}
|
|
1136
1137
|
if (0 < state.vibratoDepth) {
|
|
1137
1138
|
this.startVibrato(channel, note, startTime);
|
|
@@ -1346,16 +1347,12 @@ export class Midy {
|
|
|
1346
1347
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
1347
1348
|
const now = this.audioContext.currentTime;
|
|
1348
1349
|
const channel = this.channels[channelNumber];
|
|
1349
|
-
pressure
|
|
1350
|
+
channel.state.polyphonicKeyPressure = pressure / 127;
|
|
1351
|
+
const table = channel.polyphonicKeyPressureTable;
|
|
1350
1352
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1351
|
-
if (
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const gain = activeNote.gainL.gain.value;
|
|
1355
|
-
activeNote.volumeNode.gain
|
|
1356
|
-
.cancelScheduledValues(now)
|
|
1357
|
-
.setValueAtTime(gain * pressure, now);
|
|
1358
|
-
}
|
|
1353
|
+
if (activeNotes.has(noteNumber)) {
|
|
1354
|
+
const note = activeNotes.get(noteNumber);
|
|
1355
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1359
1356
|
}
|
|
1360
1357
|
// this.applyVoiceParams(channel, 10);
|
|
1361
1358
|
}
|
|
@@ -1369,40 +1366,21 @@ export class Midy {
|
|
|
1369
1366
|
const prev = channel.state.channelPressure;
|
|
1370
1367
|
const next = value / 127;
|
|
1371
1368
|
channel.state.channelPressure = next;
|
|
1372
|
-
if (channel.
|
|
1373
|
-
const pressureDepth = (channel.
|
|
1369
|
+
if (channel.channelPressureTable[0] !== 64) {
|
|
1370
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
1374
1371
|
channel.detune += pressureDepth * (next - prev);
|
|
1375
1372
|
}
|
|
1373
|
+
const table = channel.channelPressureTable;
|
|
1376
1374
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1377
1375
|
for (let i = 0; i < noteList.length; i++) {
|
|
1378
1376
|
const note = noteList[i];
|
|
1379
1377
|
if (!note)
|
|
1380
1378
|
continue;
|
|
1381
|
-
this.
|
|
1379
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1382
1380
|
}
|
|
1383
1381
|
});
|
|
1384
1382
|
// this.applyVoiceParams(channel, 13);
|
|
1385
1383
|
}
|
|
1386
|
-
setChannelPressure(channel, note) {
|
|
1387
|
-
if (channel.pressureTable[0] !== 64) {
|
|
1388
|
-
this.updateDetune(channel);
|
|
1389
|
-
}
|
|
1390
|
-
if (channel.pressureTable[1] !== 64 && !note.portamento) {
|
|
1391
|
-
this.setFilterEnvelope(channel, note);
|
|
1392
|
-
}
|
|
1393
|
-
if (channel.pressureTable[2] !== 64 && !note.portamento) {
|
|
1394
|
-
this.setVolumeEnvelope(channel, note);
|
|
1395
|
-
}
|
|
1396
|
-
if (channel.pressureTable[3] !== 0) {
|
|
1397
|
-
this.setModLfoToPitch(channel, note);
|
|
1398
|
-
}
|
|
1399
|
-
if (channel.pressureTable[4] !== 0) {
|
|
1400
|
-
this.setModLfoToFilterFc(channel, note);
|
|
1401
|
-
}
|
|
1402
|
-
if (channel.pressureTable[5] !== 0) {
|
|
1403
|
-
this.setModLfoToVolume(channel, note);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
1384
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1407
1385
|
const pitchBend = msb * 128 + lsb;
|
|
1408
1386
|
this.setPitchBend(channelNumber, pitchBend);
|
|
@@ -1414,16 +1392,13 @@ export class Midy {
|
|
|
1414
1392
|
const next = (value - 8192) / 8192;
|
|
1415
1393
|
state.pitchWheel = value / 16383;
|
|
1416
1394
|
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1417
|
-
this.
|
|
1395
|
+
this.updateChannelDetune(channel);
|
|
1418
1396
|
this.applyVoiceParams(channel, 14);
|
|
1419
1397
|
}
|
|
1420
|
-
setModLfoToPitch(channel, note) {
|
|
1398
|
+
setModLfoToPitch(channel, note, pressure) {
|
|
1421
1399
|
const now = this.audioContext.currentTime;
|
|
1422
|
-
const pressureDepth = channel.pressureTable[3] / 127 * 600;
|
|
1423
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1424
1400
|
const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
|
|
1425
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1426
|
-
channel.state.modulationDepth;
|
|
1401
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1427
1402
|
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1428
1403
|
note.modulationDepth.gain
|
|
1429
1404
|
.cancelScheduledValues(now)
|
|
@@ -1439,22 +1414,18 @@ export class Midy {
|
|
|
1439
1414
|
.cancelScheduledValues(now)
|
|
1440
1415
|
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1441
1416
|
}
|
|
1442
|
-
setModLfoToFilterFc(
|
|
1417
|
+
setModLfoToFilterFc(note, pressure) {
|
|
1443
1418
|
const now = this.audioContext.currentTime;
|
|
1444
|
-
const pressureDepth = channel.pressureTable[4] / 127 * 2400;
|
|
1445
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1446
1419
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
|
|
1447
1420
|
note.filterDepth.gain
|
|
1448
1421
|
.cancelScheduledValues(now)
|
|
1449
1422
|
.setValueAtTime(modLfoToFilterFc, now);
|
|
1450
1423
|
}
|
|
1451
|
-
setModLfoToVolume(
|
|
1424
|
+
setModLfoToVolume(note, pressure) {
|
|
1452
1425
|
const now = this.audioContext.currentTime;
|
|
1453
1426
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1454
1427
|
const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1455
|
-
const
|
|
1456
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
1457
|
-
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
|
|
1428
|
+
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
|
|
1458
1429
|
note.volumeDepth.gain
|
|
1459
1430
|
.cancelScheduledValues(now)
|
|
1460
1431
|
.setValueAtTime(volumeDepth, now);
|
|
@@ -1527,11 +1498,18 @@ export class Midy {
|
|
|
1527
1498
|
.cancelScheduledValues(now)
|
|
1528
1499
|
.setValueAtTime(freqModLFO, now);
|
|
1529
1500
|
}
|
|
1501
|
+
setFreqVibLFO(channel, note) {
|
|
1502
|
+
const now = this.audioContext.currentTime;
|
|
1503
|
+
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1504
|
+
note.vibratoLFO.frequency
|
|
1505
|
+
.cancelScheduledValues(now)
|
|
1506
|
+
.setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
|
|
1507
|
+
}
|
|
1530
1508
|
createVoiceParamsHandlers() {
|
|
1531
1509
|
return {
|
|
1532
1510
|
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1533
1511
|
if (0 < channel.state.modulationDepth) {
|
|
1534
|
-
this.setModLfoToPitch(channel, note);
|
|
1512
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1535
1513
|
}
|
|
1536
1514
|
},
|
|
1537
1515
|
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
@@ -1541,12 +1519,12 @@ export class Midy {
|
|
|
1541
1519
|
},
|
|
1542
1520
|
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1543
1521
|
if (0 < channel.state.modulationDepth) {
|
|
1544
|
-
this.setModLfoToFilterFc(
|
|
1522
|
+
this.setModLfoToFilterFc(note, 0);
|
|
1545
1523
|
}
|
|
1546
1524
|
},
|
|
1547
|
-
modLfoToVolume: (channel, note) => {
|
|
1525
|
+
modLfoToVolume: (channel, note, _prevValue) => {
|
|
1548
1526
|
if (0 < channel.state.modulationDepth) {
|
|
1549
|
-
this.setModLfoToVolume(
|
|
1527
|
+
this.setModLfoToVolume(note, 0);
|
|
1550
1528
|
}
|
|
1551
1529
|
},
|
|
1552
1530
|
chorusEffectsSend: (channel, note, prevValue) => {
|
|
@@ -1560,22 +1538,19 @@ export class Midy {
|
|
|
1560
1538
|
delayVibLFO: (channel, note, prevValue) => {
|
|
1561
1539
|
if (0 < channel.state.vibratoDepth) {
|
|
1562
1540
|
const now = this.audioContext.currentTime;
|
|
1563
|
-
const
|
|
1564
|
-
|
|
1541
|
+
const vibratoDelay = channel.state.vibratoDelay * 2;
|
|
1542
|
+
const prevStartTime = note.startTime + prevValue * vibratoDelay;
|
|
1565
1543
|
if (now < prevStartTime)
|
|
1566
1544
|
return;
|
|
1567
|
-
const
|
|
1568
|
-
|
|
1545
|
+
const value = note.voiceParams.delayVibLFO;
|
|
1546
|
+
const startTime = note.startTime + value * vibratoDelay;
|
|
1569
1547
|
note.vibratoLFO.stop(now);
|
|
1570
1548
|
note.vibratoLFO.start(startTime);
|
|
1571
1549
|
}
|
|
1572
1550
|
},
|
|
1573
1551
|
freqVibLFO: (channel, note, _prevValue) => {
|
|
1574
1552
|
if (0 < channel.state.vibratoDepth) {
|
|
1575
|
-
|
|
1576
|
-
note.vibratoLFO.frequency
|
|
1577
|
-
.cancelScheduledValues(now)
|
|
1578
|
-
.setValueAtTime(value * sate.vibratoRate, now);
|
|
1553
|
+
this.setFreqVibLFO(channel, note);
|
|
1579
1554
|
}
|
|
1580
1555
|
},
|
|
1581
1556
|
};
|
|
@@ -1619,7 +1594,7 @@ export class Midy {
|
|
|
1619
1594
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1620
1595
|
}
|
|
1621
1596
|
else {
|
|
1622
|
-
this.setFilterEnvelope(channel, note);
|
|
1597
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1623
1598
|
}
|
|
1624
1599
|
this.setPitchEnvelope(note);
|
|
1625
1600
|
}
|
|
@@ -1633,7 +1608,7 @@ export class Midy {
|
|
|
1633
1608
|
if (key in voiceParams)
|
|
1634
1609
|
noteVoiceParams[key] = voiceParams[key];
|
|
1635
1610
|
}
|
|
1636
|
-
this.setVolumeEnvelope(channel, note);
|
|
1611
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1637
1612
|
}
|
|
1638
1613
|
}
|
|
1639
1614
|
}
|
|
@@ -1682,8 +1657,8 @@ export class Midy {
|
|
|
1682
1657
|
if (handler) {
|
|
1683
1658
|
handler.call(this, channelNumber, value);
|
|
1684
1659
|
const channel = this.channels[channelNumber];
|
|
1685
|
-
|
|
1686
|
-
this.
|
|
1660
|
+
this.applyVoiceParams(channel, controllerType + 128);
|
|
1661
|
+
this.applyControlTable(channel, controllerType);
|
|
1687
1662
|
}
|
|
1688
1663
|
else {
|
|
1689
1664
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1694,13 +1669,14 @@ export class Midy {
|
|
|
1694
1669
|
}
|
|
1695
1670
|
updateModulation(channel) {
|
|
1696
1671
|
const now = this.audioContext.currentTime;
|
|
1672
|
+
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1697
1673
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1698
1674
|
for (let i = 0; i < noteList.length; i++) {
|
|
1699
1675
|
const note = noteList[i];
|
|
1700
1676
|
if (!note)
|
|
1701
1677
|
continue;
|
|
1702
1678
|
if (note.modulationDepth) {
|
|
1703
|
-
note.modulationDepth.gain.setValueAtTime(
|
|
1679
|
+
note.modulationDepth.gain.setValueAtTime(depth, now);
|
|
1704
1680
|
}
|
|
1705
1681
|
else {
|
|
1706
1682
|
this.setPitchEnvelope(note);
|
|
@@ -1711,8 +1687,7 @@ export class Midy {
|
|
|
1711
1687
|
}
|
|
1712
1688
|
setModulationDepth(channelNumber, modulation) {
|
|
1713
1689
|
const channel = this.channels[channelNumber];
|
|
1714
|
-
channel.state.modulationDepth =
|
|
1715
|
-
channel.modulationDepthRange;
|
|
1690
|
+
channel.state.modulationDepth = modulation / 127;
|
|
1716
1691
|
this.updateModulation(channel);
|
|
1717
1692
|
}
|
|
1718
1693
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
@@ -1854,7 +1829,7 @@ export class Midy {
|
|
|
1854
1829
|
continue;
|
|
1855
1830
|
if (note.startTime < now)
|
|
1856
1831
|
continue;
|
|
1857
|
-
this.setVolumeEnvelope(channel, note);
|
|
1832
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1858
1833
|
}
|
|
1859
1834
|
});
|
|
1860
1835
|
}
|
|
@@ -1866,7 +1841,12 @@ export class Midy {
|
|
|
1866
1841
|
const note = noteList[i];
|
|
1867
1842
|
if (!note)
|
|
1868
1843
|
continue;
|
|
1869
|
-
|
|
1844
|
+
if (note.portamento) {
|
|
1845
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1846
|
+
}
|
|
1847
|
+
else {
|
|
1848
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1849
|
+
}
|
|
1870
1850
|
}
|
|
1871
1851
|
});
|
|
1872
1852
|
}
|
|
@@ -1878,7 +1858,7 @@ export class Midy {
|
|
|
1878
1858
|
const note = noteList[i];
|
|
1879
1859
|
if (!note)
|
|
1880
1860
|
continue;
|
|
1881
|
-
this.setVolumeEnvelope(channel, note);
|
|
1861
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1882
1862
|
}
|
|
1883
1863
|
});
|
|
1884
1864
|
}
|
|
@@ -1887,21 +1867,53 @@ export class Midy {
|
|
|
1887
1867
|
channel.state.vibratoRate = vibratoRate / 64;
|
|
1888
1868
|
if (channel.vibratoDepth <= 0)
|
|
1889
1869
|
return;
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
.
|
|
1870
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1871
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1872
|
+
const note = noteList[i];
|
|
1873
|
+
if (!note)
|
|
1874
|
+
continue;
|
|
1875
|
+
this.setVibLfoToPitch(channel, note);
|
|
1876
|
+
}
|
|
1896
1877
|
});
|
|
1897
1878
|
}
|
|
1898
1879
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1899
1880
|
const channel = this.channels[channelNumber];
|
|
1881
|
+
const prev = channel.state.vibratoDepth;
|
|
1900
1882
|
channel.state.vibratoDepth = vibratoDepth / 64;
|
|
1883
|
+
if (0 < prev) {
|
|
1884
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1885
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1886
|
+
const note = noteList[i];
|
|
1887
|
+
if (!note)
|
|
1888
|
+
continue;
|
|
1889
|
+
this.setFreqVibLFO(channel, note);
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
else {
|
|
1894
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1895
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1896
|
+
const note = noteList[i];
|
|
1897
|
+
if (!note)
|
|
1898
|
+
continue;
|
|
1899
|
+
this.startVibrato(channel, note, note.startTime);
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1901
1903
|
}
|
|
1902
1904
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1903
1905
|
const channel = this.channels[channelNumber];
|
|
1904
1906
|
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1907
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1908
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1909
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1910
|
+
const note = noteList[i];
|
|
1911
|
+
if (!note)
|
|
1912
|
+
continue;
|
|
1913
|
+
this.startVibrato(channel, note, note.startTime);
|
|
1914
|
+
}
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1905
1917
|
}
|
|
1906
1918
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1907
1919
|
const channel = this.channels[channelNumber];
|
|
@@ -2066,7 +2078,7 @@ export class Midy {
|
|
|
2066
2078
|
const next = value / 128;
|
|
2067
2079
|
state.pitchWheelSensitivity = next;
|
|
2068
2080
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2069
|
-
this.
|
|
2081
|
+
this.updateChannelDetune(channel);
|
|
2070
2082
|
this.applyVoiceParams(channel, 16);
|
|
2071
2083
|
}
|
|
2072
2084
|
handleFineTuningRPN(channelNumber) {
|
|
@@ -2081,7 +2093,7 @@ export class Midy {
|
|
|
2081
2093
|
const next = (value - 8192) / 8.192; // cent
|
|
2082
2094
|
channel.fineTuning = next;
|
|
2083
2095
|
channel.detune += next - prev;
|
|
2084
|
-
this.
|
|
2096
|
+
this.updateChannelDetune(channel);
|
|
2085
2097
|
}
|
|
2086
2098
|
handleCoarseTuningRPN(channelNumber) {
|
|
2087
2099
|
const channel = this.channels[channelNumber];
|
|
@@ -2095,7 +2107,7 @@ export class Midy {
|
|
|
2095
2107
|
const next = (value - 64) * 100; // cent
|
|
2096
2108
|
channel.coarseTuning = next;
|
|
2097
2109
|
channel.detune += next - prev;
|
|
2098
|
-
this.
|
|
2110
|
+
this.updateChannelDetune(channel);
|
|
2099
2111
|
}
|
|
2100
2112
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
2101
2113
|
const channel = this.channels[channelNumber];
|
|
@@ -2106,7 +2118,6 @@ export class Midy {
|
|
|
2106
2118
|
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
2107
2119
|
const channel = this.channels[channelNumber];
|
|
2108
2120
|
channel.modulationDepthRange = modulationDepthRange;
|
|
2109
|
-
channel.modulationDepth = (modulation / 127) * modulationDepthRange;
|
|
2110
2121
|
this.updateModulation(channel);
|
|
2111
2122
|
}
|
|
2112
2123
|
allSoundOff(channelNumber) {
|
|
@@ -2127,7 +2138,7 @@ export class Midy {
|
|
|
2127
2138
|
const state = channel.state;
|
|
2128
2139
|
for (let i = 0; i < stateTypes.length; i++) {
|
|
2129
2140
|
const type = stateTypes[i];
|
|
2130
|
-
state[type] = defaultControllerState[type];
|
|
2141
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
2131
2142
|
}
|
|
2132
2143
|
const settingTypes = [
|
|
2133
2144
|
"rpnMSB",
|
|
@@ -2230,10 +2241,11 @@ export class Midy {
|
|
|
2230
2241
|
case 9:
|
|
2231
2242
|
switch (data[3]) {
|
|
2232
2243
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2233
|
-
return this.
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
//
|
|
2244
|
+
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2245
|
+
case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2246
|
+
return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
|
|
2247
|
+
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2248
|
+
return this.handleControlChangeSysEx(data);
|
|
2237
2249
|
default:
|
|
2238
2250
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2239
2251
|
}
|
|
@@ -2260,8 +2272,8 @@ export class Midy {
|
|
|
2260
2272
|
}
|
|
2261
2273
|
else {
|
|
2262
2274
|
const now = this.audioContext.currentTime;
|
|
2263
|
-
this.
|
|
2264
|
-
this.
|
|
2275
|
+
this.masterVolume.gain.cancelScheduledValues(now);
|
|
2276
|
+
this.masterVolume.gain.setValueAtTime(volume * volume, now);
|
|
2265
2277
|
}
|
|
2266
2278
|
}
|
|
2267
2279
|
handleMasterFineTuningSysEx(data) {
|
|
@@ -2273,7 +2285,7 @@ export class Midy {
|
|
|
2273
2285
|
const next = (value - 8192) / 8.192; // cent
|
|
2274
2286
|
this.masterFineTuning = next;
|
|
2275
2287
|
channel.detune += next - prev;
|
|
2276
|
-
this.
|
|
2288
|
+
this.updateChannelDetune(channel);
|
|
2277
2289
|
}
|
|
2278
2290
|
handleMasterCoarseTuningSysEx(data) {
|
|
2279
2291
|
const coarseTuning = data[4];
|
|
@@ -2284,7 +2296,7 @@ export class Midy {
|
|
|
2284
2296
|
const next = (value - 64) * 100; // cent
|
|
2285
2297
|
this.masterCoarseTuning = next;
|
|
2286
2298
|
channel.detune += next - prev;
|
|
2287
|
-
this.
|
|
2299
|
+
this.updateChannelDetune(channel);
|
|
2288
2300
|
}
|
|
2289
2301
|
handleGlobalParameterControlSysEx(data) {
|
|
2290
2302
|
if (data[7] === 1) {
|
|
@@ -2506,15 +2518,105 @@ export class Midy {
|
|
|
2506
2518
|
}
|
|
2507
2519
|
}
|
|
2508
2520
|
}
|
|
2509
|
-
|
|
2521
|
+
applyDestinationSettings(channel, note, table) {
|
|
2522
|
+
if (table[0] !== 64) {
|
|
2523
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2524
|
+
? channel.polyphonicKeyPressureTable[0] * note.pressure
|
|
2525
|
+
: 0;
|
|
2526
|
+
const pressure = (polyphonicKeyPressure - 64) / 37.5; // 2400 / 64;
|
|
2527
|
+
this.updateDetune(channel, note, pressure);
|
|
2528
|
+
}
|
|
2529
|
+
if (!note.portamento) {
|
|
2530
|
+
if (table[1] !== 64) {
|
|
2531
|
+
const channelPressure = channel.channelPressureTable[1] *
|
|
2532
|
+
channel.state.channelPressure;
|
|
2533
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2534
|
+
? channel.polyphonicKeyPressureTable[1] * note.pressure
|
|
2535
|
+
: 0;
|
|
2536
|
+
const pressure = (channelPressure + polyphonicKeyPressure - 128) * 15;
|
|
2537
|
+
this.setFilterEnvelope(channel, note, pressure);
|
|
2538
|
+
}
|
|
2539
|
+
if (table[2] !== 64) {
|
|
2540
|
+
const channelPressure = channel.channelPressureTable[2] *
|
|
2541
|
+
channel.state.channelPressure;
|
|
2542
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2543
|
+
? channel.polyphonicKeyPressureTable[2] * note.pressure
|
|
2544
|
+
: 0;
|
|
2545
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 128;
|
|
2546
|
+
this.setVolumeEnvelope(channel, note, pressure);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
if (table[3] !== 0) {
|
|
2550
|
+
const channelPressure = channel.channelPressureTable[3] *
|
|
2551
|
+
channel.state.channelPressure;
|
|
2552
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2553
|
+
? channel.polyphonicKeyPressureTable[3] * note.pressure
|
|
2554
|
+
: 0;
|
|
2555
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 600;
|
|
2556
|
+
this.setModLfoToPitch(channel, note, pressure);
|
|
2557
|
+
}
|
|
2558
|
+
if (table[4] !== 0) {
|
|
2559
|
+
const channelPressure = channel.channelPressureTable[4] *
|
|
2560
|
+
channel.state.channelPressure;
|
|
2561
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2562
|
+
? channel.polyphonicKeyPressureTable[4] * note.pressure
|
|
2563
|
+
: 0;
|
|
2564
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 2400;
|
|
2565
|
+
this.setModLfoToFilterFc(note, pressure);
|
|
2566
|
+
}
|
|
2567
|
+
if (table[5] !== 0) {
|
|
2568
|
+
const channelPressure = channel.channelPressureTable[5] *
|
|
2569
|
+
channel.state.channelPressure;
|
|
2570
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2571
|
+
? channel.polyphonicKeyPressureTable[5] * note.pressure
|
|
2572
|
+
: 0;
|
|
2573
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254;
|
|
2574
|
+
this.setModLfoToVolume(note, pressure);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
handleChannelPressureSysEx(data, tableName) {
|
|
2510
2578
|
const channelNumber = data[4];
|
|
2511
|
-
const table = this.channels[channelNumber]
|
|
2579
|
+
const table = this.channels[channelNumber][tableName];
|
|
2512
2580
|
for (let i = 5; i < data.length - 1; i += 2) {
|
|
2513
2581
|
const pp = data[i];
|
|
2514
2582
|
const rr = data[i + 1];
|
|
2515
2583
|
table[pp] = rr;
|
|
2516
2584
|
}
|
|
2517
2585
|
}
|
|
2586
|
+
initControlTable() {
|
|
2587
|
+
const channelCount = 128;
|
|
2588
|
+
const slotSize = 6;
|
|
2589
|
+
const defaultValues = [64, 64, 64, 0, 0, 0];
|
|
2590
|
+
const table = new Uint8Array(channelCount * slotSize);
|
|
2591
|
+
for (let ch = 0; ch < channelCount; ch++) {
|
|
2592
|
+
const offset = ch * slotSize;
|
|
2593
|
+
table.set(defaultValues, offset);
|
|
2594
|
+
}
|
|
2595
|
+
return table;
|
|
2596
|
+
}
|
|
2597
|
+
applyControlTable(channel, controllerType) {
|
|
2598
|
+
const slotSize = 6;
|
|
2599
|
+
const offset = controllerType * slotSize;
|
|
2600
|
+
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2601
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
2602
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
2603
|
+
const note = noteList[i];
|
|
2604
|
+
if (!note)
|
|
2605
|
+
continue;
|
|
2606
|
+
this.applyDestinationSettings(channel, note, table);
|
|
2607
|
+
}
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
handleControlChangeSysEx(data) {
|
|
2611
|
+
const channelNumber = data[4];
|
|
2612
|
+
const controllerType = data[5];
|
|
2613
|
+
const table = this.channels[channelNumber].controlTable[controllerType];
|
|
2614
|
+
for (let i = 6; i < data.length - 1; i += 2) {
|
|
2615
|
+
const pp = data[i];
|
|
2616
|
+
const rr = data[i + 1];
|
|
2617
|
+
table[pp] = rr;
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2518
2620
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
2519
2621
|
const index = keyNumber * 128 + controllerType;
|
|
2520
2622
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
@@ -2565,7 +2667,8 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2565
2667
|
currentBufferSource: null,
|
|
2566
2668
|
detune: 0,
|
|
2567
2669
|
scaleOctaveTuningTable: new Array(12).fill(0), // cent
|
|
2568
|
-
|
|
2670
|
+
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2671
|
+
polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2569
2672
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2570
2673
|
program: 0,
|
|
2571
2674
|
bank: 121 * 128,
|
|
@@ -2580,16 +2683,3 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2580
2683
|
modulationDepthRange: 50, // cent
|
|
2581
2684
|
}
|
|
2582
2685
|
});
|
|
2583
|
-
Object.defineProperty(Midy, "controllerDestinationSettings", {
|
|
2584
|
-
enumerable: true,
|
|
2585
|
-
configurable: true,
|
|
2586
|
-
writable: true,
|
|
2587
|
-
value: {
|
|
2588
|
-
pitchControl: 0,
|
|
2589
|
-
filterCutoffControl: 0,
|
|
2590
|
-
amplitudeControl: 1,
|
|
2591
|
-
lfoPitchDepth: 0,
|
|
2592
|
-
lfoFilterDepth: 0,
|
|
2593
|
-
lfoAmplitudeDepth: 0,
|
|
2594
|
-
}
|
|
2595
|
-
});
|