@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/script/midy.js
CHANGED
|
@@ -3,6 +3,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Midy = void 0;
|
|
4
4
|
const midi_file_1 = require("midi-file");
|
|
5
5
|
const soundfont_parser_1 = require("@marmooo/soundfont-parser");
|
|
6
|
+
// 2-3 times faster than Map
|
|
7
|
+
class SparseMap {
|
|
8
|
+
constructor(size) {
|
|
9
|
+
this.data = new Array(size);
|
|
10
|
+
this.activeIndices = [];
|
|
11
|
+
}
|
|
12
|
+
set(key, value) {
|
|
13
|
+
if (this.data[key] === undefined) {
|
|
14
|
+
this.activeIndices.push(key);
|
|
15
|
+
}
|
|
16
|
+
this.data[key] = value;
|
|
17
|
+
}
|
|
18
|
+
get(key) {
|
|
19
|
+
return this.data[key];
|
|
20
|
+
}
|
|
21
|
+
delete(key) {
|
|
22
|
+
if (this.data[key] !== undefined) {
|
|
23
|
+
this.data[key] = undefined;
|
|
24
|
+
const index = this.activeIndices.indexOf(key);
|
|
25
|
+
if (index !== -1) {
|
|
26
|
+
this.activeIndices.splice(index, 1);
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
has(key) {
|
|
33
|
+
return this.data[key] !== undefined;
|
|
34
|
+
}
|
|
35
|
+
get size() {
|
|
36
|
+
return this.activeIndices.length;
|
|
37
|
+
}
|
|
38
|
+
clear() {
|
|
39
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
40
|
+
const key = this.activeIndices[i];
|
|
41
|
+
this.data[key] = undefined;
|
|
42
|
+
}
|
|
43
|
+
this.activeIndices = [];
|
|
44
|
+
}
|
|
45
|
+
*[Symbol.iterator]() {
|
|
46
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
47
|
+
const key = this.activeIndices[i];
|
|
48
|
+
yield [key, this.data[key]];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
forEach(callback) {
|
|
52
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
53
|
+
const key = this.activeIndices[i];
|
|
54
|
+
callback(this.data[key], key, this);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
6
58
|
class Note {
|
|
7
59
|
constructor(noteNumber, velocity, startTime, voice, voiceParams) {
|
|
8
60
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -89,6 +141,12 @@ class Note {
|
|
|
89
141
|
writable: true,
|
|
90
142
|
value: void 0
|
|
91
143
|
});
|
|
144
|
+
Object.defineProperty(this, "pressure", {
|
|
145
|
+
enumerable: true,
|
|
146
|
+
configurable: true,
|
|
147
|
+
writable: true,
|
|
148
|
+
value: 0
|
|
149
|
+
});
|
|
92
150
|
this.noteNumber = noteNumber;
|
|
93
151
|
this.velocity = velocity;
|
|
94
152
|
this.startTime = startTime;
|
|
@@ -100,7 +158,7 @@ class Note {
|
|
|
100
158
|
const defaultControllerState = {
|
|
101
159
|
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
102
160
|
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
103
|
-
|
|
161
|
+
polyphonicKeyPressure: { type: 10, defaultValue: 0 },
|
|
104
162
|
channelPressure: { type: 13, defaultValue: 0 },
|
|
105
163
|
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
106
164
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
@@ -284,6 +342,18 @@ class Midy {
|
|
|
284
342
|
writable: true,
|
|
285
343
|
value: this.initSoundFontTable()
|
|
286
344
|
});
|
|
345
|
+
Object.defineProperty(this, "audioBufferCounter", {
|
|
346
|
+
enumerable: true,
|
|
347
|
+
configurable: true,
|
|
348
|
+
writable: true,
|
|
349
|
+
value: new Map()
|
|
350
|
+
});
|
|
351
|
+
Object.defineProperty(this, "audioBufferCache", {
|
|
352
|
+
enumerable: true,
|
|
353
|
+
configurable: true,
|
|
354
|
+
writable: true,
|
|
355
|
+
value: new Map()
|
|
356
|
+
});
|
|
287
357
|
Object.defineProperty(this, "isPlaying", {
|
|
288
358
|
enumerable: true,
|
|
289
359
|
configurable: true,
|
|
@@ -336,7 +406,7 @@ class Midy {
|
|
|
336
406
|
enumerable: true,
|
|
337
407
|
configurable: true,
|
|
338
408
|
writable: true,
|
|
339
|
-
value: new
|
|
409
|
+
value: new SparseMap(128)
|
|
340
410
|
});
|
|
341
411
|
Object.defineProperty(this, "defaultOptions", {
|
|
342
412
|
enumerable: true,
|
|
@@ -376,7 +446,7 @@ class Midy {
|
|
|
376
446
|
initSoundFontTable() {
|
|
377
447
|
const table = new Array(128);
|
|
378
448
|
for (let i = 0; i < 128; i++) {
|
|
379
|
-
table[i] = new
|
|
449
|
+
table[i] = new SparseMap(128);
|
|
380
450
|
}
|
|
381
451
|
return table;
|
|
382
452
|
}
|
|
@@ -430,14 +500,8 @@ class Midy {
|
|
|
430
500
|
state: new ControllerState(),
|
|
431
501
|
controlTable: this.initControlTable(),
|
|
432
502
|
...this.setChannelAudioNodes(audioContext),
|
|
433
|
-
scheduledNotes: new
|
|
434
|
-
sostenutoNotes: new
|
|
435
|
-
polyphonicKeyPressure: {
|
|
436
|
-
...this.constructor.controllerDestinationSettings,
|
|
437
|
-
},
|
|
438
|
-
channelPressure: {
|
|
439
|
-
...this.constructor.controllerDestinationSettings,
|
|
440
|
-
},
|
|
503
|
+
scheduledNotes: new SparseMap(128),
|
|
504
|
+
sostenutoNotes: new SparseMap(128),
|
|
441
505
|
};
|
|
442
506
|
});
|
|
443
507
|
return channels;
|
|
@@ -471,9 +535,8 @@ class Midy {
|
|
|
471
535
|
return audioBuffer;
|
|
472
536
|
}
|
|
473
537
|
}
|
|
474
|
-
|
|
538
|
+
createNoteBufferNode(audioBuffer, voiceParams) {
|
|
475
539
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
476
|
-
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
477
540
|
bufferSource.buffer = audioBuffer;
|
|
478
541
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
479
542
|
if (bufferSource.loop) {
|
|
@@ -565,6 +628,7 @@ class Midy {
|
|
|
565
628
|
await Promise.all(this.notePromises);
|
|
566
629
|
this.notePromises = [];
|
|
567
630
|
this.exclusiveClassMap.clear();
|
|
631
|
+
this.audioBufferCache.clear();
|
|
568
632
|
resolve();
|
|
569
633
|
return;
|
|
570
634
|
}
|
|
@@ -580,8 +644,9 @@ class Midy {
|
|
|
580
644
|
}
|
|
581
645
|
else if (this.isStopping) {
|
|
582
646
|
await this.stopNotes(0, true);
|
|
583
|
-
this.exclusiveClassMap.clear();
|
|
584
647
|
this.notePromises = [];
|
|
648
|
+
this.exclusiveClassMap.clear();
|
|
649
|
+
this.audioBufferCache.clear();
|
|
585
650
|
resolve();
|
|
586
651
|
this.isStopping = false;
|
|
587
652
|
this.isPaused = false;
|
|
@@ -612,6 +677,9 @@ class Midy {
|
|
|
612
677
|
secondToTicks(second, secondsPerBeat) {
|
|
613
678
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
614
679
|
}
|
|
680
|
+
getAudioBufferId(programNumber, noteNumber, velocity) {
|
|
681
|
+
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
682
|
+
}
|
|
615
683
|
extractMidiData(midi) {
|
|
616
684
|
const instruments = new Set();
|
|
617
685
|
const timeline = [];
|
|
@@ -633,6 +701,8 @@ class Midy {
|
|
|
633
701
|
switch (event.type) {
|
|
634
702
|
case "noteOn": {
|
|
635
703
|
const channel = tmpChannels[event.channel];
|
|
704
|
+
const audioBufferId = this.getAudioBufferId(channel.programNumber, event.noteNumber, event.velocity);
|
|
705
|
+
this.audioBufferCounter.set(audioBufferId, (this.audioBufferCounter.get(audioBufferId) ?? 0) + 1);
|
|
636
706
|
if (channel.programNumber < 0) {
|
|
637
707
|
channel.programNumber = event.programNumber;
|
|
638
708
|
switch (channel.bankMSB) {
|
|
@@ -682,6 +752,10 @@ class Midy {
|
|
|
682
752
|
timeline.push(event);
|
|
683
753
|
}
|
|
684
754
|
}
|
|
755
|
+
for (const [audioBufferId, count] of this.audioBufferCounter) {
|
|
756
|
+
if (count === 1)
|
|
757
|
+
this.audioBufferCounter.delete(audioBufferId);
|
|
758
|
+
}
|
|
685
759
|
const priority = {
|
|
686
760
|
controller: 0,
|
|
687
761
|
sysEx: 1,
|
|
@@ -775,7 +849,7 @@ class Midy {
|
|
|
775
849
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
776
850
|
}
|
|
777
851
|
getActiveNotes(channel, time) {
|
|
778
|
-
const activeNotes = new
|
|
852
|
+
const activeNotes = new SparseMap(128);
|
|
779
853
|
channel.scheduledNotes.forEach((noteList) => {
|
|
780
854
|
const activeNote = this.getActiveNote(noteList, time);
|
|
781
855
|
if (activeNote) {
|
|
@@ -942,28 +1016,31 @@ class Midy {
|
|
|
942
1016
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
943
1017
|
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
944
1018
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
945
|
-
const pressureDepth = (channel.
|
|
1019
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
946
1020
|
const pressure = pressureDepth * channel.state.channelPressure;
|
|
947
1021
|
return tuning + pitch + pressure;
|
|
948
1022
|
}
|
|
949
1023
|
calcNoteDetune(channel, note) {
|
|
950
1024
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
951
1025
|
}
|
|
952
|
-
|
|
953
|
-
const now = this.audioContext.currentTime;
|
|
1026
|
+
updateChannelDetune(channel) {
|
|
954
1027
|
channel.scheduledNotes.forEach((noteList) => {
|
|
955
1028
|
for (let i = 0; i < noteList.length; i++) {
|
|
956
1029
|
const note = noteList[i];
|
|
957
1030
|
if (!note)
|
|
958
1031
|
continue;
|
|
959
|
-
|
|
960
|
-
const detune = channel.detune + noteDetune;
|
|
961
|
-
note.bufferSource.detune
|
|
962
|
-
.cancelScheduledValues(now)
|
|
963
|
-
.setValueAtTime(detune, now);
|
|
1032
|
+
this.updateDetune(channel, note, 0);
|
|
964
1033
|
}
|
|
965
1034
|
});
|
|
966
1035
|
}
|
|
1036
|
+
updateDetune(channel, note, pressure) {
|
|
1037
|
+
const now = this.audioContext.currentTime;
|
|
1038
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1039
|
+
const detune = channel.detune + noteDetune + pressure;
|
|
1040
|
+
note.bufferSource.detune
|
|
1041
|
+
.cancelScheduledValues(now)
|
|
1042
|
+
.setValueAtTime(detune, now);
|
|
1043
|
+
}
|
|
967
1044
|
getPortamentoTime(channel) {
|
|
968
1045
|
const factor = 5 * Math.log(10) / 127;
|
|
969
1046
|
const time = channel.state.portamentoTime;
|
|
@@ -981,14 +1058,12 @@ class Midy {
|
|
|
981
1058
|
.setValueAtTime(0, volDelay)
|
|
982
1059
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
983
1060
|
}
|
|
984
|
-
setVolumeEnvelope(channel, note) {
|
|
1061
|
+
setVolumeEnvelope(channel, note, pressure) {
|
|
985
1062
|
const now = this.audioContext.currentTime;
|
|
986
1063
|
const state = channel.state;
|
|
987
1064
|
const { voiceParams, startTime } = note;
|
|
988
|
-
const pressureDepth = channel.pressureTable[2] / 64;
|
|
989
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
990
1065
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
991
|
-
pressure;
|
|
1066
|
+
(1 + pressure);
|
|
992
1067
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
993
1068
|
const volDelay = startTime + voiceParams.volDelay;
|
|
994
1069
|
const volAttack = volDelay + voiceParams.volAttack * state.attackTime * 2;
|
|
@@ -1036,10 +1111,8 @@ class Midy {
|
|
|
1036
1111
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1037
1112
|
const softPedalFactor = 1 -
|
|
1038
1113
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1041
|
-
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1042
|
-
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1114
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
1115
|
+
softPedalFactor *
|
|
1043
1116
|
state.brightness * 2;
|
|
1044
1117
|
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1045
1118
|
const sustainFreq = baseFreq +
|
|
@@ -1054,15 +1127,17 @@ class Midy {
|
|
|
1054
1127
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1055
1128
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1056
1129
|
}
|
|
1057
|
-
setFilterEnvelope(channel, note) {
|
|
1130
|
+
setFilterEnvelope(channel, note, pressure) {
|
|
1058
1131
|
const now = this.audioContext.currentTime;
|
|
1059
1132
|
const state = channel.state;
|
|
1060
1133
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1061
1134
|
const softPedalFactor = 1 -
|
|
1062
1135
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1063
|
-
const
|
|
1136
|
+
const baseCent = voiceParams.initialFilterFc + pressure;
|
|
1137
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
1138
|
+
state.brightness * 2;
|
|
1139
|
+
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1064
1140
|
softPedalFactor * state.brightness * 2;
|
|
1065
|
-
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
1066
1141
|
const sustainFreq = baseFreq +
|
|
1067
1142
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1068
1143
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1089,9 +1164,9 @@ class Midy {
|
|
|
1089
1164
|
gain: voiceParams.modLfoToFilterFc,
|
|
1090
1165
|
});
|
|
1091
1166
|
note.modulationDepth = new GainNode(this.audioContext);
|
|
1092
|
-
this.setModLfoToPitch(channel, note);
|
|
1167
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1093
1168
|
note.volumeDepth = new GainNode(this.audioContext);
|
|
1094
|
-
this.setModLfoToVolume(
|
|
1169
|
+
this.setModLfoToVolume(note, 0);
|
|
1095
1170
|
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
1096
1171
|
note.modulationLFO.connect(note.filterDepth);
|
|
1097
1172
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
@@ -1104,8 +1179,7 @@ class Midy {
|
|
|
1104
1179
|
const { voiceParams } = note;
|
|
1105
1180
|
const state = channel.state;
|
|
1106
1181
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1107
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1108
|
-
state.vibratoRate,
|
|
1182
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
|
|
1109
1183
|
});
|
|
1110
1184
|
note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
|
|
1111
1185
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
@@ -1113,12 +1187,31 @@ class Midy {
|
|
|
1113
1187
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
1114
1188
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
1115
1189
|
}
|
|
1190
|
+
async getAudioBuffer(program, noteNumber, velocity, voiceParams, isSF3) {
|
|
1191
|
+
const audioBufferId = this.getAudioBufferId(program, noteNumber, velocity);
|
|
1192
|
+
const cache = this.audioBufferCache.get(audioBufferId);
|
|
1193
|
+
if (cache) {
|
|
1194
|
+
cache.counter += 1;
|
|
1195
|
+
if (cache.maxCount <= cache.counter) {
|
|
1196
|
+
this.audioBufferCache.delete(audioBufferId);
|
|
1197
|
+
}
|
|
1198
|
+
return cache.audioBuffer;
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
const maxCount = this.audioBufferCounter.get(audioBufferId) ?? 0;
|
|
1202
|
+
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
1203
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
1204
|
+
this.audioBufferCache.set(audioBufferId, cache);
|
|
1205
|
+
return audioBuffer;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1116
1208
|
async createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
1117
1209
|
const state = channel.state;
|
|
1118
1210
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1119
1211
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1120
1212
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1121
|
-
|
|
1213
|
+
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
1214
|
+
note.bufferSource = this.createNoteBufferNode(audioBuffer, voiceParams);
|
|
1122
1215
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1123
1216
|
note.gainL = new GainNode(this.audioContext);
|
|
1124
1217
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1134,8 +1227,8 @@ class Midy {
|
|
|
1134
1227
|
}
|
|
1135
1228
|
else {
|
|
1136
1229
|
note.portamento = false;
|
|
1137
|
-
this.setVolumeEnvelope(channel, note);
|
|
1138
|
-
this.setFilterEnvelope(channel, note);
|
|
1230
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1231
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1139
1232
|
}
|
|
1140
1233
|
if (0 < state.vibratoDepth) {
|
|
1141
1234
|
this.startVibrato(channel, note, startTime);
|
|
@@ -1178,10 +1271,10 @@ class Midy {
|
|
|
1178
1271
|
if (soundFontIndex === undefined)
|
|
1179
1272
|
return;
|
|
1180
1273
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
1181
|
-
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1182
1274
|
const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
|
|
1183
1275
|
if (!voice)
|
|
1184
1276
|
return;
|
|
1277
|
+
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1185
1278
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
|
|
1186
1279
|
note.gainL.connect(channel.gainL);
|
|
1187
1280
|
note.gainR.connect(channel.gainR);
|
|
@@ -1350,16 +1443,12 @@ class Midy {
|
|
|
1350
1443
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
1351
1444
|
const now = this.audioContext.currentTime;
|
|
1352
1445
|
const channel = this.channels[channelNumber];
|
|
1353
|
-
pressure
|
|
1446
|
+
channel.state.polyphonicKeyPressure = pressure / 127;
|
|
1447
|
+
const table = channel.polyphonicKeyPressureTable;
|
|
1354
1448
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1355
|
-
if (
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
const gain = activeNote.gainL.gain.value;
|
|
1359
|
-
activeNote.volumeNode.gain
|
|
1360
|
-
.cancelScheduledValues(now)
|
|
1361
|
-
.setValueAtTime(gain * pressure, now);
|
|
1362
|
-
}
|
|
1449
|
+
if (activeNotes.has(noteNumber)) {
|
|
1450
|
+
const note = activeNotes.get(noteNumber);
|
|
1451
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1363
1452
|
}
|
|
1364
1453
|
// this.applyVoiceParams(channel, 10);
|
|
1365
1454
|
}
|
|
@@ -1369,22 +1458,18 @@ class Midy {
|
|
|
1369
1458
|
channel.program = program;
|
|
1370
1459
|
}
|
|
1371
1460
|
handleChannelPressure(channelNumber, value) {
|
|
1461
|
+
const now = this.audioContext.currentTime;
|
|
1372
1462
|
const channel = this.channels[channelNumber];
|
|
1373
1463
|
const prev = channel.state.channelPressure;
|
|
1374
1464
|
const next = value / 127;
|
|
1375
1465
|
channel.state.channelPressure = next;
|
|
1376
|
-
if (channel.
|
|
1377
|
-
const pressureDepth = (channel.
|
|
1466
|
+
if (channel.channelPressureTable[0] !== 64) {
|
|
1467
|
+
const pressureDepth = (channel.channelPressureTable[0] - 64) / 37.5; // 2400 / 64;
|
|
1378
1468
|
channel.detune += pressureDepth * (next - prev);
|
|
1379
1469
|
}
|
|
1380
|
-
const table = channel.
|
|
1381
|
-
channel.
|
|
1382
|
-
|
|
1383
|
-
const note = noteList[i];
|
|
1384
|
-
if (!note)
|
|
1385
|
-
continue;
|
|
1386
|
-
this.applyDestinationSettings(channel, note, table);
|
|
1387
|
-
}
|
|
1470
|
+
const table = channel.channelPressureTable;
|
|
1471
|
+
this.getActiveNotes(channel, now).forEach((note) => {
|
|
1472
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1388
1473
|
});
|
|
1389
1474
|
// this.applyVoiceParams(channel, 13);
|
|
1390
1475
|
}
|
|
@@ -1399,16 +1484,13 @@ class Midy {
|
|
|
1399
1484
|
const next = (value - 8192) / 8192;
|
|
1400
1485
|
state.pitchWheel = value / 16383;
|
|
1401
1486
|
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1402
|
-
this.
|
|
1487
|
+
this.updateChannelDetune(channel);
|
|
1403
1488
|
this.applyVoiceParams(channel, 14);
|
|
1404
1489
|
}
|
|
1405
|
-
setModLfoToPitch(channel, note) {
|
|
1490
|
+
setModLfoToPitch(channel, note, pressure) {
|
|
1406
1491
|
const now = this.audioContext.currentTime;
|
|
1407
|
-
const pressureDepth = channel.pressureTable[3] / 127 * 600;
|
|
1408
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1409
1492
|
const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
|
|
1410
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1411
|
-
channel.state.modulationDepth;
|
|
1493
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1412
1494
|
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1413
1495
|
note.modulationDepth.gain
|
|
1414
1496
|
.cancelScheduledValues(now)
|
|
@@ -1424,22 +1506,18 @@ class Midy {
|
|
|
1424
1506
|
.cancelScheduledValues(now)
|
|
1425
1507
|
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1426
1508
|
}
|
|
1427
|
-
setModLfoToFilterFc(
|
|
1509
|
+
setModLfoToFilterFc(note, pressure) {
|
|
1428
1510
|
const now = this.audioContext.currentTime;
|
|
1429
|
-
const pressureDepth = channel.pressureTable[4] / 127 * 2400;
|
|
1430
|
-
const pressure = pressureDepth * channel.state.channelPressure;
|
|
1431
1511
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
|
|
1432
1512
|
note.filterDepth.gain
|
|
1433
1513
|
.cancelScheduledValues(now)
|
|
1434
1514
|
.setValueAtTime(modLfoToFilterFc, now);
|
|
1435
1515
|
}
|
|
1436
|
-
setModLfoToVolume(
|
|
1516
|
+
setModLfoToVolume(note, pressure) {
|
|
1437
1517
|
const now = this.audioContext.currentTime;
|
|
1438
1518
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1439
1519
|
const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1440
|
-
const
|
|
1441
|
-
const pressure = 1 + pressureDepth * channel.state.channelPressure;
|
|
1442
|
-
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * pressure;
|
|
1520
|
+
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
|
|
1443
1521
|
note.volumeDepth.gain
|
|
1444
1522
|
.cancelScheduledValues(now)
|
|
1445
1523
|
.setValueAtTime(volumeDepth, now);
|
|
@@ -1512,11 +1590,18 @@ class Midy {
|
|
|
1512
1590
|
.cancelScheduledValues(now)
|
|
1513
1591
|
.setValueAtTime(freqModLFO, now);
|
|
1514
1592
|
}
|
|
1593
|
+
setFreqVibLFO(channel, note) {
|
|
1594
|
+
const now = this.audioContext.currentTime;
|
|
1595
|
+
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1596
|
+
note.vibratoLFO.frequency
|
|
1597
|
+
.cancelScheduledValues(now)
|
|
1598
|
+
.setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, now);
|
|
1599
|
+
}
|
|
1515
1600
|
createVoiceParamsHandlers() {
|
|
1516
1601
|
return {
|
|
1517
1602
|
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1518
1603
|
if (0 < channel.state.modulationDepth) {
|
|
1519
|
-
this.setModLfoToPitch(channel, note);
|
|
1604
|
+
this.setModLfoToPitch(channel, note, 0);
|
|
1520
1605
|
}
|
|
1521
1606
|
},
|
|
1522
1607
|
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
@@ -1526,12 +1611,12 @@ class Midy {
|
|
|
1526
1611
|
},
|
|
1527
1612
|
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1528
1613
|
if (0 < channel.state.modulationDepth) {
|
|
1529
|
-
this.setModLfoToFilterFc(
|
|
1614
|
+
this.setModLfoToFilterFc(note, 0);
|
|
1530
1615
|
}
|
|
1531
1616
|
},
|
|
1532
1617
|
modLfoToVolume: (channel, note, _prevValue) => {
|
|
1533
1618
|
if (0 < channel.state.modulationDepth) {
|
|
1534
|
-
this.setModLfoToVolume(
|
|
1619
|
+
this.setModLfoToVolume(note, 0);
|
|
1535
1620
|
}
|
|
1536
1621
|
},
|
|
1537
1622
|
chorusEffectsSend: (channel, note, prevValue) => {
|
|
@@ -1557,11 +1642,7 @@ class Midy {
|
|
|
1557
1642
|
},
|
|
1558
1643
|
freqVibLFO: (channel, note, _prevValue) => {
|
|
1559
1644
|
if (0 < channel.state.vibratoDepth) {
|
|
1560
|
-
|
|
1561
|
-
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1562
|
-
note.vibratoLFO.frequency
|
|
1563
|
-
.cancelScheduledValues(now)
|
|
1564
|
-
.setValueAtTime(freqVibLFO * channel.state.vibratoRate, now);
|
|
1645
|
+
this.setFreqVibLFO(channel, note);
|
|
1565
1646
|
}
|
|
1566
1647
|
},
|
|
1567
1648
|
};
|
|
@@ -1605,7 +1686,7 @@ class Midy {
|
|
|
1605
1686
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1606
1687
|
}
|
|
1607
1688
|
else {
|
|
1608
|
-
this.setFilterEnvelope(channel, note);
|
|
1689
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1609
1690
|
}
|
|
1610
1691
|
this.setPitchEnvelope(note);
|
|
1611
1692
|
}
|
|
@@ -1619,7 +1700,7 @@ class Midy {
|
|
|
1619
1700
|
if (key in voiceParams)
|
|
1620
1701
|
noteVoiceParams[key] = voiceParams[key];
|
|
1621
1702
|
}
|
|
1622
|
-
this.setVolumeEnvelope(channel, note);
|
|
1703
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1623
1704
|
}
|
|
1624
1705
|
}
|
|
1625
1706
|
}
|
|
@@ -1799,8 +1880,7 @@ class Midy {
|
|
|
1799
1880
|
channel.state.sostenutoPedal = value / 127;
|
|
1800
1881
|
if (64 <= value) {
|
|
1801
1882
|
const now = this.audioContext.currentTime;
|
|
1802
|
-
|
|
1803
|
-
channel.sostenutoNotes = new Map(activeNotes);
|
|
1883
|
+
channel.sostenutoNotes = this.getActiveNotes(channel, now);
|
|
1804
1884
|
}
|
|
1805
1885
|
else {
|
|
1806
1886
|
this.releaseSostenutoPedal(channelNumber, value);
|
|
@@ -1840,7 +1920,7 @@ class Midy {
|
|
|
1840
1920
|
continue;
|
|
1841
1921
|
if (note.startTime < now)
|
|
1842
1922
|
continue;
|
|
1843
|
-
this.setVolumeEnvelope(channel, note);
|
|
1923
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1844
1924
|
}
|
|
1845
1925
|
});
|
|
1846
1926
|
}
|
|
@@ -1852,7 +1932,12 @@ class Midy {
|
|
|
1852
1932
|
const note = noteList[i];
|
|
1853
1933
|
if (!note)
|
|
1854
1934
|
continue;
|
|
1855
|
-
|
|
1935
|
+
if (note.portamento) {
|
|
1936
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1937
|
+
}
|
|
1938
|
+
else {
|
|
1939
|
+
this.setFilterEnvelope(channel, note, 0);
|
|
1940
|
+
}
|
|
1856
1941
|
}
|
|
1857
1942
|
});
|
|
1858
1943
|
}
|
|
@@ -1864,7 +1949,7 @@ class Midy {
|
|
|
1864
1949
|
const note = noteList[i];
|
|
1865
1950
|
if (!note)
|
|
1866
1951
|
continue;
|
|
1867
|
-
this.setVolumeEnvelope(channel, note);
|
|
1952
|
+
this.setVolumeEnvelope(channel, note, 0);
|
|
1868
1953
|
}
|
|
1869
1954
|
});
|
|
1870
1955
|
}
|
|
@@ -1873,21 +1958,53 @@ class Midy {
|
|
|
1873
1958
|
channel.state.vibratoRate = vibratoRate / 64;
|
|
1874
1959
|
if (channel.vibratoDepth <= 0)
|
|
1875
1960
|
return;
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
.
|
|
1961
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1962
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1963
|
+
const note = noteList[i];
|
|
1964
|
+
if (!note)
|
|
1965
|
+
continue;
|
|
1966
|
+
this.setVibLfoToPitch(channel, note);
|
|
1967
|
+
}
|
|
1882
1968
|
});
|
|
1883
1969
|
}
|
|
1884
1970
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1885
1971
|
const channel = this.channels[channelNumber];
|
|
1972
|
+
const prev = channel.state.vibratoDepth;
|
|
1886
1973
|
channel.state.vibratoDepth = vibratoDepth / 64;
|
|
1974
|
+
if (0 < prev) {
|
|
1975
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1976
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1977
|
+
const note = noteList[i];
|
|
1978
|
+
if (!note)
|
|
1979
|
+
continue;
|
|
1980
|
+
this.setFreqVibLFO(channel, note);
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
else {
|
|
1985
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1986
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1987
|
+
const note = noteList[i];
|
|
1988
|
+
if (!note)
|
|
1989
|
+
continue;
|
|
1990
|
+
this.startVibrato(channel, note, note.startTime);
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1887
1994
|
}
|
|
1888
1995
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1889
1996
|
const channel = this.channels[channelNumber];
|
|
1890
1997
|
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1998
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1999
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
2000
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
2001
|
+
const note = noteList[i];
|
|
2002
|
+
if (!note)
|
|
2003
|
+
continue;
|
|
2004
|
+
this.startVibrato(channel, note, note.startTime);
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
1891
2008
|
}
|
|
1892
2009
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1893
2010
|
const channel = this.channels[channelNumber];
|
|
@@ -2052,7 +2169,7 @@ class Midy {
|
|
|
2052
2169
|
const next = value / 128;
|
|
2053
2170
|
state.pitchWheelSensitivity = next;
|
|
2054
2171
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2055
|
-
this.
|
|
2172
|
+
this.updateChannelDetune(channel);
|
|
2056
2173
|
this.applyVoiceParams(channel, 16);
|
|
2057
2174
|
}
|
|
2058
2175
|
handleFineTuningRPN(channelNumber) {
|
|
@@ -2067,7 +2184,7 @@ class Midy {
|
|
|
2067
2184
|
const next = (value - 8192) / 8.192; // cent
|
|
2068
2185
|
channel.fineTuning = next;
|
|
2069
2186
|
channel.detune += next - prev;
|
|
2070
|
-
this.
|
|
2187
|
+
this.updateChannelDetune(channel);
|
|
2071
2188
|
}
|
|
2072
2189
|
handleCoarseTuningRPN(channelNumber) {
|
|
2073
2190
|
const channel = this.channels[channelNumber];
|
|
@@ -2081,7 +2198,7 @@ class Midy {
|
|
|
2081
2198
|
const next = (value - 64) * 100; // cent
|
|
2082
2199
|
channel.coarseTuning = next;
|
|
2083
2200
|
channel.detune += next - prev;
|
|
2084
|
-
this.
|
|
2201
|
+
this.updateChannelDetune(channel);
|
|
2085
2202
|
}
|
|
2086
2203
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
2087
2204
|
const channel = this.channels[channelNumber];
|
|
@@ -2112,7 +2229,7 @@ class Midy {
|
|
|
2112
2229
|
const state = channel.state;
|
|
2113
2230
|
for (let i = 0; i < stateTypes.length; i++) {
|
|
2114
2231
|
const type = stateTypes[i];
|
|
2115
|
-
state[type] = defaultControllerState[type];
|
|
2232
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
2116
2233
|
}
|
|
2117
2234
|
const settingTypes = [
|
|
2118
2235
|
"rpnMSB",
|
|
@@ -2144,7 +2261,10 @@ class Midy {
|
|
|
2144
2261
|
switch (data[3]) {
|
|
2145
2262
|
case 8:
|
|
2146
2263
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2147
|
-
return this.handleScaleOctaveTuning1ByteFormatSysEx(data);
|
|
2264
|
+
return this.handleScaleOctaveTuning1ByteFormatSysEx(data, false);
|
|
2265
|
+
case 9:
|
|
2266
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2267
|
+
return this.handleScaleOctaveTuning2ByteFormatSysEx(data, false);
|
|
2148
2268
|
default:
|
|
2149
2269
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2150
2270
|
}
|
|
@@ -2206,8 +2326,10 @@ class Midy {
|
|
|
2206
2326
|
case 8:
|
|
2207
2327
|
switch (data[3]) {
|
|
2208
2328
|
case 8: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2209
|
-
|
|
2210
|
-
|
|
2329
|
+
return this.handleScaleOctaveTuning1ByteFormatSysEx(data, true);
|
|
2330
|
+
case 9:
|
|
2331
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2332
|
+
return this.handleScaleOctaveTuning2ByteFormatSysEx(data, true);
|
|
2211
2333
|
default:
|
|
2212
2334
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2213
2335
|
}
|
|
@@ -2215,7 +2337,9 @@ class Midy {
|
|
|
2215
2337
|
case 9:
|
|
2216
2338
|
switch (data[3]) {
|
|
2217
2339
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2218
|
-
return this.
|
|
2340
|
+
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2341
|
+
case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2342
|
+
return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
|
|
2219
2343
|
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2220
2344
|
return this.handleControlChangeSysEx(data);
|
|
2221
2345
|
default:
|
|
@@ -2257,7 +2381,7 @@ class Midy {
|
|
|
2257
2381
|
const next = (value - 8192) / 8.192; // cent
|
|
2258
2382
|
this.masterFineTuning = next;
|
|
2259
2383
|
channel.detune += next - prev;
|
|
2260
|
-
this.
|
|
2384
|
+
this.updateChannelDetune(channel);
|
|
2261
2385
|
}
|
|
2262
2386
|
handleMasterCoarseTuningSysEx(data) {
|
|
2263
2387
|
const coarseTuning = data[4];
|
|
@@ -2268,7 +2392,7 @@ class Midy {
|
|
|
2268
2392
|
const next = (value - 64) * 100; // cent
|
|
2269
2393
|
this.masterCoarseTuning = next;
|
|
2270
2394
|
channel.detune += next - prev;
|
|
2271
|
-
this.
|
|
2395
|
+
this.updateChannelDetune(channel);
|
|
2272
2396
|
}
|
|
2273
2397
|
handleGlobalParameterControlSysEx(data) {
|
|
2274
2398
|
if (data[7] === 1) {
|
|
@@ -2475,8 +2599,8 @@ class Midy {
|
|
|
2475
2599
|
}
|
|
2476
2600
|
return bitmap;
|
|
2477
2601
|
}
|
|
2478
|
-
handleScaleOctaveTuning1ByteFormatSysEx(data) {
|
|
2479
|
-
if (data.length <
|
|
2602
|
+
handleScaleOctaveTuning1ByteFormatSysEx(data, realtime) {
|
|
2603
|
+
if (data.length < 19) {
|
|
2480
2604
|
console.error("Data length is too short");
|
|
2481
2605
|
return;
|
|
2482
2606
|
}
|
|
@@ -2484,37 +2608,96 @@ class Midy {
|
|
|
2484
2608
|
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2485
2609
|
if (!channelBitmap[i])
|
|
2486
2610
|
continue;
|
|
2611
|
+
const channel = this.channels[i];
|
|
2487
2612
|
for (let j = 0; j < 12; j++) {
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2613
|
+
const centValue = data[j + 7] - 64;
|
|
2614
|
+
channel.scaleOctaveTuningTable[j] = centValue;
|
|
2490
2615
|
}
|
|
2616
|
+
if (realtime)
|
|
2617
|
+
this.updateChannelDetune(channel);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
handleScaleOctaveTuning2ByteFormatSysEx(data, realtime) {
|
|
2621
|
+
if (data.length < 31) {
|
|
2622
|
+
console.error("Data length is too short");
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
const channelBitmap = this.getChannelBitmap(data);
|
|
2626
|
+
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2627
|
+
if (!channelBitmap[i])
|
|
2628
|
+
continue;
|
|
2629
|
+
const channel = this.channels[i];
|
|
2630
|
+
for (let j = 0; j < 12; j++) {
|
|
2631
|
+
const index = 7 + j * 2;
|
|
2632
|
+
const msb = data[index] & 0x7F;
|
|
2633
|
+
const lsb = data[index + 1] & 0x7F;
|
|
2634
|
+
const value14bit = msb * 128 + lsb;
|
|
2635
|
+
const centValue = (value14bit - 8192) / 8.192;
|
|
2636
|
+
channel.scaleOctaveTuningTable[j] = centValue;
|
|
2637
|
+
}
|
|
2638
|
+
if (realtime)
|
|
2639
|
+
this.updateChannelDetune(channel);
|
|
2491
2640
|
}
|
|
2492
2641
|
}
|
|
2493
2642
|
applyDestinationSettings(channel, note, table) {
|
|
2494
2643
|
if (table[0] !== 64) {
|
|
2495
|
-
|
|
2644
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2645
|
+
? channel.polyphonicKeyPressureTable[0] * note.pressure
|
|
2646
|
+
: 0;
|
|
2647
|
+
const pressure = (polyphonicKeyPressure - 64) / 37.5; // 2400 / 64;
|
|
2648
|
+
this.updateDetune(channel, note, pressure);
|
|
2496
2649
|
}
|
|
2497
2650
|
if (!note.portamento) {
|
|
2498
2651
|
if (table[1] !== 64) {
|
|
2499
|
-
|
|
2652
|
+
const channelPressure = channel.channelPressureTable[1] *
|
|
2653
|
+
channel.state.channelPressure;
|
|
2654
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2655
|
+
? channel.polyphonicKeyPressureTable[1] * note.pressure
|
|
2656
|
+
: 0;
|
|
2657
|
+
const pressure = (channelPressure + polyphonicKeyPressure - 128) * 15;
|
|
2658
|
+
this.setFilterEnvelope(channel, note, pressure);
|
|
2500
2659
|
}
|
|
2501
2660
|
if (table[2] !== 64) {
|
|
2502
|
-
|
|
2661
|
+
const channelPressure = channel.channelPressureTable[2] *
|
|
2662
|
+
channel.state.channelPressure;
|
|
2663
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2664
|
+
? channel.polyphonicKeyPressureTable[2] * note.pressure
|
|
2665
|
+
: 0;
|
|
2666
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 128;
|
|
2667
|
+
this.setVolumeEnvelope(channel, note, pressure);
|
|
2503
2668
|
}
|
|
2504
2669
|
}
|
|
2505
2670
|
if (table[3] !== 0) {
|
|
2506
|
-
|
|
2671
|
+
const channelPressure = channel.channelPressureTable[3] *
|
|
2672
|
+
channel.state.channelPressure;
|
|
2673
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2674
|
+
? channel.polyphonicKeyPressureTable[3] * note.pressure
|
|
2675
|
+
: 0;
|
|
2676
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 600;
|
|
2677
|
+
this.setModLfoToPitch(channel, note, pressure);
|
|
2507
2678
|
}
|
|
2508
2679
|
if (table[4] !== 0) {
|
|
2509
|
-
|
|
2680
|
+
const channelPressure = channel.channelPressureTable[4] *
|
|
2681
|
+
channel.state.channelPressure;
|
|
2682
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2683
|
+
? channel.polyphonicKeyPressureTable[4] * note.pressure
|
|
2684
|
+
: 0;
|
|
2685
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254 * 2400;
|
|
2686
|
+
this.setModLfoToFilterFc(note, pressure);
|
|
2510
2687
|
}
|
|
2511
2688
|
if (table[5] !== 0) {
|
|
2512
|
-
|
|
2689
|
+
const channelPressure = channel.channelPressureTable[5] *
|
|
2690
|
+
channel.state.channelPressure;
|
|
2691
|
+
const polyphonicKeyPressure = (0 < note.pressure)
|
|
2692
|
+
? channel.polyphonicKeyPressureTable[5] * note.pressure
|
|
2693
|
+
: 0;
|
|
2694
|
+
const pressure = (channelPressure + polyphonicKeyPressure) / 254;
|
|
2695
|
+
this.setModLfoToVolume(note, pressure);
|
|
2513
2696
|
}
|
|
2514
2697
|
}
|
|
2515
|
-
handleChannelPressureSysEx(data) {
|
|
2698
|
+
handleChannelPressureSysEx(data, tableName) {
|
|
2516
2699
|
const channelNumber = data[4];
|
|
2517
|
-
const table = this.channels[channelNumber]
|
|
2700
|
+
const table = this.channels[channelNumber][tableName];
|
|
2518
2701
|
for (let i = 5; i < data.length - 1; i += 2) {
|
|
2519
2702
|
const pp = data[i];
|
|
2520
2703
|
const rr = data[i + 1];
|
|
@@ -2605,8 +2788,9 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2605
2788
|
value: {
|
|
2606
2789
|
currentBufferSource: null,
|
|
2607
2790
|
detune: 0,
|
|
2608
|
-
scaleOctaveTuningTable: new
|
|
2609
|
-
|
|
2791
|
+
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
2792
|
+
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2793
|
+
polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2610
2794
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2611
2795
|
program: 0,
|
|
2612
2796
|
bank: 121 * 128,
|
|
@@ -2621,16 +2805,3 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2621
2805
|
modulationDepthRange: 50, // cent
|
|
2622
2806
|
}
|
|
2623
2807
|
});
|
|
2624
|
-
Object.defineProperty(Midy, "controllerDestinationSettings", {
|
|
2625
|
-
enumerable: true,
|
|
2626
|
-
configurable: true,
|
|
2627
|
-
writable: true,
|
|
2628
|
-
value: {
|
|
2629
|
-
pitchControl: 0,
|
|
2630
|
-
filterCutoffControl: 0,
|
|
2631
|
-
amplitudeControl: 1,
|
|
2632
|
-
lfoPitchDepth: 0,
|
|
2633
|
-
lfoFilterDepth: 0,
|
|
2634
|
-
lfoAmplitudeDepth: 0,
|
|
2635
|
-
}
|
|
2636
|
-
});
|