@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/esm/midy.js
CHANGED
|
@@ -44,24 +44,6 @@ class Note {
|
|
|
44
44
|
writable: true,
|
|
45
45
|
value: void 0
|
|
46
46
|
});
|
|
47
|
-
Object.defineProperty(this, "volumeNode", {
|
|
48
|
-
enumerable: true,
|
|
49
|
-
configurable: true,
|
|
50
|
-
writable: true,
|
|
51
|
-
value: void 0
|
|
52
|
-
});
|
|
53
|
-
Object.defineProperty(this, "gainL", {
|
|
54
|
-
enumerable: true,
|
|
55
|
-
configurable: true,
|
|
56
|
-
writable: true,
|
|
57
|
-
value: void 0
|
|
58
|
-
});
|
|
59
|
-
Object.defineProperty(this, "gainR", {
|
|
60
|
-
enumerable: true,
|
|
61
|
-
configurable: true,
|
|
62
|
-
writable: true,
|
|
63
|
-
value: void 0
|
|
64
|
-
});
|
|
65
47
|
Object.defineProperty(this, "modulationLFO", {
|
|
66
48
|
enumerable: true,
|
|
67
49
|
configurable: true,
|
|
@@ -214,6 +196,16 @@ class ControllerState {
|
|
|
214
196
|
}
|
|
215
197
|
}
|
|
216
198
|
}
|
|
199
|
+
const volumeEnvelopeKeys = [
|
|
200
|
+
"volDelay",
|
|
201
|
+
"volAttack",
|
|
202
|
+
"volHold",
|
|
203
|
+
"volDecay",
|
|
204
|
+
"volSustain",
|
|
205
|
+
"volRelease",
|
|
206
|
+
"initialAttenuation",
|
|
207
|
+
];
|
|
208
|
+
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
217
209
|
const filterEnvelopeKeys = [
|
|
218
210
|
"modEnvToPitch",
|
|
219
211
|
"initialFilterFc",
|
|
@@ -223,22 +215,20 @@ const filterEnvelopeKeys = [
|
|
|
223
215
|
"modHold",
|
|
224
216
|
"modDecay",
|
|
225
217
|
"modSustain",
|
|
226
|
-
"modRelease",
|
|
227
|
-
"playbackRate",
|
|
228
218
|
];
|
|
229
219
|
const filterEnvelopeKeySet = new Set(filterEnvelopeKeys);
|
|
230
|
-
const
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
"
|
|
237
|
-
"
|
|
220
|
+
const pitchEnvelopeKeys = [
|
|
221
|
+
"modEnvToPitch",
|
|
222
|
+
"modDelay",
|
|
223
|
+
"modAttack",
|
|
224
|
+
"modHold",
|
|
225
|
+
"modDecay",
|
|
226
|
+
"modSustain",
|
|
227
|
+
"playbackRate",
|
|
238
228
|
];
|
|
239
|
-
const
|
|
229
|
+
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
240
230
|
export class Midy {
|
|
241
|
-
constructor(audioContext
|
|
231
|
+
constructor(audioContext) {
|
|
242
232
|
Object.defineProperty(this, "mode", {
|
|
243
233
|
enumerable: true,
|
|
244
234
|
configurable: true,
|
|
@@ -262,6 +252,7 @@ export class Midy {
|
|
|
262
252
|
configurable: true,
|
|
263
253
|
writable: true,
|
|
264
254
|
value: {
|
|
255
|
+
algorithm: "SchroederReverb",
|
|
265
256
|
time: this.getReverbTime(64),
|
|
266
257
|
feedback: 0.8,
|
|
267
258
|
}
|
|
@@ -410,30 +401,7 @@ export class Midy {
|
|
|
410
401
|
writable: true,
|
|
411
402
|
value: new Array(this.numChannels * drumExclusiveClassCount)
|
|
412
403
|
});
|
|
413
|
-
Object.defineProperty(this, "defaultOptions", {
|
|
414
|
-
enumerable: true,
|
|
415
|
-
configurable: true,
|
|
416
|
-
writable: true,
|
|
417
|
-
value: {
|
|
418
|
-
reverbAlgorithm: (audioContext) => {
|
|
419
|
-
const { time: rt60, feedback } = this.reverb;
|
|
420
|
-
// const delay = this.calcDelay(rt60, feedback);
|
|
421
|
-
// const impulse = this.createConvolutionReverbImpulse(
|
|
422
|
-
// audioContext,
|
|
423
|
-
// rt60,
|
|
424
|
-
// delay,
|
|
425
|
-
// );
|
|
426
|
-
// return this.createConvolutionReverb(audioContext, impulse);
|
|
427
|
-
const combFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
428
|
-
const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
429
|
-
const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
430
|
-
const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
431
|
-
return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
|
|
432
|
-
},
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
404
|
this.audioContext = audioContext;
|
|
436
|
-
this.options = { ...this.defaultOptions, ...options };
|
|
437
405
|
this.masterVolume = new GainNode(audioContext);
|
|
438
406
|
this.scheduler = new GainNode(audioContext, { gain: 0 });
|
|
439
407
|
this.schedulerBuffer = new AudioBuffer({
|
|
@@ -443,7 +411,7 @@ export class Midy {
|
|
|
443
411
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
444
412
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
445
413
|
this.channels = this.createChannels(audioContext);
|
|
446
|
-
this.reverbEffect = this.
|
|
414
|
+
this.reverbEffect = this.createReverbEffect(audioContext);
|
|
447
415
|
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
448
416
|
this.chorusEffect.output.connect(this.masterVolume);
|
|
449
417
|
this.reverbEffect.output.connect(this.masterVolume);
|
|
@@ -507,7 +475,7 @@ export class Midy {
|
|
|
507
475
|
this.timeline = midiData.timeline;
|
|
508
476
|
this.totalTime = this.calcTotalTime();
|
|
509
477
|
}
|
|
510
|
-
|
|
478
|
+
createChannelAudioNodes(audioContext) {
|
|
511
479
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
512
480
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
513
481
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
@@ -522,10 +490,10 @@ export class Midy {
|
|
|
522
490
|
};
|
|
523
491
|
}
|
|
524
492
|
resetChannelTable(channel) {
|
|
525
|
-
|
|
493
|
+
channel.controlTable.fill(-1);
|
|
526
494
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
527
|
-
channel.channelPressureTable.
|
|
528
|
-
channel.polyphonicKeyPressureTable.
|
|
495
|
+
channel.channelPressureTable.fill(-1);
|
|
496
|
+
channel.polyphonicKeyPressureTable.fill(-1);
|
|
529
497
|
channel.keyBasedInstrumentControlTable.fill(-1);
|
|
530
498
|
}
|
|
531
499
|
createChannels(audioContext) {
|
|
@@ -535,15 +503,17 @@ export class Midy {
|
|
|
535
503
|
isDrum: false,
|
|
536
504
|
state: new ControllerState(),
|
|
537
505
|
...this.constructor.channelSettings,
|
|
538
|
-
...this.
|
|
506
|
+
...this.createChannelAudioNodes(audioContext),
|
|
539
507
|
scheduledNotes: [],
|
|
540
508
|
sustainNotes: [],
|
|
541
509
|
sostenutoNotes: [],
|
|
542
510
|
controlTable: this.initControlTable(),
|
|
543
511
|
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
544
|
-
channelPressureTable: new
|
|
545
|
-
polyphonicKeyPressureTable: new
|
|
512
|
+
channelPressureTable: new Int8Array(6).fill(-1),
|
|
513
|
+
polyphonicKeyPressureTable: new Int8Array(6).fill(-1),
|
|
546
514
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
|
|
515
|
+
keyBasedGainLs: new Array(128),
|
|
516
|
+
keyBasedGainRs: new Array(128),
|
|
547
517
|
};
|
|
548
518
|
});
|
|
549
519
|
return channels;
|
|
@@ -585,10 +555,9 @@ export class Midy {
|
|
|
585
555
|
createBufferSource(channel, noteNumber, voiceParams, audioBuffer) {
|
|
586
556
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
587
557
|
bufferSource.buffer = audioBuffer;
|
|
588
|
-
bufferSource.loop =
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
558
|
+
bufferSource.loop = channel.isDrum
|
|
559
|
+
? this.isLoopDrum(channel, noteNumber)
|
|
560
|
+
: (voiceParams.sampleModes % 2 !== 0);
|
|
592
561
|
if (bufferSource.loop) {
|
|
593
562
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
594
563
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -832,7 +801,7 @@ export class Midy {
|
|
|
832
801
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
833
802
|
const channel = this.channels[channelNumber];
|
|
834
803
|
const promises = [];
|
|
835
|
-
this.processScheduledNotes(channel, (note) => {
|
|
804
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
836
805
|
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
837
806
|
this.notePromises.push(promise);
|
|
838
807
|
promises.push(promise);
|
|
@@ -891,7 +860,7 @@ export class Midy {
|
|
|
891
860
|
const now = this.audioContext.currentTime;
|
|
892
861
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
893
862
|
}
|
|
894
|
-
processScheduledNotes(channel, callback) {
|
|
863
|
+
processScheduledNotes(channel, scheduleTime, callback) {
|
|
895
864
|
const scheduledNotes = channel.scheduledNotes;
|
|
896
865
|
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
897
866
|
const note = scheduledNotes[i];
|
|
@@ -899,6 +868,8 @@ export class Midy {
|
|
|
899
868
|
continue;
|
|
900
869
|
if (note.ending)
|
|
901
870
|
continue;
|
|
871
|
+
if (note.startTime < scheduleTime)
|
|
872
|
+
continue;
|
|
902
873
|
callback(note);
|
|
903
874
|
}
|
|
904
875
|
}
|
|
@@ -911,7 +882,7 @@ export class Midy {
|
|
|
911
882
|
if (note.ending)
|
|
912
883
|
continue;
|
|
913
884
|
if (scheduleTime < note.startTime)
|
|
914
|
-
|
|
885
|
+
break;
|
|
915
886
|
callback(note);
|
|
916
887
|
}
|
|
917
888
|
}
|
|
@@ -1000,6 +971,22 @@ export class Midy {
|
|
|
1000
971
|
const output = allpasses.at(-1);
|
|
1001
972
|
return { input, output };
|
|
1002
973
|
}
|
|
974
|
+
createReverbEffect(audioContext) {
|
|
975
|
+
const { algorithm, time: rt60, feedback } = this.reverb;
|
|
976
|
+
switch (algorithm) {
|
|
977
|
+
case "ConvolutionReverb": {
|
|
978
|
+
const impulse = this.createConvolutionReverbImpulse(audioContext, rt60, this.calcDelay(rt60, feedback));
|
|
979
|
+
return this.createConvolutionReverb(audioContext, impulse);
|
|
980
|
+
}
|
|
981
|
+
case "SchroederReverb": {
|
|
982
|
+
const combFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
983
|
+
const combDelays = combFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
984
|
+
const allpassFeedbacks = this.generateDistributedArray(feedback, 4);
|
|
985
|
+
const allpassDelays = allpassFeedbacks.map((feedback) => this.calcDelay(rt60, feedback));
|
|
986
|
+
return this.createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
1003
990
|
createChorusEffect(audioContext) {
|
|
1004
991
|
const input = new GainNode(audioContext);
|
|
1005
992
|
const output = new GainNode(audioContext);
|
|
@@ -1064,15 +1051,22 @@ export class Midy {
|
|
|
1064
1051
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
1065
1052
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
1066
1053
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1054
|
+
const channelPressureRaw = channel.channelPressureTable[0];
|
|
1055
|
+
if (0 <= channelPressureRaw) {
|
|
1056
|
+
const channelPressureDepth = (channelPressureRaw - 64) / 37.5; // 2400 / 64;
|
|
1057
|
+
const channelPressure = channelPressureDepth *
|
|
1058
|
+
channel.state.channelPressure;
|
|
1059
|
+
return tuning + pitch + channelPressure;
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
return tuning + pitch;
|
|
1063
|
+
}
|
|
1070
1064
|
}
|
|
1071
1065
|
calcNoteDetune(channel, note) {
|
|
1072
1066
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
1073
1067
|
}
|
|
1074
1068
|
updateChannelDetune(channel, scheduleTime) {
|
|
1075
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1069
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1076
1070
|
this.updateDetune(channel, note, scheduleTime);
|
|
1077
1071
|
});
|
|
1078
1072
|
}
|
|
@@ -1321,14 +1315,11 @@ export class Midy {
|
|
|
1321
1315
|
async createNote(channel, voice, noteNumber, velocity, startTime, isSF3) {
|
|
1322
1316
|
const now = this.audioContext.currentTime;
|
|
1323
1317
|
const state = channel.state;
|
|
1324
|
-
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1318
|
+
const controllerState = this.getControllerState(channel, noteNumber, velocity, 0);
|
|
1325
1319
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1326
1320
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1327
1321
|
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1328
1322
|
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1329
|
-
note.volumeNode = new GainNode(this.audioContext);
|
|
1330
|
-
note.gainL = new GainNode(this.audioContext);
|
|
1331
|
-
note.gainR = new GainNode(this.audioContext);
|
|
1332
1323
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
1333
1324
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
1334
1325
|
type: "lowpass",
|
|
@@ -1361,9 +1352,6 @@ export class Midy {
|
|
|
1361
1352
|
}
|
|
1362
1353
|
note.bufferSource.connect(note.filterNode);
|
|
1363
1354
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1364
|
-
note.volumeEnvelopeNode.connect(note.volumeNode);
|
|
1365
|
-
note.volumeNode.connect(note.gainL);
|
|
1366
|
-
note.volumeNode.connect(note.gainR);
|
|
1367
1355
|
if (0 < state.chorusSendLevel) {
|
|
1368
1356
|
this.setChorusEffectsSend(channel, note, 0, now);
|
|
1369
1357
|
}
|
|
@@ -1422,7 +1410,7 @@ export class Midy {
|
|
|
1422
1410
|
}
|
|
1423
1411
|
this.drumExclusiveClassNotes[index] = note;
|
|
1424
1412
|
}
|
|
1425
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime
|
|
1413
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1426
1414
|
const channel = this.channels[channelNumber];
|
|
1427
1415
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
1428
1416
|
const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
|
|
@@ -1434,9 +1422,18 @@ export class Midy {
|
|
|
1434
1422
|
return;
|
|
1435
1423
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1436
1424
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1425
|
+
if (channel.isDrum) {
|
|
1426
|
+
const audioContext = this.audioContext;
|
|
1427
|
+
const { gainL, gainR } = this.createChannelAudioNodes(audioContext);
|
|
1428
|
+
channel.keyBasedGainLs[noteNumber] = gainL;
|
|
1429
|
+
channel.keyBasedGainRs[noteNumber] = gainR;
|
|
1430
|
+
note.volumeEnvelopeNode.connect(gainL);
|
|
1431
|
+
note.volumeEnvelopeNode.connect(gainR);
|
|
1432
|
+
}
|
|
1433
|
+
else {
|
|
1434
|
+
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
1435
|
+
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
1436
|
+
}
|
|
1440
1437
|
if (0.5 <= channel.state.sustainPedal) {
|
|
1441
1438
|
channel.sustainNotes.push(note);
|
|
1442
1439
|
}
|
|
@@ -1454,9 +1451,6 @@ export class Midy {
|
|
|
1454
1451
|
note.bufferSource.disconnect();
|
|
1455
1452
|
note.filterNode.disconnect();
|
|
1456
1453
|
note.volumeEnvelopeNode.disconnect();
|
|
1457
|
-
note.volumeNode.disconnect();
|
|
1458
|
-
note.gainL.disconnect();
|
|
1459
|
-
note.gainR.disconnect();
|
|
1460
1454
|
if (note.modulationDepth) {
|
|
1461
1455
|
note.volumeDepth.disconnect();
|
|
1462
1456
|
note.modulationDepth.disconnect();
|
|
@@ -1582,10 +1576,10 @@ export class Midy {
|
|
|
1582
1576
|
}
|
|
1583
1577
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure, scheduleTime) {
|
|
1584
1578
|
const channel = this.channels[channelNumber];
|
|
1585
|
-
channel.state.polyphonicKeyPressure = pressure / 127;
|
|
1586
1579
|
const table = channel.polyphonicKeyPressureTable;
|
|
1587
1580
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1588
1581
|
if (note.noteNumber === noteNumber) {
|
|
1582
|
+
note.pressure = pressure;
|
|
1589
1583
|
this.setControllerParameters(channel, note, table);
|
|
1590
1584
|
}
|
|
1591
1585
|
});
|
|
@@ -1613,9 +1607,10 @@ export class Midy {
|
|
|
1613
1607
|
const prev = channel.state.channelPressure;
|
|
1614
1608
|
const next = value / 127;
|
|
1615
1609
|
channel.state.channelPressure = next;
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1610
|
+
const channelPressureRaw = channel.channelPressureTable[0];
|
|
1611
|
+
if (0 <= channelPressureRaw) {
|
|
1612
|
+
const channelPressureDepth = (channelPressureRaw - 64) / 37.5; // 2400 / 64;
|
|
1613
|
+
channel.detune += channelPressureDepth * (next - prev);
|
|
1619
1614
|
}
|
|
1620
1615
|
const table = channel.channelPressureTable;
|
|
1621
1616
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
@@ -1675,10 +1670,12 @@ export class Midy {
|
|
|
1675
1670
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1676
1671
|
}
|
|
1677
1672
|
setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1678
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1679
1673
|
let value = note.voiceParams.reverbEffectsSend;
|
|
1680
|
-
if (
|
|
1681
|
-
|
|
1674
|
+
if (channel.isDrum) {
|
|
1675
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1676
|
+
if (0 <= keyBasedValue) {
|
|
1677
|
+
value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
|
|
1678
|
+
}
|
|
1682
1679
|
}
|
|
1683
1680
|
if (0 < prevValue) {
|
|
1684
1681
|
if (0 < value) {
|
|
@@ -1696,17 +1693,19 @@ export class Midy {
|
|
|
1696
1693
|
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1697
1694
|
gain: value,
|
|
1698
1695
|
});
|
|
1699
|
-
note.
|
|
1696
|
+
note.volumeEnvelopeNode.connect(note.reverbEffectsSend);
|
|
1700
1697
|
}
|
|
1701
1698
|
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
1702
1699
|
}
|
|
1703
1700
|
}
|
|
1704
1701
|
}
|
|
1705
1702
|
setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1706
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1707
1703
|
let value = note.voiceParams.chorusEffectsSend;
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1704
|
+
if (channel.isDrum) {
|
|
1705
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1706
|
+
if (0 <= keyBasedValue) {
|
|
1707
|
+
value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
|
|
1708
|
+
}
|
|
1710
1709
|
}
|
|
1711
1710
|
if (0 < prevValue) {
|
|
1712
1711
|
if (0 < vaule) {
|
|
@@ -1724,7 +1723,7 @@ export class Midy {
|
|
|
1724
1723
|
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1725
1724
|
gain: value,
|
|
1726
1725
|
});
|
|
1727
|
-
note.
|
|
1726
|
+
note.volumeEnvelopeNode.connect(note.chorusEffectsSend);
|
|
1728
1727
|
}
|
|
1729
1728
|
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1730
1729
|
}
|
|
@@ -1799,21 +1798,22 @@ export class Midy {
|
|
|
1799
1798
|
},
|
|
1800
1799
|
};
|
|
1801
1800
|
}
|
|
1802
|
-
getControllerState(channel, noteNumber, velocity) {
|
|
1801
|
+
getControllerState(channel, noteNumber, velocity, polyphonicKeyPressure) {
|
|
1803
1802
|
const state = new Float32Array(channel.state.array.length);
|
|
1804
1803
|
state.set(channel.state.array);
|
|
1805
1804
|
state[2] = velocity / 127;
|
|
1806
1805
|
state[3] = noteNumber / 127;
|
|
1807
|
-
state[10] =
|
|
1806
|
+
state[10] = polyphonicKeyPressure / 127;
|
|
1808
1807
|
state[13] = state.channelPressure / 127;
|
|
1809
1808
|
return state;
|
|
1810
1809
|
}
|
|
1811
1810
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1812
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1813
|
-
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1811
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1812
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity, note.pressure);
|
|
1814
1813
|
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1815
|
-
let
|
|
1816
|
-
let
|
|
1814
|
+
let applyVolumeEnvelope = false;
|
|
1815
|
+
let applyFilterEnvelope = false;
|
|
1816
|
+
let applyPitchEnvelope = false;
|
|
1817
1817
|
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1818
1818
|
const prevValue = note.voiceParams[key];
|
|
1819
1819
|
if (value === prevValue)
|
|
@@ -1822,37 +1822,23 @@ export class Midy {
|
|
|
1822
1822
|
if (key in this.voiceParamsHandlers) {
|
|
1823
1823
|
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1824
1824
|
}
|
|
1825
|
-
else
|
|
1826
|
-
if (
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
if (key in voiceParams)
|
|
1833
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1834
|
-
}
|
|
1835
|
-
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1836
|
-
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
1837
|
-
}
|
|
1838
|
-
else {
|
|
1839
|
-
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1840
|
-
}
|
|
1841
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1842
|
-
}
|
|
1843
|
-
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1844
|
-
if (appliedVolumeEnvelope)
|
|
1845
|
-
continue;
|
|
1846
|
-
appliedVolumeEnvelope = true;
|
|
1847
|
-
const noteVoiceParams = note.voiceParams;
|
|
1848
|
-
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1849
|
-
const key = volumeEnvelopeKeys[i];
|
|
1850
|
-
if (key in voiceParams)
|
|
1851
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1852
|
-
}
|
|
1853
|
-
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1825
|
+
else {
|
|
1826
|
+
if (volumeEnvelopeKeySet.has(key))
|
|
1827
|
+
applyVolumeEnvelope = true;
|
|
1828
|
+
if (filterEnvelopeKeySet.has(key))
|
|
1829
|
+
applyFilterEnvelope = true;
|
|
1830
|
+
if (pitchEnvelopeKeySet.has(key))
|
|
1831
|
+
applyPitchEnvelope = true;
|
|
1854
1832
|
}
|
|
1855
1833
|
}
|
|
1834
|
+
if (applyVolumeEnvelope) {
|
|
1835
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1836
|
+
}
|
|
1837
|
+
if (applyFilterEnvelope) {
|
|
1838
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1839
|
+
}
|
|
1840
|
+
if (applyPitchEnvelope)
|
|
1841
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1856
1842
|
});
|
|
1857
1843
|
}
|
|
1858
1844
|
createControlChangeHandlers() {
|
|
@@ -1899,7 +1885,7 @@ export class Midy {
|
|
|
1899
1885
|
handler.call(this, channelNumber, value, scheduleTime);
|
|
1900
1886
|
const channel = this.channels[channelNumber];
|
|
1901
1887
|
this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
|
|
1902
|
-
this.applyControlTable(channel, controllerType);
|
|
1888
|
+
this.applyControlTable(channel, controllerType, scheduleTime);
|
|
1903
1889
|
}
|
|
1904
1890
|
else {
|
|
1905
1891
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1910,7 +1896,7 @@ export class Midy {
|
|
|
1910
1896
|
}
|
|
1911
1897
|
updateModulation(channel, scheduleTime) {
|
|
1912
1898
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1913
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1899
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1914
1900
|
if (note.modulationDepth) {
|
|
1915
1901
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1916
1902
|
}
|
|
@@ -1929,7 +1915,7 @@ export class Midy {
|
|
|
1929
1915
|
this.updateModulation(channel, scheduleTime);
|
|
1930
1916
|
}
|
|
1931
1917
|
updatePortamento(channel, scheduleTime) {
|
|
1932
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1918
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1933
1919
|
if (0.5 <= channel.state.portamento) {
|
|
1934
1920
|
if (0 <= note.portamentoNoteNumber) {
|
|
1935
1921
|
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
@@ -1956,22 +1942,12 @@ export class Midy {
|
|
|
1956
1942
|
return;
|
|
1957
1943
|
this.updatePortamento(channel, scheduleTime);
|
|
1958
1944
|
}
|
|
1959
|
-
setKeyBasedVolume(channel, scheduleTime) {
|
|
1960
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1961
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1962
|
-
if (0 <= keyBasedValue) {
|
|
1963
|
-
note.volumeNode.gain
|
|
1964
|
-
.cancelScheduledValues(scheduleTime)
|
|
1965
|
-
.setValueAtTime(keyBasedValue / 127, scheduleTime);
|
|
1966
|
-
}
|
|
1967
|
-
});
|
|
1968
|
-
}
|
|
1969
1945
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1970
1946
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1971
1947
|
const channel = this.channels[channelNumber];
|
|
1972
1948
|
channel.state.volume = volume / 127;
|
|
1973
1949
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1974
|
-
this.
|
|
1950
|
+
this.updateKeyBasedVolume(channel, scheduleTime);
|
|
1975
1951
|
}
|
|
1976
1952
|
panToGain(pan) {
|
|
1977
1953
|
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
@@ -1980,26 +1956,12 @@ export class Midy {
|
|
|
1980
1956
|
gainRight: Math.sin(theta),
|
|
1981
1957
|
};
|
|
1982
1958
|
}
|
|
1983
|
-
setKeyBasedPan(channel, scheduleTime) {
|
|
1984
|
-
this.processScheduledNotes(channel, (note) => {
|
|
1985
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1986
|
-
if (0 <= keyBasedValue) {
|
|
1987
|
-
const { gainLeft, gainRight } = this.panToGain(keyBasedValue / 127);
|
|
1988
|
-
note.gainL.gain
|
|
1989
|
-
.cancelScheduledValues(scheduleTime)
|
|
1990
|
-
.setValueAtTime(gainLeft, scheduleTime);
|
|
1991
|
-
note.gainR.gain
|
|
1992
|
-
.cancelScheduledValues(scheduleTime)
|
|
1993
|
-
.setValueAtTime(gainRight, scheduleTime);
|
|
1994
|
-
}
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
1959
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1998
1960
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1999
1961
|
const channel = this.channels[channelNumber];
|
|
2000
1962
|
channel.state.pan = pan / 127;
|
|
2001
1963
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2002
|
-
this.
|
|
1964
|
+
this.updateKeyBasedVolume(channel, scheduleTime);
|
|
2003
1965
|
}
|
|
2004
1966
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
2005
1967
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -2025,6 +1987,34 @@ export class Midy {
|
|
|
2025
1987
|
.cancelScheduledValues(scheduleTime)
|
|
2026
1988
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2027
1989
|
}
|
|
1990
|
+
updateKeyBasedVolume(channel, scheduleTime) {
|
|
1991
|
+
if (!channel.isDrum)
|
|
1992
|
+
return;
|
|
1993
|
+
const state = channel.state;
|
|
1994
|
+
const defaultVolume = state.volume * state.expression;
|
|
1995
|
+
const defaultPan = state.pan;
|
|
1996
|
+
for (let i = 0; i < 128; i++) {
|
|
1997
|
+
const gainL = channel.keyBasedGainLs[i];
|
|
1998
|
+
const gainR = channel.keyBasedGainLs[i];
|
|
1999
|
+
if (!gainL)
|
|
2000
|
+
continue;
|
|
2001
|
+
if (!gainR)
|
|
2002
|
+
continue;
|
|
2003
|
+
const keyBasedVolume = this.getKeyBasedInstrumentControlValue(channel, i, 7);
|
|
2004
|
+
const volume = (0 <= keyBasedVolume)
|
|
2005
|
+
? defaultVolume * keyBasedVolume / 64
|
|
2006
|
+
: defaultVolume;
|
|
2007
|
+
const keyBasedPan = this.getKeyBasedInstrumentControlValue(channel, i, 10);
|
|
2008
|
+
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2009
|
+
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2010
|
+
gainL.gain
|
|
2011
|
+
.cancelScheduledValues(scheduleTime)
|
|
2012
|
+
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
2013
|
+
gainR.gain
|
|
2014
|
+
.cancelScheduledValues(scheduleTime)
|
|
2015
|
+
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2028
2018
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2029
2019
|
const channel = this.channels[channelNumber];
|
|
2030
2020
|
if (channel.isDrum)
|
|
@@ -2032,7 +2022,7 @@ export class Midy {
|
|
|
2032
2022
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2033
2023
|
channel.state.sustainPedal = value / 127;
|
|
2034
2024
|
if (64 <= value) {
|
|
2035
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2025
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2036
2026
|
channel.sustainNotes.push(note);
|
|
2037
2027
|
});
|
|
2038
2028
|
}
|
|
@@ -2075,7 +2065,7 @@ export class Midy {
|
|
|
2075
2065
|
const state = channel.state;
|
|
2076
2066
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2077
2067
|
state.softPedal = softPedal / 127;
|
|
2078
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2068
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2079
2069
|
if (0.5 <= state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
2080
2070
|
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
2081
2071
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
@@ -2093,7 +2083,7 @@ export class Midy {
|
|
|
2093
2083
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2094
2084
|
const state = channel.state;
|
|
2095
2085
|
state.filterResonance = filterResonance / 127;
|
|
2096
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2086
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2097
2087
|
const Q = note.voiceParams.initialFilterQ / 5 * state.filterResonance;
|
|
2098
2088
|
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
2099
2089
|
});
|
|
@@ -2111,7 +2101,7 @@ export class Midy {
|
|
|
2111
2101
|
return;
|
|
2112
2102
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2113
2103
|
channel.state.attackTime = attackTime / 127;
|
|
2114
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2104
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2115
2105
|
if (note.startTime < scheduleTime)
|
|
2116
2106
|
return false;
|
|
2117
2107
|
this.setVolumeEnvelope(channel, note);
|
|
@@ -2124,7 +2114,7 @@ export class Midy {
|
|
|
2124
2114
|
const state = channel.state;
|
|
2125
2115
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2126
2116
|
state.brightness = brightness / 127;
|
|
2127
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2117
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2128
2118
|
if (0.5 <= state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
2129
2119
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
2130
2120
|
}
|
|
@@ -2139,7 +2129,7 @@ export class Midy {
|
|
|
2139
2129
|
return;
|
|
2140
2130
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2141
2131
|
channel.state.decayTime = dacayTime / 127;
|
|
2142
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2132
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2143
2133
|
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
2144
2134
|
});
|
|
2145
2135
|
}
|
|
@@ -2151,7 +2141,7 @@ export class Midy {
|
|
|
2151
2141
|
channel.state.vibratoRate = vibratoRate / 127;
|
|
2152
2142
|
if (channel.vibratoDepth <= 0)
|
|
2153
2143
|
return;
|
|
2154
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2144
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2155
2145
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
2156
2146
|
});
|
|
2157
2147
|
}
|
|
@@ -2163,12 +2153,12 @@ export class Midy {
|
|
|
2163
2153
|
const prev = channel.state.vibratoDepth;
|
|
2164
2154
|
channel.state.vibratoDepth = vibratoDepth / 127;
|
|
2165
2155
|
if (0 < prev) {
|
|
2166
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2156
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2167
2157
|
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
2168
2158
|
});
|
|
2169
2159
|
}
|
|
2170
2160
|
else {
|
|
2171
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2161
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2172
2162
|
this.startVibrato(channel, note, scheduleTime);
|
|
2173
2163
|
});
|
|
2174
2164
|
}
|
|
@@ -2180,7 +2170,7 @@ export class Midy {
|
|
|
2180
2170
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2181
2171
|
channel.state.vibratoDelay = vibratoDelay / 127;
|
|
2182
2172
|
if (0 < channel.state.vibratoDepth) {
|
|
2183
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2173
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2184
2174
|
this.startVibrato(channel, note, scheduleTime);
|
|
2185
2175
|
});
|
|
2186
2176
|
}
|
|
@@ -2198,7 +2188,7 @@ export class Midy {
|
|
|
2198
2188
|
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2199
2189
|
}
|
|
2200
2190
|
else {
|
|
2201
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2191
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2202
2192
|
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2203
2193
|
return false;
|
|
2204
2194
|
if (note.reverbEffectsSend)
|
|
@@ -2208,7 +2198,7 @@ export class Midy {
|
|
|
2208
2198
|
}
|
|
2209
2199
|
else {
|
|
2210
2200
|
if (0 < reverbSendLevel) {
|
|
2211
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2201
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2212
2202
|
this.setReverbEffectsSend(channel, note, 0, scheduleTime);
|
|
2213
2203
|
});
|
|
2214
2204
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
@@ -2231,7 +2221,7 @@ export class Midy {
|
|
|
2231
2221
|
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2232
2222
|
}
|
|
2233
2223
|
else {
|
|
2234
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2224
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2235
2225
|
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2236
2226
|
return false;
|
|
2237
2227
|
if (note.chorusEffectsSend)
|
|
@@ -2241,7 +2231,7 @@ export class Midy {
|
|
|
2241
2231
|
}
|
|
2242
2232
|
else {
|
|
2243
2233
|
if (0 < chorusSendLevel) {
|
|
2244
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2234
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2245
2235
|
this.setChorusEffectsSend(channel, note, 0, scheduleTime);
|
|
2246
2236
|
});
|
|
2247
2237
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
@@ -2656,8 +2646,7 @@ export class Midy {
|
|
|
2656
2646
|
setReverbType(type) {
|
|
2657
2647
|
this.reverb.time = this.getReverbTimeFromType(type);
|
|
2658
2648
|
this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
|
|
2659
|
-
|
|
2660
|
-
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
2649
|
+
this.reverbEffect = this.createReverbEffect(this.audioContext);
|
|
2661
2650
|
}
|
|
2662
2651
|
getReverbTimeFromType(type) {
|
|
2663
2652
|
switch (type) {
|
|
@@ -2679,8 +2668,7 @@ export class Midy {
|
|
|
2679
2668
|
}
|
|
2680
2669
|
setReverbTime(value) {
|
|
2681
2670
|
this.reverb.time = this.getReverbTime(value);
|
|
2682
|
-
|
|
2683
|
-
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
2671
|
+
this.reverbEffect = this.createReverbEffect(this.audioContext);
|
|
2684
2672
|
}
|
|
2685
2673
|
getReverbTime(value) {
|
|
2686
2674
|
return Math.exp((value - 40) * 0.025);
|
|
@@ -2875,65 +2863,88 @@ export class Midy {
|
|
|
2875
2863
|
}
|
|
2876
2864
|
}
|
|
2877
2865
|
getPitchControl(channel, note) {
|
|
2878
|
-
const
|
|
2866
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[0];
|
|
2867
|
+
if (polyphonicKeyPressureRaw < 0)
|
|
2868
|
+
return 0;
|
|
2869
|
+
const polyphonicKeyPressure = (polyphonicKeyPressureRaw - 64) *
|
|
2879
2870
|
note.pressure;
|
|
2880
2871
|
return polyphonicKeyPressure * note.pressure / 37.5; // 2400 / 64;
|
|
2881
2872
|
}
|
|
2882
2873
|
getFilterCutoffControl(channel, note) {
|
|
2883
|
-
const
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2874
|
+
const channelPressureRaw = channel.channelPressureTable[1];
|
|
2875
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2876
|
+
? (channelPressureRaw - 64) * channel.state.channelPressure
|
|
2877
|
+
: 0;
|
|
2878
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[1];
|
|
2879
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2880
|
+
? (polyphonicKeyPressureRaw - 64) * note.pressure
|
|
2881
|
+
: 0;
|
|
2887
2882
|
return (channelPressure + polyphonicKeyPressure) * 15;
|
|
2888
2883
|
}
|
|
2889
2884
|
getAmplitudeControl(channel, note) {
|
|
2890
|
-
const
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2885
|
+
const channelPressureRaw = channel.channelPressureTable[2];
|
|
2886
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2887
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2888
|
+
: 0;
|
|
2889
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[2];
|
|
2890
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2891
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2892
|
+
: 0;
|
|
2894
2893
|
return (channelPressure + polyphonicKeyPressure) / 128;
|
|
2895
2894
|
}
|
|
2896
2895
|
getLFOPitchDepth(channel, note) {
|
|
2897
|
-
const
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2896
|
+
const channelPressureRaw = channel.channelPressureTable[3];
|
|
2897
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2898
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2899
|
+
: 0;
|
|
2900
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[3];
|
|
2901
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2902
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2903
|
+
: 0;
|
|
2901
2904
|
return (channelPressure + polyphonicKeyPressure) / 254 * 600;
|
|
2902
2905
|
}
|
|
2903
2906
|
getLFOFilterDepth(channel, note) {
|
|
2904
|
-
const
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2907
|
+
const channelPressureRaw = channel.channelPressureTable[4];
|
|
2908
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2909
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2910
|
+
: 0;
|
|
2911
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[4];
|
|
2912
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2913
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2914
|
+
: 0;
|
|
2908
2915
|
return (channelPressure + polyphonicKeyPressure) / 254 * 2400;
|
|
2909
2916
|
}
|
|
2910
2917
|
getLFOAmplitudeDepth(channel, note) {
|
|
2911
|
-
const
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2918
|
+
const channelPressureRaw = channel.channelPressureTable[5];
|
|
2919
|
+
const channelPressure = (0 <= channelPressureRaw)
|
|
2920
|
+
? channelPressureRaw * channel.state.channelPressure
|
|
2921
|
+
: 0;
|
|
2922
|
+
const polyphonicKeyPressureRaw = channel.polyphonicKeyPressureTable[5];
|
|
2923
|
+
const polyphonicKeyPressure = (0 <= polyphonicKeyPressureRaw)
|
|
2924
|
+
? polyphonicKeyPressureRaw * note.pressure
|
|
2925
|
+
: 0;
|
|
2915
2926
|
return (channelPressure + polyphonicKeyPressure) / 254;
|
|
2916
2927
|
}
|
|
2917
2928
|
setControllerParameters(channel, note, table) {
|
|
2918
|
-
if (table[0]
|
|
2929
|
+
if (0 <= table[0])
|
|
2919
2930
|
this.updateDetune(channel, note);
|
|
2920
2931
|
if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
|
|
2921
|
-
if (table[1]
|
|
2932
|
+
if (0 <= table[1])
|
|
2922
2933
|
this.setPortamentoFilterEnvelope(channel, note);
|
|
2923
|
-
if (table[2]
|
|
2934
|
+
if (0 <= table[2])
|
|
2924
2935
|
this.setPortamentoVolumeEnvelope(channel, note);
|
|
2925
2936
|
}
|
|
2926
2937
|
else {
|
|
2927
|
-
if (table[1]
|
|
2938
|
+
if (0 <= table[1])
|
|
2928
2939
|
this.setFilterEnvelope(channel, note);
|
|
2929
|
-
if (table[2]
|
|
2940
|
+
if (0 <= table[2])
|
|
2930
2941
|
this.setVolumeEnvelope(channel, note);
|
|
2931
2942
|
}
|
|
2932
|
-
if (table[3]
|
|
2943
|
+
if (0 <= table[3])
|
|
2933
2944
|
this.setModLfoToPitch(channel, note);
|
|
2934
|
-
if (table[4]
|
|
2945
|
+
if (0 <= table[4])
|
|
2935
2946
|
this.setModLfoToFilterFc(channel, note);
|
|
2936
|
-
if (table[5]
|
|
2947
|
+
if (0 <= table[5])
|
|
2937
2948
|
this.setModLfoToVolume(channel, note);
|
|
2938
2949
|
}
|
|
2939
2950
|
handlePressureSysEx(data, tableName) {
|
|
@@ -2949,26 +2960,15 @@ export class Midy {
|
|
|
2949
2960
|
}
|
|
2950
2961
|
}
|
|
2951
2962
|
initControlTable() {
|
|
2952
|
-
const
|
|
2963
|
+
const ccCount = 128;
|
|
2953
2964
|
const slotSize = 6;
|
|
2954
|
-
|
|
2955
|
-
return this.resetControlTable(table);
|
|
2965
|
+
return new Int8Array(ccCount * slotSize).fill(-1);
|
|
2956
2966
|
}
|
|
2957
|
-
|
|
2958
|
-
const channelCount = 128;
|
|
2959
|
-
const slotSize = 6;
|
|
2960
|
-
const defaultValues = [64, 64, 64, 0, 0, 0];
|
|
2961
|
-
for (let ch = 0; ch < channelCount; ch++) {
|
|
2962
|
-
const offset = ch * slotSize;
|
|
2963
|
-
table.set(defaultValues, offset);
|
|
2964
|
-
}
|
|
2965
|
-
return table;
|
|
2966
|
-
}
|
|
2967
|
-
applyControlTable(channel, controllerType) {
|
|
2967
|
+
applyControlTable(channel, controllerType, scheduleTime) {
|
|
2968
2968
|
const slotSize = 6;
|
|
2969
2969
|
const offset = controllerType * slotSize;
|
|
2970
2970
|
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2971
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2971
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
2972
2972
|
this.setControllerParameters(channel, note, table);
|
|
2973
2973
|
});
|
|
2974
2974
|
}
|
|
@@ -2993,7 +2993,7 @@ export class Midy {
|
|
|
2993
2993
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
2994
2994
|
const channelNumber = data[4];
|
|
2995
2995
|
const channel = this.channels[channelNumber];
|
|
2996
|
-
if (channel.isDrum)
|
|
2996
|
+
if (!channel.isDrum)
|
|
2997
2997
|
return;
|
|
2998
2998
|
const keyNumber = data[5];
|
|
2999
2999
|
const table = channel.keyBasedInstrumentControlTable;
|