@marmooo/midy 0.2.3 → 0.2.5
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 +22 -4
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +120 -23
- package/esm/midy-GM2.d.ts +34 -24
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +194 -133
- package/esm/midy-GMLite.d.ts +23 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +122 -23
- package/esm/midy.d.ts +37 -23
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +303 -132
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +22 -4
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +120 -23
- package/script/midy-GM2.d.ts +34 -24
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +194 -133
- package/script/midy-GMLite.d.ts +23 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +122 -23
- package/script/midy.d.ts +37 -23
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +303 -132
package/esm/midy.js
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
import { parseMidi } from "midi-file";
|
|
2
2
|
import { parse, SoundFont } from "@marmooo/soundfont-parser";
|
|
3
|
+
// 2-3 times faster than Map
|
|
4
|
+
class SparseMap {
|
|
5
|
+
constructor(size) {
|
|
6
|
+
this.data = new Array(size);
|
|
7
|
+
this.activeIndices = [];
|
|
8
|
+
}
|
|
9
|
+
set(key, value) {
|
|
10
|
+
if (this.data[key] === undefined) {
|
|
11
|
+
this.activeIndices.push(key);
|
|
12
|
+
}
|
|
13
|
+
this.data[key] = value;
|
|
14
|
+
}
|
|
15
|
+
get(key) {
|
|
16
|
+
return this.data[key];
|
|
17
|
+
}
|
|
18
|
+
delete(key) {
|
|
19
|
+
if (this.data[key] !== undefined) {
|
|
20
|
+
this.data[key] = undefined;
|
|
21
|
+
const index = this.activeIndices.indexOf(key);
|
|
22
|
+
if (index !== -1) {
|
|
23
|
+
this.activeIndices.splice(index, 1);
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
has(key) {
|
|
30
|
+
return this.data[key] !== undefined;
|
|
31
|
+
}
|
|
32
|
+
get size() {
|
|
33
|
+
return this.activeIndices.length;
|
|
34
|
+
}
|
|
35
|
+
clear() {
|
|
36
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
37
|
+
const key = this.activeIndices[i];
|
|
38
|
+
this.data[key] = undefined;
|
|
39
|
+
}
|
|
40
|
+
this.activeIndices = [];
|
|
41
|
+
}
|
|
42
|
+
*[Symbol.iterator]() {
|
|
43
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
44
|
+
const key = this.activeIndices[i];
|
|
45
|
+
yield [key, this.data[key]];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
forEach(callback) {
|
|
49
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
50
|
+
const key = this.activeIndices[i];
|
|
51
|
+
callback(this.data[key], key, this);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
3
55
|
class Note {
|
|
4
56
|
constructor(noteNumber, velocity, startTime, voice, voiceParams) {
|
|
5
57
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -86,6 +138,12 @@ class Note {
|
|
|
86
138
|
writable: true,
|
|
87
139
|
value: void 0
|
|
88
140
|
});
|
|
141
|
+
Object.defineProperty(this, "pressure", {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
configurable: true,
|
|
144
|
+
writable: true,
|
|
145
|
+
value: 0
|
|
146
|
+
});
|
|
89
147
|
this.noteNumber = noteNumber;
|
|
90
148
|
this.velocity = velocity;
|
|
91
149
|
this.startTime = startTime;
|
|
@@ -97,7 +155,7 @@ class Note {
|
|
|
97
155
|
const defaultControllerState = {
|
|
98
156
|
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
99
157
|
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
100
|
-
|
|
158
|
+
polyphonicKeyPressure: { type: 10, defaultValue: 0 },
|
|
101
159
|
channelPressure: { type: 13, defaultValue: 0 },
|
|
102
160
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
103
161
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
@@ -281,6 +339,18 @@ export class Midy {
|
|
|
281
339
|
writable: true,
|
|
282
340
|
value: this.initSoundFontTable()
|
|
283
341
|
});
|
|
342
|
+
Object.defineProperty(this, "audioBufferCounter", {
|
|
343
|
+
enumerable: true,
|
|
344
|
+
configurable: true,
|
|
345
|
+
writable: true,
|
|
346
|
+
value: new Map()
|
|
347
|
+
});
|
|
348
|
+
Object.defineProperty(this, "audioBufferCache", {
|
|
349
|
+
enumerable: true,
|
|
350
|
+
configurable: true,
|
|
351
|
+
writable: true,
|
|
352
|
+
value: new Map()
|
|
353
|
+
});
|
|
284
354
|
Object.defineProperty(this, "isPlaying", {
|
|
285
355
|
enumerable: true,
|
|
286
356
|
configurable: true,
|
|
@@ -333,7 +403,7 @@ export class Midy {
|
|
|
333
403
|
enumerable: true,
|
|
334
404
|
configurable: true,
|
|
335
405
|
writable: true,
|
|
336
|
-
value: new
|
|
406
|
+
value: new SparseMap(128)
|
|
337
407
|
});
|
|
338
408
|
Object.defineProperty(this, "defaultOptions", {
|
|
339
409
|
enumerable: true,
|
|
@@ -373,7 +443,7 @@ export class Midy {
|
|
|
373
443
|
initSoundFontTable() {
|
|
374
444
|
const table = new Array(128);
|
|
375
445
|
for (let i = 0; i < 128; i++) {
|
|
376
|
-
table[i] = new
|
|
446
|
+
table[i] = new SparseMap(128);
|
|
377
447
|
}
|
|
378
448
|
return table;
|
|
379
449
|
}
|
|
@@ -427,14 +497,8 @@ export class Midy {
|
|
|
427
497
|
state: new ControllerState(),
|
|
428
498
|
controlTable: this.initControlTable(),
|
|
429
499
|
...this.setChannelAudioNodes(audioContext),
|
|
430
|
-
scheduledNotes: new
|
|
431
|
-
sostenutoNotes: new
|
|
432
|
-
polyphonicKeyPressure: {
|
|
433
|
-
...this.constructor.controllerDestinationSettings,
|
|
434
|
-
},
|
|
435
|
-
channelPressure: {
|
|
436
|
-
...this.constructor.controllerDestinationSettings,
|
|
437
|
-
},
|
|
500
|
+
scheduledNotes: new SparseMap(128),
|
|
501
|
+
sostenutoNotes: new SparseMap(128),
|
|
438
502
|
};
|
|
439
503
|
});
|
|
440
504
|
return channels;
|
|
@@ -468,9 +532,8 @@ export class Midy {
|
|
|
468
532
|
return audioBuffer;
|
|
469
533
|
}
|
|
470
534
|
}
|
|
471
|
-
|
|
535
|
+
createNoteBufferNode(audioBuffer, voiceParams) {
|
|
472
536
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
473
|
-
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
474
537
|
bufferSource.buffer = audioBuffer;
|
|
475
538
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
476
539
|
if (bufferSource.loop) {
|
|
@@ -562,6 +625,7 @@ export class Midy {
|
|
|
562
625
|
await Promise.all(this.notePromises);
|
|
563
626
|
this.notePromises = [];
|
|
564
627
|
this.exclusiveClassMap.clear();
|
|
628
|
+
this.audioBufferCache.clear();
|
|
565
629
|
resolve();
|
|
566
630
|
return;
|
|
567
631
|
}
|
|
@@ -577,8 +641,9 @@ export class Midy {
|
|
|
577
641
|
}
|
|
578
642
|
else if (this.isStopping) {
|
|
579
643
|
await this.stopNotes(0, true);
|
|
580
|
-
this.exclusiveClassMap.clear();
|
|
581
644
|
this.notePromises = [];
|
|
645
|
+
this.exclusiveClassMap.clear();
|
|
646
|
+
this.audioBufferCache.clear();
|
|
582
647
|
resolve();
|
|
583
648
|
this.isStopping = false;
|
|
584
649
|
this.isPaused = false;
|
|
@@ -609,6 +674,9 @@ export class Midy {
|
|
|
609
674
|
secondToTicks(second, secondsPerBeat) {
|
|
610
675
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
611
676
|
}
|
|
677
|
+
getAudioBufferId(programNumber, noteNumber, velocity) {
|
|
678
|
+
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
679
|
+
}
|
|
612
680
|
extractMidiData(midi) {
|
|
613
681
|
const instruments = new Set();
|
|
614
682
|
const timeline = [];
|
|
@@ -630,6 +698,8 @@ export class Midy {
|
|
|
630
698
|
switch (event.type) {
|
|
631
699
|
case "noteOn": {
|
|
632
700
|
const channel = tmpChannels[event.channel];
|
|
701
|
+
const audioBufferId = this.getAudioBufferId(channel.programNumber, event.noteNumber, event.velocity);
|
|
702
|
+
this.audioBufferCounter.set(audioBufferId, (this.audioBufferCounter.get(audioBufferId) ?? 0) + 1);
|
|
633
703
|
if (channel.programNumber < 0) {
|
|
634
704
|
channel.programNumber = event.programNumber;
|
|
635
705
|
switch (channel.bankMSB) {
|
|
@@ -679,6 +749,10 @@ export class Midy {
|
|
|
679
749
|
timeline.push(event);
|
|
680
750
|
}
|
|
681
751
|
}
|
|
752
|
+
for (const [audioBufferId, count] of this.audioBufferCounter) {
|
|
753
|
+
if (count === 1)
|
|
754
|
+
this.audioBufferCounter.delete(audioBufferId);
|
|
755
|
+
}
|
|
682
756
|
const priority = {
|
|
683
757
|
controller: 0,
|
|
684
758
|
sysEx: 1,
|
|
@@ -772,7 +846,7 @@ export class Midy {
|
|
|
772
846
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
773
847
|
}
|
|
774
848
|
getActiveNotes(channel, time) {
|
|
775
|
-
const activeNotes = new
|
|
849
|
+
const activeNotes = new SparseMap(128);
|
|
776
850
|
channel.scheduledNotes.forEach((noteList) => {
|
|
777
851
|
const activeNote = this.getActiveNote(noteList, time);
|
|
778
852
|
if (activeNote) {
|
|
@@ -939,28 +1013,31 @@ export class Midy {
|
|
|
939
1013
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
940
1014
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
941
1015
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
942
|
-
const pressureDepth = (channel.
|
|
1016
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
943
1017
|
const pressure = pressureDepth * channel.state.channelPressure;
|
|
944
1018
|
return tuning + pitch + pressure;
|
|
945
1019
|
}
|
|
946
1020
|
calcNoteDetune(channel, note) {
|
|
947
1021
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
948
1022
|
}
|
|
949
|
-
|
|
950
|
-
const now = this.audioContext.currentTime;
|
|
1023
|
+
updateChannelDetune(channel) {
|
|
951
1024
|
channel.scheduledNotes.forEach((noteList) => {
|
|
952
1025
|
for (let i = 0; i < noteList.length; i++) {
|
|
953
1026
|
const note = noteList[i];
|
|
954
1027
|
if (!note)
|
|
955
1028
|
continue;
|
|
956
|
-
|
|
957
|
-
const detune = channel.detune + noteDetune;
|
|
958
|
-
note.bufferSource.detune
|
|
959
|
-
.cancelScheduledValues(now)
|
|
960
|
-
.setValueAtTime(detune, now);
|
|
1029
|
+
this.updateDetune(channel, note, 0);
|
|
961
1030
|
}
|
|
962
1031
|
});
|
|
963
1032
|
}
|
|
1033
|
+
updateDetune(channel, note, pressure) {
|
|
1034
|
+
const now = this.audioContext.currentTime;
|
|
1035
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1036
|
+
const detune = channel.detune + noteDetune + pressure;
|
|
1037
|
+
note.bufferSource.detune
|
|
1038
|
+
.cancelScheduledValues(now)
|
|
1039
|
+
.setValueAtTime(detune, now);
|
|
1040
|
+
}
|
|
964
1041
|
getPortamentoTime(channel) {
|
|
965
1042
|
const factor = 5 * Math.log(10) / 127;
|
|
966
1043
|
const time = channel.state.portamentoTime;
|
|
@@ -978,14 +1055,12 @@ export class Midy {
|
|
|
978
1055
|
.setValueAtTime(0, volDelay)
|
|
979
1056
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
980
1057
|
}
|
|
981
|
-
setVolumeEnvelope(channel, note) {
|
|
1058
|
+
setVolumeEnvelope(channel, note, pressure) {
|
|
982
1059
|
const now = this.audioContext.currentTime;
|
|
983
1060
|
const state = channel.state;
|
|
984
1061
|
const { voiceParams, startTime } = note;
|
|
985
|
-
const pressureDepth = channel.pressureTable[2] / 64;
|
|
986
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
987
1062
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
988
|
-
pressure;
|
|
1063
|
+
(1 + pressure);
|
|
989
1064
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
990
1065
|
const volDelay = startTime + voiceParams.volDelay;
|
|
991
1066
|
const volAttack = volDelay + voiceParams.volAttack * state.attackTime * 2;
|
|
@@ -1033,10 +1108,8 @@ export class Midy {
|
|
|
1033
1108
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1034
1109
|
const softPedalFactor = 1 -
|
|
1035
1110
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1039
|
-
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1111
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
1112
|
+
softPedalFactor *
|
|
1040
1113
|
state.brightness * 2;
|
|
1041
1114
|
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1042
1115
|
const sustainFreq = baseFreq +
|
|
@@ -1051,15 +1124,17 @@ export class Midy {
|
|
|
1051
1124
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1052
1125
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1053
1126
|
}
|
|
1054
|
-
setFilterEnvelope(channel, note) {
|
|
1127
|
+
setFilterEnvelope(channel, note, pressure) {
|
|
1055
1128
|
const now = this.audioContext.currentTime;
|
|
1056
1129
|
const state = channel.state;
|
|
1057
1130
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1058
1131
|
const softPedalFactor = 1 -
|
|
1059
1132
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1060
|
-
const
|
|
1133
|
+
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1134
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1135
|
+
state.brightness * 2;
|
|
1136
|
+
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1061
1137
|
softPedalFactor * state.brightness * 2;
|
|
1062
|
-
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1063
1138
|
const sustainFreq = baseFreq +
|
|
1064
1139
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1065
1140
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1086,9 +1161,9 @@ export class Midy {
|
|
|
1086
1161
|
gain: voiceParams.modLfoToFilterFc,
|
|
1087
1162
|
});
|
|
1088
1163
|
note.modulationDepth = new GainNode(this.audioContext);
|
|
1089
|
-
this.setModLfoToPitch(channel, note);
|
|
1164
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1090
1165
|
note.volumeDepth = new GainNode(this.audioContext);
|
|
1091
|
-
this.setModLfoToVolume(
|
|
1166
|
+
this.setModLfoToVolume(note, 0);
|
|
1092
1167
|
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
1093
1168
|
note.modulationLFO.connect(note.filterDepth);
|
|
1094
1169
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
@@ -1101,8 +1176,7 @@ export class Midy {
|
|
|
1101
1176
|
const { voiceParams } = note;
|
|
1102
1177
|
const state = channel.state;
|
|
1103
1178
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1104
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1105
|
-
state.vibratoRate,
|
|
1179
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
|
|
1106
1180
|
});
|
|
1107
1181
|
note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
|
|
1108
1182
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
@@ -1110,12 +1184,31 @@ export class Midy {
|
|
|
1110
1184
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
1111
1185
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
1112
1186
|
}
|
|
1187
|
+
async getAudioBuffer(program, noteNumber, velocity, voiceParams, isSF3) {
|
|
1188
|
+
const audioBufferId = this.getAudioBufferId(program, noteNumber, velocity);
|
|
1189
|
+
const cache = this.audioBufferCache.get(audioBufferId);
|
|
1190
|
+
if (cache) {
|
|
1191
|
+
cache.counter += 1;
|
|
1192
|
+
if (cache.maxCount <= cache.counter) {
|
|
1193
|
+
this.audioBufferCache.delete(audioBufferId);
|
|
1194
|
+
}
|
|
1195
|
+
return cache.audioBuffer;
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
const maxCount = this.audioBufferCounter.get(audioBufferId) ?? 0;
|
|
1199
|
+
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
1200
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
1201
|
+
this.audioBufferCache.set(audioBufferId, cache);
|
|
1202
|
+
return audioBuffer;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1113
1205
|
async createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
1114
1206
|
const state = channel.state;
|
|
1115
1207
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1116
1208
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1117
1209
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1118
|
-
|
|
1210
|
+
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
1211
|
+
note.bufferSource = this.createNoteBufferNode(audioBuffer, voiceParams);
|
|
1119
1212
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1120
1213
|
note.gainL = new GainNode(this.audioContext);
|
|
1121
1214
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1131,8 +1224,8 @@ export class Midy {
|
|
|
1131
1224
|
}
|
|
1132
1225
|
else {
|
|
1133
1226
|
note.portamento = false;
|
|
1134
|
-
this.setVolumeEnvelope(channel, note);
|
|
1135
|
-
this.setFilterEnvelope(channel, note);
|
|
1227
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1228
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1136
1229
|
}
|
|
1137
1230
|
if (0 < state.vibratoDepth) {
|
|
1138
1231
|
this.startVibrato(channel, note, startTime);
|
|
@@ -1175,10 +1268,10 @@ export class Midy {
|
|
|
1175
1268
|
if (soundFontIndex === undefined)
|
|
1176
1269
|
return;
|
|
1177
1270
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
1178
|
-
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1179
1271
|
const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
|
|
1180
1272
|
if (!voice)
|
|
1181
1273
|
return;
|
|
1274
|
+
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1182
1275
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
|
|
1183
1276
|
note.gainL.connect(channel.gainL);
|
|
1184
1277
|
note.gainR.connect(channel.gainR);
|
|
@@ -1347,16 +1440,12 @@ export class Midy {
|
|
|
1347
1440
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
1348
1441
|
const now = this.audioContext.currentTime;
|
|
1349
1442
|
const channel = this.channels[channelNumber];
|
|
1350
|
-
pressure
|
|
1443
|
+
channel.state.polyphonicKeyPressure = pressure / 127;
|
|
1444
|
+
const table = channel.polyphonicKeyPressureTable;
|
|
1351
1445
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1352
|
-
if (
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
const gain = activeNote.gainL.gain.value;
|
|
1356
|
-
activeNote.volumeNode.gain
|
|
1357
|
-
.cancelScheduledValues(now)
|
|
1358
|
-
.setValueAtTime(gain * pressure, now);
|
|
1359
|
-
}
|
|
1446
|
+
if (activeNotes.has(noteNumber)) {
|
|
1447
|
+
const note = activeNotes.get(noteNumber);
|
|
1448
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1360
1449
|
}
|
|
1361
1450
|
// this.applyVoiceParams(channel, 10);
|
|
1362
1451
|
}
|
|
@@ -1366,22 +1455,18 @@ export class Midy {
|
|
|
1366
1455
|
channel.program = program;
|
|
1367
1456
|
}
|
|
1368
1457
|
handleChannelPressure(channelNumber, value) {
|
|
1458
|
+
const now = this.audioContext.currentTime;
|
|
1369
1459
|
const channel = this.channels[channelNumber];
|
|
1370
1460
|
const prev = channel.state.channelPressure;
|
|
1371
1461
|
const next = value / 127;
|
|
1372
1462
|
channel.state.channelPressure = next;
|
|
1373
|
-
if (channel.
|
|
1374
|
-
const pressureDepth = (channel.
|
|
1463
|
+
if (channel.channelPressureTable[0] !== 64) {
|
|
1464
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
1375
1465
|
channel.detune += pressureDepth * (next - prev);
|
|
1376
1466
|
}
|
|
1377
|
-
const table = channel.
|
|
1378
|
-
channel.
|
|
1379
|
-
|
|
1380
|
-
const note = noteList[i];
|
|
1381
|
-
if (!note)
|
|
1382
|
-
continue;
|
|
1383
|
-
this.applyDestinationSettings(channel, note, table);
|
|
1384
|
-
}
|
|
1467
|
+
const table = channel.channelPressureTable;
|
|
1468
|
+
this.getActiveNotes(channel, now).forEach((note) => {
|
|
1469
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1385
1470
|
});
|
|
1386
1471
|
// this.applyVoiceParams(channel, 13);
|
|
1387
1472
|
}
|
|
@@ -1396,16 +1481,13 @@ export class Midy {
|
|
|
1396
1481
|
const next = (value - 8192) / 8192;
|
|
1397
1482
|
state.pitchWheel = value / 16383;
|
|
1398
1483
|
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1399
|
-
this.
|
|
1484
|
+
this.updateChannelDetune(channel);
|
|
1400
1485
|
this.applyVoiceParams(channel, 14);
|
|
1401
1486
|
}
|
|
1402
|
-
setModLfoToPitch(channel, note) {
|
|
1487
|
+
setModLfoToPitch(channel, note, pressure) {
|
|
1403
1488
|
const now = this.audioContext.currentTime;
|
|
1404
|
-
const pressureDepth = channel.pressureTable[3] / 127 * 600;
|
|
1405
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1406
1489
|
const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
|
|
1407
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1408
|
-
channel.state.modulationDepth;
|
|
1490
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1409
1491
|
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1410
1492
|
note.modulationDepth.gain
|
|
1411
1493
|
.cancelScheduledValues(now)
|
|
@@ -1421,22 +1503,18 @@ export class Midy {
|
|
|
1421
1503
|
.cancelScheduledValues(now)
|
|
1422
1504
|
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1423
1505
|
}
|
|
1424
|
-
setModLfoToFilterFc(
|
|
1506
|
+
setModLfoToFilterFc(note, pressure) {
|
|
1425
1507
|
const now = this.audioContext.currentTime;
|
|
1426
|
-
const pressureDepth = channel.pressureTable[4] / 127 * 2400;
|
|
1427
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1428
1508
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
|
|
1429
1509
|
note.filterDepth.gain
|
|
1430
1510
|
.cancelScheduledValues(now)
|
|
1431
1511
|
.setValueAtTime(modLfoToFilterFc, now);
|
|
1432
1512
|
}
|
|
1433
|
-
setModLfoToVolume(
|
|
1513
|
+
setModLfoToVolume(note, pressure) {
|
|
1434
1514
|
const now = this.audioContext.currentTime;
|
|
1435
1515
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1436
1516
|
const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1437
|
-
const
|
|
1438
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
1439
|
-
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
|
|
1517
|
+
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
|
|
1440
1518
|
note.volumeDepth.gain
|
|
1441
1519
|
.cancelScheduledValues(now)
|
|
1442
1520
|
.setValueAtTime(volumeDepth, now);
|
|
@@ -1509,11 +1587,18 @@ export class Midy {
|
|
|
1509
1587
|
.cancelScheduledValues(now)
|
|
1510
1588
|
.setValueAtTime(freqModLFO, now);
|
|
1511
1589
|
}
|
|
1590
|
+
setFreqVibLFO(channel, note) {
|
|
1591
|
+
const now = this.audioContext.currentTime;
|
|
1592
|
+
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1593
|
+
note.vibratoLFO.frequency
|
|
1594
|
+
.cancelScheduledValues(now)
|
|
1595
|
+
.setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
|
|
1596
|
+
}
|
|
1512
1597
|
createVoiceParamsHandlers() {
|
|
1513
1598
|
return {
|
|
1514
1599
|
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1515
1600
|
if (0 < channel.state.modulationDepth) {
|
|
1516
|
-
this.setModLfoToPitch(channel, note);
|
|
1601
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1517
1602
|
}
|
|
1518
1603
|
},
|
|
1519
1604
|
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
@@ -1523,12 +1608,12 @@ export class Midy {
|
|
|
1523
1608
|
},
|
|
1524
1609
|
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1525
1610
|
if (0 < channel.state.modulationDepth) {
|
|
1526
|
-
this.setModLfoToFilterFc(
|
|
1611
|
+
this.setModLfoToFilterFc(note, 0);
|
|
1527
1612
|
}
|
|
1528
1613
|
},
|
|
1529
1614
|
modLfoToVolume: (channel, note, _prevValue) => {
|
|
1530
1615
|
if (0 < channel.state.modulationDepth) {
|
|
1531
|
-
this.setModLfoToVolume(
|
|
1616
|
+
this.setModLfoToVolume(note, 0);
|
|
1532
1617
|
}
|
|
1533
1618
|
},
|
|
1534
1619
|
chorusEffectsSend: (channel, note, prevValue) => {
|
|
@@ -1554,11 +1639,7 @@ export class Midy {
|
|
|
1554
1639
|
},
|
|
1555
1640
|
freqVibLFO: (channel, note, _prevValue) => {
|
|
1556
1641
|
if (0 < channel.state.vibratoDepth) {
|
|
1557
|
-
|
|
1558
|
-
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1559
|
-
note.vibratoLFO.frequency
|
|
1560
|
-
.cancelScheduledValues(now)
|
|
1561
|
-
.setValueAtTime(freqVibLFO * channel.state.vibratoRate, now);
|
|
1642
|
+
this.setFreqVibLFO(channel, note);
|
|
1562
1643
|
}
|
|
1563
1644
|
},
|
|
1564
1645
|
};
|
|
@@ -1602,7 +1683,7 @@ export class Midy {
|
|
|
1602
1683
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1603
1684
|
}
|
|
1604
1685
|
else {
|
|
1605
|
-
this.setFilterEnvelope(channel, note);
|
|
1686
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1606
1687
|
}
|
|
1607
1688
|
this.setPitchEnvelope(note);
|
|
1608
1689
|
}
|
|
@@ -1616,7 +1697,7 @@ export class Midy {
|
|
|
1616
1697
|
if (key in voiceParams)
|
|
1617
1698
|
noteVoiceParams[key] = voiceParams[key];
|
|
1618
1699
|
}
|
|
1619
|
-
this.setVolumeEnvelope(channel, note);
|
|
1700
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1620
1701
|
}
|
|
1621
1702
|
}
|
|
1622
1703
|
}
|
|
@@ -1796,8 +1877,7 @@ export class Midy {
|
|
|
1796
1877
|
channel.state.sostenutoPedal = value / 127;
|
|
1797
1878
|
if (64 <= value) {
|
|
1798
1879
|
const now = this.audioContext.currentTime;
|
|
1799
|
-
|
|
1800
|
-
channel.sostenutoNotes = new Map(activeNotes);
|
|
1880
|
+
channel.sostenutoNotes = this.getActiveNotes(channel, now);
|
|
1801
1881
|
}
|
|
1802
1882
|
else {
|
|
1803
1883
|
this.releaseSostenutoPedal(channelNumber, value);
|
|
@@ -1837,7 +1917,7 @@ export class Midy {
|
|
|
1837
1917
|
continue;
|
|
1838
1918
|
if (note.startTime < now)
|
|
1839
1919
|
continue;
|
|
1840
|
-
this.setVolumeEnvelope(channel, note);
|
|
1920
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1841
1921
|
}
|
|
1842
1922
|
});
|
|
1843
1923
|
}
|
|
@@ -1849,7 +1929,12 @@ export class Midy {
|
|
|
1849
1929
|
const note = noteList[i];
|
|
1850
1930
|
if (!note)
|
|
1851
1931
|
continue;
|
|
1852
|
-
|
|
1932
|
+
if (note.portamento) {
|
|
1933
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1934
|
+
}
|
|
1935
|
+
else {
|
|
1936
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1937
|
+
}
|
|
1853
1938
|
}
|
|
1854
1939
|
});
|
|
1855
1940
|
}
|
|
@@ -1861,7 +1946,7 @@ export class Midy {
|
|
|
1861
1946
|
const note = noteList[i];
|
|
1862
1947
|
if (!note)
|
|
1863
1948
|
continue;
|
|
1864
|
-
this.setVolumeEnvelope(channel, note);
|
|
1949
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1865
1950
|
}
|
|
1866
1951
|
});
|
|
1867
1952
|
}
|
|
@@ -1870,21 +1955,53 @@ export class Midy {
|
|
|
1870
1955
|
channel.state.vibratoRate = vibratoRate / 64;
|
|
1871
1956
|
if (channel.vibratoDepth <= 0)
|
|
1872
1957
|
return;
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
.
|
|
1958
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1959
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1960
|
+
const note = noteList[i];
|
|
1961
|
+
if (!note)
|
|
1962
|
+
continue;
|
|
1963
|
+
this.setVibLfoToPitch(channel, note);
|
|
1964
|
+
}
|
|
1879
1965
|
});
|
|
1880
1966
|
}
|
|
1881
1967
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1882
1968
|
const channel = this.channels[channelNumber];
|
|
1969
|
+
const prev = channel.state.vibratoDepth;
|
|
1883
1970
|
channel.state.vibratoDepth = vibratoDepth / 64;
|
|
1971
|
+
if (0 < prev) {
|
|
1972
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1973
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1974
|
+
const note = noteList[i];
|
|
1975
|
+
if (!note)
|
|
1976
|
+
continue;
|
|
1977
|
+
this.setFreqVibLFO(channel, note);
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
else {
|
|
1982
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1983
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1984
|
+
const note = noteList[i];
|
|
1985
|
+
if (!note)
|
|
1986
|
+
continue;
|
|
1987
|
+
this.startVibrato(channel, note, note.startTime);
|
|
1988
|
+
}
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1884
1991
|
}
|
|
1885
1992
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1886
1993
|
const channel = this.channels[channelNumber];
|
|
1887
1994
|
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1995
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1996
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1997
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1998
|
+
const note = noteList[i];
|
|
1999
|
+
if (!note)
|
|
2000
|
+
continue;
|
|
2001
|
+
this.startVibrato(channel, note, note.startTime);
|
|
2002
|
+
}
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
1888
2005
|
}
|
|
1889
2006
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1890
2007
|
const channel = this.channels[channelNumber];
|
|
@@ -2049,7 +2166,7 @@ export class Midy {
|
|
|
2049
2166
|
const next = value / 128;
|
|
2050
2167
|
state.pitchWheelSensitivity = next;
|
|
2051
2168
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2052
|
-
this.
|
|
2169
|
+
this.updateChannelDetune(channel);
|
|
2053
2170
|
this.applyVoiceParams(channel, 16);
|
|
2054
2171
|
}
|
|
2055
2172
|
handleFineTuningRPN(channelNumber) {
|
|
@@ -2064,7 +2181,7 @@ export class Midy {
|
|
|
2064
2181
|
const next = (value - 8192) / 8.192; // cent
|
|
2065
2182
|
channel.fineTuning = next;
|
|
2066
2183
|
channel.detune += next - prev;
|
|
2067
|
-
this.
|
|
2184
|
+
this.updateChannelDetune(channel);
|
|
2068
2185
|
}
|
|
2069
2186
|
handleCoarseTuningRPN(channelNumber) {
|
|
2070
2187
|
const channel = this.channels[channelNumber];
|
|
@@ -2078,7 +2195,7 @@ export class Midy {
|
|
|
2078
2195
|
const next = (value - 64) * 100; // cent
|
|
2079
2196
|
channel.coarseTuning = next;
|
|
2080
2197
|
channel.detune += next - prev;
|
|
2081
|
-
this.
|
|
2198
|
+
this.updateChannelDetune(channel);
|
|
2082
2199
|
}
|
|
2083
2200
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
2084
2201
|
const channel = this.channels[channelNumber];
|
|
@@ -2109,7 +2226,7 @@ export class Midy {
|
|
|
2109
2226
|
const state = channel.state;
|
|
2110
2227
|
for (let i = 0; i < stateTypes.length; i++) {
|
|
2111
2228
|
const type = stateTypes[i];
|
|
2112
|
-
state[type] = defaultControllerState[type];
|
|
2229
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
2113
2230
|
}
|
|
2114
2231
|
const settingTypes = [
|
|
2115
2232
|
"rpnMSB",
|
|
@@ -2141,7 +2258,10 @@ export class Midy {
|
|
|
2141
2258
|
switch (data[3]) {
|
|
2142
2259
|
case 8:
|
|
2143
2260
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2144
|
-
return this.handleScaleOctaveTuning1ByteFormatSysEx(data);
|
|
2261
|
+
return this.handleScaleOctaveTuning1ByteFormatSysEx(data, false);
|
|
2262
|
+
case 9:
|
|
2263
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2264
|
+
return this.handleScaleOctaveTuning2ByteFormatSysEx(data, false);
|
|
2145
2265
|
default:
|
|
2146
2266
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2147
2267
|
}
|
|
@@ -2203,8 +2323,10 @@ export class Midy {
|
|
|
2203
2323
|
case 8:
|
|
2204
2324
|
switch (data[3]) {
|
|
2205
2325
|
case 8: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2206
|
-
|
|
2207
|
-
|
|
2326
|
+
return this.handleScaleOctaveTuning1ByteFormatSysEx(data, true);
|
|
2327
|
+
case 9:
|
|
2328
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2329
|
+
return this.handleScaleOctaveTuning2ByteFormatSysEx(data, true);
|
|
2208
2330
|
default:
|
|
2209
2331
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2210
2332
|
}
|
|
@@ -2212,7 +2334,9 @@ export class Midy {
|
|
|
2212
2334
|
case 9:
|
|
2213
2335
|
switch (data[3]) {
|
|
2214
2336
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2215
|
-
return this.
|
|
2337
|
+
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2338
|
+
case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2339
|
+
return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
|
|
2216
2340
|
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2217
2341
|
return this.handleControlChangeSysEx(data);
|
|
2218
2342
|
default:
|
|
@@ -2254,7 +2378,7 @@ export class Midy {
|
|
|
2254
2378
|
const next = (value - 8192) / 8.192; // cent
|
|
2255
2379
|
this.masterFineTuning = next;
|
|
2256
2380
|
channel.detune += next - prev;
|
|
2257
|
-
this.
|
|
2381
|
+
this.updateChannelDetune(channel);
|
|
2258
2382
|
}
|
|
2259
2383
|
handleMasterCoarseTuningSysEx(data) {
|
|
2260
2384
|
const coarseTuning = data[4];
|
|
@@ -2265,7 +2389,7 @@ export class Midy {
|
|
|
2265
2389
|
const next = (value - 64) * 100; // cent
|
|
2266
2390
|
this.masterCoarseTuning = next;
|
|
2267
2391
|
channel.detune += next - prev;
|
|
2268
|
-
this.
|
|
2392
|
+
this.updateChannelDetune(channel);
|
|
2269
2393
|
}
|
|
2270
2394
|
handleGlobalParameterControlSysEx(data) {
|
|
2271
2395
|
if (data[7] === 1) {
|
|
@@ -2472,8 +2596,8 @@ export class Midy {
|
|
|
2472
2596
|
}
|
|
2473
2597
|
return bitmap;
|
|
2474
2598
|
}
|
|
2475
|
-
handleScaleOctaveTuning1ByteFormatSysEx(data) {
|
|
2476
|
-
if (data.length <
|
|
2599
|
+
handleScaleOctaveTuning1ByteFormatSysEx(data, realtime) {
|
|
2600
|
+
if (data.length < 19) {
|
|
2477
2601
|
console.error("Data length is too short");
|
|
2478
2602
|
return;
|
|
2479
2603
|
}
|
|
@@ -2481,37 +2605,96 @@ export class Midy {
|
|
|
2481
2605
|
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2482
2606
|
if (!channelBitmap[i])
|
|
2483
2607
|
continue;
|
|
2608
|
+
const channel = this.channels[i];
|
|
2484
2609
|
for (let j = 0; j < 12; j++) {
|
|
2485
|
-
const
|
|
2486
|
-
|
|
2610
|
+
const centValue = data[j + 7] - 64;
|
|
2611
|
+
channel.scaleOctaveTuningTable[j] = centValue;
|
|
2487
2612
|
}
|
|
2613
|
+
if (realtime)
|
|
2614
|
+
this.updateChannelDetune(channel);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
handleScaleOctaveTuning2ByteFormatSysEx(data, realtime) {
|
|
2618
|
+
if (data.length < 31) {
|
|
2619
|
+
console.error("Data length is too short");
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
const channelBitmap = this.getChannelBitmap(data);
|
|
2623
|
+
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2624
|
+
if (!channelBitmap[i])
|
|
2625
|
+
continue;
|
|
2626
|
+
const channel = this.channels[i];
|
|
2627
|
+
for (let j = 0; j < 12; j++) {
|
|
2628
|
+
const index = 7 + j * 2;
|
|
2629
|
+
const msb = data[index] & 0x7F;
|
|
2630
|
+
const lsb = data[index + 1] & 0x7F;
|
|
2631
|
+
const value14bit = msb * 128 + lsb;
|
|
2632
|
+
const centValue = (value14bit - 8192) / 8.192;
|
|
2633
|
+
channel.scaleOctaveTuningTable[j] = centValue;
|
|
2634
|
+
}
|
|
2635
|
+
if (realtime)
|
|
2636
|
+
this.updateChannelDetune(channel);
|
|
2488
2637
|
}
|
|
2489
2638
|
}
|
|
2490
2639
|
applyDestinationSettings(channel, note, table) {
|
|
2491
2640
|
if (table[0] !== 64) {
|
|
2492
|
-
|
|
2641
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2642
|
+
? channel.polyphonicKeyPressureTable[0] * note.pressure
|
|
2643
|
+
: 0;
|
|
2644
|
+
const pressure = (polyphonicKeyPressure - 64) / 37.5; // 2400 / 64;
|
|
2645
|
+
this.updateDetune(channel, note, pressure);
|
|
2493
2646
|
}
|
|
2494
2647
|
if (!note.portamento) {
|
|
2495
2648
|
if (table[1] !== 64) {
|
|
2496
|
-
|
|
2649
|
+
const channelPressure = channel.channelPressureTable[1] *
|
|
2650
|
+
channel.state.channelPressure;
|
|
2651
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2652
|
+
? channel.polyphonicKeyPressureTable[1] * note.pressure
|
|
2653
|
+
: 0;
|
|
2654
|
+
const pressure = (channelPressure + polyphonicKeyPressure - 128) * 15;
|
|
2655
|
+
this.setFilterEnvelope(channel, note, pressure);
|
|
2497
2656
|
}
|
|
2498
2657
|
if (table[2] !== 64) {
|
|
2499
|
-
|
|
2658
|
+
const channelPressure = channel.channelPressureTable[2] *
|
|
2659
|
+
channel.state.channelPressure;
|
|
2660
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2661
|
+
? channel.polyphonicKeyPressureTable[2] * note.pressure
|
|
2662
|
+
: 0;
|
|
2663
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 128;
|
|
2664
|
+
this.setVolumeEnvelope(channel, note, pressure);
|
|
2500
2665
|
}
|
|
2501
2666
|
}
|
|
2502
2667
|
if (table[3] !== 0) {
|
|
2503
|
-
|
|
2668
|
+
const channelPressure = channel.channelPressureTable[3] *
|
|
2669
|
+
channel.state.channelPressure;
|
|
2670
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2671
|
+
? channel.polyphonicKeyPressureTable[3] * note.pressure
|
|
2672
|
+
: 0;
|
|
2673
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 600;
|
|
2674
|
+
this.setModLfoToPitch(channel, note, pressure);
|
|
2504
2675
|
}
|
|
2505
2676
|
if (table[4] !== 0) {
|
|
2506
|
-
|
|
2677
|
+
const channelPressure = channel.channelPressureTable[4] *
|
|
2678
|
+
channel.state.channelPressure;
|
|
2679
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2680
|
+
? channel.polyphonicKeyPressureTable[4] * note.pressure
|
|
2681
|
+
: 0;
|
|
2682
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 2400;
|
|
2683
|
+
this.setModLfoToFilterFc(note, pressure);
|
|
2507
2684
|
}
|
|
2508
2685
|
if (table[5] !== 0) {
|
|
2509
|
-
|
|
2686
|
+
const channelPressure = channel.channelPressureTable[5] *
|
|
2687
|
+
channel.state.channelPressure;
|
|
2688
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2689
|
+
? channel.polyphonicKeyPressureTable[5] * note.pressure
|
|
2690
|
+
: 0;
|
|
2691
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254;
|
|
2692
|
+
this.setModLfoToVolume(note, pressure);
|
|
2510
2693
|
}
|
|
2511
2694
|
}
|
|
2512
|
-
handleChannelPressureSysEx(data) {
|
|
2695
|
+
handleChannelPressureSysEx(data, tableName) {
|
|
2513
2696
|
const channelNumber = data[4];
|
|
2514
|
-
const table = this.channels[channelNumber]
|
|
2697
|
+
const table = this.channels[channelNumber][tableName];
|
|
2515
2698
|
for (let i = 5; i < data.length - 1; i += 2) {
|
|
2516
2699
|
const pp = data[i];
|
|
2517
2700
|
const rr = data[i + 1];
|
|
@@ -2601,8 +2784,9 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2601
2784
|
value: {
|
|
2602
2785
|
currentBufferSource: null,
|
|
2603
2786
|
detune: 0,
|
|
2604
|
-
scaleOctaveTuningTable: new
|
|
2605
|
-
|
|
2787
|
+
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
2788
|
+
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2789
|
+
polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2606
2790
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2607
2791
|
program: 0,
|
|
2608
2792
|
bank: 121 * 128,
|
|
@@ -2617,16 +2801,3 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2617
2801
|
modulationDepthRange: 50, // cent
|
|
2618
2802
|
}
|
|
2619
2803
|
});
|
|
2620
|
-
Object.defineProperty(Midy, "controllerDestinationSettings", {
|
|
2621
|
-
enumerable: true,
|
|
2622
|
-
configurable: true,
|
|
2623
|
-
writable: true,
|
|
2624
|
-
value: {
|
|
2625
|
-
pitchControl: 0,
|
|
2626
|
-
filterCutoffControl: 0,
|
|
2627
|
-
amplitudeControl: 1,
|
|
2628
|
-
lfoPitchDepth: 0,
|
|
2629
|
-
lfoFilterDepth: 0,
|
|
2630
|
-
lfoAmplitudeDepth: 0,
|
|
2631
|
-
}
|
|
2632
|
-
});
|