@marmooo/midy 0.1.7 → 0.2.0
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 +48 -23
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +358 -135
- package/esm/midy-GM2.d.ts +50 -33
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +512 -220
- package/esm/midy-GMLite.d.ts +46 -22
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +339 -137
- package/esm/midy.d.ts +50 -38
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +526 -237
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +48 -23
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +358 -135
- package/script/midy-GM2.d.ts +50 -33
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +512 -220
- package/script/midy-GMLite.d.ts +46 -22
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +339 -137
- package/script/midy.d.ts +50 -38
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +526 -237
package/script/midy.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.Midy = void 0;
|
|
|
4
4
|
const midi_file_1 = require("midi-file");
|
|
5
5
|
const soundfont_parser_1 = require("@marmooo/soundfont-parser");
|
|
6
6
|
class Note {
|
|
7
|
-
constructor(noteNumber, velocity, startTime,
|
|
7
|
+
constructor(noteNumber, velocity, startTime, voice, voiceParams) {
|
|
8
8
|
Object.defineProperty(this, "bufferSource", {
|
|
9
9
|
enumerable: true,
|
|
10
10
|
configurable: true,
|
|
@@ -65,12 +65,106 @@ class Note {
|
|
|
65
65
|
writable: true,
|
|
66
66
|
value: void 0
|
|
67
67
|
});
|
|
68
|
+
Object.defineProperty(this, "portamento", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: void 0
|
|
73
|
+
});
|
|
68
74
|
this.noteNumber = noteNumber;
|
|
69
75
|
this.velocity = velocity;
|
|
70
76
|
this.startTime = startTime;
|
|
71
|
-
this.
|
|
77
|
+
this.voice = voice;
|
|
78
|
+
this.voiceParams = voiceParams;
|
|
72
79
|
}
|
|
73
80
|
}
|
|
81
|
+
// normalized to 0-1 for use with the SF2 modulator model
|
|
82
|
+
const defaultControllerState = {
|
|
83
|
+
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
84
|
+
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
85
|
+
polyPressure: { type: 10, defaultValue: 0 },
|
|
86
|
+
channelPressure: { type: 13, defaultValue: 0 },
|
|
87
|
+
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
88
|
+
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
89
|
+
link: { type: 127, defaultValue: 0 },
|
|
90
|
+
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
91
|
+
modulationDepth: { type: 128 + 1, defaultValue: 0 },
|
|
92
|
+
portamentoTime: { type: 128 + 5, defaultValue: 0 },
|
|
93
|
+
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
94
|
+
volume: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
95
|
+
pan: { type: 128 + 10, defaultValue: 0.5 },
|
|
96
|
+
expression: { type: 128 + 11, defaultValue: 1 },
|
|
97
|
+
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
98
|
+
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
99
|
+
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
100
|
+
portamento: { type: 128 + 65, defaultValue: 0 },
|
|
101
|
+
sostenutoPedal: { type: 128 + 66, defaultValue: 0 },
|
|
102
|
+
softPedal: { type: 128 + 67, defaultValue: 0 },
|
|
103
|
+
filterResonance: { type: 128 + 71, defaultValue: 0.5 },
|
|
104
|
+
releaseTime: { type: 128 + 72, defaultValue: 0.5 },
|
|
105
|
+
attackTime: { type: 128 + 73, defaultValue: 0.5 },
|
|
106
|
+
brightness: { type: 128 + 74, defaultValue: 0.5 },
|
|
107
|
+
decayTime: { type: 128 + 75, defaultValue: 0.5 },
|
|
108
|
+
vibratoRate: { type: 128 + 76, defaultValue: 0.5 },
|
|
109
|
+
vibratoDepth: { type: 128 + 77, defaultValue: 0.5 },
|
|
110
|
+
vibratoDelay: { type: 128 + 78, defaultValue: 0.5 },
|
|
111
|
+
reverbSendLevel: { type: 128 + 91, defaultValue: 0 },
|
|
112
|
+
chorusSendLevel: { type: 128 + 93, defaultValue: 0 },
|
|
113
|
+
// dataIncrement: { type: 128 + 96, defaultValue: 0 },
|
|
114
|
+
// dataDecrement: { type: 128 + 97, defaultValue: 0 },
|
|
115
|
+
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
116
|
+
// rpnMSB: { type: 128 + 101, defaultValue: 127 },
|
|
117
|
+
// allSoundOff: { type: 128 + 120, defaultValue: 0 },
|
|
118
|
+
// resetAllControllers: { type: 128 + 121, defaultValue: 0 },
|
|
119
|
+
// allNotesOff: { type: 128 + 123, defaultValue: 0 },
|
|
120
|
+
// omniOff: { type: 128 + 124, defaultValue: 0 },
|
|
121
|
+
// omniOn: { type: 128 + 125, defaultValue: 0 },
|
|
122
|
+
// monoOn: { type: 128 + 126, defaultValue: 0 },
|
|
123
|
+
// polyOn: { type: 128 + 127, defaultValue: 0 },
|
|
124
|
+
};
|
|
125
|
+
class ControllerState {
|
|
126
|
+
constructor() {
|
|
127
|
+
Object.defineProperty(this, "array", {
|
|
128
|
+
enumerable: true,
|
|
129
|
+
configurable: true,
|
|
130
|
+
writable: true,
|
|
131
|
+
value: new Float32Array(256)
|
|
132
|
+
});
|
|
133
|
+
const entries = Object.entries(defaultControllerState);
|
|
134
|
+
for (const [name, { type, defaultValue }] of entries) {
|
|
135
|
+
this.array[type] = defaultValue;
|
|
136
|
+
Object.defineProperty(this, name, {
|
|
137
|
+
get: () => this.array[type],
|
|
138
|
+
set: (value) => this.array[type] = value,
|
|
139
|
+
enumerable: true,
|
|
140
|
+
configurable: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const filterEnvelopeKeys = [
|
|
146
|
+
"modEnvToPitch",
|
|
147
|
+
"initialFilterFc",
|
|
148
|
+
"modEnvToFilterFc",
|
|
149
|
+
"modDelay",
|
|
150
|
+
"modAttack",
|
|
151
|
+
"modHold",
|
|
152
|
+
"modDecay",
|
|
153
|
+
"modSustain",
|
|
154
|
+
"modRelease",
|
|
155
|
+
"playbackRate",
|
|
156
|
+
];
|
|
157
|
+
const filterEnvelopeKeySet = new Set(filterEnvelopeKeys);
|
|
158
|
+
const volumeEnvelopeKeys = [
|
|
159
|
+
"volDelay",
|
|
160
|
+
"volAttack",
|
|
161
|
+
"volHold",
|
|
162
|
+
"volDecay",
|
|
163
|
+
"volSustain",
|
|
164
|
+
"volRelease",
|
|
165
|
+
"initialAttenuation",
|
|
166
|
+
];
|
|
167
|
+
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
74
168
|
class Midy {
|
|
75
169
|
constructor(audioContext, options = this.defaultOptions) {
|
|
76
170
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -251,6 +345,7 @@ class Midy {
|
|
|
251
345
|
this.audioContext = audioContext;
|
|
252
346
|
this.options = { ...this.defaultOptions, ...options };
|
|
253
347
|
this.masterGain = new GainNode(audioContext);
|
|
348
|
+
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
254
349
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
255
350
|
this.channels = this.createChannels(audioContext);
|
|
256
351
|
this.reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
@@ -297,7 +392,7 @@ class Midy {
|
|
|
297
392
|
this.totalTime = this.calcTotalTime();
|
|
298
393
|
}
|
|
299
394
|
setChannelAudioNodes(audioContext) {
|
|
300
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
395
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
301
396
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
302
397
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
303
398
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -314,7 +409,7 @@ class Midy {
|
|
|
314
409
|
const channels = Array.from({ length: 16 }, () => {
|
|
315
410
|
return {
|
|
316
411
|
...this.constructor.channelSettings,
|
|
317
|
-
|
|
412
|
+
state: new ControllerState(),
|
|
318
413
|
...this.setChannelAudioNodes(audioContext),
|
|
319
414
|
scheduledNotes: new Map(),
|
|
320
415
|
sostenutoNotes: new Map(),
|
|
@@ -328,11 +423,11 @@ class Midy {
|
|
|
328
423
|
});
|
|
329
424
|
return channels;
|
|
330
425
|
}
|
|
331
|
-
async createNoteBuffer(
|
|
332
|
-
const sampleStart =
|
|
333
|
-
const sampleEnd =
|
|
426
|
+
async createNoteBuffer(voiceParams, isSF3) {
|
|
427
|
+
const sampleStart = voiceParams.start;
|
|
428
|
+
const sampleEnd = voiceParams.sample.length + voiceParams.end;
|
|
334
429
|
if (isSF3) {
|
|
335
|
-
const sample =
|
|
430
|
+
const sample = voiceParams.sample;
|
|
336
431
|
const start = sample.byteOffset + sampleStart;
|
|
337
432
|
const end = sample.byteOffset + sampleEnd;
|
|
338
433
|
const buffer = sample.buffer.slice(start, end);
|
|
@@ -340,14 +435,14 @@ class Midy {
|
|
|
340
435
|
return audioBuffer;
|
|
341
436
|
}
|
|
342
437
|
else {
|
|
343
|
-
const sample =
|
|
438
|
+
const sample = voiceParams.sample;
|
|
344
439
|
const start = sample.byteOffset + sampleStart;
|
|
345
440
|
const end = sample.byteOffset + sampleEnd;
|
|
346
441
|
const buffer = sample.buffer.slice(start, end);
|
|
347
442
|
const audioBuffer = new AudioBuffer({
|
|
348
443
|
numberOfChannels: 1,
|
|
349
444
|
length: sample.length,
|
|
350
|
-
sampleRate:
|
|
445
|
+
sampleRate: voiceParams.sampleRate,
|
|
351
446
|
});
|
|
352
447
|
const channelData = audioBuffer.getChannelData(0);
|
|
353
448
|
const int16Array = new Int16Array(buffer);
|
|
@@ -357,15 +452,14 @@ class Midy {
|
|
|
357
452
|
return audioBuffer;
|
|
358
453
|
}
|
|
359
454
|
}
|
|
360
|
-
async createNoteBufferNode(
|
|
455
|
+
async createNoteBufferNode(voiceParams, isSF3) {
|
|
361
456
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
362
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
457
|
+
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
363
458
|
bufferSource.buffer = audioBuffer;
|
|
364
|
-
bufferSource.loop =
|
|
459
|
+
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
365
460
|
if (bufferSource.loop) {
|
|
366
|
-
bufferSource.loopStart =
|
|
367
|
-
|
|
368
|
-
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
461
|
+
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
462
|
+
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
369
463
|
}
|
|
370
464
|
return bufferSource;
|
|
371
465
|
}
|
|
@@ -422,7 +516,7 @@ class Midy {
|
|
|
422
516
|
this.handleChannelPressure(event.channel, event.amount);
|
|
423
517
|
break;
|
|
424
518
|
case "pitchBend":
|
|
425
|
-
this.setPitchBend(event.channel, event.value);
|
|
519
|
+
this.setPitchBend(event.channel, event.value + 8192);
|
|
426
520
|
break;
|
|
427
521
|
case "sysEx":
|
|
428
522
|
this.handleSysEx(event.data);
|
|
@@ -819,53 +913,63 @@ class Midy {
|
|
|
819
913
|
calcSemitoneOffset(channel) {
|
|
820
914
|
const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
|
|
821
915
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return instrumentKey.playbackRate(noteNumber) *
|
|
827
|
-
Math.pow(2, semitoneOffset / 12);
|
|
916
|
+
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
917
|
+
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 128;
|
|
918
|
+
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
919
|
+
return masterTuning + channelTuning + pitch;
|
|
828
920
|
}
|
|
829
921
|
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
830
|
-
const
|
|
831
|
-
const
|
|
832
|
-
const
|
|
833
|
-
const
|
|
834
|
-
const
|
|
922
|
+
const now = this.audioContext.currentTime;
|
|
923
|
+
const { voiceParams, startTime } = note;
|
|
924
|
+
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation);
|
|
925
|
+
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
926
|
+
const volDelay = startTime + voiceParams.volDelay;
|
|
927
|
+
const portamentoTime = volDelay + channel.state.portamentoTime;
|
|
835
928
|
note.volumeNode.gain
|
|
836
|
-
.cancelScheduledValues(
|
|
929
|
+
.cancelScheduledValues(now)
|
|
837
930
|
.setValueAtTime(0, volDelay)
|
|
838
931
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
839
932
|
}
|
|
840
933
|
setVolumeEnvelope(channel, note) {
|
|
841
|
-
const
|
|
842
|
-
const
|
|
843
|
-
const
|
|
844
|
-
const
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
const
|
|
934
|
+
const now = this.audioContext.currentTime;
|
|
935
|
+
const state = channel.state;
|
|
936
|
+
const { voiceParams, startTime } = note;
|
|
937
|
+
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation);
|
|
938
|
+
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
939
|
+
const volDelay = startTime + voiceParams.volDelay;
|
|
940
|
+
const volAttack = volDelay + voiceParams.volAttack * state.attackTime * 2;
|
|
941
|
+
const volHold = volAttack + voiceParams.volHold;
|
|
942
|
+
const volDecay = volHold + voiceParams.volDecay * state.decayTime * 2;
|
|
848
943
|
note.volumeNode.gain
|
|
849
|
-
.cancelScheduledValues(
|
|
944
|
+
.cancelScheduledValues(now)
|
|
850
945
|
.setValueAtTime(0, startTime)
|
|
851
946
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
852
947
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
853
948
|
.setValueAtTime(attackVolume, volHold)
|
|
854
949
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
855
950
|
}
|
|
856
|
-
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
951
|
+
setPlaybackRate(note) {
|
|
952
|
+
const now = this.audioContext.currentTime;
|
|
953
|
+
note.bufferSource.playbackRate
|
|
954
|
+
.cancelScheduledValues(now)
|
|
955
|
+
.setValueAtTime(note.voiceParams.playbackRate, now);
|
|
956
|
+
}
|
|
957
|
+
setPitch(channel, note) {
|
|
958
|
+
const now = this.audioContext.currentTime;
|
|
959
|
+
const { startTime } = note;
|
|
960
|
+
const basePitch = this.calcSemitoneOffset(channel) * 100;
|
|
961
|
+
note.bufferSource.detune
|
|
962
|
+
.cancelScheduledValues(now)
|
|
963
|
+
.setValueAtTime(basePitch, startTime);
|
|
964
|
+
const modEnvToPitch = note.voiceParams.modEnvToPitch;
|
|
860
965
|
if (modEnvToPitch === 0)
|
|
861
966
|
return;
|
|
862
|
-
const
|
|
863
|
-
const
|
|
864
|
-
const
|
|
865
|
-
const
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
note.bufferSource.playbackRate.value
|
|
967
|
+
const peekPitch = basePitch + modEnvToPitch;
|
|
968
|
+
const modDelay = startTime + voiceParams.modDelay;
|
|
969
|
+
const modAttack = modDelay + voiceParams.modAttack;
|
|
970
|
+
const modHold = modAttack + voiceParams.modHold;
|
|
971
|
+
const modDecay = modHold + voiceParams.modDecay;
|
|
972
|
+
note.bufferSource.detune
|
|
869
973
|
.setValueAtTime(basePitch, modDelay)
|
|
870
974
|
.exponentialRampToValueAtTime(peekPitch, modAttack)
|
|
871
975
|
.setValueAtTime(peekPitch, modHold)
|
|
@@ -877,42 +981,46 @@ class Midy {
|
|
|
877
981
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
878
982
|
}
|
|
879
983
|
setPortamentoStartFilterEnvelope(channel, note) {
|
|
880
|
-
const
|
|
984
|
+
const now = this.audioContext.currentTime;
|
|
985
|
+
const state = channel.state;
|
|
986
|
+
const { voiceParams, noteNumber, startTime } = note;
|
|
881
987
|
const softPedalFactor = 1 -
|
|
882
|
-
(0.1 + (noteNumber / 127) * 0.2) *
|
|
883
|
-
const baseFreq = this.centToHz(
|
|
884
|
-
softPedalFactor *
|
|
885
|
-
const peekFreq = this.centToHz(
|
|
988
|
+
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
989
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
990
|
+
softPedalFactor * state.brightness * 2;
|
|
991
|
+
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
886
992
|
const sustainFreq = baseFreq +
|
|
887
|
-
(peekFreq - baseFreq) * (1 -
|
|
993
|
+
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
888
994
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
889
995
|
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
890
|
-
const portamentoTime = startTime + channel.portamentoTime;
|
|
891
|
-
const modDelay = startTime +
|
|
996
|
+
const portamentoTime = startTime + channel.state.portamentoTime;
|
|
997
|
+
const modDelay = startTime + voiceParams.modDelay;
|
|
892
998
|
note.filterNode.frequency
|
|
893
|
-
.cancelScheduledValues(
|
|
999
|
+
.cancelScheduledValues(now)
|
|
894
1000
|
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
895
1001
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
896
1002
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
897
1003
|
}
|
|
898
1004
|
setFilterEnvelope(channel, note) {
|
|
899
|
-
const
|
|
1005
|
+
const now = this.audioContext.currentTime;
|
|
1006
|
+
const state = channel.state;
|
|
1007
|
+
const { voiceParams, noteNumber, startTime } = note;
|
|
900
1008
|
const softPedalFactor = 1 -
|
|
901
|
-
(0.1 + (noteNumber / 127) * 0.2) *
|
|
902
|
-
const baseFreq = this.centToHz(
|
|
903
|
-
softPedalFactor *
|
|
904
|
-
const peekFreq = this.centToHz(
|
|
1009
|
+
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1010
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc) *
|
|
1011
|
+
softPedalFactor * state.brightness * 2;
|
|
1012
|
+
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * softPedalFactor * state.brightness * 2;
|
|
905
1013
|
const sustainFreq = baseFreq +
|
|
906
|
-
(peekFreq - baseFreq) * (1 -
|
|
1014
|
+
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
907
1015
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
908
1016
|
const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
|
|
909
1017
|
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
910
|
-
const modDelay = startTime +
|
|
911
|
-
const modAttack = modDelay +
|
|
912
|
-
const modHold = modAttack +
|
|
913
|
-
const modDecay = modHold +
|
|
1018
|
+
const modDelay = startTime + voiceParams.modDelay;
|
|
1019
|
+
const modAttack = modDelay + voiceParams.modAttack;
|
|
1020
|
+
const modHold = modAttack + voiceParams.modHold;
|
|
1021
|
+
const modDecay = modHold + voiceParams.modDecay;
|
|
914
1022
|
note.filterNode.frequency
|
|
915
|
-
.cancelScheduledValues(
|
|
1023
|
+
.cancelScheduledValues(now)
|
|
916
1024
|
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
917
1025
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
918
1026
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
@@ -920,25 +1028,18 @@ class Midy {
|
|
|
920
1028
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
921
1029
|
}
|
|
922
1030
|
startModulation(channel, note, startTime) {
|
|
923
|
-
const {
|
|
924
|
-
const { modLfoToPitch, modLfoToVolume } = instrumentKey;
|
|
1031
|
+
const { voiceParams } = note;
|
|
925
1032
|
note.modulationLFO = new OscillatorNode(this.audioContext, {
|
|
926
|
-
frequency: this.centToHz(
|
|
1033
|
+
frequency: this.centToHz(voiceParams.freqModLFO),
|
|
927
1034
|
});
|
|
928
1035
|
note.filterDepth = new GainNode(this.audioContext, {
|
|
929
|
-
gain:
|
|
930
|
-
});
|
|
931
|
-
const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
|
|
932
|
-
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
933
|
-
note.modulationDepth = new GainNode(this.audioContext, {
|
|
934
|
-
gain: modulationDepth * modulationDepthSign,
|
|
935
|
-
});
|
|
936
|
-
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
937
|
-
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
938
|
-
note.volumeDepth = new GainNode(this.audioContext, {
|
|
939
|
-
gain: volumeDepth * volumeDepthSign,
|
|
1036
|
+
gain: voiceParams.modLfoToFilterFc,
|
|
940
1037
|
});
|
|
941
|
-
note.
|
|
1038
|
+
note.modulationDepth = new GainNode(this.audioContext);
|
|
1039
|
+
this.setModLfoToPitch(channel, note);
|
|
1040
|
+
note.volumeDepth = new GainNode(this.audioContext);
|
|
1041
|
+
this.setModLfoToVolume(note);
|
|
1042
|
+
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
942
1043
|
note.modulationLFO.connect(note.filterDepth);
|
|
943
1044
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
944
1045
|
note.modulationLFO.connect(note.modulationDepth);
|
|
@@ -947,67 +1048,58 @@ class Midy {
|
|
|
947
1048
|
note.volumeDepth.connect(note.volumeNode.gain);
|
|
948
1049
|
}
|
|
949
1050
|
startVibrato(channel, note, startTime) {
|
|
950
|
-
const {
|
|
951
|
-
const
|
|
1051
|
+
const { voiceParams } = note;
|
|
1052
|
+
const state = channel.state;
|
|
952
1053
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
953
|
-
frequency: this.centToHz(
|
|
954
|
-
|
|
955
|
-
});
|
|
956
|
-
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.vibratoDepth;
|
|
957
|
-
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
958
|
-
note.vibratoDepth = new GainNode(this.audioContext, {
|
|
959
|
-
gain: vibratoDepth * vibratoDepthSign,
|
|
1054
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1055
|
+
state.vibratoRate,
|
|
960
1056
|
});
|
|
961
|
-
note.vibratoLFO.start(startTime +
|
|
1057
|
+
note.vibratoLFO.start(startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
|
|
1058
|
+
note.vibratoDepth = new GainNode(this.audioContext);
|
|
1059
|
+
this.setVibLfoToPitch(channel, note);
|
|
962
1060
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
963
1061
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
964
1062
|
}
|
|
965
|
-
async createNote(channel,
|
|
966
|
-
const
|
|
967
|
-
const
|
|
968
|
-
|
|
1063
|
+
async createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
1064
|
+
const state = channel.state;
|
|
1065
|
+
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1066
|
+
const voiceParams = voice.getAllParams(controllerState);
|
|
1067
|
+
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1068
|
+
note.bufferSource = await this.createNoteBufferNode(voiceParams, isSF3);
|
|
969
1069
|
note.volumeNode = new GainNode(this.audioContext);
|
|
970
1070
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
971
1071
|
type: "lowpass",
|
|
972
|
-
Q:
|
|
1072
|
+
Q: voiceParams.initialFilterQ / 5 * state.filterResonance, // dB
|
|
973
1073
|
});
|
|
974
1074
|
if (portamento) {
|
|
1075
|
+
note.portamento = true;
|
|
975
1076
|
this.setPortamentoStartVolumeEnvelope(channel, note);
|
|
976
1077
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
977
1078
|
}
|
|
978
1079
|
else {
|
|
1080
|
+
note.portamento = false;
|
|
979
1081
|
this.setVolumeEnvelope(channel, note);
|
|
980
1082
|
this.setFilterEnvelope(channel, note);
|
|
981
1083
|
}
|
|
982
|
-
if (0 <
|
|
1084
|
+
if (0 < state.vibratoDepth) {
|
|
983
1085
|
this.startVibrato(channel, note, startTime);
|
|
984
1086
|
}
|
|
985
|
-
|
|
986
|
-
|
|
1087
|
+
this.setPlaybackRate(note);
|
|
1088
|
+
if (0 < state.modulationDepth) {
|
|
1089
|
+
this.setPitch(channel, note);
|
|
987
1090
|
this.startModulation(channel, note, startTime);
|
|
988
1091
|
}
|
|
989
|
-
else {
|
|
990
|
-
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
991
|
-
}
|
|
992
1092
|
if (this.mono && channel.currentBufferSource) {
|
|
993
1093
|
channel.currentBufferSource.stop(startTime);
|
|
994
1094
|
channel.currentBufferSource = note.bufferSource;
|
|
995
1095
|
}
|
|
996
1096
|
note.bufferSource.connect(note.filterNode);
|
|
997
1097
|
note.filterNode.connect(note.volumeNode);
|
|
998
|
-
if (0 < channel.
|
|
999
|
-
|
|
1000
|
-
gain: instrumentKey.reverbEffectsSend,
|
|
1001
|
-
});
|
|
1002
|
-
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1003
|
-
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
1098
|
+
if (0 < channel.chorusSendLevel) {
|
|
1099
|
+
this.setChorusEffectsSend(channel, note, 0);
|
|
1004
1100
|
}
|
|
1005
|
-
if (0 < channel.
|
|
1006
|
-
|
|
1007
|
-
gain: instrumentKey.chorusEffectsSend,
|
|
1008
|
-
});
|
|
1009
|
-
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1010
|
-
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1101
|
+
if (0 < channel.reverbSendLevel) {
|
|
1102
|
+
this.setReverbEffectsSend(channel, note, 0);
|
|
1011
1103
|
}
|
|
1012
1104
|
note.bufferSource.start(startTime);
|
|
1013
1105
|
return note;
|
|
@@ -1029,16 +1121,16 @@ class Midy {
|
|
|
1029
1121
|
return;
|
|
1030
1122
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
1031
1123
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1032
|
-
const
|
|
1033
|
-
if (!
|
|
1124
|
+
const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
|
|
1125
|
+
if (!voice)
|
|
1034
1126
|
return;
|
|
1035
|
-
const note = await this.createNote(channel,
|
|
1127
|
+
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
|
|
1036
1128
|
note.volumeNode.connect(channel.gainL);
|
|
1037
1129
|
note.volumeNode.connect(channel.gainR);
|
|
1038
|
-
if (channel.sostenutoPedal) {
|
|
1130
|
+
if (channel.state.sostenutoPedal) {
|
|
1039
1131
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
1040
1132
|
}
|
|
1041
|
-
const exclusiveClass =
|
|
1133
|
+
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
1042
1134
|
if (exclusiveClass !== 0) {
|
|
1043
1135
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1044
1136
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
@@ -1100,8 +1192,9 @@ class Midy {
|
|
|
1100
1192
|
}
|
|
1101
1193
|
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
|
|
1102
1194
|
const channel = this.channels[channelNumber];
|
|
1195
|
+
const state = channel.state;
|
|
1103
1196
|
if (!force) {
|
|
1104
|
-
if (
|
|
1197
|
+
if (0.5 < state.sustainPedal)
|
|
1105
1198
|
return;
|
|
1106
1199
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
1107
1200
|
return;
|
|
@@ -1117,8 +1210,8 @@ class Midy {
|
|
|
1117
1210
|
continue;
|
|
1118
1211
|
if (portamentoNoteNumber === undefined) {
|
|
1119
1212
|
const volRelease = endTime +
|
|
1120
|
-
note.
|
|
1121
|
-
const modRelease = endTime + note.
|
|
1213
|
+
note.voiceParams.volRelease * state.releaseTime * 2;
|
|
1214
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1122
1215
|
note.filterNode.frequency
|
|
1123
1216
|
.cancelScheduledValues(endTime)
|
|
1124
1217
|
.linearRampToValueAtTime(0, modRelease);
|
|
@@ -1126,7 +1219,7 @@ class Midy {
|
|
|
1126
1219
|
return this.stopNote(endTime, stopTime, scheduledNotes, i);
|
|
1127
1220
|
}
|
|
1128
1221
|
else {
|
|
1129
|
-
const portamentoTime = endTime +
|
|
1222
|
+
const portamentoTime = endTime + state.portamentoTime;
|
|
1130
1223
|
const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
|
|
1131
1224
|
const detune = note.bufferSource.detune.value + detuneChange;
|
|
1132
1225
|
note.bufferSource.detune
|
|
@@ -1144,7 +1237,7 @@ class Midy {
|
|
|
1144
1237
|
const velocity = halfVelocity * 2;
|
|
1145
1238
|
const channel = this.channels[channelNumber];
|
|
1146
1239
|
const promises = [];
|
|
1147
|
-
channel.sustainPedal =
|
|
1240
|
+
channel.state.sustainPedal = halfVelocity;
|
|
1148
1241
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1149
1242
|
for (let i = 0; i < noteList.length; i++) {
|
|
1150
1243
|
const note = noteList[i];
|
|
@@ -1161,7 +1254,7 @@ class Midy {
|
|
|
1161
1254
|
const velocity = halfVelocity * 2;
|
|
1162
1255
|
const channel = this.channels[channelNumber];
|
|
1163
1256
|
const promises = [];
|
|
1164
|
-
channel.sostenutoPedal =
|
|
1257
|
+
channel.state.sostenutoPedal = 0;
|
|
1165
1258
|
channel.sostenutoNotes.forEach((activeNote) => {
|
|
1166
1259
|
const { noteNumber } = activeNote;
|
|
1167
1260
|
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
@@ -1206,6 +1299,7 @@ class Midy {
|
|
|
1206
1299
|
.setValueAtTime(gain * pressure, now);
|
|
1207
1300
|
}
|
|
1208
1301
|
}
|
|
1302
|
+
// this.applyVoiceParams(channel, 10);
|
|
1209
1303
|
}
|
|
1210
1304
|
handleProgramChange(channelNumber, program) {
|
|
1211
1305
|
const channel = this.channels[channelNumber];
|
|
@@ -1226,18 +1320,232 @@ class Midy {
|
|
|
1226
1320
|
.setValueAtTime(gain * pressure, now);
|
|
1227
1321
|
});
|
|
1228
1322
|
}
|
|
1323
|
+
// this.applyVoiceParams(channel, 13);
|
|
1229
1324
|
}
|
|
1230
1325
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1231
|
-
const pitchBend = msb * 128 + lsb
|
|
1326
|
+
const pitchBend = msb * 128 + lsb;
|
|
1232
1327
|
this.setPitchBend(channelNumber, pitchBend);
|
|
1233
1328
|
}
|
|
1234
|
-
setPitchBend(channelNumber,
|
|
1329
|
+
setPitchBend(channelNumber, value) {
|
|
1235
1330
|
const channel = this.channels[channelNumber];
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1331
|
+
const state = channel.state;
|
|
1332
|
+
state.pitchWheel = value / 16383;
|
|
1333
|
+
const pitchWheel = (value - 8192) / 8192;
|
|
1334
|
+
const detuneChange = pitchWheel * state.pitchWheelSensitivity * 12800;
|
|
1240
1335
|
this.updateDetune(channel, detuneChange);
|
|
1336
|
+
this.applyVoiceParams(channel, 14);
|
|
1337
|
+
}
|
|
1338
|
+
setModLfoToPitch(channel, note) {
|
|
1339
|
+
const now = this.audioContext.currentTime;
|
|
1340
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch;
|
|
1341
|
+
const modulationDepth = Math.abs(modLfoToPitch) +
|
|
1342
|
+
channel.state.modulationDepth;
|
|
1343
|
+
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
1344
|
+
note.modulationDepth.gain
|
|
1345
|
+
.cancelScheduledValues(now)
|
|
1346
|
+
.setValueAtTime(modulationDepth * modulationDepthSign, now);
|
|
1347
|
+
}
|
|
1348
|
+
setModLfoToVolume(note) {
|
|
1349
|
+
const now = this.audioContext.currentTime;
|
|
1350
|
+
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1351
|
+
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1352
|
+
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
1353
|
+
note.volumeDepth.gain
|
|
1354
|
+
.cancelScheduledValues(now)
|
|
1355
|
+
.setValueAtTime(volumeDepth * volumeDepthSign, now);
|
|
1356
|
+
}
|
|
1357
|
+
setChorusEffectsSend(note, prevValue) {
|
|
1358
|
+
if (0 < prevValue) {
|
|
1359
|
+
if (0 < note.voiceParams.chorusEffectsSend) {
|
|
1360
|
+
const now = this.audioContext.currentTime;
|
|
1361
|
+
const value = note.voiceParams.chorusEffectsSend;
|
|
1362
|
+
note.chorusEffectsSend.gain
|
|
1363
|
+
.cancelScheduledValues(now)
|
|
1364
|
+
.setValueAtTime(value, now);
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
note.chorusEffectsSend.disconnect();
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
else {
|
|
1371
|
+
if (0 < note.voiceParams.chorusEffectsSend) {
|
|
1372
|
+
if (!note.chorusEffectsSend) {
|
|
1373
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1374
|
+
gain: note.voiceParams.chorusEffectsSend,
|
|
1375
|
+
});
|
|
1376
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1377
|
+
}
|
|
1378
|
+
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
setReverbEffectsSend(note, prevValue) {
|
|
1383
|
+
if (0 < prevValue) {
|
|
1384
|
+
if (0 < note.voiceParams.reverbEffectsSend) {
|
|
1385
|
+
const now = this.audioContext.currentTime;
|
|
1386
|
+
const value = note.voiceParams.reverbEffectsSend;
|
|
1387
|
+
note.reverbEffectsSend.gain
|
|
1388
|
+
.cancelScheduledValues(now)
|
|
1389
|
+
.setValueAtTime(value, now);
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
note.reverbEffectsSend.disconnect();
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
if (0 < note.voiceParams.reverbEffectsSend) {
|
|
1397
|
+
if (!note.reverbEffectsSend) {
|
|
1398
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1399
|
+
gain: note.voiceParams.reverbEffectsSend,
|
|
1400
|
+
});
|
|
1401
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1402
|
+
}
|
|
1403
|
+
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
setVibLfoToPitch(channel, note) {
|
|
1408
|
+
const now = this.audioContext.currentTime;
|
|
1409
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1410
|
+
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
|
|
1411
|
+
2;
|
|
1412
|
+
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
1413
|
+
note.vibratoDepth.gain
|
|
1414
|
+
.cancelScheduledValues(now)
|
|
1415
|
+
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1416
|
+
}
|
|
1417
|
+
setModLfoToFilterFc(note) {
|
|
1418
|
+
const now = this.audioContext.currentTime;
|
|
1419
|
+
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
1420
|
+
note.filterDepth.gain
|
|
1421
|
+
.cancelScheduledValues(now)
|
|
1422
|
+
.setValueAtTime(modLfoToFilterFc, now);
|
|
1423
|
+
}
|
|
1424
|
+
setDelayModLFO(note) {
|
|
1425
|
+
const now = this.audioContext.currentTime;
|
|
1426
|
+
const startTime = note.startTime;
|
|
1427
|
+
if (startTime < now)
|
|
1428
|
+
return;
|
|
1429
|
+
note.modulationLFO.stop(now);
|
|
1430
|
+
note.modulationLFO.start(startTime + note.voiceParams.delayModLFO);
|
|
1431
|
+
note.modulationLFO.connect(note.filterDepth);
|
|
1432
|
+
}
|
|
1433
|
+
setFreqModLFO(note) {
|
|
1434
|
+
const now = this.audioContext.currentTime;
|
|
1435
|
+
const freqModLFO = note.voiceParams.freqModLFO;
|
|
1436
|
+
note.modulationLFO.frequency
|
|
1437
|
+
.cancelScheduledValues(now)
|
|
1438
|
+
.setValueAtTime(freqModLFO, now);
|
|
1439
|
+
}
|
|
1440
|
+
createVoiceParamsHandlers() {
|
|
1441
|
+
return {
|
|
1442
|
+
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1443
|
+
if (0 < channel.state.modulationDepth) {
|
|
1444
|
+
this.setModLfoToPitch(channel, note);
|
|
1445
|
+
}
|
|
1446
|
+
},
|
|
1447
|
+
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
1448
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1449
|
+
this.setVibLfoToPitch(channel, note);
|
|
1450
|
+
}
|
|
1451
|
+
},
|
|
1452
|
+
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1453
|
+
if (0 < channel.state.modulationDepth)
|
|
1454
|
+
this.setModLfoToFilterFc(note);
|
|
1455
|
+
},
|
|
1456
|
+
modLfoToVolume: (channel, note) => {
|
|
1457
|
+
if (0 < channel.state.modulationDepth)
|
|
1458
|
+
this.setModLfoToVolume(note);
|
|
1459
|
+
},
|
|
1460
|
+
chorusEffectsSend: (_channel, note, prevValue) => {
|
|
1461
|
+
this.setChorusEffectsSend(note, prevValue);
|
|
1462
|
+
},
|
|
1463
|
+
reverbEffectsSend: (_channel, note, prevValue) => {
|
|
1464
|
+
this.setReverbEffectsSend(note, prevValue);
|
|
1465
|
+
},
|
|
1466
|
+
delayModLFO: (_channel, note, _prevValue) => this.setDelayModLFO(note),
|
|
1467
|
+
freqModLFO: (_channel, note, _prevValue) => this.setFreqModLFO(note),
|
|
1468
|
+
delayVibLFO: (channel, note, prevValue) => {
|
|
1469
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1470
|
+
const now = this.audioContext.currentTime;
|
|
1471
|
+
const prevStartTime = note.startTime +
|
|
1472
|
+
prevValue * channel.state.vibratoDelay * 2;
|
|
1473
|
+
if (now < prevStartTime)
|
|
1474
|
+
return;
|
|
1475
|
+
const startTime = note.startTime +
|
|
1476
|
+
value * channel.state.vibratoDelay * 2;
|
|
1477
|
+
note.vibratoLFO.stop(now);
|
|
1478
|
+
note.vibratoLFO.start(startTime);
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
freqVibLFO: (channel, note, _prevValue) => {
|
|
1482
|
+
if (0 < channel.state.vibratoDepth) {
|
|
1483
|
+
const now = this.audioContext.currentTime;
|
|
1484
|
+
note.vibratoLFO.frequency
|
|
1485
|
+
.cancelScheduledValues(now)
|
|
1486
|
+
.setValueAtTime(value * sate.vibratoRate, now);
|
|
1487
|
+
}
|
|
1488
|
+
},
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
getControllerState(channel, noteNumber, velocity) {
|
|
1492
|
+
const state = new Float32Array(channel.state.array.length);
|
|
1493
|
+
state.set(channel.state.array);
|
|
1494
|
+
state[2] = velocity / 127;
|
|
1495
|
+
state[3] = noteNumber / 127;
|
|
1496
|
+
return state;
|
|
1497
|
+
}
|
|
1498
|
+
applyVoiceParams(channel, controllerType) {
|
|
1499
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1500
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1501
|
+
const note = noteList[i];
|
|
1502
|
+
if (!note)
|
|
1503
|
+
continue;
|
|
1504
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1505
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1506
|
+
let appliedFilterEnvelope = false;
|
|
1507
|
+
let appliedVolumeEnvelope = false;
|
|
1508
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1509
|
+
const prevValue = note.voiceParams[key];
|
|
1510
|
+
if (value === prevValue)
|
|
1511
|
+
continue;
|
|
1512
|
+
note.voiceParams[key] = value;
|
|
1513
|
+
if (key in this.voiceParamsHandlers) {
|
|
1514
|
+
this.voiceParamsHandlers[key](channel, note, prevValue);
|
|
1515
|
+
}
|
|
1516
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1517
|
+
if (appliedFilterEnvelope)
|
|
1518
|
+
continue;
|
|
1519
|
+
appliedFilterEnvelope = true;
|
|
1520
|
+
const noteVoiceParams = note.voiceParams;
|
|
1521
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1522
|
+
const key = filterEnvelopeKeys[i];
|
|
1523
|
+
if (key in voiceParams)
|
|
1524
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1525
|
+
}
|
|
1526
|
+
if (note.portamento) {
|
|
1527
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1528
|
+
}
|
|
1529
|
+
else {
|
|
1530
|
+
this.setFilterEnvelope(channel, note);
|
|
1531
|
+
}
|
|
1532
|
+
this.setPitch(channel, note);
|
|
1533
|
+
}
|
|
1534
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1535
|
+
if (appliedVolumeEnvelope)
|
|
1536
|
+
continue;
|
|
1537
|
+
appliedVolumeEnvelope = true;
|
|
1538
|
+
const noteVoiceParams = note.voiceParams;
|
|
1539
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1540
|
+
const key = volumeEnvelopeKeys[i];
|
|
1541
|
+
if (key in voiceParams)
|
|
1542
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1543
|
+
}
|
|
1544
|
+
this.setVolumeEnvelope(channel, note);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1241
1549
|
}
|
|
1242
1550
|
createControlChangeHandlers() {
|
|
1243
1551
|
return {
|
|
@@ -1277,13 +1585,16 @@ class Midy {
|
|
|
1277
1585
|
127: this.polyOn,
|
|
1278
1586
|
};
|
|
1279
1587
|
}
|
|
1280
|
-
handleControlChange(channelNumber,
|
|
1281
|
-
const handler = this.controlChangeHandlers[
|
|
1588
|
+
handleControlChange(channelNumber, controllerType, value) {
|
|
1589
|
+
const handler = this.controlChangeHandlers[controllerType];
|
|
1282
1590
|
if (handler) {
|
|
1283
1591
|
handler.call(this, channelNumber, value);
|
|
1592
|
+
const channel = this.channels[channelNumber];
|
|
1593
|
+
const controller = 128 + controllerType;
|
|
1594
|
+
this.applyVoiceParams(channel, controller);
|
|
1284
1595
|
}
|
|
1285
1596
|
else {
|
|
1286
|
-
console.warn(`Unsupported Control change:
|
|
1597
|
+
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
1287
1598
|
}
|
|
1288
1599
|
}
|
|
1289
1600
|
setBankMSB(channelNumber, msb) {
|
|
@@ -1297,11 +1608,10 @@ class Midy {
|
|
|
1297
1608
|
if (!note)
|
|
1298
1609
|
continue;
|
|
1299
1610
|
if (note.modulationDepth) {
|
|
1300
|
-
note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
1611
|
+
note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
|
|
1301
1612
|
}
|
|
1302
1613
|
else {
|
|
1303
|
-
|
|
1304
|
-
this.setPitch(note, semitoneOffset);
|
|
1614
|
+
this.setPitch(channel, note);
|
|
1305
1615
|
this.startModulation(channel, note, now);
|
|
1306
1616
|
}
|
|
1307
1617
|
}
|
|
@@ -1309,21 +1619,22 @@ class Midy {
|
|
|
1309
1619
|
}
|
|
1310
1620
|
setModulationDepth(channelNumber, modulation) {
|
|
1311
1621
|
const channel = this.channels[channelNumber];
|
|
1312
|
-
channel.modulationDepth = (modulation / 127) *
|
|
1622
|
+
channel.state.modulationDepth = (modulation / 127) *
|
|
1623
|
+
channel.modulationDepthRange;
|
|
1313
1624
|
this.updateModulation(channel);
|
|
1314
1625
|
}
|
|
1315
1626
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1316
1627
|
const channel = this.channels[channelNumber];
|
|
1317
1628
|
const factor = 5 * Math.log(10) / 127;
|
|
1318
|
-
channel.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1629
|
+
channel.state.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1319
1630
|
}
|
|
1320
1631
|
setVolume(channelNumber, volume) {
|
|
1321
1632
|
const channel = this.channels[channelNumber];
|
|
1322
|
-
channel.volume = volume / 127;
|
|
1633
|
+
channel.state.volume = volume / 127;
|
|
1323
1634
|
this.updateChannelVolume(channel);
|
|
1324
1635
|
}
|
|
1325
1636
|
panToGain(pan) {
|
|
1326
|
-
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
1637
|
+
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
1327
1638
|
return {
|
|
1328
1639
|
gainLeft: Math.cos(theta),
|
|
1329
1640
|
gainRight: Math.sin(theta),
|
|
@@ -1331,12 +1642,12 @@ class Midy {
|
|
|
1331
1642
|
}
|
|
1332
1643
|
setPan(channelNumber, pan) {
|
|
1333
1644
|
const channel = this.channels[channelNumber];
|
|
1334
|
-
channel.pan = pan;
|
|
1645
|
+
channel.state.pan = pan / 127;
|
|
1335
1646
|
this.updateChannelVolume(channel);
|
|
1336
1647
|
}
|
|
1337
1648
|
setExpression(channelNumber, expression) {
|
|
1338
1649
|
const channel = this.channels[channelNumber];
|
|
1339
|
-
channel.expression = expression / 127;
|
|
1650
|
+
channel.state.expression = expression / 127;
|
|
1340
1651
|
this.updateChannelVolume(channel);
|
|
1341
1652
|
}
|
|
1342
1653
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -1348,8 +1659,9 @@ class Midy {
|
|
|
1348
1659
|
}
|
|
1349
1660
|
updateChannelVolume(channel) {
|
|
1350
1661
|
const now = this.audioContext.currentTime;
|
|
1351
|
-
const
|
|
1352
|
-
const
|
|
1662
|
+
const state = channel.state;
|
|
1663
|
+
const volume = state.volume * state.expression;
|
|
1664
|
+
const { gainLeft, gainRight } = this.panToGain(state.pan);
|
|
1353
1665
|
channel.gainL.gain
|
|
1354
1666
|
.cancelScheduledValues(now)
|
|
1355
1667
|
.setValueAtTime(volume * gainLeft, now);
|
|
@@ -1358,24 +1670,24 @@ class Midy {
|
|
|
1358
1670
|
.setValueAtTime(volume * gainRight, now);
|
|
1359
1671
|
}
|
|
1360
1672
|
setSustainPedal(channelNumber, value) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
if (!isOn) {
|
|
1673
|
+
this.channels[channelNumber].state.sustainPedal = value / 127;
|
|
1674
|
+
if (value < 64) {
|
|
1364
1675
|
this.releaseSustainPedal(channelNumber, value);
|
|
1365
1676
|
}
|
|
1366
1677
|
}
|
|
1367
1678
|
setPortamento(channelNumber, value) {
|
|
1368
|
-
this.channels[channelNumber].portamento = value
|
|
1679
|
+
this.channels[channelNumber].state.portamento = value / 127;
|
|
1369
1680
|
}
|
|
1370
1681
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1371
1682
|
const channel = this.channels[channelNumber];
|
|
1683
|
+
const state = channel.state;
|
|
1372
1684
|
const reverbEffect = this.reverbEffect;
|
|
1373
|
-
if (0 <
|
|
1685
|
+
if (0 < state.reverbSendLevel) {
|
|
1374
1686
|
if (0 < reverbSendLevel) {
|
|
1375
1687
|
const now = this.audioContext.currentTime;
|
|
1376
|
-
|
|
1688
|
+
state.reverbSendLevel = reverbSendLevel / 127;
|
|
1377
1689
|
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1378
|
-
reverbEffect.input.gain.setValueAtTime(
|
|
1690
|
+
reverbEffect.input.gain.setValueAtTime(state.reverbSendLevel, now);
|
|
1379
1691
|
}
|
|
1380
1692
|
else {
|
|
1381
1693
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -1383,7 +1695,7 @@ class Midy {
|
|
|
1383
1695
|
const note = noteList[i];
|
|
1384
1696
|
if (!note)
|
|
1385
1697
|
continue;
|
|
1386
|
-
if (note.
|
|
1698
|
+
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
1387
1699
|
continue;
|
|
1388
1700
|
note.reverbEffectsSend.disconnect();
|
|
1389
1701
|
}
|
|
@@ -1398,32 +1710,25 @@ class Midy {
|
|
|
1398
1710
|
const note = noteList[i];
|
|
1399
1711
|
if (!note)
|
|
1400
1712
|
continue;
|
|
1401
|
-
|
|
1402
|
-
continue;
|
|
1403
|
-
if (!note.reverbEffectsSend) {
|
|
1404
|
-
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1405
|
-
gain: note.instrumentKey.reverbEffectsSend,
|
|
1406
|
-
});
|
|
1407
|
-
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1408
|
-
}
|
|
1409
|
-
note.reverbEffectsSend.connect(reverbEffect.input);
|
|
1713
|
+
this.setReverbEffectsSend(note, 0);
|
|
1410
1714
|
}
|
|
1411
1715
|
});
|
|
1412
|
-
|
|
1716
|
+
state.reverbSendLevel = reverbSendLevel / 127;
|
|
1413
1717
|
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1414
|
-
reverbEffect.input.gain.setValueAtTime(
|
|
1718
|
+
reverbEffect.input.gain.setValueAtTime(state.reverbSendLevel, now);
|
|
1415
1719
|
}
|
|
1416
1720
|
}
|
|
1417
1721
|
}
|
|
1418
1722
|
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1419
1723
|
const channel = this.channels[channelNumber];
|
|
1724
|
+
const state = channel.state;
|
|
1420
1725
|
const chorusEffect = this.chorusEffect;
|
|
1421
|
-
if (0 <
|
|
1726
|
+
if (0 < state.chorusSendLevel) {
|
|
1422
1727
|
if (0 < chorusSendLevel) {
|
|
1423
1728
|
const now = this.audioContext.currentTime;
|
|
1424
|
-
|
|
1729
|
+
state.chorusSendLevel = chorusSendLevel / 127;
|
|
1425
1730
|
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1426
|
-
chorusEffect.input.gain.setValueAtTime(
|
|
1731
|
+
chorusEffect.input.gain.setValueAtTime(state.chorusSendLevel, now);
|
|
1427
1732
|
}
|
|
1428
1733
|
else {
|
|
1429
1734
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -1431,7 +1736,7 @@ class Midy {
|
|
|
1431
1736
|
const note = noteList[i];
|
|
1432
1737
|
if (!note)
|
|
1433
1738
|
continue;
|
|
1434
|
-
if (note.
|
|
1739
|
+
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
1435
1740
|
continue;
|
|
1436
1741
|
note.chorusEffectsSend.disconnect();
|
|
1437
1742
|
}
|
|
@@ -1446,28 +1751,19 @@ class Midy {
|
|
|
1446
1751
|
const note = noteList[i];
|
|
1447
1752
|
if (!note)
|
|
1448
1753
|
continue;
|
|
1449
|
-
|
|
1450
|
-
continue;
|
|
1451
|
-
if (!note.chorusEffectsSend) {
|
|
1452
|
-
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1453
|
-
gain: note.instrumentKey.chorusEffectsSend,
|
|
1454
|
-
});
|
|
1455
|
-
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1456
|
-
}
|
|
1457
|
-
note.chorusEffectsSend.connect(chorusEffect.input);
|
|
1754
|
+
this.setChorusEffectsSend(note, 0);
|
|
1458
1755
|
}
|
|
1459
1756
|
});
|
|
1460
|
-
|
|
1757
|
+
state.chorusSendLevel = chorusSendLevel / 127;
|
|
1461
1758
|
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1462
|
-
chorusEffect.input.gain.setValueAtTime(
|
|
1759
|
+
chorusEffect.input.gain.setValueAtTime(state.chorusSendLevel, now);
|
|
1463
1760
|
}
|
|
1464
1761
|
}
|
|
1465
1762
|
}
|
|
1466
1763
|
setSostenutoPedal(channelNumber, value) {
|
|
1467
|
-
const isOn = value >= 64;
|
|
1468
1764
|
const channel = this.channels[channelNumber];
|
|
1469
|
-
channel.sostenutoPedal =
|
|
1470
|
-
if (
|
|
1765
|
+
channel.state.sostenutoPedal = value / 127;
|
|
1766
|
+
if (64 <= value) {
|
|
1471
1767
|
const now = this.audioContext.currentTime;
|
|
1472
1768
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1473
1769
|
channel.sostenutoNotes = new Map(activeNotes);
|
|
@@ -1478,31 +1774,31 @@ class Midy {
|
|
|
1478
1774
|
}
|
|
1479
1775
|
setSoftPedal(channelNumber, softPedal) {
|
|
1480
1776
|
const channel = this.channels[channelNumber];
|
|
1481
|
-
channel.softPedal = softPedal / 127;
|
|
1777
|
+
channel.state.softPedal = softPedal / 127;
|
|
1482
1778
|
}
|
|
1483
1779
|
setFilterResonance(channelNumber, filterResonance) {
|
|
1484
1780
|
const now = this.audioContext.currentTime;
|
|
1485
1781
|
const channel = this.channels[channelNumber];
|
|
1486
|
-
|
|
1782
|
+
const state = channel.state;
|
|
1783
|
+
state.filterResonance = filterResonance / 64;
|
|
1487
1784
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1488
1785
|
for (let i = 0; i < noteList.length; i++) {
|
|
1489
1786
|
const note = noteList[i];
|
|
1490
1787
|
if (!note)
|
|
1491
1788
|
continue;
|
|
1492
|
-
const Q = note.
|
|
1493
|
-
channel.filterResonance;
|
|
1789
|
+
const Q = note.voiceParams.initialFilterQ / 5 * state.filterResonance;
|
|
1494
1790
|
note.filterNode.Q.setValueAtTime(Q, now);
|
|
1495
1791
|
}
|
|
1496
1792
|
});
|
|
1497
1793
|
}
|
|
1498
1794
|
setReleaseTime(channelNumber, releaseTime) {
|
|
1499
1795
|
const channel = this.channels[channelNumber];
|
|
1500
|
-
channel.releaseTime = releaseTime / 64;
|
|
1796
|
+
channel.state.releaseTime = releaseTime / 64;
|
|
1501
1797
|
}
|
|
1502
1798
|
setAttackTime(channelNumber, attackTime) {
|
|
1503
1799
|
const now = this.audioContext.currentTime;
|
|
1504
1800
|
const channel = this.channels[channelNumber];
|
|
1505
|
-
channel.attackTime = attackTime / 64;
|
|
1801
|
+
channel.state.attackTime = attackTime / 64;
|
|
1506
1802
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1507
1803
|
for (let i = 0; i < noteList.length; i++) {
|
|
1508
1804
|
const note = noteList[i];
|
|
@@ -1516,7 +1812,7 @@ class Midy {
|
|
|
1516
1812
|
}
|
|
1517
1813
|
setBrightness(channelNumber, brightness) {
|
|
1518
1814
|
const channel = this.channels[channelNumber];
|
|
1519
|
-
channel.brightness = brightness / 64;
|
|
1815
|
+
channel.state.brightness = brightness / 64;
|
|
1520
1816
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1521
1817
|
for (let i = 0; i < noteList.length; i++) {
|
|
1522
1818
|
const note = noteList[i];
|
|
@@ -1528,7 +1824,7 @@ class Midy {
|
|
|
1528
1824
|
}
|
|
1529
1825
|
setDecayTime(channelNumber, dacayTime) {
|
|
1530
1826
|
const channel = this.channels[channelNumber];
|
|
1531
|
-
channel.decayTime = dacayTime / 64;
|
|
1827
|
+
channel.state.decayTime = dacayTime / 64;
|
|
1532
1828
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1533
1829
|
for (let i = 0; i < noteList.length; i++) {
|
|
1534
1830
|
const note = noteList[i];
|
|
@@ -1540,7 +1836,7 @@ class Midy {
|
|
|
1540
1836
|
}
|
|
1541
1837
|
setVibratoRate(channelNumber, vibratoRate) {
|
|
1542
1838
|
const channel = this.channels[channelNumber];
|
|
1543
|
-
channel.vibratoRate = vibratoRate / 64;
|
|
1839
|
+
channel.state.vibratoRate = vibratoRate / 64;
|
|
1544
1840
|
if (channel.vibratoDepth <= 0)
|
|
1545
1841
|
return;
|
|
1546
1842
|
const now = this.audioContext.currentTime;
|
|
@@ -1548,16 +1844,16 @@ class Midy {
|
|
|
1548
1844
|
activeNotes.forEach((activeNote) => {
|
|
1549
1845
|
activeNote.vibratoLFO.frequency
|
|
1550
1846
|
.cancelScheduledValues(now)
|
|
1551
|
-
.setValueAtTime(channel.vibratoRate, now);
|
|
1847
|
+
.setValueAtTime(channel.state.vibratoRate, now);
|
|
1552
1848
|
});
|
|
1553
1849
|
}
|
|
1554
1850
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1555
1851
|
const channel = this.channels[channelNumber];
|
|
1556
|
-
channel.vibratoDepth = vibratoDepth / 64;
|
|
1852
|
+
channel.state.vibratoDepth = vibratoDepth / 64;
|
|
1557
1853
|
}
|
|
1558
1854
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1559
1855
|
const channel = this.channels[channelNumber];
|
|
1560
|
-
channel.vibratoDelay = vibratoDelay / 64;
|
|
1856
|
+
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1561
1857
|
}
|
|
1562
1858
|
limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
|
|
1563
1859
|
if (maxLSB < channel.dataLSB) {
|
|
@@ -1627,7 +1923,7 @@ class Midy {
|
|
|
1627
1923
|
this.channels[channelNumber].dataMSB = value;
|
|
1628
1924
|
this.handleRPN(channelNumber, 0);
|
|
1629
1925
|
}
|
|
1630
|
-
updateDetune(channel,
|
|
1926
|
+
updateDetune(channel, detune) {
|
|
1631
1927
|
const now = this.audioContext.currentTime;
|
|
1632
1928
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1633
1929
|
for (let i = 0; i < noteList.length; i++) {
|
|
@@ -1635,7 +1931,6 @@ class Midy {
|
|
|
1635
1931
|
if (!note)
|
|
1636
1932
|
continue;
|
|
1637
1933
|
const { bufferSource } = note;
|
|
1638
|
-
const detune = bufferSource.detune.value + detuneChange;
|
|
1639
1934
|
bufferSource.detune
|
|
1640
1935
|
.cancelScheduledValues(now)
|
|
1641
1936
|
.setValueAtTime(detune, now);
|
|
@@ -1648,13 +1943,13 @@ class Midy {
|
|
|
1648
1943
|
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1649
1944
|
this.setPitchBendRange(channelNumber, pitchBendRange);
|
|
1650
1945
|
}
|
|
1651
|
-
setPitchBendRange(channelNumber,
|
|
1946
|
+
setPitchBendRange(channelNumber, pitchWheelSensitivity) {
|
|
1652
1947
|
const channel = this.channels[channelNumber];
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
this.
|
|
1948
|
+
const state = channel.state;
|
|
1949
|
+
state.pitchWheelSensitivity = pitchWheelSensitivity / 128;
|
|
1950
|
+
const detune = (state.pitchWheel * 2 - 1) * pitchWheelSensitivity * 100;
|
|
1951
|
+
this.updateDetune(channel, detune);
|
|
1952
|
+
this.applyVoiceParams(channel, 16);
|
|
1658
1953
|
}
|
|
1659
1954
|
handleFineTuningRPN(channelNumber) {
|
|
1660
1955
|
const channel = this.channels[channelNumber];
|
|
@@ -1698,7 +1993,30 @@ class Midy {
|
|
|
1698
1993
|
return this.stopChannelNotes(channelNumber, 0, true);
|
|
1699
1994
|
}
|
|
1700
1995
|
resetAllControllers(channelNumber) {
|
|
1701
|
-
|
|
1996
|
+
const stateTypes = [
|
|
1997
|
+
"expression",
|
|
1998
|
+
"modulationDepth",
|
|
1999
|
+
"sustainPedal",
|
|
2000
|
+
"portamento",
|
|
2001
|
+
"sostenutoPedal",
|
|
2002
|
+
"softPedal",
|
|
2003
|
+
"channelPressure",
|
|
2004
|
+
"pitchWheelSensitivity",
|
|
2005
|
+
];
|
|
2006
|
+
const channel = this.channels[channelNumber];
|
|
2007
|
+
const state = channel.state;
|
|
2008
|
+
for (let i = 0; i < stateTypes.length; i++) {
|
|
2009
|
+
const type = stateTypes[i];
|
|
2010
|
+
state[type] = defaultControllerState[type];
|
|
2011
|
+
}
|
|
2012
|
+
const settingTypes = [
|
|
2013
|
+
"rpnMSB",
|
|
2014
|
+
"rpnLSB",
|
|
2015
|
+
];
|
|
2016
|
+
for (let i = 0; i < settingTypes.length; i++) {
|
|
2017
|
+
const type = settingTypes[i];
|
|
2018
|
+
channel[type] = this.constructor.channelSettings[type];
|
|
2019
|
+
}
|
|
1702
2020
|
}
|
|
1703
2021
|
allNotesOff(channelNumber) {
|
|
1704
2022
|
return this.stopChannelNotes(channelNumber, 0, false);
|
|
@@ -2061,48 +2379,19 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2061
2379
|
writable: true,
|
|
2062
2380
|
value: {
|
|
2063
2381
|
currentBufferSource: null,
|
|
2064
|
-
|
|
2065
|
-
pan: 64,
|
|
2066
|
-
portamentoTime: 1, // sec
|
|
2067
|
-
filterResonance: 1,
|
|
2068
|
-
releaseTime: 1,
|
|
2069
|
-
attackTime: 1,
|
|
2070
|
-
brightness: 1,
|
|
2071
|
-
decayTime: 1,
|
|
2072
|
-
reverbSendLevel: 0,
|
|
2073
|
-
chorusSendLevel: 0,
|
|
2074
|
-
vibratoRate: 1,
|
|
2075
|
-
vibratoDepth: 1,
|
|
2076
|
-
vibratoDelay: 1,
|
|
2382
|
+
program: 0,
|
|
2077
2383
|
bank: 121 * 128,
|
|
2078
2384
|
bankMSB: 121,
|
|
2079
2385
|
bankLSB: 0,
|
|
2080
2386
|
dataMSB: 0,
|
|
2081
2387
|
dataLSB: 0,
|
|
2082
|
-
|
|
2083
|
-
|
|
2388
|
+
rpnMSB: 127,
|
|
2389
|
+
rpnLSB: 127,
|
|
2084
2390
|
fineTuning: 0, // cb
|
|
2085
2391
|
coarseTuning: 0, // cb
|
|
2086
2392
|
modulationDepthRange: 50, // cent
|
|
2087
2393
|
}
|
|
2088
2394
|
});
|
|
2089
|
-
Object.defineProperty(Midy, "effectSettings", {
|
|
2090
|
-
enumerable: true,
|
|
2091
|
-
configurable: true,
|
|
2092
|
-
writable: true,
|
|
2093
|
-
value: {
|
|
2094
|
-
expression: 1,
|
|
2095
|
-
modulationDepth: 0,
|
|
2096
|
-
sustainPedal: false,
|
|
2097
|
-
portamento: false,
|
|
2098
|
-
sostenutoPedal: false,
|
|
2099
|
-
softPedal: 0,
|
|
2100
|
-
rpnMSB: 127,
|
|
2101
|
-
rpnLSB: 127,
|
|
2102
|
-
channelPressure: 0,
|
|
2103
|
-
pitchBendRange: 2,
|
|
2104
|
-
}
|
|
2105
|
-
});
|
|
2106
2395
|
Object.defineProperty(Midy, "controllerDestinationSettings", {
|
|
2107
2396
|
enumerable: true,
|
|
2108
2397
|
configurable: true,
|