@marmooo/midy 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
- package/esm/midy-GM1.d.ts +27 -36
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +199 -135
- package/esm/midy-GM2.d.ts +51 -35
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +234 -141
- package/esm/midy-GMLite.d.ts +25 -36
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +187 -135
- package/esm/midy.d.ts +68 -24
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +274 -141
- package/package.json +1 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
- package/script/midy-GM1.d.ts +27 -36
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +199 -135
- package/script/midy-GM2.d.ts +51 -35
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +234 -141
- package/script/midy-GMLite.d.ts +25 -36
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +187 -135
- package/script/midy.d.ts +68 -24
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +274 -141
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
package/script/midy.js
CHANGED
|
@@ -2,7 +2,57 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Midy = void 0;
|
|
4
4
|
const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
|
|
5
|
-
const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.
|
|
5
|
+
const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js");
|
|
6
|
+
class Note {
|
|
7
|
+
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
8
|
+
Object.defineProperty(this, "bufferSource", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: void 0
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(this, "gainNode", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "filterNode", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "modLFO", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "modLFOGain", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "vibLFO", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: void 0
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "vibLFOGain", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: void 0
|
|
49
|
+
});
|
|
50
|
+
this.noteNumber = noteNumber;
|
|
51
|
+
this.velocity = velocity;
|
|
52
|
+
this.startTime = startTime;
|
|
53
|
+
this.instrumentKey = instrumentKey;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
6
56
|
class Midy {
|
|
7
57
|
constructor(audioContext) {
|
|
8
58
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -184,10 +234,8 @@ class Midy {
|
|
|
184
234
|
const pannerNode = new StereoPannerNode(audioContext, {
|
|
185
235
|
pan: Midy.channelSettings.pan,
|
|
186
236
|
});
|
|
187
|
-
const modulationEffect = this.createModulationEffect(audioContext);
|
|
188
237
|
const reverbEffect = this.createReverbEffect(audioContext);
|
|
189
238
|
const chorusEffect = this.createChorusEffect(audioContext);
|
|
190
|
-
modulationEffect.lfo.start();
|
|
191
239
|
chorusEffect.lfo.start();
|
|
192
240
|
reverbEffect.dryGain.connect(pannerNode);
|
|
193
241
|
reverbEffect.wetGain.connect(pannerNode);
|
|
@@ -196,7 +244,6 @@ class Midy {
|
|
|
196
244
|
return {
|
|
197
245
|
gainNode,
|
|
198
246
|
pannerNode,
|
|
199
|
-
modulationEffect,
|
|
200
247
|
reverbEffect,
|
|
201
248
|
chorusEffect,
|
|
202
249
|
};
|
|
@@ -209,15 +256,21 @@ class Midy {
|
|
|
209
256
|
...this.setChannelAudioNodes(audioContext),
|
|
210
257
|
scheduledNotes: new Map(),
|
|
211
258
|
sostenutoNotes: new Map(),
|
|
259
|
+
polyphonicKeyPressure: {
|
|
260
|
+
...Midy.controllerDestinationSettings,
|
|
261
|
+
},
|
|
262
|
+
channelPressure: {
|
|
263
|
+
...Midy.controllerDestinationSettings,
|
|
264
|
+
},
|
|
212
265
|
};
|
|
213
266
|
});
|
|
214
267
|
return channels;
|
|
215
268
|
}
|
|
216
|
-
async createNoteBuffer(
|
|
217
|
-
const sampleEnd =
|
|
269
|
+
async createNoteBuffer(instrumentKey, isSF3) {
|
|
270
|
+
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
218
271
|
if (isSF3) {
|
|
219
|
-
const sample = new Uint8Array(
|
|
220
|
-
sample.set(
|
|
272
|
+
const sample = new Uint8Array(instrumentKey.sample.length);
|
|
273
|
+
sample.set(instrumentKey.sample);
|
|
221
274
|
const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
|
|
222
275
|
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
|
|
223
276
|
const channelData = audioBuffer.getChannelData(channel);
|
|
@@ -226,26 +279,27 @@ class Midy {
|
|
|
226
279
|
return audioBuffer;
|
|
227
280
|
}
|
|
228
281
|
else {
|
|
229
|
-
const sample =
|
|
282
|
+
const sample = instrumentKey.sample.subarray(0, sampleEnd);
|
|
230
283
|
const floatSample = this.convertToFloat32Array(sample);
|
|
231
284
|
const audioBuffer = new AudioBuffer({
|
|
232
285
|
numberOfChannels: 1,
|
|
233
286
|
length: sample.length,
|
|
234
|
-
sampleRate:
|
|
287
|
+
sampleRate: instrumentKey.sampleRate,
|
|
235
288
|
});
|
|
236
289
|
const channelData = audioBuffer.getChannelData(0);
|
|
237
290
|
channelData.set(floatSample);
|
|
238
291
|
return audioBuffer;
|
|
239
292
|
}
|
|
240
293
|
}
|
|
241
|
-
async createNoteBufferNode(
|
|
294
|
+
async createNoteBufferNode(instrumentKey, isSF3) {
|
|
242
295
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
243
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
296
|
+
const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
|
|
244
297
|
bufferSource.buffer = audioBuffer;
|
|
245
|
-
bufferSource.loop =
|
|
298
|
+
bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
|
|
246
299
|
if (bufferSource.loop) {
|
|
247
|
-
bufferSource.loopStart =
|
|
248
|
-
|
|
300
|
+
bufferSource.loopStart = instrumentKey.loopStart /
|
|
301
|
+
instrumentKey.sampleRate;
|
|
302
|
+
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
249
303
|
}
|
|
250
304
|
return bufferSource;
|
|
251
305
|
}
|
|
@@ -263,9 +317,6 @@ class Midy {
|
|
|
263
317
|
if (event.startTime > t + this.lookAhead)
|
|
264
318
|
break;
|
|
265
319
|
switch (event.type) {
|
|
266
|
-
case "controller":
|
|
267
|
-
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
268
|
-
break;
|
|
269
320
|
case "noteOn":
|
|
270
321
|
if (event.velocity !== 0) {
|
|
271
322
|
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
@@ -279,9 +330,21 @@ class Midy {
|
|
|
279
330
|
}
|
|
280
331
|
break;
|
|
281
332
|
}
|
|
333
|
+
case "noteAftertouch":
|
|
334
|
+
this.handlePolyphonicKeyPressure(event.channel, event.noteNumber, event.amount);
|
|
335
|
+
break;
|
|
336
|
+
case "controller":
|
|
337
|
+
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
338
|
+
break;
|
|
282
339
|
case "programChange":
|
|
283
340
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
284
341
|
break;
|
|
342
|
+
case "channelAftertouch":
|
|
343
|
+
this.handleChannelPressure(event.channel, event.amount);
|
|
344
|
+
break;
|
|
345
|
+
case "pitchBend":
|
|
346
|
+
this.handlePitchBend(event.channel, event.value);
|
|
347
|
+
break;
|
|
285
348
|
case "sysEx":
|
|
286
349
|
this.handleSysEx(event.data);
|
|
287
350
|
}
|
|
@@ -518,30 +581,26 @@ class Midy {
|
|
|
518
581
|
const now = this.audioContext.currentTime;
|
|
519
582
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
520
583
|
}
|
|
521
|
-
getActiveNotes(channel) {
|
|
584
|
+
getActiveNotes(channel, time) {
|
|
522
585
|
const activeNotes = new Map();
|
|
523
|
-
channel.scheduledNotes.forEach((
|
|
524
|
-
const activeNote = this.
|
|
586
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
587
|
+
const activeNote = this.getActiveNote(noteList, time);
|
|
525
588
|
if (activeNote) {
|
|
526
589
|
activeNotes.set(activeNote.noteNumber, activeNote);
|
|
527
590
|
}
|
|
528
591
|
});
|
|
529
592
|
return activeNotes;
|
|
530
593
|
}
|
|
531
|
-
|
|
532
|
-
for (let i =
|
|
533
|
-
const
|
|
534
|
-
if (
|
|
535
|
-
return
|
|
594
|
+
getActiveNote(noteList, time) {
|
|
595
|
+
for (let i = noteList.length - 1; i >= 0; i--) {
|
|
596
|
+
const note = noteList[i];
|
|
597
|
+
if (!note)
|
|
598
|
+
return;
|
|
599
|
+
if (time < note.startTime)
|
|
600
|
+
continue;
|
|
601
|
+
return (note.ending) ? null : note;
|
|
536
602
|
}
|
|
537
|
-
|
|
538
|
-
createModulationEffect(audioContext) {
|
|
539
|
-
const lfo = new OscillatorNode(audioContext, {
|
|
540
|
-
frequency: 5,
|
|
541
|
-
});
|
|
542
|
-
return {
|
|
543
|
-
lfo,
|
|
544
|
-
};
|
|
603
|
+
return noteList[0];
|
|
545
604
|
}
|
|
546
605
|
createReverbEffect(audioContext, options = {}) {
|
|
547
606
|
const { decay = 0.8, preDecay = 0, } = options;
|
|
@@ -645,79 +704,116 @@ class Midy {
|
|
|
645
704
|
centToHz(cent) {
|
|
646
705
|
return 8.176 * Math.pow(2, cent / 1200);
|
|
647
706
|
}
|
|
648
|
-
|
|
707
|
+
calcSemitoneOffset(channel) {
|
|
649
708
|
const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
|
|
650
709
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
651
710
|
const tuning = masterTuning + channelTuning;
|
|
652
|
-
|
|
653
|
-
|
|
711
|
+
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
712
|
+
}
|
|
713
|
+
calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
|
|
714
|
+
return instrumentKey.playbackRate(noteNumber) *
|
|
654
715
|
Math.pow(2, semitoneOffset / 12);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
716
|
+
}
|
|
717
|
+
setVolumeEnvelope(channel, note) {
|
|
718
|
+
const { instrumentKey, startTime, velocity } = note;
|
|
719
|
+
note.gainNode = new GainNode(this.audioContext, {
|
|
659
720
|
gain: 0,
|
|
660
721
|
});
|
|
661
722
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
662
723
|
if (volume === 0)
|
|
663
724
|
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
664
|
-
const attackVolume = this.cbToRatio(-
|
|
665
|
-
|
|
666
|
-
const
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
const
|
|
670
|
-
|
|
725
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
726
|
+
volume;
|
|
727
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
728
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
729
|
+
const volAttack = volDelay + instrumentKey.volAttack;
|
|
730
|
+
const volHold = volAttack + instrumentKey.volHold;
|
|
731
|
+
const volDecay = volHold + instrumentKey.volDecay;
|
|
732
|
+
note.gainNode.gain
|
|
671
733
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
672
734
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
673
735
|
.setValueAtTime(attackVolume, volHold)
|
|
674
736
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
675
|
-
|
|
737
|
+
}
|
|
738
|
+
setFilterEnvelope(channel, note) {
|
|
739
|
+
const { instrumentKey, startTime, noteNumber } = note;
|
|
676
740
|
const softPedalFactor = 1 -
|
|
677
741
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
678
742
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
679
|
-
const baseFreq = this.centToHz(
|
|
680
|
-
|
|
743
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
744
|
+
softPedalFactor;
|
|
745
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
681
746
|
const sustainFreq = (baseFreq +
|
|
682
|
-
(peekFreq - baseFreq) * (1 -
|
|
747
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
|
|
748
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
749
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
750
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
751
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
683
752
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
684
753
|
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
685
754
|
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
686
|
-
|
|
755
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
687
756
|
type: "lowpass",
|
|
688
|
-
Q:
|
|
757
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
689
758
|
frequency: adjustedBaseFreq,
|
|
690
759
|
});
|
|
691
|
-
|
|
692
|
-
const modAttack = modDelay + noteInfo.modAttack;
|
|
693
|
-
const modHold = modAttack + noteInfo.modHold;
|
|
694
|
-
const modDecay = modHold + noteInfo.modDecay;
|
|
695
|
-
filterNode.frequency
|
|
760
|
+
note.filterNode.frequency
|
|
696
761
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
697
762
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
698
763
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
699
764
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
700
|
-
|
|
765
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
766
|
+
}
|
|
767
|
+
startModulation(channel, note, time) {
|
|
768
|
+
const { instrumentKey } = note;
|
|
769
|
+
note.modLFOGain = new GainNode(this.audioContext, {
|
|
770
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
|
|
771
|
+
});
|
|
772
|
+
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
773
|
+
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
774
|
+
});
|
|
775
|
+
note.modLFO.start(time);
|
|
776
|
+
note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
|
|
777
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
|
|
778
|
+
note.modLFO.connect(note.modLFOGain);
|
|
779
|
+
note.modLFOGain.connect(note.bufferSource.detune);
|
|
780
|
+
}
|
|
781
|
+
startVibrato(channel, note, time) {
|
|
782
|
+
const { instrumentKey } = note;
|
|
783
|
+
note.vibLFOGain = new GainNode(this.audioContext, {
|
|
784
|
+
gain: channel.vibratoDepth,
|
|
785
|
+
});
|
|
786
|
+
note.vibLFO = new OscillatorNode(this.audioContext, {
|
|
787
|
+
frequency: this.centToHz(instrumentKey.freqModLFO) +
|
|
788
|
+
channel.vibratoRate,
|
|
789
|
+
});
|
|
790
|
+
note.vibLFO.start(time + channel.vibratoDelay);
|
|
791
|
+
note.vibLFO.connect(note.vibLFOGain);
|
|
792
|
+
note.vibLFOGain.connect(note.bufferSource.detune);
|
|
793
|
+
}
|
|
794
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
795
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
796
|
+
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
797
|
+
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
798
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
799
|
+
this.setVolumeEnvelope(channel, note);
|
|
800
|
+
this.setFilterEnvelope(channel, note);
|
|
701
801
|
if (channel.modulation > 0) {
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
709
|
-
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
710
|
-
channel.modulationEffect.lfo.connect(lfoGain);
|
|
711
|
-
lfoGain.connect(bufferSource.detune);
|
|
802
|
+
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
803
|
+
this.startModulation(channel, note, delayModLFO);
|
|
804
|
+
}
|
|
805
|
+
if (channel.vibratoDepth > 0) {
|
|
806
|
+
const delayVibLFO = startTime + instrumentKey.delayVibLFO;
|
|
807
|
+
this.startVibrato(channel, note, delayVibLFO);
|
|
712
808
|
}
|
|
713
|
-
bufferSource.connect(filterNode);
|
|
714
|
-
filterNode.connect(gainNode);
|
|
715
809
|
if (this.mono && channel.currentBufferSource) {
|
|
716
810
|
channel.currentBufferSource.stop(startTime);
|
|
717
|
-
channel.currentBufferSource = bufferSource;
|
|
811
|
+
channel.currentBufferSource = note.bufferSource;
|
|
718
812
|
}
|
|
719
|
-
bufferSource.
|
|
720
|
-
|
|
813
|
+
note.bufferSource.connect(note.filterNode);
|
|
814
|
+
note.filterNode.connect(note.gainNode);
|
|
815
|
+
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
816
|
+
return note;
|
|
721
817
|
}
|
|
722
818
|
calcBank(channel, channelNumber) {
|
|
723
819
|
if (channel.bankMSB === 121) {
|
|
@@ -736,36 +832,20 @@ class Midy {
|
|
|
736
832
|
return;
|
|
737
833
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
738
834
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
739
|
-
const
|
|
740
|
-
if (!
|
|
835
|
+
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
836
|
+
if (!instrumentKey)
|
|
741
837
|
return;
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
this.connectNoteEffects(channel, gainNode);
|
|
838
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
839
|
+
this.connectNoteEffects(channel, note.gainNode);
|
|
745
840
|
if (channel.sostenutoPedal) {
|
|
746
|
-
channel.sostenutoNotes.set(noteNumber,
|
|
747
|
-
gainNode,
|
|
748
|
-
filterNode,
|
|
749
|
-
bufferSource,
|
|
750
|
-
noteNumber,
|
|
751
|
-
noteInfo,
|
|
752
|
-
});
|
|
841
|
+
channel.sostenutoNotes.set(noteNumber, note);
|
|
753
842
|
}
|
|
754
843
|
const scheduledNotes = channel.scheduledNotes;
|
|
755
|
-
const scheduledNote = {
|
|
756
|
-
bufferSource,
|
|
757
|
-
filterNode,
|
|
758
|
-
gainNode,
|
|
759
|
-
lfoGain,
|
|
760
|
-
noteInfo,
|
|
761
|
-
noteNumber,
|
|
762
|
-
startTime,
|
|
763
|
-
};
|
|
764
844
|
if (scheduledNotes.has(noteNumber)) {
|
|
765
|
-
scheduledNotes.get(noteNumber).push(
|
|
845
|
+
scheduledNotes.get(noteNumber).push(note);
|
|
766
846
|
}
|
|
767
847
|
else {
|
|
768
|
-
scheduledNotes.set(noteNumber, [
|
|
848
|
+
scheduledNotes.set(noteNumber, [note]);
|
|
769
849
|
}
|
|
770
850
|
}
|
|
771
851
|
noteOn(channelNumber, noteNumber, velocity) {
|
|
@@ -787,15 +867,15 @@ class Midy {
|
|
|
787
867
|
continue;
|
|
788
868
|
if (targetNote.ending)
|
|
789
869
|
continue;
|
|
790
|
-
const { bufferSource, filterNode, gainNode,
|
|
870
|
+
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, vibLFO, vibLFOGain, instrumentKey, } = targetNote;
|
|
791
871
|
const velocityRate = (velocity + 127) / 127;
|
|
792
|
-
const volEndTime = stopTime +
|
|
872
|
+
const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
|
|
793
873
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
794
874
|
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
795
875
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
796
|
-
const baseFreq = this.centToHz(
|
|
876
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
797
877
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
798
|
-
const modEndTime = stopTime +
|
|
878
|
+
const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
|
|
799
879
|
filterNode.frequency
|
|
800
880
|
.cancelScheduledValues(stopTime)
|
|
801
881
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
@@ -809,8 +889,14 @@ class Midy {
|
|
|
809
889
|
bufferSource.disconnect(0);
|
|
810
890
|
filterNode.disconnect(0);
|
|
811
891
|
gainNode.disconnect(0);
|
|
812
|
-
if (
|
|
813
|
-
|
|
892
|
+
if (modLFOGain)
|
|
893
|
+
modLFOGain.disconnect(0);
|
|
894
|
+
if (vibLFOGain)
|
|
895
|
+
vibLFOGain.disconnect(0);
|
|
896
|
+
if (modLFO)
|
|
897
|
+
modLFO.stop();
|
|
898
|
+
if (vibLFO)
|
|
899
|
+
vibLFO.stop();
|
|
814
900
|
resolve();
|
|
815
901
|
};
|
|
816
902
|
bufferSource.stop(volEndTime);
|
|
@@ -859,7 +945,7 @@ class Midy {
|
|
|
859
945
|
case 0x90:
|
|
860
946
|
return this.noteOn(channelNumber, data1, data2);
|
|
861
947
|
case 0xA0:
|
|
862
|
-
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
948
|
+
return; // this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
863
949
|
case 0xB0:
|
|
864
950
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
865
951
|
case 0xC0:
|
|
@@ -867,7 +953,7 @@ class Midy {
|
|
|
867
953
|
case 0xD0:
|
|
868
954
|
return this.handleChannelPressure(channelNumber, data1);
|
|
869
955
|
case 0xE0:
|
|
870
|
-
return this.
|
|
956
|
+
return this.handlePitchBendMessage(channelNumber, data1, data2);
|
|
871
957
|
default:
|
|
872
958
|
console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
|
|
873
959
|
}
|
|
@@ -875,17 +961,16 @@ class Midy {
|
|
|
875
961
|
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
876
962
|
const now = this.audioContext.currentTime;
|
|
877
963
|
const channel = this.channels[channelNumber];
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
});
|
|
964
|
+
pressure /= 64;
|
|
965
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
966
|
+
if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
|
|
967
|
+
if (activeNotes.has(noteNumber)) {
|
|
968
|
+
const activeNote = activeNotes.get(noteNumber);
|
|
969
|
+
const gain = activeNote.gainNode.gain.value;
|
|
970
|
+
activeNote.gainNode.gain
|
|
971
|
+
.cancelScheduledValues(now)
|
|
972
|
+
.setValueAtTime(gain * pressure, now);
|
|
973
|
+
}
|
|
889
974
|
}
|
|
890
975
|
}
|
|
891
976
|
handleProgramChange(channelNumber, program) {
|
|
@@ -894,11 +979,37 @@ class Midy {
|
|
|
894
979
|
channel.program = program;
|
|
895
980
|
}
|
|
896
981
|
handleChannelPressure(channelNumber, pressure) {
|
|
897
|
-
this.
|
|
982
|
+
const now = this.audioContext.currentTime;
|
|
983
|
+
const channel = this.channels[channelNumber];
|
|
984
|
+
pressure /= 64;
|
|
985
|
+
channel.channelPressure = pressure;
|
|
986
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
987
|
+
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
988
|
+
activeNotes.forEach((activeNote) => {
|
|
989
|
+
const gain = activeNote.gainNode.gain.value;
|
|
990
|
+
activeNote.gainNode.gain
|
|
991
|
+
.cancelScheduledValues(now)
|
|
992
|
+
.setValueAtTime(gain * pressure, now);
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
997
|
+
const pitchBend = msb * 128 + lsb;
|
|
998
|
+
this.handlePitchBend(channelNumber, pitchBend);
|
|
898
999
|
}
|
|
899
|
-
handlePitchBend(channelNumber,
|
|
900
|
-
const
|
|
901
|
-
this.channels[channelNumber]
|
|
1000
|
+
handlePitchBend(channelNumber, pitchBend) {
|
|
1001
|
+
const now = this.audioContext.currentTime;
|
|
1002
|
+
const channel = this.channels[channelNumber];
|
|
1003
|
+
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
1004
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
1005
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1006
|
+
activeNotes.forEach((activeNote) => {
|
|
1007
|
+
const { bufferSource, instrumentKey, noteNumber } = activeNote;
|
|
1008
|
+
const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
1009
|
+
bufferSource.playbackRate
|
|
1010
|
+
.cancelScheduledValues(now)
|
|
1011
|
+
.setValueAtTime(playbackRate * pressure, now);
|
|
1012
|
+
});
|
|
902
1013
|
}
|
|
903
1014
|
handleControlChange(channelNumber, controller, value) {
|
|
904
1015
|
switch (controller) {
|
|
@@ -969,9 +1080,20 @@ class Midy {
|
|
|
969
1080
|
this.channels[channelNumber].bankMSB = msb;
|
|
970
1081
|
}
|
|
971
1082
|
setModulation(channelNumber, modulation) {
|
|
1083
|
+
const now = this.audioContext.currentTime;
|
|
972
1084
|
const channel = this.channels[channelNumber];
|
|
973
1085
|
channel.modulation = (modulation / 127) *
|
|
974
1086
|
(channel.modulationDepthRange * 100);
|
|
1087
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1088
|
+
activeNotes.forEach((activeNote) => {
|
|
1089
|
+
if (activeNote.modLFO) {
|
|
1090
|
+
activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
|
|
1091
|
+
channel.modulation, now);
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
this.startModulation(channel, activeNote, now);
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
975
1097
|
}
|
|
976
1098
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
977
1099
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1032,7 +1154,8 @@ class Midy {
|
|
|
1032
1154
|
const channel = this.channels[channelNumber];
|
|
1033
1155
|
channel.sostenutoPedal = isOn;
|
|
1034
1156
|
if (isOn) {
|
|
1035
|
-
const
|
|
1157
|
+
const now = this.audioContext.currentTime;
|
|
1158
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1036
1159
|
channel.sostenutoNotes = new Map(activeNotes);
|
|
1037
1160
|
}
|
|
1038
1161
|
else {
|
|
@@ -1044,20 +1167,12 @@ class Midy {
|
|
|
1044
1167
|
channel.softPedal = softPedal / 127;
|
|
1045
1168
|
}
|
|
1046
1169
|
setVibratoRate(channelNumber, vibratoRate) {
|
|
1047
|
-
const now = this.audioContext.currentTime;
|
|
1048
1170
|
const channel = this.channels[channelNumber];
|
|
1049
1171
|
channel.vibratoRate = vibratoRate / 127 * 4 + 3; // 3-7Hz
|
|
1050
|
-
channel.modulationEffect.lfo.frequency
|
|
1051
|
-
.cancelScheduledValues(now)
|
|
1052
|
-
.setValueAtTime(channel.vibratoRate, now);
|
|
1053
1172
|
}
|
|
1054
1173
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1055
|
-
const now = this.audioContext.currentTime;
|
|
1056
1174
|
const channel = this.channels[channelNumber];
|
|
1057
1175
|
channel.vibratoDepth = vibratoDepth / 127;
|
|
1058
|
-
channel.modulationEffect.lfoGain.gain
|
|
1059
|
-
.cancelScheduledValues(now)
|
|
1060
|
-
.setValueAtTime(channel.vibratoDepth, now);
|
|
1061
1176
|
}
|
|
1062
1177
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1063
1178
|
// Access Virus: 0-10sec
|
|
@@ -1138,8 +1253,8 @@ class Midy {
|
|
|
1138
1253
|
const velocity = 0;
|
|
1139
1254
|
const stopPedal = true;
|
|
1140
1255
|
const promises = [];
|
|
1141
|
-
channel.scheduledNotes.forEach((
|
|
1142
|
-
const activeNote = this.
|
|
1256
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1257
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1143
1258
|
if (activeNote) {
|
|
1144
1259
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1145
1260
|
promises.push(notePromise);
|
|
@@ -1156,8 +1271,8 @@ class Midy {
|
|
|
1156
1271
|
const velocity = 0;
|
|
1157
1272
|
const stopPedal = false;
|
|
1158
1273
|
const promises = [];
|
|
1159
|
-
channel.scheduledNotes.forEach((
|
|
1160
|
-
const activeNote = this.
|
|
1274
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1275
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1161
1276
|
if (activeNote) {
|
|
1162
1277
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1163
1278
|
promises.push(notePromise);
|
|
@@ -1243,10 +1358,10 @@ class Midy {
|
|
|
1243
1358
|
switch (data[3]) {
|
|
1244
1359
|
// case 1:
|
|
1245
1360
|
// // TODO
|
|
1246
|
-
// return this.
|
|
1361
|
+
// return this.setChannelPressure();
|
|
1247
1362
|
// case 3:
|
|
1248
1363
|
// // TODO
|
|
1249
|
-
// return this.
|
|
1364
|
+
// return this.setControlChange();
|
|
1250
1365
|
default:
|
|
1251
1366
|
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1252
1367
|
}
|
|
@@ -1265,20 +1380,25 @@ class Midy {
|
|
|
1265
1380
|
}
|
|
1266
1381
|
}
|
|
1267
1382
|
handleMasterVolumeSysEx(data) {
|
|
1268
|
-
const volume = (data[5] * 128 + data[4]
|
|
1383
|
+
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
1269
1384
|
this.handleMasterVolume(volume);
|
|
1270
1385
|
}
|
|
1271
1386
|
handleMasterVolume(volume) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1387
|
+
if (volume < 0 && 1 < volume) {
|
|
1388
|
+
console.error("Master Volume is out of range");
|
|
1389
|
+
}
|
|
1390
|
+
else {
|
|
1391
|
+
const now = this.audioContext.currentTime;
|
|
1392
|
+
this.masterGain.gain.cancelScheduledValues(now);
|
|
1393
|
+
this.masterGain.gain.setValueAtTime(volume * volume, now);
|
|
1394
|
+
}
|
|
1275
1395
|
}
|
|
1276
1396
|
handleMasterFineTuningSysEx(data) {
|
|
1277
1397
|
const fineTuning = (data[5] * 128 + data[4] - 8192) / 8192;
|
|
1278
1398
|
this.handleMasterFineTuning(fineTuning);
|
|
1279
1399
|
}
|
|
1280
1400
|
handleMasterFineTuning(fineTuning) {
|
|
1281
|
-
if (fineTuning <
|
|
1401
|
+
if (fineTuning < -1 && 1 < fineTuning) {
|
|
1282
1402
|
console.error("Master Fine Tuning value is out of range");
|
|
1283
1403
|
}
|
|
1284
1404
|
else {
|
|
@@ -1366,3 +1486,16 @@ Object.defineProperty(Midy, "effectSettings", {
|
|
|
1366
1486
|
pitchBendRange: 2,
|
|
1367
1487
|
}
|
|
1368
1488
|
});
|
|
1489
|
+
Object.defineProperty(Midy, "controllerDestinationSettings", {
|
|
1490
|
+
enumerable: true,
|
|
1491
|
+
configurable: true,
|
|
1492
|
+
writable: true,
|
|
1493
|
+
value: {
|
|
1494
|
+
pitchControl: 0,
|
|
1495
|
+
filterCutoffControl: 0,
|
|
1496
|
+
amplitudeControl: 1,
|
|
1497
|
+
lfoPitchDepth: 0,
|
|
1498
|
+
lfoFilterDepth: 0,
|
|
1499
|
+
lfoAmplitudeDepth: 0,
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"+esm.d.ts","sourceRoot":"","sources":["../../../../../../src/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.js"],"names":[],"mappings":";;AAMszX;IAAQ,oBAA2H;IAAd,YAAa;IAAC,qDAAoJ;IAAA,mCAAoP;IAAA,uCAAwQ;IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAAi/D;IAAA,qBAA0I;CAAC;AAAA,mCAAwC;AAAl+H,+BAA8F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAA98D;;;;;;;;;;;;;;;;EAA27B;AAAr4I;IAAqQ,wBAAwD;IAArT,4BAAyN;IAApB,QAAS;IAAC,QAAS;IAAC,oBAAoC;CAAyD;AAAllG;IAAwjC,gCAA8d;CAAC"}
|