@marmooo/midy 0.3.3 → 0.3.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/esm/midy-GM1.d.ts +3 -3
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +47 -50
- package/esm/midy-GM2.d.ts +13 -31
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +185 -206
- package/esm/midy-GMLite.d.ts +3 -3
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +47 -50
- package/esm/midy.d.ts +14 -31
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +225 -225
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +3 -3
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +47 -50
- package/script/midy-GM2.d.ts +13 -31
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +185 -206
- package/script/midy-GMLite.d.ts +3 -3
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +47 -50
- package/script/midy.d.ts +14 -31
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +225 -225
package/script/midy.js
CHANGED
|
@@ -47,24 +47,6 @@ class Note {
|
|
|
47
47
|
writable: true,
|
|
48
48
|
value: void 0
|
|
49
49
|
});
|
|
50
|
-
Object.defineProperty(this, "volumeNode", {
|
|
51
|
-
enumerable: true,
|
|
52
|
-
configurable: true,
|
|
53
|
-
writable: true,
|
|
54
|
-
value: void 0
|
|
55
|
-
});
|
|
56
|
-
Object.defineProperty(this, "gainL", {
|
|
57
|
-
enumerable: true,
|
|
58
|
-
configurable: true,
|
|
59
|
-
writable: true,
|
|
60
|
-
value: void 0
|
|
61
|
-
});
|
|
62
|
-
Object.defineProperty(this, "gainR", {
|
|
63
|
-
enumerable: true,
|
|
64
|
-
configurable: true,
|
|
65
|
-
writable: true,
|
|
66
|
-
value: void 0
|
|
67
|
-
});
|
|
68
50
|
Object.defineProperty(this, "modulationLFO", {
|
|
69
51
|
enumerable: true,
|
|
70
52
|
configurable: true,
|
|
@@ -217,6 +199,16 @@ class ControllerState {
|
|
|
217
199
|
}
|
|
218
200
|
}
|
|
219
201
|
}
|
|
202
|
+
const volumeEnvelopeKeys = [
|
|
203
|
+
"volDelay",
|
|
204
|
+
"volAttack",
|
|
205
|
+
"volHold",
|
|
206
|
+
"volDecay",
|
|
207
|
+
"volSustain",
|
|
208
|
+
"volRelease",
|
|
209
|
+
"initialAttenuation",
|
|
210
|
+
];
|
|
211
|
+
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
220
212
|
const filterEnvelopeKeys = [
|
|
221
213
|
"modEnvToPitch",
|
|
222
214
|
"initialFilterFc",
|
|
@@ -226,22 +218,20 @@ const filterEnvelopeKeys = [
|
|
|
226
218
|
"modHold",
|
|
227
219
|
"modDecay",
|
|
228
220
|
"modSustain",
|
|
229
|
-
"modRelease",
|
|
230
|
-
"playbackRate",
|
|
231
221
|
];
|
|
232
222
|
const filterEnvelopeKeySet = new Set(filterEnvelopeKeys);
|
|
233
|
-
const
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
"
|
|
237
|
-
"
|
|
238
|
-
"
|
|
239
|
-
"
|
|
240
|
-
"
|
|
223
|
+
const pitchEnvelopeKeys = [
|
|
224
|
+
"modEnvToPitch",
|
|
225
|
+
"modDelay",
|
|
226
|
+
"modAttack",
|
|
227
|
+
"modHold",
|
|
228
|
+
"modDecay",
|
|
229
|
+
"modSustain",
|
|
230
|
+
"playbackRate",
|
|
241
231
|
];
|
|
242
|
-
const
|
|
232
|
+
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
243
233
|
class Midy {
|
|
244
|
-
constructor(audioContext
|
|
234
|
+
constructor(audioContext) {
|
|
245
235
|
Object.defineProperty(this, "mode", {
|
|
246
236
|
enumerable: true,
|
|
247
237
|
configurable: true,
|
|
@@ -265,6 +255,7 @@ class Midy {
|
|
|
265
255
|
configurable: true,
|
|
266
256
|
writable: true,
|
|
267
257
|
value: {
|
|
258
|
+
algorithm: "SchroederReverb",
|
|
268
259
|
time: this.getReverbTime(64),
|
|
269
260
|
feedback: 0.8,
|
|
270
261
|
}
|
|
@@ -413,30 +404,7 @@ class Midy {
|
|
|
413
404
|
writable: true,
|
|
414
405
|
value: new Array(this.numChannels * drumExclusiveClassCount)
|
|
415
406
|
});
|
|
416
|
-
Object.defineProperty(this, "defaultOptions", {
|
|
417
|
-
enumerable: true,
|
|
418
|
-
configurable: true,
|
|
419
|
-
writable: true,
|
|
420
|
-
value: {
|
|
421
|
-
reverbAlgorithm: (audioContext) => {
|
|
422
|
-
const { time: rt60, feedback } = this.reverb;
|
|
423
|
-
// const delay = this.calcDelay(rt60, feedback);
|
|
424
|
-
// const impulse = this.createConvolutionReverbImpulse(
|
|
425
|
-
// audioContext,
|
|
426
|
-
// rt60,
|
|
427
|
-
// delay,
|
|
428
|
-
// );
|
|
429
|
-
// return this.createConvolutionReverb(audioContext, impulse);
|
|
430
|
-
const combFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
431
|
-
const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
432
|
-
const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
433
|
-
const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
434
|
-
return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
|
|
435
|
-
},
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
407
|
this.audioContext = audioContext;
|
|
439
|
-
this.options = { ...this.defaultOptions, ...options };
|
|
440
408
|
this.masterVolume = new GainNode(audioContext);
|
|
441
409
|
this.scheduler = new GainNode(audioContext, { gain: 0 });
|
|
442
410
|
this.schedulerBuffer = new AudioBuffer({
|
|
@@ -446,7 +414,7 @@ class Midy {
|
|
|
446
414
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
447
415
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
448
416
|
this.channels = this.createChannels(audioContext);
|
|
449
|
-
this.reverbEffect = this.
|
|
417
|
+
this.reverbEffect = this.createReverbEffect(audioContext);
|
|
450
418
|
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
451
419
|
this.chorusEffect.output.connect(this.masterVolume);
|
|
452
420
|
this.reverbEffect.output.connect(this.masterVolume);
|
|
@@ -510,7 +478,7 @@ class Midy {
|
|
|
510
478
|
this.timeline = midiData.timeline;
|
|
511
479
|
this.totalTime = this.calcTotalTime();
|
|
512
480
|
}
|
|
513
|
-
|
|
481
|
+
createChannelAudioNodes(audioContext) {
|
|
514
482
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
515
483
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
516
484
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
@@ -525,10 +493,10 @@ class Midy {
|
|
|
525
493
|
};
|
|
526
494
|
}
|
|
527
495
|
resetChannelTable(channel) {
|
|
528
|
-
|
|
496
|
+
channel.controlTable.fill(-1);
|
|
529
497
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
530
|
-
channel.channelPressureTable.
|
|
531
|
-
channel.polyphonicKeyPressureTable.
|
|
498
|
+
channel.channelPressureTable.fill(-1);
|
|
499
|
+
channel.polyphonicKeyPressureTable.fill(-1);
|
|
532
500
|
channel.keyBasedInstrumentControlTable.fill(-1);
|
|
533
501
|
}
|
|
534
502
|
createChannels(audioContext) {
|
|
@@ -538,15 +506,17 @@ class Midy {
|
|
|
538
506
|
isDrum: false,
|
|
539
507
|
state: new ControllerState(),
|
|
540
508
|
...this.constructor.channelSettings,
|
|
541
|
-
...this.
|
|
509
|
+
...this.createChannelAudioNodes(audioContext),
|
|
542
510
|
scheduledNotes: [],
|
|
543
511
|
sustainNotes: [],
|
|
544
512
|
sostenutoNotes: [],
|
|
545
513
|
controlTable: this.initControlTable(),
|
|
546
514
|
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
547
|
-
channelPressureTable: new
|
|
548
|
-
polyphonicKeyPressureTable: new
|
|
515
|
+
channelPressureTable: new Int8Array(6).fill(-1),
|
|
516
|
+
polyphonicKeyPressureTable: new Int8Array(6).fill(-1),
|
|
549
517
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
|
|
518
|
+
keyBasedGainLs: new Array(128),
|
|
519
|
+
keyBasedGainRs: new Array(128),
|
|
550
520
|
};
|
|
551
521
|
});
|
|
552
522
|
return channels;
|
|
@@ -588,10 +558,9 @@ class Midy {
|
|
|
588
558
|
createBufferSource(channel, noteNumber, voiceParams, audioBuffer) {
|
|
589
559
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
590
560
|
bufferSource.buffer = audioBuffer;
|
|
591
|
-
bufferSource.loop =
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
561
|
+
bufferSource.loop = channel.isDrum
|
|
562
|
+
? this.isLoopDrum(channel, noteNumber)
|
|
563
|
+
: (voiceParams.sampleModes % 2 !== 0);
|
|
595
564
|
if (bufferSource.loop) {
|
|
596
565
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
597
566
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -835,7 +804,7 @@ class Midy {
|
|
|
835
804
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
836
805
|
const channel = this.channels[channelNumber];
|
|
837
806
|
const promises = [];
|
|
838
|
-
this.processScheduledNotes(channel, (note) => {
|
|
807
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
839
808
|
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
840
809
|
this.notePromises.push(promise);
|
|
841
810
|
promises.push(promise);
|
|
@@ -894,7 +863,7 @@ class Midy {
|
|
|
894
863
|
const now = this.audioContext.currentTime;
|
|
895
864
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
896
865
|
}
|
|
897
|
-
processScheduledNotes(channel, callback) {
|
|
866
|
+
processScheduledNotes(channel, scheduleTime, callback) {
|
|
898
867
|
const scheduledNotes = channel.scheduledNotes;
|
|
899
868
|
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
900
869
|
const note = scheduledNotes[i];
|
|
@@ -902,6 +871,8 @@ class Midy {
|
|
|
902
871
|
continue;
|
|
903
872
|
if (note.ending)
|
|
904
873
|
continue;
|
|
874
|
+
if (note.startTime < scheduleTime)
|
|
875
|
+
continue;
|
|
905
876
|
callback(note);
|
|
906
877
|
}
|
|
907
878
|
}
|
|
@@ -914,7 +885,7 @@ class Midy {
|
|
|
914
885
|
if (note.ending)
|
|
915
886
|
continue;
|
|
916
887
|
if (scheduleTime < note.startTime)
|
|
917
|
-
|
|
888
|
+
break;
|
|
918
889
|
callback(note);
|
|
919
890
|
}
|
|
920
891
|
}
|
|
@@ -1003,6 +974,22 @@ class Midy {
|
|
|
1003
974
|
const output = allpasses.at(-1);
|
|
1004
975
|
return { input, output };
|
|
1005
976
|
}
|
|
977
|
+
createReverbEffect(audioContext) {
|
|
978
|
+
const { algorithm, time: rt60, feedback } = this.reverb;
|
|
979
|
+
switch (algorithm) {
|
|
980
|
+
case "ConvolutionReverb": {
|
|
981
|
+
const impulse = this.createConvolutionReverbImpulse(audioContext, rt60, this.calcDelay(rt60, feedback));
|
|
982
|
+
return this.createConvolutionReverb(audioContext, impulse);
|
|
983
|
+
}
|
|
984
|
+
case "SchroederReverb": {
|
|
985
|
+
const combFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
986
|
+
const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
987
|
+
const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
988
|
+
const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
989
|
+
return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
1006
993
|
createChorusEffect(audioContext) {
|
|
1007
994
|
const input = new GainNode(audioContext);
|
|
1008
995
|
const output = new GainNode(audioContext);
|
|
@@ -1067,15 +1054,22 @@ class Midy {
|
|
|
1067
1054
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
1068
1055
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
1069
1056
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1057
|
+
const channelPressureRaw = channel.channelPressureTable[0];
|
|
1058
|
+
if (0 <= channelPressureRaw) {
|
|
1059
|
+
const channelPressureDepth = (channelPressureRaw - 64) / 37.5; // 2400 / 64;
|
|
1060
|
+
const channelPressure = channelPressureDepth *
|
|
1061
|
+
channel.state.channelPressure;
|
|
1062
|
+
return tuning + pitch + channelPressure;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
return tuning + pitch;
|
|
1066
|
+
}
|
|
1073
1067
|
}
|
|
1074
1068
|
calcNoteDetune(channel, note) {
|
|
1075
1069
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
1076
1070
|
}
|
|
1077
1071
|
updateChannelDetune(channel, scheduleTime) {
|
|
1078
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1072
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1079
1073
|
this.updateDetune(channel, note, scheduleTime);
|
|
1080
1074
|
});
|
|
1081
1075
|
}
|
|
@@ -1324,14 +1318,11 @@ class Midy {
|
|
|
1324
1318
|
async createNote(channel, voice, noteNumber, velocity, startTime, isSF3) {
|
|
1325
1319
|
const now = this.audioContext.currentTime;
|
|
1326
1320
|
const state = channel.state;
|
|
1327
|
-
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1321
|
+
const controllerState = this.getControllerState(channel, noteNumber, velocity, 0);
|
|
1328
1322
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1329
1323
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1330
1324
|
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1331
1325
|
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1332
|
-
note.volumeNode = new GainNode(this.audioContext);
|
|
1333
|
-
note.gainL = new GainNode(this.audioContext);
|
|
1334
|
-
note.gainR = new GainNode(this.audioContext);
|
|
1335
1326
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
1336
1327
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
1337
1328
|
type: "lowpass",
|
|
@@ -1364,9 +1355,6 @@ class Midy {
|
|
|
1364
1355
|
}
|
|
1365
1356
|
note.bufferSource.connect(note.filterNode);
|
|
1366
1357
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1367
|
-
note.volumeEnvelopeNode.connect(note.volumeNode);
|
|
1368
|
-
note.volumeNode.connect(note.gainL);
|
|
1369
|
-
note.volumeNode.connect(note.gainR);
|
|
1370
1358
|
if (0 < state.chorusSendLevel) {
|
|
1371
1359
|
this.setChorusEffectsSend(channel, note, 0, now);
|
|
1372
1360
|
}
|
|
@@ -1425,7 +1413,7 @@ class Midy {
|
|
|
1425
1413
|
}
|
|
1426
1414
|
this.drumExclusiveClassNotes[index] = note;
|
|
1427
1415
|
}
|
|
1428
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime
|
|
1416
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1429
1417
|
const channel = this.channels[channelNumber];
|
|
1430
1418
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
1431
1419
|
const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
|
|
@@ -1437,9 +1425,18 @@ class Midy {
|
|
|
1437
1425
|
return;
|
|
1438
1426
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1439
1427
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1428
|
+
if (channel.isDrum) {
|
|
1429
|
+
const audioContext = this.audioContext;
|
|
1430
|
+
const { gainL, gainR } = this.createChannelAudioNodes(audioContext);
|
|
1431
|
+
channel.keyBasedGainLs[noteNumber] = gainL;
|
|
1432
|
+
channel.keyBasedGainRs[noteNumber] = gainR;
|
|
1433
|
+
note.volumeEnvelopeNode.connect(gainL);
|
|
1434
|
+
note.volumeEnvelopeNode.connect(gainR);
|
|
1435
|
+
}
|
|
1436
|
+
else {
|
|
1437
|
+
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
1438
|
+
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
1439
|
+
}
|
|
1443
1440
|
if (0.5 <= channel.state.sustainPedal) {
|
|
1444
1441
|
channel.sustainNotes.push(note);
|
|
1445
1442
|
}
|
|
@@ -1457,9 +1454,6 @@ class Midy {
|
|
|
1457
1454
|
note.bufferSource.disconnect();
|
|
1458
1455
|
note.filterNode.disconnect();
|
|
1459
1456
|
note.volumeEnvelopeNode.disconnect();
|
|
1460
|
-
note.volumeNode.disconnect();
|
|
1461
|
-
note.gainL.disconnect();
|
|
1462
|
-
note.gainR.disconnect();
|
|
1463
1457
|
if (note.modulationDepth) {
|
|
1464
1458
|
note.volumeDepth.disconnect();
|
|
1465
1459
|
note.modulationDepth.disconnect();
|
|
@@ -1585,10 +1579,10 @@ class Midy {
|
|
|
1585
1579
|
}
|
|
1586
1580
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure, scheduleTime) {
|
|
1587
1581
|
const channel = this.channels[channelNumber];
|
|
1588
|
-
channel.state.polyphonicKeyPressure = pressure / 127;
|
|
1589
1582
|
const table = channel.polyphonicKeyPressureTable;
|
|
1590
1583
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1591
1584
|
if (note.noteNumber === noteNumber) {
|
|
1585
|
+
note.pressure = pressure;
|
|
1592
1586
|
this.setControllerParameters(channel, note, table);
|
|
1593
1587
|
}
|
|
1594
1588
|
});
|
|
@@ -1616,9 +1610,10 @@ class Midy {
|
|
|
1616
1610
|
const prev = channel.state.channelPressure;
|
|
1617
1611
|
const next = value / 127;
|
|
1618
1612
|
channel.state.channelPressure = next;
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1613
|
+
const channelPressureRaw = channel.channelPressureTable[0];
|
|
1614
|
+
if (0 <= channelPressureRaw) {
|
|
1615
|
+
const channelPressureDepth = (channelPressureRaw - 64) / 37.5; // 2400 / 64;
|
|
1616
|
+
channel.detune += channelPressureDepth * (next - prev);
|
|
1622
1617
|
}
|
|
1623
1618
|
const table = channel.channelPressureTable;
|
|
1624
1619
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
@@ -1678,10 +1673,12 @@ class Midy {
|
|
|
1678
1673
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1679
1674
|
}
|
|
1680
1675
|
setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1681
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1682
1676
|
let value = note.voiceParams.reverbEffectsSend;
|
|
1683
|
-
if (
|
|
1684
|
-
|
|
1677
|
+
if (channel.isDrum) {
|
|
1678
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1679
|
+
if (0 <= keyBasedValue) {
|
|
1680
|
+
value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
|
|
1681
|
+
}
|
|
1685
1682
|
}
|
|
1686
1683
|
if (0 < prevValue) {
|
|
1687
1684
|
if (0 < value) {
|
|
@@ -1699,17 +1696,19 @@ class Midy {
|
|
|
1699
1696
|
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1700
1697
|
gain: value,
|
|
1701
1698
|
});
|
|
1702
|
-
note.
|
|
1699
|
+
note.volumeEnvelopeNode.connect(note.reverbEffectsSend);
|
|
1703
1700
|
}
|
|
1704
1701
|
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
1705
1702
|
}
|
|
1706
1703
|
}
|
|
1707
1704
|
}
|
|
1708
1705
|
setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1709
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1710
1706
|
let value = note.voiceParams.chorusEffectsSend;
|
|
1711
|
-
if (
|
|
1712
|
-
|
|
1707
|
+
if (channel.isDrum) {
|
|
1708
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1709
|
+
if (0 <= keyBasedValue) {
|
|
1710
|
+
value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
|
|
1711
|
+
}
|
|
1713
1712
|
}
|
|
1714
1713
|
if (0 < prevValue) {
|
|
1715
1714
|
if (0 < vaule) {
|
|
@@ -1727,7 +1726,7 @@ class Midy {
|
|
|
1727
1726
|
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1728
1727
|
gain: value,
|
|
1729
1728
|
});
|
|
1730
|
-
note.
|
|
1729
|
+
note.volumeEnvelopeNode.connect(note.chorusEffectsSend);
|
|
1731
1730
|
}
|
|
1732
1731
|
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1733
1732
|
}
|
|
@@ -1802,21 +1801,22 @@ class Midy {
|
|
|
1802
1801
|
},
|
|
1803
1802
|
};
|
|
1804
1803
|
}
|
|
1805
|
-
getControllerState(channel, noteNumber, velocity) {
|
|
1804
|
+
getControllerState(channel, noteNumber, velocity, polyphonicKeyPressure) {
|
|
1806
1805
|
const state = new Float32Array(channel.state.array.length);
|
|
1807
1806
|
state.set(channel.state.array);
|
|
1808
1807
|
state[2] = velocity / 127;
|
|
1809
1808
|
state[3] = noteNumber / 127;
|
|
1810
|
-
state[10] =
|
|
1809
|
+
state[10] = polyphonicKeyPressure / 127;
|
|
1811
1810
|
state[13] = state.channelPressure / 127;
|
|
1812
1811
|
return state;
|
|
1813
1812
|
}
|
|
1814
1813
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1815
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1816
|
-
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1814
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1815
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity, note.pressure);
|
|
1817
1816
|
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1818
|
-
let
|
|
1819
|
-
let
|
|
1817
|
+
let applyVolumeEnvelope = false;
|
|
1818
|
+
let applyFilterEnvelope = false;
|
|
1819
|
+
let applyPitchEnvelope = false;
|
|
1820
1820
|
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1821
1821
|
const prevValue = note.voiceParams[key];
|
|
1822
1822
|
if (value === prevValue)
|
|
@@ -1825,37 +1825,23 @@ class Midy {
|
|
|
1825
1825
|
if (key in this.voiceParamsHandlers) {
|
|
1826
1826
|
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1827
1827
|
}
|
|
1828
|
-
else
|
|
1829
|
-
if (
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
if (key in voiceParams)
|
|
1836
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1837
|
-
}
|
|
1838
|
-
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1839
|
-
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
1840
|
-
}
|
|
1841
|
-
else {
|
|
1842
|
-
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1843
|
-
}
|
|
1844
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1845
|
-
}
|
|
1846
|
-
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1847
|
-
if (appliedVolumeEnvelope)
|
|
1848
|
-
continue;
|
|
1849
|
-
appliedVolumeEnvelope = true;
|
|
1850
|
-
const noteVoiceParams = note.voiceParams;
|
|
1851
|
-
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1852
|
-
const key = volumeEnvelopeKeys[i];
|
|
1853
|
-
if (key in voiceParams)
|
|
1854
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1855
|
-
}
|
|
1856
|
-
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1828
|
+
else {
|
|
1829
|
+
if (volumeEnvelopeKeySet.has(key))
|
|
1830
|
+
applyVolumeEnvelope = true;
|
|
1831
|
+
if (filterEnvelopeKeySet.has(key))
|
|
1832
|
+
applyFilterEnvelope = true;
|
|
1833
|
+
if (pitchEnvelopeKeySet.has(key))
|
|
1834
|
+
applyPitchEnvelope = true;
|
|
1857
1835
|
}
|
|
1858
1836
|
}
|
|
1837
|
+
if (applyVolumeEnvelope) {
|
|
1838
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1839
|
+
}
|
|
1840
|
+
if (applyFilterEnvelope) {
|
|
1841
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1842
|
+
}
|
|
1843
|
+
if (applyPitchEnvelope)
|
|
1844
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1859
1845
|
});
|
|
1860
1846
|
}
|
|
1861
1847
|
createControlChangeHandlers() {
|
|
@@ -1902,7 +1888,7 @@ class Midy {
|
|
|
1902
1888
|
handler.call(this, channelNumber, value, scheduleTime);
|
|
1903
1889
|
const channel = this.channels[channelNumber];
|
|
1904
1890
|
this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
|
|
1905
|
-
this.applyControlTable(channel, controllerType);
|
|
1891
|
+
this.applyControlTable(channel, controllerType, scheduleTime);
|
|
1906
1892
|
}
|
|
1907
1893
|
else {
|
|
1908
1894
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1913,7 +1899,7 @@ class Midy {
|
|
|
1913
1899
|
}
|
|
1914
1900
|
updateModulation(channel, scheduleTime) {
|
|
1915
1901
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1916
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1902
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1917
1903
|
if (note.modulationDepth) {
|
|
1918
1904
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1919
1905
|
}
|
|
@@ -1932,7 +1918,7 @@ class Midy {
|
|
|
1932
1918
|
this.updateModulation(channel, scheduleTime);
|
|
1933
1919
|
}
|
|
1934
1920
|
updatePortamento(channel, scheduleTime) {
|
|
1935
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1921
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1936
1922
|
if (0.5 <= channel.state.portamento) {
|
|
1937
1923
|
if (0 <= note.portamentoNoteNumber) {
|
|
1938
1924
|
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
@@ -1959,22 +1945,12 @@ class Midy {
|
|
|
1959
1945
|
return;
|
|
1960
1946
|
this.updatePortamento(channel, scheduleTime);
|
|
1961
1947
|
}
|
|
1962
|
-
setKeyBasedVolume(channel, scheduleTime) {
|
|
1963
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1964
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1965
|
-
if (0 <= keyBasedValue) {
|
|
1966
|
-
note.volumeNode.gain
|
|
1967
|
-
.cancelScheduledValues(scheduleTime)
|
|
1968
|
-
.setValueAtTime(keyBasedValue / 127, scheduleTime);
|
|
1969
|
-
}
|
|
1970
|
-
});
|
|
1971
|
-
}
|
|
1972
1948
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1973
1949
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1974
1950
|
const channel = this.channels[channelNumber];
|
|
1975
1951
|
channel.state.volume = volume / 127;
|
|
1976
1952
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1977
|
-
this.
|
|
1953
|
+
this.updateKeyBasedVolume(channel, scheduleTime);
|
|
1978
1954
|
}
|
|
1979
1955
|
panToGain(pan) {
|
|
1980
1956
|
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
@@ -1983,26 +1959,12 @@ class Midy {
|
|
|
1983
1959
|
gainRight: Math.sin(theta),
|
|
1984
1960
|
};
|
|
1985
1961
|
}
|
|
1986
|
-
setKeyBasedPan(channel, scheduleTime) {
|
|
1987
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1988
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1989
|
-
if (0 <= keyBasedValue) {
|
|
1990
|
-
const { gainLeft, gainRight } = this.panToGain(keyBasedValue / 127);
|
|
1991
|
-
note.gainL.gain
|
|
1992
|
-
.cancelScheduledValues(scheduleTime)
|
|
1993
|
-
.setValueAtTime(gainLeft, scheduleTime);
|
|
1994
|
-
note.gainR.gain
|
|
1995
|
-
.cancelScheduledValues(scheduleTime)
|
|
1996
|
-
.setValueAtTime(gainRight, scheduleTime);
|
|
1997
|
-
}
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
1962
|
setPan(channelNumber, pan, scheduleTime) {
|
|
2001
1963
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2002
1964
|
const channel = this.channels[channelNumber];
|
|
2003
1965
|
channel.state.pan = pan / 127;
|
|
2004
1966
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2005
|
-
this.
|
|
1967
|
+
this.updateKeyBasedVolume(channel, scheduleTime);
|
|
2006
1968
|
}
|
|
2007
1969
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
2008
1970
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -2028,6 +1990,34 @@ class Midy {
|
|
|
2028
1990
|
.cancelScheduledValues(scheduleTime)
|
|
2029
1991
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2030
1992
|
}
|
|
1993
|
+
updateKeyBasedVolume(channel, scheduleTime) {
|
|
1994
|
+
if (!channel.isDrum)
|
|
1995
|
+
return;
|
|
1996
|
+
const state = channel.state;
|
|
1997
|
+
const defaultVolume = state.volume * state.expression;
|
|
1998
|
+
const defaultPan = state.pan;
|
|
1999
|
+
for (let i = 0; i < 128; i++) {
|
|
2000
|
+
const gainL = channel.keyBasedGainLs[i];
|
|
2001
|
+
const gainR = channel.keyBasedGainLs[i];
|
|
2002
|
+
if (!gainL)
|
|
2003
|
+
continue;
|
|
2004
|
+
if (!gainR)
|
|
2005
|
+
continue;
|
|
2006
|
+
const keyBasedVolume = this.getKeyBasedInstrumentControlValue(channel, i, 7);
|
|
2007
|
+
const volume = (0 <= keyBasedVolume)
|
|
2008
|
+
? defaultVolume * keyBasedVolume / 64
|
|
2009
|
+
: defaultVolume;
|
|
2010
|
+
const keyBasedPan = this.getKeyBasedInstrumentControlValue(channel, i, 10);
|
|
2011
|
+
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2012
|
+
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2013
|
+
gainL.gain
|
|
2014
|
+
.cancelScheduledValues(scheduleTime)
|
|
2015
|
+
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
2016
|
+
gainR.gain
|
|
2017
|
+
.cancelScheduledValues(scheduleTime)
|
|
2018
|
+
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2031
2021
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2032
2022
|
const channel = this.channels[channelNumber];
|
|
2033
2023
|
if (channel.isDrum)
|
|
@@ -2035,7 +2025,7 @@ class Midy {
|
|
|
2035
2025
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2036
2026
|
channel.state.sustainPedal = value / 127;
|
|
2037
2027
|
if (64 <= value) {
|
|
2038
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2028
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2039
2029
|
channel.sustainNotes.push(note);
|
|
2040
2030
|
});
|
|
2041
2031
|
}
|
|
@@ -2078,7 +2068,7 @@ class Midy {
|
|
|
2078
2068
|
const state = channel.state;
|
|
2079
2069
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2080
2070
|
state.softPedal = softPedal / 127;
|
|
2081
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2071
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2082
2072
|
if (0.5 <= state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
2083
2073
|
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
2084
2074
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
@@ -2096,7 +2086,7 @@ class Midy {
|
|
|
2096
2086
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2097
2087
|
const state = channel.state;
|
|
2098
2088
|
state.filterResonance = filterResonance / 127;
|
|
2099
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2089
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2100
2090
|
const Q = note.voiceParams.initialFilterQ / 5 * state.filterResonance;
|
|
2101
2091
|
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
2102
2092
|
});
|
|
@@ -2114,7 +2104,7 @@ class Midy {
|
|
|
2114
2104
|
return;
|
|
2115
2105
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2116
2106
|
channel.state.attackTime = attackTime / 127;
|
|
2117
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2107
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2118
2108
|
if (note.startTime < scheduleTime)
|
|
2119
2109
|
return false;
|
|
2120
2110
|
this.setVolumeEnvelope(channel, note);
|
|
@@ -2127,7 +2117,7 @@ class Midy {
|
|
|
2127
2117
|
const state = channel.state;
|
|
2128
2118
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2129
2119
|
state.brightness = brightness / 127;
|
|
2130
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2120
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2131
2121
|
if (0.5 <= state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
2132
2122
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
2133
2123
|
}
|
|
@@ -2142,7 +2132,7 @@ class Midy {
|
|
|
2142
2132
|
return;
|
|
2143
2133
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2144
2134
|
channel.state.decayTime = dacayTime / 127;
|
|
2145
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2135
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2146
2136
|
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
2147
2137
|
});
|
|
2148
2138
|
}
|
|
@@ -2154,7 +2144,7 @@ class Midy {
|
|
|
2154
2144
|
channel.state.vibratoRate = vibratoRate / 127;
|
|
2155
2145
|
if (channel.vibratoDepth <= 0)
|
|
2156
2146
|
return;
|
|
2157
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2147
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2158
2148
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
2159
2149
|
});
|
|
2160
2150
|
}
|
|
@@ -2166,12 +2156,12 @@ class Midy {
|
|
|
2166
2156
|
const prev = channel.state.vibratoDepth;
|
|
2167
2157
|
channel.state.vibratoDepth = vibratoDepth / 127;
|
|
2168
2158
|
if (0 < prev) {
|
|
2169
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2159
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2170
2160
|
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
2171
2161
|
});
|
|
2172
2162
|
}
|
|
2173
2163
|
else {
|
|
2174
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2164
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2175
2165
|
this.startVibrato(channel, note, scheduleTime);
|
|
2176
2166
|
});
|
|
2177
2167
|
}
|
|
@@ -2183,7 +2173,7 @@ class Midy {
|
|
|
2183
2173
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2184
2174
|
channel.state.vibratoDelay = vibratoDelay / 127;
|
|
2185
2175
|
if (0 < channel.state.vibratoDepth) {
|
|
2186
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2176
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2187
2177
|
this.startVibrato(channel, note, scheduleTime);
|
|
2188
2178
|
});
|
|
2189
2179
|
}
|
|
@@ -2201,7 +2191,7 @@ class Midy {
|
|
|
2201
2191
|
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2202
2192
|
}
|
|
2203
2193
|
else {
|
|
2204
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2194
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2205
2195
|
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2206
2196
|
return false;
|
|
2207
2197
|
if (note.reverbEffectsSend)
|
|
@@ -2211,7 +2201,7 @@ class Midy {
|
|
|
2211
2201
|
}
|
|
2212
2202
|
else {
|
|
2213
2203
|
if (0 < reverbSendLevel) {
|
|
2214
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2204
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2215
2205
|
this.setReverbEffectsSend(channel, note, 0, scheduleTime);
|
|
2216
2206
|
});
|
|
2217
2207
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
@@ -2234,7 +2224,7 @@ class Midy {
|
|
|
2234
2224
|
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2235
2225
|
}
|
|
2236
2226
|
else {
|
|
2237
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2227
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2238
2228
|
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2239
2229
|
return false;
|
|
2240
2230
|
if (note.chorusEffectsSend)
|
|
@@ -2244,7 +2234,7 @@ class Midy {
|
|
|
2244
2234
|
}
|
|
2245
2235
|
else {
|
|
2246
2236
|
if (0 < chorusSendLevel) {
|
|
2247
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2237
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2248
2238
|
this.setChorusEffectsSend(channel, note, 0, scheduleTime);
|
|
2249
2239
|
});
|
|
2250
2240
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
@@ -2659,8 +2649,7 @@ class Midy {
|
|
|
2659
2649
|
setReverbType(type) {
|
|
2660
2650
|
this.reverb.time = this.getReverbTimeFromType(type);
|
|
2661
2651
|
this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
|
|
2662
|
-
|
|
2663
|
-
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
2652
|
+
this.reverbEffect = this.createReverbEffect(this.audioContext);
|
|
2664
2653
|
}
|
|
2665
2654
|
getReverbTimeFromType(type) {
|
|
2666
2655
|
switch (type) {
|
|
@@ -2682,8 +2671,7 @@ class Midy {
|
|
|
2682
2671
|
}
|
|
2683
2672
|
setReverbTime(value) {
|
|
2684
2673
|
this.reverb.time = this.getReverbTime(value);
|
|
2685
|
-
|
|
2686
|
-
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
2674
|
+
this.reverbEffect = this.createReverbEffect(this.audioContext);
|
|
2687
2675
|
}
|
|
2688
2676
|
getReverbTime(value) {
|
|
2689
2677
|
return Math.exp((value - 40) * 0.025);
|
|
@@ -2878,65 +2866,88 @@ class Midy {
|
|
|
2878
2866
|
}
|
|
2879
2867
|
}
|
|
2880
2868
|
getPitchControl(channel, note) {
|
|
2881
|
-
const
|
|
2869
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[0];
|
|
2870
|
+
if (polyphonicKeyPressureRaw < 0)
|
|
2871
|
+
return 0;
|
|
2872
|
+
const polyphonicKeyPressure = (polyphonicKeyPressureRaw - 64) *
|
|
2882
2873
|
note.pressure;
|
|
2883
2874
|
return polyphonicKeyPressure * note.pressure / 37.5; // 2400 / 64;
|
|
2884
2875
|
}
|
|
2885
2876
|
getFilterCutoffControl(channel, note) {
|
|
2886
|
-
const
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2877
|
+
const channelPressureRaw = channel.channelPressureTable[1];
|
|
2878
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2879
|
+
? (channelPressureRaw - 64) * channel.state.channelPressure
|
|
2880
|
+
: 0;
|
|
2881
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[1];
|
|
2882
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2883
|
+
? (polyphonicKeyPressureRaw - 64) * note.pressure
|
|
2884
|
+
: 0;
|
|
2890
2885
|
return (channelPressure + polyphonicKeyPressure) * 15;
|
|
2891
2886
|
}
|
|
2892
2887
|
getAmplitudeControl(channel, note) {
|
|
2893
|
-
const
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2888
|
+
const channelPressureRaw = channel.channelPressureTable[2];
|
|
2889
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2890
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2891
|
+
: 0;
|
|
2892
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[2];
|
|
2893
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2894
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2895
|
+
: 0;
|
|
2897
2896
|
return (channelPressure + polyphonicKeyPressure) / 128;
|
|
2898
2897
|
}
|
|
2899
2898
|
getLFOPitchDepth(channel, note) {
|
|
2900
|
-
const
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2899
|
+
const channelPressureRaw = channel.channelPressureTable[3];
|
|
2900
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2901
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2902
|
+
: 0;
|
|
2903
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[3];
|
|
2904
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2905
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2906
|
+
: 0;
|
|
2904
2907
|
return (channelPressure + polyphonicKeyPressure) / 254 * 600;
|
|
2905
2908
|
}
|
|
2906
2909
|
getLFOFilterDepth(channel, note) {
|
|
2907
|
-
const
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2910
|
+
const channelPressureRaw = channel.channelPressureTable[4];
|
|
2911
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2912
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2913
|
+
: 0;
|
|
2914
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[4];
|
|
2915
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2916
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2917
|
+
: 0;
|
|
2911
2918
|
return (channelPressure + polyphonicKeyPressure) / 254 * 2400;
|
|
2912
2919
|
}
|
|
2913
2920
|
getLFOAmplitudeDepth(channel, note) {
|
|
2914
|
-
const
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2921
|
+
const channelPressureRaw = channel.channelPressureTable[5];
|
|
2922
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2923
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2924
|
+
: 0;
|
|
2925
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[5];
|
|
2926
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2927
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2928
|
+
: 0;
|
|
2918
2929
|
return (channelPressure + polyphonicKeyPressure) / 254;
|
|
2919
2930
|
}
|
|
2920
2931
|
setControllerParameters(channel, note, table) {
|
|
2921
|
-
if (table[0]
|
|
2932
|
+
if (0 <= table[0])
|
|
2922
2933
|
this.updateDetune(channel, note);
|
|
2923
2934
|
if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
|
|
2924
|
-
if (table[1]
|
|
2935
|
+
if (0 <= table[1])
|
|
2925
2936
|
this.setPortamentoFilterEnvelope(channel, note);
|
|
2926
|
-
if (table[2]
|
|
2937
|
+
if (0 <= table[2])
|
|
2927
2938
|
this.setPortamentoVolumeEnvelope(channel, note);
|
|
2928
2939
|
}
|
|
2929
2940
|
else {
|
|
2930
|
-
if (table[1]
|
|
2941
|
+
if (0 <= table[1])
|
|
2931
2942
|
this.setFilterEnvelope(channel, note);
|
|
2932
|
-
if (table[2]
|
|
2943
|
+
if (0 <= table[2])
|
|
2933
2944
|
this.setVolumeEnvelope(channel, note);
|
|
2934
2945
|
}
|
|
2935
|
-
if (table[3]
|
|
2946
|
+
if (0 <= table[3])
|
|
2936
2947
|
this.setModLfoToPitch(channel, note);
|
|
2937
|
-
if (table[4]
|
|
2948
|
+
if (0 <= table[4])
|
|
2938
2949
|
this.setModLfoToFilterFc(channel, note);
|
|
2939
|
-
if (table[5]
|
|
2950
|
+
if (0 <= table[5])
|
|
2940
2951
|
this.setModLfoToVolume(channel, note);
|
|
2941
2952
|
}
|
|
2942
2953
|
handlePressureSysEx(data, tableName) {
|
|
@@ -2952,26 +2963,15 @@ class Midy {
|
|
|
2952
2963
|
}
|
|
2953
2964
|
}
|
|
2954
2965
|
initControlTable() {
|
|
2955
|
-
const
|
|
2966
|
+
const ccCount = 128;
|
|
2956
2967
|
const slotSize = 6;
|
|
2957
|
-
|
|
2958
|
-
return this.resetControlTable(table);
|
|
2968
|
+
return new Int8Array(ccCount * slotSize).fill(-1);
|
|
2959
2969
|
}
|
|
2960
|
-
|
|
2961
|
-
const channelCount = 128;
|
|
2962
|
-
const slotSize = 6;
|
|
2963
|
-
const defaultValues = [64, 64, 64, 0, 0, 0];
|
|
2964
|
-
for (let ch = 0; ch < channelCount; ch++) {
|
|
2965
|
-
const offset = ch * slotSize;
|
|
2966
|
-
table.set(defaultValues, offset);
|
|
2967
|
-
}
|
|
2968
|
-
return table;
|
|
2969
|
-
}
|
|
2970
|
-
applyControlTable(channel, controllerType) {
|
|
2970
|
+
applyControlTable(channel, controllerType, scheduleTime) {
|
|
2971
2971
|
const slotSize = 6;
|
|
2972
2972
|
const offset = controllerType * slotSize;
|
|
2973
2973
|
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2974
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2974
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2975
2975
|
this.setControllerParameters(channel, note, table);
|
|
2976
2976
|
});
|
|
2977
2977
|
}
|
|
@@ -2996,7 +2996,7 @@ class Midy {
|
|
|
2996
2996
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
2997
2997
|
const channelNumber = data[4];
|
|
2998
2998
|
const channel = this.channels[channelNumber];
|
|
2999
|
-
if (channel.isDrum)
|
|
2999
|
+
if (!channel.isDrum)
|
|
3000
3000
|
return;
|
|
3001
3001
|
const keyNumber = data[5];
|
|
3002
3002
|
const table = channel.keyBasedInstrumentControlTable;
|