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