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