@marmooo/midy 0.1.7 → 0.2.1
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/LICENSE +201 -0
- package/README.md +118 -0
- package/esm/midy-GM1.d.ts +53 -26
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +397 -167
- package/esm/midy-GM2.d.ts +61 -38
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +617 -278
- package/esm/midy-GMLite.d.ts +49 -23
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +361 -156
- package/esm/midy.d.ts +63 -45
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +699 -354
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +53 -26
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +397 -167
- package/script/midy-GM2.d.ts +61 -38
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +617 -278
- package/script/midy-GMLite.d.ts +49 -23
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +361 -156
- package/script/midy.d.ts +63 -45
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +699 -354
package/script/midy-GM1.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.MidyGM1 = 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,
|
|
@@ -56,9 +56,75 @@ class Note {
|
|
|
56
56
|
this.noteNumber = noteNumber;
|
|
57
57
|
this.velocity = velocity;
|
|
58
58
|
this.startTime = startTime;
|
|
59
|
-
this.
|
|
59
|
+
this.voice = voice;
|
|
60
|
+
this.voiceParams = voiceParams;
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
// normalized to 0-1 for use with the SF2 modulator model
|
|
64
|
+
const defaultControllerState = {
|
|
65
|
+
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
66
|
+
noteOnKeyNumber: { type: 3, defaultValue: 0 },
|
|
67
|
+
pitchWheel: { type: 14, defaultValue: 8192 / 16383 },
|
|
68
|
+
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
69
|
+
link: { type: 127, defaultValue: 0 },
|
|
70
|
+
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
71
|
+
modulationDepth: { type: 128 + 1, defaultValue: 0 },
|
|
72
|
+
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
73
|
+
volume: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
74
|
+
pan: { type: 128 + 10, defaultValue: 0.5 },
|
|
75
|
+
expression: { type: 128 + 11, defaultValue: 1 },
|
|
76
|
+
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
77
|
+
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
78
|
+
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
79
|
+
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
80
|
+
// rpnMSB: { type: 128 + 101, defaultValue: 127 },
|
|
81
|
+
// allSoundOff: { type: 128 + 120, defaultValue: 0 },
|
|
82
|
+
// resetAllControllers: { type: 128 + 121, defaultValue: 0 },
|
|
83
|
+
// allNotesOff: { type: 128 + 123, defaultValue: 0 },
|
|
84
|
+
};
|
|
85
|
+
class ControllerState {
|
|
86
|
+
constructor() {
|
|
87
|
+
Object.defineProperty(this, "array", {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: true,
|
|
90
|
+
writable: true,
|
|
91
|
+
value: new Float32Array(256)
|
|
92
|
+
});
|
|
93
|
+
const entries = Object.entries(defaultControllerState);
|
|
94
|
+
for (const [name, { type, defaultValue }] of entries) {
|
|
95
|
+
this.array[type] = defaultValue;
|
|
96
|
+
Object.defineProperty(this, name, {
|
|
97
|
+
get: () => this.array[type],
|
|
98
|
+
set: (value) => this.array[type] = value,
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const filterEnvelopeKeys = [
|
|
106
|
+
"modEnvToPitch",
|
|
107
|
+
"initialFilterFc",
|
|
108
|
+
"modEnvToFilterFc",
|
|
109
|
+
"modDelay",
|
|
110
|
+
"modAttack",
|
|
111
|
+
"modHold",
|
|
112
|
+
"modDecay",
|
|
113
|
+
"modSustain",
|
|
114
|
+
"modRelease",
|
|
115
|
+
"playbackRate",
|
|
116
|
+
];
|
|
117
|
+
const filterEnvelopeKeySet = new Set(filterEnvelopeKeys);
|
|
118
|
+
const volumeEnvelopeKeys = [
|
|
119
|
+
"volDelay",
|
|
120
|
+
"volAttack",
|
|
121
|
+
"volHold",
|
|
122
|
+
"volDecay",
|
|
123
|
+
"volSustain",
|
|
124
|
+
"volRelease",
|
|
125
|
+
"initialAttenuation",
|
|
126
|
+
];
|
|
127
|
+
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
62
128
|
class MidyGM1 {
|
|
63
129
|
constructor(audioContext) {
|
|
64
130
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -171,6 +237,7 @@ class MidyGM1 {
|
|
|
171
237
|
});
|
|
172
238
|
this.audioContext = audioContext;
|
|
173
239
|
this.masterGain = new GainNode(audioContext);
|
|
240
|
+
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
174
241
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
175
242
|
this.channels = this.createChannels(audioContext);
|
|
176
243
|
this.masterGain.connect(audioContext.destination);
|
|
@@ -213,7 +280,7 @@ class MidyGM1 {
|
|
|
213
280
|
this.totalTime = this.calcTotalTime();
|
|
214
281
|
}
|
|
215
282
|
setChannelAudioNodes(audioContext) {
|
|
216
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
283
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
217
284
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
218
285
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
219
286
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -230,18 +297,18 @@ class MidyGM1 {
|
|
|
230
297
|
const channels = Array.from({ length: 16 }, () => {
|
|
231
298
|
return {
|
|
232
299
|
...this.constructor.channelSettings,
|
|
233
|
-
|
|
300
|
+
state: new ControllerState(),
|
|
234
301
|
...this.setChannelAudioNodes(audioContext),
|
|
235
302
|
scheduledNotes: new Map(),
|
|
236
303
|
};
|
|
237
304
|
});
|
|
238
305
|
return channels;
|
|
239
306
|
}
|
|
240
|
-
async createNoteBuffer(
|
|
241
|
-
const sampleStart =
|
|
242
|
-
const sampleEnd =
|
|
307
|
+
async createNoteBuffer(voiceParams, isSF3) {
|
|
308
|
+
const sampleStart = voiceParams.start;
|
|
309
|
+
const sampleEnd = voiceParams.sample.length + voiceParams.end;
|
|
243
310
|
if (isSF3) {
|
|
244
|
-
const sample =
|
|
311
|
+
const sample = voiceParams.sample;
|
|
245
312
|
const start = sample.byteOffset + sampleStart;
|
|
246
313
|
const end = sample.byteOffset + sampleEnd;
|
|
247
314
|
const buffer = sample.buffer.slice(start, end);
|
|
@@ -249,14 +316,14 @@ class MidyGM1 {
|
|
|
249
316
|
return audioBuffer;
|
|
250
317
|
}
|
|
251
318
|
else {
|
|
252
|
-
const sample =
|
|
319
|
+
const sample = voiceParams.sample;
|
|
253
320
|
const start = sample.byteOffset + sampleStart;
|
|
254
321
|
const end = sample.byteOffset + sampleEnd;
|
|
255
322
|
const buffer = sample.buffer.slice(start, end);
|
|
256
323
|
const audioBuffer = new AudioBuffer({
|
|
257
324
|
numberOfChannels: 1,
|
|
258
325
|
length: sample.length,
|
|
259
|
-
sampleRate:
|
|
326
|
+
sampleRate: voiceParams.sampleRate,
|
|
260
327
|
});
|
|
261
328
|
const channelData = audioBuffer.getChannelData(0);
|
|
262
329
|
const int16Array = new Int16Array(buffer);
|
|
@@ -266,15 +333,14 @@ class MidyGM1 {
|
|
|
266
333
|
return audioBuffer;
|
|
267
334
|
}
|
|
268
335
|
}
|
|
269
|
-
async createNoteBufferNode(
|
|
336
|
+
async createNoteBufferNode(voiceParams, isSF3) {
|
|
270
337
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
271
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
338
|
+
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
272
339
|
bufferSource.buffer = audioBuffer;
|
|
273
|
-
bufferSource.loop =
|
|
340
|
+
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
274
341
|
if (bufferSource.loop) {
|
|
275
|
-
bufferSource.loopStart =
|
|
276
|
-
|
|
277
|
-
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
342
|
+
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
343
|
+
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
278
344
|
}
|
|
279
345
|
return bufferSource;
|
|
280
346
|
}
|
|
@@ -304,7 +370,7 @@ class MidyGM1 {
|
|
|
304
370
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
305
371
|
break;
|
|
306
372
|
case "pitchBend":
|
|
307
|
-
this.setPitchBend(event.channel, event.value);
|
|
373
|
+
this.setPitchBend(event.channel, event.value + 8192);
|
|
308
374
|
break;
|
|
309
375
|
case "sysEx":
|
|
310
376
|
this.handleSysEx(event.data);
|
|
@@ -530,50 +596,74 @@ class MidyGM1 {
|
|
|
530
596
|
cbToRatio(cb) {
|
|
531
597
|
return Math.pow(10, cb / 200);
|
|
532
598
|
}
|
|
599
|
+
rateToCent(rate) {
|
|
600
|
+
return 1200 * Math.log2(rate);
|
|
601
|
+
}
|
|
602
|
+
centToRate(cent) {
|
|
603
|
+
return Math.pow(2, cent / 1200);
|
|
604
|
+
}
|
|
533
605
|
centToHz(cent) {
|
|
534
|
-
return 8.176 *
|
|
606
|
+
return 8.176 * this.centToRate(cent);
|
|
535
607
|
}
|
|
536
|
-
|
|
608
|
+
calcChannelDetune(channel) {
|
|
537
609
|
const tuning = channel.coarseTuning + channel.fineTuning;
|
|
538
|
-
|
|
610
|
+
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
611
|
+
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
612
|
+
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
613
|
+
return tuning + pitch;
|
|
539
614
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
615
|
+
updateDetune(channel) {
|
|
616
|
+
const now = this.audioContext.currentTime;
|
|
617
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
618
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
619
|
+
const note = noteList[i];
|
|
620
|
+
if (!note)
|
|
621
|
+
continue;
|
|
622
|
+
note.bufferSource.detune
|
|
623
|
+
.cancelScheduledValues(now)
|
|
624
|
+
.setValueAtTime(channel.detune, now);
|
|
625
|
+
}
|
|
626
|
+
});
|
|
543
627
|
}
|
|
544
628
|
setVolumeEnvelope(note) {
|
|
545
|
-
const
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
const
|
|
550
|
-
const
|
|
551
|
-
const
|
|
629
|
+
const now = this.audioContext.currentTime;
|
|
630
|
+
const { voiceParams, startTime } = note;
|
|
631
|
+
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation);
|
|
632
|
+
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
633
|
+
const volDelay = startTime + voiceParams.volDelay;
|
|
634
|
+
const volAttack = volDelay + voiceParams.volAttack;
|
|
635
|
+
const volHold = volAttack + voiceParams.volHold;
|
|
636
|
+
const volDecay = volHold + voiceParams.volDecay;
|
|
552
637
|
note.volumeNode.gain
|
|
553
|
-
.cancelScheduledValues(
|
|
638
|
+
.cancelScheduledValues(now)
|
|
554
639
|
.setValueAtTime(0, startTime)
|
|
555
640
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
556
641
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
557
642
|
.setValueAtTime(attackVolume, volHold)
|
|
558
643
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
559
644
|
}
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
const
|
|
563
|
-
|
|
645
|
+
setPitchEnvelope(note) {
|
|
646
|
+
const now = this.audioContext.currentTime;
|
|
647
|
+
const { voiceParams } = note;
|
|
648
|
+
const baseRate = voiceParams.playbackRate;
|
|
649
|
+
note.bufferSource.playbackRate
|
|
650
|
+
.cancelScheduledValues(now)
|
|
651
|
+
.setValueAtTime(baseRate, now);
|
|
652
|
+
const modEnvToPitch = voiceParams.modEnvToPitch;
|
|
564
653
|
if (modEnvToPitch === 0)
|
|
565
654
|
return;
|
|
566
|
-
const basePitch =
|
|
567
|
-
const peekPitch =
|
|
568
|
-
const
|
|
569
|
-
const
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
.
|
|
575
|
-
.
|
|
576
|
-
.
|
|
655
|
+
const basePitch = this.rateToCent(baseRate);
|
|
656
|
+
const peekPitch = basePitch + modEnvToPitch;
|
|
657
|
+
const peekRate = this.centToRate(peekPitch);
|
|
658
|
+
const modDelay = startTime + voiceParams.modDelay;
|
|
659
|
+
const modAttack = modDelay + voiceParams.modAttack;
|
|
660
|
+
const modHold = modAttack + voiceParams.modHold;
|
|
661
|
+
const modDecay = modHold + voiceParams.modDecay;
|
|
662
|
+
note.bufferSource.playbackRate
|
|
663
|
+
.setValueAtTime(baseRate, modDelay)
|
|
664
|
+
.exponentialRampToValueAtTime(peekRate, modAttack)
|
|
665
|
+
.setValueAtTime(peekRate, modHold)
|
|
666
|
+
.linearRampToValueAtTime(baseRate, modDecay);
|
|
577
667
|
}
|
|
578
668
|
clampCutoffFrequency(frequency) {
|
|
579
669
|
const minFrequency = 20; // min Hz of initialFilterFc
|
|
@@ -581,20 +671,21 @@ class MidyGM1 {
|
|
|
581
671
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
582
672
|
}
|
|
583
673
|
setFilterEnvelope(note) {
|
|
584
|
-
const
|
|
585
|
-
const
|
|
586
|
-
const
|
|
674
|
+
const now = this.audioContext.currentTime;
|
|
675
|
+
const { voiceParams, startTime } = note;
|
|
676
|
+
const baseFreq = this.centToHz(voiceParams.initialFilterFc);
|
|
677
|
+
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc);
|
|
587
678
|
const sustainFreq = baseFreq +
|
|
588
|
-
(peekFreq - baseFreq) * (1 -
|
|
679
|
+
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
589
680
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
590
681
|
const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
|
|
591
682
|
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
592
|
-
const modDelay = startTime +
|
|
593
|
-
const modAttack = modDelay +
|
|
594
|
-
const modHold = modAttack +
|
|
595
|
-
const modDecay = modHold +
|
|
683
|
+
const modDelay = startTime + voiceParams.modDelay;
|
|
684
|
+
const modAttack = modDelay + voiceParams.modAttack;
|
|
685
|
+
const modHold = modAttack + voiceParams.modHold;
|
|
686
|
+
const modDecay = modHold + voiceParams.modDecay;
|
|
596
687
|
note.filterNode.frequency
|
|
597
|
-
.cancelScheduledValues(
|
|
688
|
+
.cancelScheduledValues(now)
|
|
598
689
|
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
599
690
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
600
691
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
@@ -602,25 +693,18 @@ class MidyGM1 {
|
|
|
602
693
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
603
694
|
}
|
|
604
695
|
startModulation(channel, note, startTime) {
|
|
605
|
-
const {
|
|
606
|
-
const { modLfoToPitch, modLfoToVolume } = instrumentKey;
|
|
696
|
+
const { voiceParams } = note;
|
|
607
697
|
note.modulationLFO = new OscillatorNode(this.audioContext, {
|
|
608
|
-
frequency: this.centToHz(
|
|
698
|
+
frequency: this.centToHz(voiceParams.freqModLFO),
|
|
609
699
|
});
|
|
610
700
|
note.filterDepth = new GainNode(this.audioContext, {
|
|
611
|
-
gain:
|
|
701
|
+
gain: voiceParams.modLfoToFilterFc,
|
|
612
702
|
});
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
note.
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
619
|
-
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
620
|
-
note.volumeDepth = new GainNode(this.audioContext, {
|
|
621
|
-
gain: volumeDepth * volumeDepthSign,
|
|
622
|
-
});
|
|
623
|
-
note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
|
|
703
|
+
note.modulationDepth = new GainNode(this.audioContext);
|
|
704
|
+
this.setModLfoToPitch(channel, note);
|
|
705
|
+
note.volumeDepth = new GainNode(this.audioContext);
|
|
706
|
+
this.setModLfoToVolume(note);
|
|
707
|
+
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
624
708
|
note.modulationLFO.connect(note.filterDepth);
|
|
625
709
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
626
710
|
note.modulationLFO.connect(note.modulationDepth);
|
|
@@ -628,24 +712,22 @@ class MidyGM1 {
|
|
|
628
712
|
note.modulationLFO.connect(note.volumeDepth);
|
|
629
713
|
note.volumeDepth.connect(note.volumeNode.gain);
|
|
630
714
|
}
|
|
631
|
-
async createNote(channel,
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
note
|
|
715
|
+
async createNote(channel, voice, noteNumber, velocity, startTime, isSF3) {
|
|
716
|
+
const state = channel.state;
|
|
717
|
+
const voiceParams = voice.getAllParams(state.array);
|
|
718
|
+
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
719
|
+
note.bufferSource = await this.createNoteBufferNode(voiceParams, isSF3);
|
|
635
720
|
note.volumeNode = new GainNode(this.audioContext);
|
|
636
721
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
637
722
|
type: "lowpass",
|
|
638
|
-
Q:
|
|
723
|
+
Q: voiceParams.initialFilterQ / 10, // dB
|
|
639
724
|
});
|
|
640
725
|
this.setVolumeEnvelope(note);
|
|
641
726
|
this.setFilterEnvelope(note);
|
|
642
|
-
|
|
643
|
-
|
|
727
|
+
this.setPitchEnvelope(note);
|
|
728
|
+
if (0 < state.modulationDepth) {
|
|
644
729
|
this.startModulation(channel, note, startTime);
|
|
645
730
|
}
|
|
646
|
-
else {
|
|
647
|
-
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
648
|
-
}
|
|
649
731
|
note.bufferSource.connect(note.filterNode);
|
|
650
732
|
note.filterNode.connect(note.volumeNode);
|
|
651
733
|
note.bufferSource.start(startTime);
|
|
@@ -653,19 +735,19 @@ class MidyGM1 {
|
|
|
653
735
|
}
|
|
654
736
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
655
737
|
const channel = this.channels[channelNumber];
|
|
656
|
-
const bankNumber =
|
|
738
|
+
const bankNumber = channel.bank;
|
|
657
739
|
const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
|
|
658
740
|
if (soundFontIndex === undefined)
|
|
659
741
|
return;
|
|
660
742
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
661
743
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
662
|
-
const
|
|
663
|
-
if (!
|
|
744
|
+
const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
|
|
745
|
+
if (!voice)
|
|
664
746
|
return;
|
|
665
|
-
const note = await this.createNote(channel,
|
|
747
|
+
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
|
|
666
748
|
note.volumeNode.connect(channel.gainL);
|
|
667
749
|
note.volumeNode.connect(channel.gainR);
|
|
668
|
-
const exclusiveClass =
|
|
750
|
+
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
669
751
|
if (exclusiveClass !== 0) {
|
|
670
752
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
671
753
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
@@ -721,7 +803,7 @@ class MidyGM1 {
|
|
|
721
803
|
}
|
|
722
804
|
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
723
805
|
const channel = this.channels[channelNumber];
|
|
724
|
-
if (!force && channel.sustainPedal)
|
|
806
|
+
if (!force && 0.5 < channel.state.sustainPedal)
|
|
725
807
|
return;
|
|
726
808
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
727
809
|
return;
|
|
@@ -732,8 +814,8 @@ class MidyGM1 {
|
|
|
732
814
|
continue;
|
|
733
815
|
if (note.ending)
|
|
734
816
|
continue;
|
|
735
|
-
const volRelease = endTime + note.
|
|
736
|
-
const modRelease = endTime + note.
|
|
817
|
+
const volRelease = endTime + note.voiceParams.volRelease;
|
|
818
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
737
819
|
note.filterNode.frequency
|
|
738
820
|
.cancelScheduledValues(endTime)
|
|
739
821
|
.linearRampToValueAtTime(0, modRelease);
|
|
@@ -749,7 +831,7 @@ class MidyGM1 {
|
|
|
749
831
|
const velocity = halfVelocity * 2;
|
|
750
832
|
const channel = this.channels[channelNumber];
|
|
751
833
|
const promises = [];
|
|
752
|
-
channel.sustainPedal =
|
|
834
|
+
channel.state.sustainPedal = halfVelocity;
|
|
753
835
|
channel.scheduledNotes.forEach((noteList) => {
|
|
754
836
|
for (let i = 0; i < noteList.length; i++) {
|
|
755
837
|
const note = noteList[i];
|
|
@@ -785,16 +867,171 @@ class MidyGM1 {
|
|
|
785
867
|
channel.program = program;
|
|
786
868
|
}
|
|
787
869
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
788
|
-
const pitchBend = msb * 128 + lsb
|
|
870
|
+
const pitchBend = msb * 128 + lsb;
|
|
789
871
|
this.setPitchBend(channelNumber, pitchBend);
|
|
790
872
|
}
|
|
791
|
-
setPitchBend(channelNumber,
|
|
873
|
+
setPitchBend(channelNumber, value) {
|
|
792
874
|
const channel = this.channels[channelNumber];
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
875
|
+
const state = channel.state;
|
|
876
|
+
const prev = state.pitchWheel * 2 - 1;
|
|
877
|
+
const next = (value - 8192) / 8192;
|
|
878
|
+
state.pitchWheel = value / 16383;
|
|
879
|
+
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
880
|
+
this.updateDetune(channel);
|
|
881
|
+
this.applyVoiceParams(channel, 14);
|
|
882
|
+
}
|
|
883
|
+
setModLfoToPitch(channel, note) {
|
|
884
|
+
const now = this.audioContext.currentTime;
|
|
885
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch;
|
|
886
|
+
const modulationDepth = Math.abs(modLfoToPitch) +
|
|
887
|
+
channel.state.modulationDepth;
|
|
888
|
+
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
889
|
+
note.modulationDepth.gain
|
|
890
|
+
.cancelScheduledValues(now)
|
|
891
|
+
.setValueAtTime(modulationDepth * modulationDepthSign, now);
|
|
892
|
+
}
|
|
893
|
+
setVibLfoToPitch(channel, note) {
|
|
894
|
+
const now = this.audioContext.currentTime;
|
|
895
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
896
|
+
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
|
|
897
|
+
2;
|
|
898
|
+
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
899
|
+
note.vibratoDepth.gain
|
|
900
|
+
.cancelScheduledValues(now)
|
|
901
|
+
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
902
|
+
}
|
|
903
|
+
setModLfoToFilterFc(note) {
|
|
904
|
+
const now = this.audioContext.currentTime;
|
|
905
|
+
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
906
|
+
note.filterDepth.gain
|
|
907
|
+
.cancelScheduledValues(now)
|
|
908
|
+
.setValueAtTime(modLfoToFilterFc, now);
|
|
909
|
+
}
|
|
910
|
+
setModLfoToVolume(note) {
|
|
911
|
+
const now = this.audioContext.currentTime;
|
|
912
|
+
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
913
|
+
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
914
|
+
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
915
|
+
note.volumeDepth.gain
|
|
916
|
+
.cancelScheduledValues(now)
|
|
917
|
+
.setValueAtTime(volumeDepth * volumeDepthSign, now);
|
|
918
|
+
}
|
|
919
|
+
setDelayModLFO(note) {
|
|
920
|
+
const now = this.audioContext.currentTime;
|
|
921
|
+
const startTime = note.startTime;
|
|
922
|
+
if (startTime < now)
|
|
923
|
+
return;
|
|
924
|
+
note.modulationLFO.stop(now);
|
|
925
|
+
note.modulationLFO.start(startTime + note.voiceParams.delayModLFO);
|
|
926
|
+
note.modulationLFO.connect(note.filterDepth);
|
|
927
|
+
}
|
|
928
|
+
setFreqModLFO(note) {
|
|
929
|
+
const now = this.audioContext.currentTime;
|
|
930
|
+
const freqModLFO = note.voiceParams.freqModLFO;
|
|
931
|
+
note.modulationLFO.frequency
|
|
932
|
+
.cancelScheduledValues(now)
|
|
933
|
+
.setValueAtTime(freqModLFO, now);
|
|
934
|
+
}
|
|
935
|
+
createVoiceParamsHandlers() {
|
|
936
|
+
return {
|
|
937
|
+
modLfoToPitch: (channel, note, _prevValue) => {
|
|
938
|
+
if (0 < channel.state.modulationDepth) {
|
|
939
|
+
this.setModLfoToPitch(channel, note);
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
943
|
+
if (0 < channel.state.vibratoDepth) {
|
|
944
|
+
this.setVibLfoToPitch(channel, note);
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
948
|
+
if (0 < channel.state.modulationDepth)
|
|
949
|
+
this.setModLfoToFilterFc(note);
|
|
950
|
+
},
|
|
951
|
+
modLfoToVolume: (channel, note) => {
|
|
952
|
+
if (0 < channel.state.modulationDepth)
|
|
953
|
+
this.setModLfoToVolume(note);
|
|
954
|
+
},
|
|
955
|
+
chorusEffectsSend: (_channel, _note, _prevValue) => { },
|
|
956
|
+
reverbEffectsSend: (_channel, _note, _prevValue) => { },
|
|
957
|
+
delayModLFO: (_channel, note, _prevValue) => this.setDelayModLFO(note),
|
|
958
|
+
freqModLFO: (_channel, note, _prevValue) => this.setFreqModLFO(note),
|
|
959
|
+
delayVibLFO: (channel, note, prevValue) => {
|
|
960
|
+
if (0 < channel.state.vibratoDepth) {
|
|
961
|
+
const now = this.audioContext.currentTime;
|
|
962
|
+
const prevStartTime = note.startTime +
|
|
963
|
+
prevValue * channel.state.vibratoDelay * 2;
|
|
964
|
+
if (now < prevStartTime)
|
|
965
|
+
return;
|
|
966
|
+
const startTime = note.startTime +
|
|
967
|
+
value * channel.state.vibratoDelay * 2;
|
|
968
|
+
note.vibratoLFO.stop(now);
|
|
969
|
+
note.vibratoLFO.start(startTime);
|
|
970
|
+
}
|
|
971
|
+
},
|
|
972
|
+
freqVibLFO: (channel, note, _prevValue) => {
|
|
973
|
+
if (0 < channel.state.vibratoDepth) {
|
|
974
|
+
const now = this.audioContext.currentTime;
|
|
975
|
+
note.vibratoLFO.frequency
|
|
976
|
+
.cancelScheduledValues(now)
|
|
977
|
+
.setValueAtTime(value * sate.vibratoRate, now);
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
getControllerState(channel, noteNumber, velocity) {
|
|
983
|
+
const state = new Float32Array(channel.state.array.length);
|
|
984
|
+
state.set(channel.state.array);
|
|
985
|
+
state[2] = velocity / 127;
|
|
986
|
+
state[3] = noteNumber / 127;
|
|
987
|
+
return state;
|
|
988
|
+
}
|
|
989
|
+
applyVoiceParams(channel, controllerType) {
|
|
990
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
991
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
992
|
+
const note = noteList[i];
|
|
993
|
+
if (!note)
|
|
994
|
+
continue;
|
|
995
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
996
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
997
|
+
let appliedFilterEnvelope = false;
|
|
998
|
+
let appliedVolumeEnvelope = false;
|
|
999
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1000
|
+
const prevValue = note.voiceParams[key];
|
|
1001
|
+
if (value === prevValue)
|
|
1002
|
+
continue;
|
|
1003
|
+
note.voiceParams[key] = value;
|
|
1004
|
+
if (key in this.voiceParamsHandlers) {
|
|
1005
|
+
this.voiceParamsHandlers[key](channel, note, prevValue);
|
|
1006
|
+
}
|
|
1007
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1008
|
+
if (appliedFilterEnvelope)
|
|
1009
|
+
continue;
|
|
1010
|
+
appliedFilterEnvelope = true;
|
|
1011
|
+
const noteVoiceParams = note.voiceParams;
|
|
1012
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1013
|
+
const key = filterEnvelopeKeys[i];
|
|
1014
|
+
if (key in voiceParams)
|
|
1015
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1016
|
+
}
|
|
1017
|
+
this.setFilterEnvelope(channel, note);
|
|
1018
|
+
this.setPitchEnvelope(note);
|
|
1019
|
+
}
|
|
1020
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1021
|
+
if (appliedVolumeEnvelope)
|
|
1022
|
+
continue;
|
|
1023
|
+
appliedVolumeEnvelope = true;
|
|
1024
|
+
const noteVoiceParams = note.voiceParams;
|
|
1025
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1026
|
+
const key = volumeEnvelopeKeys[i];
|
|
1027
|
+
if (key in voiceParams)
|
|
1028
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1029
|
+
}
|
|
1030
|
+
this.setVolumeEnvelope(channel, note);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
798
1035
|
}
|
|
799
1036
|
createControlChangeHandlers() {
|
|
800
1037
|
return {
|
|
@@ -812,13 +1049,13 @@ class MidyGM1 {
|
|
|
812
1049
|
123: this.allNotesOff,
|
|
813
1050
|
};
|
|
814
1051
|
}
|
|
815
|
-
handleControlChange(channelNumber,
|
|
816
|
-
const handler = this.controlChangeHandlers[
|
|
1052
|
+
handleControlChange(channelNumber, controllerType, value) {
|
|
1053
|
+
const handler = this.controlChangeHandlers[controllerType];
|
|
817
1054
|
if (handler) {
|
|
818
1055
|
handler.call(this, channelNumber, value);
|
|
819
1056
|
}
|
|
820
1057
|
else {
|
|
821
|
-
console.warn(`Unsupported Control change:
|
|
1058
|
+
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
822
1059
|
}
|
|
823
1060
|
}
|
|
824
1061
|
updateModulation(channel) {
|
|
@@ -829,11 +1066,10 @@ class MidyGM1 {
|
|
|
829
1066
|
if (!note)
|
|
830
1067
|
continue;
|
|
831
1068
|
if (note.modulationDepth) {
|
|
832
|
-
note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
1069
|
+
note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
|
|
833
1070
|
}
|
|
834
1071
|
else {
|
|
835
|
-
|
|
836
|
-
this.setPitch(note, semitoneOffset);
|
|
1072
|
+
this.setPitchEnvelope(note);
|
|
837
1073
|
this.startModulation(channel, note, now);
|
|
838
1074
|
}
|
|
839
1075
|
}
|
|
@@ -841,16 +1077,17 @@ class MidyGM1 {
|
|
|
841
1077
|
}
|
|
842
1078
|
setModulationDepth(channelNumber, modulation) {
|
|
843
1079
|
const channel = this.channels[channelNumber];
|
|
844
|
-
channel.modulationDepth = (modulation / 127) *
|
|
1080
|
+
channel.state.modulationDepth = (modulation / 127) *
|
|
1081
|
+
channel.modulationDepthRange;
|
|
845
1082
|
this.updateModulation(channel);
|
|
846
1083
|
}
|
|
847
1084
|
setVolume(channelNumber, volume) {
|
|
848
1085
|
const channel = this.channels[channelNumber];
|
|
849
|
-
channel.volume = volume / 127;
|
|
1086
|
+
channel.state.volume = volume / 127;
|
|
850
1087
|
this.updateChannelVolume(channel);
|
|
851
1088
|
}
|
|
852
1089
|
panToGain(pan) {
|
|
853
|
-
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
1090
|
+
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
854
1091
|
return {
|
|
855
1092
|
gainLeft: Math.cos(theta),
|
|
856
1093
|
gainRight: Math.sin(theta),
|
|
@@ -858,12 +1095,12 @@ class MidyGM1 {
|
|
|
858
1095
|
}
|
|
859
1096
|
setPan(channelNumber, pan) {
|
|
860
1097
|
const channel = this.channels[channelNumber];
|
|
861
|
-
channel.pan = pan;
|
|
1098
|
+
channel.state.pan = pan / 127;
|
|
862
1099
|
this.updateChannelVolume(channel);
|
|
863
1100
|
}
|
|
864
1101
|
setExpression(channelNumber, expression) {
|
|
865
1102
|
const channel = this.channels[channelNumber];
|
|
866
|
-
channel.expression = expression / 127;
|
|
1103
|
+
channel.state.expression = expression / 127;
|
|
867
1104
|
this.updateChannelVolume(channel);
|
|
868
1105
|
}
|
|
869
1106
|
dataEntryLSB(channelNumber, value) {
|
|
@@ -872,8 +1109,9 @@ class MidyGM1 {
|
|
|
872
1109
|
}
|
|
873
1110
|
updateChannelVolume(channel) {
|
|
874
1111
|
const now = this.audioContext.currentTime;
|
|
875
|
-
const
|
|
876
|
-
const
|
|
1112
|
+
const state = channel.state;
|
|
1113
|
+
const volume = state.volume * state.expression;
|
|
1114
|
+
const { gainLeft, gainRight } = this.panToGain(state.pan);
|
|
877
1115
|
channel.gainL.gain
|
|
878
1116
|
.cancelScheduledValues(now)
|
|
879
1117
|
.setValueAtTime(volume * gainLeft, now);
|
|
@@ -882,9 +1120,8 @@ class MidyGM1 {
|
|
|
882
1120
|
.setValueAtTime(volume * gainRight, now);
|
|
883
1121
|
}
|
|
884
1122
|
setSustainPedal(channelNumber, value) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
if (!isOn) {
|
|
1123
|
+
this.channels[channelNumber].state.sustainPedal = value / 127;
|
|
1124
|
+
if (value < 64) {
|
|
888
1125
|
this.releaseSustainPedal(channelNumber, value);
|
|
889
1126
|
}
|
|
890
1127
|
}
|
|
@@ -941,66 +1178,74 @@ class MidyGM1 {
|
|
|
941
1178
|
this.channels[channelNumber].dataMSB = value;
|
|
942
1179
|
this.handleRPN(channelNumber);
|
|
943
1180
|
}
|
|
944
|
-
updateDetune(channel, detuneChange) {
|
|
945
|
-
const now = this.audioContext.currentTime;
|
|
946
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
947
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
948
|
-
const note = noteList[i];
|
|
949
|
-
if (!note)
|
|
950
|
-
continue;
|
|
951
|
-
const { bufferSource } = note;
|
|
952
|
-
const detune = bufferSource.detune.value + detuneChange;
|
|
953
|
-
bufferSource.detune
|
|
954
|
-
.cancelScheduledValues(now)
|
|
955
|
-
.setValueAtTime(detune, now);
|
|
956
|
-
}
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
1181
|
handlePitchBendRangeRPN(channelNumber) {
|
|
960
1182
|
const channel = this.channels[channelNumber];
|
|
961
1183
|
this.limitData(channel, 0, 127, 0, 99);
|
|
962
1184
|
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
963
1185
|
this.setPitchBendRange(channelNumber, pitchBendRange);
|
|
964
1186
|
}
|
|
965
|
-
setPitchBendRange(channelNumber,
|
|
1187
|
+
setPitchBendRange(channelNumber, value) {
|
|
966
1188
|
const channel = this.channels[channelNumber];
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
1189
|
+
const state = channel.state;
|
|
1190
|
+
const prev = state.pitchWheelSensitivity;
|
|
1191
|
+
const next = value / 128;
|
|
1192
|
+
state.pitchWheelSensitivity = next;
|
|
1193
|
+
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1194
|
+
this.updateDetune(channel);
|
|
1195
|
+
this.applyVoiceParams(channel, 16);
|
|
972
1196
|
}
|
|
973
1197
|
handleFineTuningRPN(channelNumber) {
|
|
974
1198
|
const channel = this.channels[channelNumber];
|
|
975
1199
|
this.limitData(channel, 0, 127, 0, 127);
|
|
976
|
-
const fineTuning =
|
|
1200
|
+
const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
|
|
977
1201
|
this.setFineTuning(channelNumber, fineTuning);
|
|
978
1202
|
}
|
|
979
|
-
setFineTuning(channelNumber,
|
|
1203
|
+
setFineTuning(channelNumber, value) {
|
|
980
1204
|
const channel = this.channels[channelNumber];
|
|
981
|
-
const
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1205
|
+
const prev = channel.fineTuning;
|
|
1206
|
+
const next = (value - 8192) / 8.192; // cent
|
|
1207
|
+
channel.fineTuning = next;
|
|
1208
|
+
channel.detune += next - prev;
|
|
1209
|
+
this.updateDetune(channel);
|
|
985
1210
|
}
|
|
986
1211
|
handleCoarseTuningRPN(channelNumber) {
|
|
987
1212
|
const channel = this.channels[channelNumber];
|
|
988
1213
|
this.limitDataMSB(channel, 0, 127);
|
|
989
|
-
const coarseTuning = channel.dataMSB
|
|
990
|
-
this.
|
|
1214
|
+
const coarseTuning = channel.dataMSB;
|
|
1215
|
+
this.setCoarseTuning(channelNumber, coarseTuning);
|
|
991
1216
|
}
|
|
992
|
-
setCoarseTuning(channelNumber,
|
|
1217
|
+
setCoarseTuning(channelNumber, value) {
|
|
993
1218
|
const channel = this.channels[channelNumber];
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1219
|
+
const prev = channel.coarseTuning;
|
|
1220
|
+
const next = (value - 64) * 100; // cent
|
|
1221
|
+
channel.coarseTuning = next;
|
|
1222
|
+
channel.detune += next - prev;
|
|
1223
|
+
this.updateDetune(channel);
|
|
998
1224
|
}
|
|
999
1225
|
allSoundOff(channelNumber) {
|
|
1000
1226
|
return this.stopChannelNotes(channelNumber, 0, true);
|
|
1001
1227
|
}
|
|
1002
1228
|
resetAllControllers(channelNumber) {
|
|
1003
|
-
|
|
1229
|
+
const stateTypes = [
|
|
1230
|
+
"expression",
|
|
1231
|
+
"modulationDepth",
|
|
1232
|
+
"sustainPedal",
|
|
1233
|
+
"pitchWheelSensitivity",
|
|
1234
|
+
];
|
|
1235
|
+
const channel = this.channels[channelNumber];
|
|
1236
|
+
const state = channel.state;
|
|
1237
|
+
for (let i = 0; i < stateTypes.length; i++) {
|
|
1238
|
+
const type = stateTypes[i];
|
|
1239
|
+
state[type] = defaultControllerState[type];
|
|
1240
|
+
}
|
|
1241
|
+
const settingTypes = [
|
|
1242
|
+
"rpnMSB",
|
|
1243
|
+
"rpnLSB",
|
|
1244
|
+
];
|
|
1245
|
+
for (let i = 0; i < settingTypes.length; i++) {
|
|
1246
|
+
const type = settingTypes[i];
|
|
1247
|
+
channel[type] = this.constructor.channelSettings[type];
|
|
1248
|
+
}
|
|
1004
1249
|
}
|
|
1005
1250
|
allNotesOff(channelNumber) {
|
|
1006
1251
|
return this.stopChannelNotes(channelNumber, 0, false);
|
|
@@ -1025,11 +1270,8 @@ class MidyGM1 {
|
|
|
1025
1270
|
GM1SystemOn() {
|
|
1026
1271
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1027
1272
|
const channel = this.channels[i];
|
|
1028
|
-
channel.bankMSB = 0;
|
|
1029
|
-
channel.bankLSB = 0;
|
|
1030
1273
|
channel.bank = 0;
|
|
1031
1274
|
}
|
|
1032
|
-
this.channels[9].bankMSB = 1;
|
|
1033
1275
|
this.channels[9].bank = 128;
|
|
1034
1276
|
}
|
|
1035
1277
|
handleUniversalRealTimeExclusiveMessage(data) {
|
|
@@ -1091,28 +1333,16 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1091
1333
|
configurable: true,
|
|
1092
1334
|
writable: true,
|
|
1093
1335
|
value: {
|
|
1094
|
-
|
|
1095
|
-
|
|
1336
|
+
currentBufferSource: null,
|
|
1337
|
+
detune: 0,
|
|
1338
|
+
program: 0,
|
|
1096
1339
|
bank: 0,
|
|
1097
1340
|
dataMSB: 0,
|
|
1098
1341
|
dataLSB: 0,
|
|
1099
|
-
|
|
1100
|
-
|
|
1342
|
+
rpnMSB: 127,
|
|
1343
|
+
rpnLSB: 127,
|
|
1101
1344
|
fineTuning: 0, // cb
|
|
1102
1345
|
coarseTuning: 0, // cb
|
|
1103
1346
|
modulationDepthRange: 50, // cent
|
|
1104
1347
|
}
|
|
1105
1348
|
});
|
|
1106
|
-
Object.defineProperty(MidyGM1, "effectSettings", {
|
|
1107
|
-
enumerable: true,
|
|
1108
|
-
configurable: true,
|
|
1109
|
-
writable: true,
|
|
1110
|
-
value: {
|
|
1111
|
-
expression: 1,
|
|
1112
|
-
modulationDepth: 0,
|
|
1113
|
-
sustainPedal: false,
|
|
1114
|
-
rpnMSB: 127,
|
|
1115
|
-
rpnLSB: 127,
|
|
1116
|
-
pitchBendRange: 2,
|
|
1117
|
-
}
|
|
1118
|
-
});
|