@marmooo/midy 0.0.4 → 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 +24 -34
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +167 -106
- package/esm/midy-GM2.d.ts +123 -21
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +170 -116
- package/esm/midy-GMLite.d.ts +23 -35
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +156 -107
- package/esm/midy.d.ts +25 -23
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +191 -120
- 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 +24 -34
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +167 -106
- package/script/midy-GM2.d.ts +123 -21
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +170 -116
- package/script/midy-GMLite.d.ts +23 -35
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +156 -107
- package/script/midy.d.ts +25 -23
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +191 -120
- 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/esm/midy.js
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
import { parseMidi } from "./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js";
|
|
2
|
-
import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.
|
|
2
|
+
import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js";
|
|
3
|
+
class Note {
|
|
4
|
+
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
5
|
+
Object.defineProperty(this, "bufferSource", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "gainNode", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "filterNode", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "modLFO", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: void 0
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "modLFOGain", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "vibLFO", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: void 0
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(this, "vibLFOGain", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: void 0
|
|
46
|
+
});
|
|
47
|
+
this.noteNumber = noteNumber;
|
|
48
|
+
this.velocity = velocity;
|
|
49
|
+
this.startTime = startTime;
|
|
50
|
+
this.instrumentKey = instrumentKey;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
3
53
|
export class Midy {
|
|
4
54
|
constructor(audioContext) {
|
|
5
55
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
@@ -181,10 +231,8 @@ export class Midy {
|
|
|
181
231
|
const pannerNode = new StereoPannerNode(audioContext, {
|
|
182
232
|
pan: Midy.channelSettings.pan,
|
|
183
233
|
});
|
|
184
|
-
const modulationEffect = this.createModulationEffect(audioContext);
|
|
185
234
|
const reverbEffect = this.createReverbEffect(audioContext);
|
|
186
235
|
const chorusEffect = this.createChorusEffect(audioContext);
|
|
187
|
-
modulationEffect.lfo.start();
|
|
188
236
|
chorusEffect.lfo.start();
|
|
189
237
|
reverbEffect.dryGain.connect(pannerNode);
|
|
190
238
|
reverbEffect.wetGain.connect(pannerNode);
|
|
@@ -193,7 +241,6 @@ export class Midy {
|
|
|
193
241
|
return {
|
|
194
242
|
gainNode,
|
|
195
243
|
pannerNode,
|
|
196
|
-
modulationEffect,
|
|
197
244
|
reverbEffect,
|
|
198
245
|
chorusEffect,
|
|
199
246
|
};
|
|
@@ -216,11 +263,11 @@ export class Midy {
|
|
|
216
263
|
});
|
|
217
264
|
return channels;
|
|
218
265
|
}
|
|
219
|
-
async createNoteBuffer(
|
|
220
|
-
const sampleEnd =
|
|
266
|
+
async createNoteBuffer(instrumentKey, isSF3) {
|
|
267
|
+
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
221
268
|
if (isSF3) {
|
|
222
|
-
const sample = new Uint8Array(
|
|
223
|
-
sample.set(
|
|
269
|
+
const sample = new Uint8Array(instrumentKey.sample.length);
|
|
270
|
+
sample.set(instrumentKey.sample);
|
|
224
271
|
const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
|
|
225
272
|
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
|
|
226
273
|
const channelData = audioBuffer.getChannelData(channel);
|
|
@@ -229,26 +276,27 @@ export class Midy {
|
|
|
229
276
|
return audioBuffer;
|
|
230
277
|
}
|
|
231
278
|
else {
|
|
232
|
-
const sample =
|
|
279
|
+
const sample = instrumentKey.sample.subarray(0, sampleEnd);
|
|
233
280
|
const floatSample = this.convertToFloat32Array(sample);
|
|
234
281
|
const audioBuffer = new AudioBuffer({
|
|
235
282
|
numberOfChannels: 1,
|
|
236
283
|
length: sample.length,
|
|
237
|
-
sampleRate:
|
|
284
|
+
sampleRate: instrumentKey.sampleRate,
|
|
238
285
|
});
|
|
239
286
|
const channelData = audioBuffer.getChannelData(0);
|
|
240
287
|
channelData.set(floatSample);
|
|
241
288
|
return audioBuffer;
|
|
242
289
|
}
|
|
243
290
|
}
|
|
244
|
-
async createNoteBufferNode(
|
|
291
|
+
async createNoteBufferNode(instrumentKey, isSF3) {
|
|
245
292
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
246
|
-
const audioBuffer = await this.createNoteBuffer(
|
|
293
|
+
const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
|
|
247
294
|
bufferSource.buffer = audioBuffer;
|
|
248
|
-
bufferSource.loop =
|
|
295
|
+
bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
|
|
249
296
|
if (bufferSource.loop) {
|
|
250
|
-
bufferSource.loopStart =
|
|
251
|
-
|
|
297
|
+
bufferSource.loopStart = instrumentKey.loopStart /
|
|
298
|
+
instrumentKey.sampleRate;
|
|
299
|
+
bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
|
|
252
300
|
}
|
|
253
301
|
return bufferSource;
|
|
254
302
|
}
|
|
@@ -530,30 +578,26 @@ export class Midy {
|
|
|
530
578
|
const now = this.audioContext.currentTime;
|
|
531
579
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
532
580
|
}
|
|
533
|
-
getActiveNotes(channel) {
|
|
581
|
+
getActiveNotes(channel, time) {
|
|
534
582
|
const activeNotes = new Map();
|
|
535
|
-
channel.scheduledNotes.forEach((
|
|
536
|
-
const activeNote = this.
|
|
583
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
584
|
+
const activeNote = this.getActiveNote(noteList, time);
|
|
537
585
|
if (activeNote) {
|
|
538
586
|
activeNotes.set(activeNote.noteNumber, activeNote);
|
|
539
587
|
}
|
|
540
588
|
});
|
|
541
589
|
return activeNotes;
|
|
542
590
|
}
|
|
543
|
-
|
|
544
|
-
for (let i =
|
|
545
|
-
const
|
|
546
|
-
if (
|
|
547
|
-
return
|
|
591
|
+
getActiveNote(noteList, time) {
|
|
592
|
+
for (let i = noteList.length - 1; i >= 0; i--) {
|
|
593
|
+
const note = noteList[i];
|
|
594
|
+
if (!note)
|
|
595
|
+
return;
|
|
596
|
+
if (time < note.startTime)
|
|
597
|
+
continue;
|
|
598
|
+
return (note.ending) ? null : note;
|
|
548
599
|
}
|
|
549
|
-
|
|
550
|
-
createModulationEffect(audioContext) {
|
|
551
|
-
const lfo = new OscillatorNode(audioContext, {
|
|
552
|
-
frequency: 5,
|
|
553
|
-
});
|
|
554
|
-
return {
|
|
555
|
-
lfo,
|
|
556
|
-
};
|
|
600
|
+
return noteList[0];
|
|
557
601
|
}
|
|
558
602
|
createReverbEffect(audioContext, options = {}) {
|
|
559
603
|
const { decay = 0.8, preDecay = 0, } = options;
|
|
@@ -663,77 +707,110 @@ export class Midy {
|
|
|
663
707
|
const tuning = masterTuning + channelTuning;
|
|
664
708
|
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
665
709
|
}
|
|
666
|
-
calcPlaybackRate(
|
|
667
|
-
return
|
|
710
|
+
calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
|
|
711
|
+
return instrumentKey.playbackRate(noteNumber) *
|
|
712
|
+
Math.pow(2, semitoneOffset / 12);
|
|
668
713
|
}
|
|
669
|
-
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
673
|
-
// volume envelope
|
|
674
|
-
const gainNode = new GainNode(this.audioContext, {
|
|
714
|
+
setVolumeEnvelope(channel, note) {
|
|
715
|
+
const { instrumentKey, startTime, velocity } = note;
|
|
716
|
+
note.gainNode = new GainNode(this.audioContext, {
|
|
675
717
|
gain: 0,
|
|
676
718
|
});
|
|
677
719
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
678
720
|
if (volume === 0)
|
|
679
721
|
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
680
|
-
const attackVolume = this.cbToRatio(-
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
const
|
|
684
|
-
const
|
|
685
|
-
const
|
|
686
|
-
|
|
722
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
723
|
+
volume;
|
|
724
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
725
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
726
|
+
const volAttack = volDelay + instrumentKey.volAttack;
|
|
727
|
+
const volHold = volAttack + instrumentKey.volHold;
|
|
728
|
+
const volDecay = volHold + instrumentKey.volDecay;
|
|
729
|
+
note.gainNode.gain
|
|
687
730
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
688
731
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
689
732
|
.setValueAtTime(attackVolume, volHold)
|
|
690
733
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
691
|
-
|
|
734
|
+
}
|
|
735
|
+
setFilterEnvelope(channel, note) {
|
|
736
|
+
const { instrumentKey, startTime, noteNumber } = note;
|
|
692
737
|
const softPedalFactor = 1 -
|
|
693
738
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
694
739
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
695
|
-
const baseFreq = this.centToHz(
|
|
696
|
-
|
|
740
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
741
|
+
softPedalFactor;
|
|
742
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
697
743
|
const sustainFreq = (baseFreq +
|
|
698
|
-
(peekFreq - baseFreq) * (1 -
|
|
744
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
|
|
745
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
746
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
747
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
748
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
699
749
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
700
750
|
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
701
751
|
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
702
|
-
|
|
752
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
703
753
|
type: "lowpass",
|
|
704
|
-
Q:
|
|
754
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
705
755
|
frequency: adjustedBaseFreq,
|
|
706
756
|
});
|
|
707
|
-
|
|
708
|
-
const modAttack = modDelay + noteInfo.modAttack;
|
|
709
|
-
const modHold = modAttack + noteInfo.modHold;
|
|
710
|
-
const modDecay = modHold + noteInfo.modDecay;
|
|
711
|
-
filterNode.frequency
|
|
757
|
+
note.filterNode.frequency
|
|
712
758
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
713
759
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
714
760
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
715
761
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
716
|
-
|
|
762
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
763
|
+
}
|
|
764
|
+
startModulation(channel, note, time) {
|
|
765
|
+
const { instrumentKey } = note;
|
|
766
|
+
note.modLFOGain = new GainNode(this.audioContext, {
|
|
767
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
|
|
768
|
+
});
|
|
769
|
+
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
770
|
+
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
771
|
+
});
|
|
772
|
+
note.modLFO.start(time);
|
|
773
|
+
note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
|
|
774
|
+
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
|
|
775
|
+
note.modLFO.connect(note.modLFOGain);
|
|
776
|
+
note.modLFOGain.connect(note.bufferSource.detune);
|
|
777
|
+
}
|
|
778
|
+
startVibrato(channel, note, time) {
|
|
779
|
+
const { instrumentKey } = note;
|
|
780
|
+
note.vibLFOGain = new GainNode(this.audioContext, {
|
|
781
|
+
gain: channel.vibratoDepth,
|
|
782
|
+
});
|
|
783
|
+
note.vibLFO = new OscillatorNode(this.audioContext, {
|
|
784
|
+
frequency: this.centToHz(instrumentKey.freqModLFO) +
|
|
785
|
+
channel.vibratoRate,
|
|
786
|
+
});
|
|
787
|
+
note.vibLFO.start(time + channel.vibratoDelay);
|
|
788
|
+
note.vibLFO.connect(note.vibLFOGain);
|
|
789
|
+
note.vibLFOGain.connect(note.bufferSource.detune);
|
|
790
|
+
}
|
|
791
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
792
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
793
|
+
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
794
|
+
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
795
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
796
|
+
this.setVolumeEnvelope(channel, note);
|
|
797
|
+
this.setFilterEnvelope(channel, note);
|
|
717
798
|
if (channel.modulation > 0) {
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
725
|
-
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
726
|
-
channel.modulationEffect.lfo.connect(lfoGain);
|
|
727
|
-
lfoGain.connect(bufferSource.detune);
|
|
799
|
+
const delayModLFO = startTime + instrumentKey.delayModLFO;
|
|
800
|
+
this.startModulation(channel, note, delayModLFO);
|
|
801
|
+
}
|
|
802
|
+
if (channel.vibratoDepth > 0) {
|
|
803
|
+
const delayVibLFO = startTime + instrumentKey.delayVibLFO;
|
|
804
|
+
this.startVibrato(channel, note, delayVibLFO);
|
|
728
805
|
}
|
|
729
|
-
bufferSource.connect(filterNode);
|
|
730
|
-
filterNode.connect(gainNode);
|
|
731
806
|
if (this.mono && channel.currentBufferSource) {
|
|
732
807
|
channel.currentBufferSource.stop(startTime);
|
|
733
|
-
channel.currentBufferSource = bufferSource;
|
|
808
|
+
channel.currentBufferSource = note.bufferSource;
|
|
734
809
|
}
|
|
735
|
-
bufferSource.
|
|
736
|
-
|
|
810
|
+
note.bufferSource.connect(note.filterNode);
|
|
811
|
+
note.filterNode.connect(note.gainNode);
|
|
812
|
+
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
813
|
+
return note;
|
|
737
814
|
}
|
|
738
815
|
calcBank(channel, channelNumber) {
|
|
739
816
|
if (channel.bankMSB === 121) {
|
|
@@ -752,36 +829,20 @@ export class Midy {
|
|
|
752
829
|
return;
|
|
753
830
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
754
831
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
755
|
-
const
|
|
756
|
-
if (!
|
|
832
|
+
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
833
|
+
if (!instrumentKey)
|
|
757
834
|
return;
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
this.connectNoteEffects(channel, gainNode);
|
|
835
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
836
|
+
this.connectNoteEffects(channel, note.gainNode);
|
|
761
837
|
if (channel.sostenutoPedal) {
|
|
762
|
-
channel.sostenutoNotes.set(noteNumber,
|
|
763
|
-
gainNode,
|
|
764
|
-
filterNode,
|
|
765
|
-
bufferSource,
|
|
766
|
-
noteNumber,
|
|
767
|
-
noteInfo,
|
|
768
|
-
});
|
|
838
|
+
channel.sostenutoNotes.set(noteNumber, note);
|
|
769
839
|
}
|
|
770
840
|
const scheduledNotes = channel.scheduledNotes;
|
|
771
|
-
const scheduledNote = {
|
|
772
|
-
bufferSource,
|
|
773
|
-
filterNode,
|
|
774
|
-
gainNode,
|
|
775
|
-
lfoGain,
|
|
776
|
-
noteInfo,
|
|
777
|
-
noteNumber,
|
|
778
|
-
startTime,
|
|
779
|
-
};
|
|
780
841
|
if (scheduledNotes.has(noteNumber)) {
|
|
781
|
-
scheduledNotes.get(noteNumber).push(
|
|
842
|
+
scheduledNotes.get(noteNumber).push(note);
|
|
782
843
|
}
|
|
783
844
|
else {
|
|
784
|
-
scheduledNotes.set(noteNumber, [
|
|
845
|
+
scheduledNotes.set(noteNumber, [note]);
|
|
785
846
|
}
|
|
786
847
|
}
|
|
787
848
|
noteOn(channelNumber, noteNumber, velocity) {
|
|
@@ -803,15 +864,15 @@ export class Midy {
|
|
|
803
864
|
continue;
|
|
804
865
|
if (targetNote.ending)
|
|
805
866
|
continue;
|
|
806
|
-
const { bufferSource, filterNode, gainNode,
|
|
867
|
+
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, vibLFO, vibLFOGain, instrumentKey, } = targetNote;
|
|
807
868
|
const velocityRate = (velocity + 127) / 127;
|
|
808
|
-
const volEndTime = stopTime +
|
|
869
|
+
const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
|
|
809
870
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
810
871
|
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
811
872
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
812
|
-
const baseFreq = this.centToHz(
|
|
873
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
813
874
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
814
|
-
const modEndTime = stopTime +
|
|
875
|
+
const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
|
|
815
876
|
filterNode.frequency
|
|
816
877
|
.cancelScheduledValues(stopTime)
|
|
817
878
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
@@ -825,8 +886,14 @@ export class Midy {
|
|
|
825
886
|
bufferSource.disconnect(0);
|
|
826
887
|
filterNode.disconnect(0);
|
|
827
888
|
gainNode.disconnect(0);
|
|
828
|
-
if (
|
|
829
|
-
|
|
889
|
+
if (modLFOGain)
|
|
890
|
+
modLFOGain.disconnect(0);
|
|
891
|
+
if (vibLFOGain)
|
|
892
|
+
vibLFOGain.disconnect(0);
|
|
893
|
+
if (modLFO)
|
|
894
|
+
modLFO.stop();
|
|
895
|
+
if (vibLFO)
|
|
896
|
+
vibLFO.stop();
|
|
830
897
|
resolve();
|
|
831
898
|
};
|
|
832
899
|
bufferSource.stop(volEndTime);
|
|
@@ -892,7 +959,7 @@ export class Midy {
|
|
|
892
959
|
const now = this.audioContext.currentTime;
|
|
893
960
|
const channel = this.channels[channelNumber];
|
|
894
961
|
pressure /= 64;
|
|
895
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
962
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
896
963
|
if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
|
|
897
964
|
if (activeNotes.has(noteNumber)) {
|
|
898
965
|
const activeNote = activeNotes.get(noteNumber);
|
|
@@ -913,7 +980,7 @@ export class Midy {
|
|
|
913
980
|
const channel = this.channels[channelNumber];
|
|
914
981
|
pressure /= 64;
|
|
915
982
|
channel.channelPressure = pressure;
|
|
916
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
983
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
917
984
|
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
918
985
|
activeNotes.forEach((activeNote) => {
|
|
919
986
|
const gain = activeNote.gainNode.gain.value;
|
|
@@ -932,10 +999,10 @@ export class Midy {
|
|
|
932
999
|
const channel = this.channels[channelNumber];
|
|
933
1000
|
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
934
1001
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
935
|
-
const activeNotes = this.getActiveNotes(channel);
|
|
1002
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
936
1003
|
activeNotes.forEach((activeNote) => {
|
|
937
|
-
const { bufferSource,
|
|
938
|
-
const playbackRate = calcPlaybackRate(
|
|
1004
|
+
const { bufferSource, instrumentKey, noteNumber } = activeNote;
|
|
1005
|
+
const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
939
1006
|
bufferSource.playbackRate
|
|
940
1007
|
.cancelScheduledValues(now)
|
|
941
1008
|
.setValueAtTime(playbackRate * pressure, now);
|
|
@@ -1010,9 +1077,20 @@ export class Midy {
|
|
|
1010
1077
|
this.channels[channelNumber].bankMSB = msb;
|
|
1011
1078
|
}
|
|
1012
1079
|
setModulation(channelNumber, modulation) {
|
|
1080
|
+
const now = this.audioContext.currentTime;
|
|
1013
1081
|
const channel = this.channels[channelNumber];
|
|
1014
1082
|
channel.modulation = (modulation / 127) *
|
|
1015
1083
|
(channel.modulationDepthRange * 100);
|
|
1084
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1085
|
+
activeNotes.forEach((activeNote) => {
|
|
1086
|
+
if (activeNote.modLFO) {
|
|
1087
|
+
activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
|
|
1088
|
+
channel.modulation, now);
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
this.startModulation(channel, activeNote, now);
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1016
1094
|
}
|
|
1017
1095
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1018
1096
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1073,7 +1151,8 @@ export class Midy {
|
|
|
1073
1151
|
const channel = this.channels[channelNumber];
|
|
1074
1152
|
channel.sostenutoPedal = isOn;
|
|
1075
1153
|
if (isOn) {
|
|
1076
|
-
const
|
|
1154
|
+
const now = this.audioContext.currentTime;
|
|
1155
|
+
const activeNotes = this.getActiveNotes(channel, now);
|
|
1077
1156
|
channel.sostenutoNotes = new Map(activeNotes);
|
|
1078
1157
|
}
|
|
1079
1158
|
else {
|
|
@@ -1085,20 +1164,12 @@ export class Midy {
|
|
|
1085
1164
|
channel.softPedal = softPedal / 127;
|
|
1086
1165
|
}
|
|
1087
1166
|
setVibratoRate(channelNumber, vibratoRate) {
|
|
1088
|
-
const now = this.audioContext.currentTime;
|
|
1089
1167
|
const channel = this.channels[channelNumber];
|
|
1090
1168
|
channel.vibratoRate = vibratoRate / 127 * 4 + 3; // 3-7Hz
|
|
1091
|
-
channel.modulationEffect.lfo.frequency
|
|
1092
|
-
.cancelScheduledValues(now)
|
|
1093
|
-
.setValueAtTime(channel.vibratoRate, now);
|
|
1094
1169
|
}
|
|
1095
1170
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1096
|
-
const now = this.audioContext.currentTime;
|
|
1097
1171
|
const channel = this.channels[channelNumber];
|
|
1098
1172
|
channel.vibratoDepth = vibratoDepth / 127;
|
|
1099
|
-
channel.modulationEffect.lfoGain.gain
|
|
1100
|
-
.cancelScheduledValues(now)
|
|
1101
|
-
.setValueAtTime(channel.vibratoDepth, now);
|
|
1102
1173
|
}
|
|
1103
1174
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1104
1175
|
// Access Virus: 0-10sec
|
|
@@ -1179,8 +1250,8 @@ export class Midy {
|
|
|
1179
1250
|
const velocity = 0;
|
|
1180
1251
|
const stopPedal = true;
|
|
1181
1252
|
const promises = [];
|
|
1182
|
-
channel.scheduledNotes.forEach((
|
|
1183
|
-
const activeNote = this.
|
|
1253
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1254
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1184
1255
|
if (activeNote) {
|
|
1185
1256
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1186
1257
|
promises.push(notePromise);
|
|
@@ -1197,8 +1268,8 @@ export class Midy {
|
|
|
1197
1268
|
const velocity = 0;
|
|
1198
1269
|
const stopPedal = false;
|
|
1199
1270
|
const promises = [];
|
|
1200
|
-
channel.scheduledNotes.forEach((
|
|
1201
|
-
const activeNote = this.
|
|
1271
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1272
|
+
const activeNote = this.getActiveNote(noteList, now);
|
|
1202
1273
|
if (activeNote) {
|
|
1203
1274
|
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1204
1275
|
promises.push(notePromise);
|
package/package.json
CHANGED
|
@@ -12,7 +12,14 @@ declare class F {
|
|
|
12
12
|
sampleName: any;
|
|
13
13
|
sampleModes: number;
|
|
14
14
|
playbackRate: (e: any) => number;
|
|
15
|
+
modLfoToPitch: number;
|
|
16
|
+
vibLfoToPitch: number;
|
|
15
17
|
modEnvToPitch: number;
|
|
18
|
+
initialFilterFc: number;
|
|
19
|
+
initialFilterQ: number;
|
|
20
|
+
modLfoToFilterFc: number;
|
|
21
|
+
modEnvToFilterFc: number;
|
|
22
|
+
modLfoToVolume: number;
|
|
16
23
|
scaleTuning: number;
|
|
17
24
|
start: number;
|
|
18
25
|
end: number;
|
|
@@ -32,12 +39,12 @@ declare class F {
|
|
|
32
39
|
modRelease: number;
|
|
33
40
|
keyRange: d;
|
|
34
41
|
velRange: d;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
delayModLFO: number;
|
|
43
|
+
freqModLFO: number;
|
|
44
|
+
delayVibLFO: number;
|
|
45
|
+
freqVibLFO: number;
|
|
38
46
|
initialAttenuation: number;
|
|
39
|
-
|
|
40
|
-
pan: undefined;
|
|
47
|
+
pan: number;
|
|
41
48
|
} | null;
|
|
42
49
|
getPresetNames(): {};
|
|
43
50
|
}
|
|
@@ -95,7 +102,7 @@ declare namespace T {
|
|
|
95
102
|
let initialFilterQ: number;
|
|
96
103
|
let initialFilterFc: number;
|
|
97
104
|
let sampleModes: number;
|
|
98
|
-
let pan:
|
|
105
|
+
let pan: number;
|
|
99
106
|
}
|
|
100
107
|
declare function b(e: any, n?: {}): {
|
|
101
108
|
samples: any;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"+esm.d.ts","sourceRoot":"","sources":["../../../../../../src/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js"],"names":[],"mappings":";;AAMizX;IAAQ,oBAA2H;IAAd,YAAa;IAAC,qDAAoJ;IAAA,mCAAoP;IAAA,uCAAwQ;IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAAkqE;IAAA,qBAA0I;CAAC;AAAA,mCAAwC;AAA9oI,+BAA8F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAA98D;;;;;;;;;;;;;;;;EAA27B;AAAr4I;IAAqQ,wBAAwD;IAArT,4BAAyN;IAApB,QAAS;IAAC,QAAS;IAAC,oBAAoC;CAAyD;AAAllG;IAAwjC,gCAA8d;CAAC"}
|
|
@@ -6,7 +6,7 @@ exports.createGeneratorObject = I;
|
|
|
6
6
|
exports.parse = b;
|
|
7
7
|
/**
|
|
8
8
|
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.37.0.
|
|
9
|
-
* Original file: /npm/@marmooo/soundfont-parser@0.0.
|
|
9
|
+
* Original file: /npm/@marmooo/soundfont-parser@0.0.2/esm/mod.js
|
|
10
10
|
*
|
|
11
11
|
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
|
12
12
|
*/
|
|
@@ -114,7 +114,7 @@ function I(e) { const t = {}; for (const r of e) {
|
|
|
114
114
|
const e = r.type;
|
|
115
115
|
void 0 !== e && (t[e] = r.value);
|
|
116
116
|
} return t; }
|
|
117
|
-
const T = { keynum: void 0, instrument: void 0, velocity: void 0, exclusiveClass: void 0, keyRange: new d(0, 127), velRange: new d(0, 127), sampleID: void 0, delayVolEnv: -12e3, attackVolEnv: -12e3, decayVolEnv: -12e3, holdVolEnv: -12e3, sustainVolEnv: 0, releaseVolEnv: -12e3, delayModEnv: -12e3, attackModEnv: -12e3, decayModEnv: -12e3, holdModEnv: -12e3, sustainModEnv: 0, releaseModEnv: -12e3, modEnvToPitch: 0, modEnvToFilterFc: 0, modLfoToFilterFc: 0, modLfoToPitch: 0, modLfoToVolume: 0, vibLfoToPitch: 0, chorusEffectsSend: 0, reverbEffectsSend: 0, delayModLFO: 0, freqModLFO: 0, delayVibLFO: 0, keynumToModEnvDecay: 0, keynumToModEnvHold: 0, keynumToVolEnvDecay: 0, keynumToVolEnvHold: 0, coarseTune: 0, fineTune: 0, scaleTuning: 100, freqVibLFO: 0, startAddrsOffset: 0, startAddrsCoarseOffset: 0, endAddrsOffset: 0, endAddrsCoarseOffset: 0, startloopAddrsOffset: 0, startloopAddrsCoarseOffset: 0, initialAttenuation: 0, endloopAddrsOffset: 0, endloopAddrsCoarseOffset: 0, overridingRootKey: void 0, initialFilterQ: 1, initialFilterFc: 13500, sampleModes: 0, pan:
|
|
117
|
+
const T = { keynum: void 0, instrument: void 0, velocity: void 0, exclusiveClass: void 0, keyRange: new d(0, 127), velRange: new d(0, 127), sampleID: void 0, delayVolEnv: -12e3, attackVolEnv: -12e3, decayVolEnv: -12e3, holdVolEnv: -12e3, sustainVolEnv: 0, releaseVolEnv: -12e3, delayModEnv: -12e3, attackModEnv: -12e3, decayModEnv: -12e3, holdModEnv: -12e3, sustainModEnv: 0, releaseModEnv: -12e3, modEnvToPitch: 0, modEnvToFilterFc: 0, modLfoToFilterFc: 0, modLfoToPitch: 0, modLfoToVolume: 0, vibLfoToPitch: 0, chorusEffectsSend: 0, reverbEffectsSend: 0, delayModLFO: 0, freqModLFO: 0, delayVibLFO: 0, keynumToModEnvDecay: 0, keynumToModEnvHold: 0, keynumToVolEnvDecay: 0, keynumToVolEnvHold: 0, coarseTune: 0, fineTune: 0, scaleTuning: 100, freqVibLFO: 0, startAddrsOffset: 0, startAddrsCoarseOffset: 0, endAddrsOffset: 0, endAddrsCoarseOffset: 0, startloopAddrsOffset: 0, startloopAddrsCoarseOffset: 0, initialAttenuation: 0, endloopAddrsOffset: 0, endloopAddrsCoarseOffset: 0, overridingRootKey: void 0, initialFilterQ: 1, initialFilterFc: 13500, sampleModes: 0, pan: 0 };
|
|
118
118
|
exports.defaultInstrumentZone = T;
|
|
119
119
|
class F {
|
|
120
120
|
constructor(e) { Object.defineProperty(this, "parsed", { enumerable: !0, configurable: !0, writable: !0, value: void 0 }), this.parsed = e; }
|
|
@@ -151,12 +151,12 @@ class F {
|
|
|
151
151
|
break;
|
|
152
152
|
} if (!l)
|
|
153
153
|
return console.warn("instrument not found: bank=%s instrument=%s", e, t), null; if (void 0 === l.sampleID)
|
|
154
|
-
throw new Error("Invalid SoundFont: sampleID not found"); const d = { ...T, ...
|
|
154
|
+
throw new Error("Invalid SoundFont: sampleID not found"); const d = { ...T, ...L(o || {}), ...L(s || {}), ...L(l) }, u = this.parsed.samples[d.sampleID], c = this.parsed.sampleHeaders[d.sampleID], f = d.coarseTune + d.fineTune / 100 + c.pitchCorrection / 100 - (d.overridingRootKey || c.originalPitch), p = d.scaleTuning / 100; return { sample: u, sampleRate: c.sampleRate, sampleName: c.sampleName, sampleModes: d.sampleModes, playbackRate: e => Math.pow(Math.pow(2, 1 / 12), (e + f) * p), modLfoToPitch: d.modLfoToPitch, vibLfoToPitch: d.vibLfoToPitch, modEnvToPitch: d.modEnvToPitch, initialFilterFc: d.initialFilterFc, initialFilterQ: d.initialFilterQ, modLfoToFilterFc: d.modLfoToFilterFc, modEnvToFilterFc: d.modEnvToFilterFc, modLfoToVolume: d.modLfoToVolume, scaleTuning: p, start: 32768 * d.startAddrsCoarseOffset + d.startAddrsOffset, end: 32768 * d.endAddrsCoarseOffset + d.endAddrsOffset, loopStart: c.loopStart + 32768 * d.startloopAddrsCoarseOffset + d.startloopAddrsOffset, loopEnd: c.loopEnd + 32768 * d.endloopAddrsCoarseOffset + d.endloopAddrsOffset, volDelay: M(d.delayVolEnv), volAttack: M(d.attackVolEnv), volHold: M(d.holdVolEnv), volDecay: M(d.decayVolEnv), volSustain: d.sustainVolEnv / 1e3, volRelease: M(d.releaseVolEnv), modDelay: M(d.delayModEnv), modAttack: M(d.attackModEnv), modHold: M(d.holdModEnv), modDecay: M(d.decayModEnv), modSustain: d.sustainModEnv / 1e3, modRelease: M(d.releaseModEnv), keyRange: d.keyRange, velRange: d.velRange, delayModLFO: M(d.delayModLFO), freqModLFO: d.freqModLFO, delayVibLFO: M(d.delayVibLFO), freqVibLFO: d.freqVibLFO, initialAttenuation: d.initialAttenuation, pan: d.pan }; }
|
|
155
155
|
getPresetNames() { const e = {}; return this.parsed.presetHeaders.forEach((t => { e[t.bank] || (e[t.bank] = {}), e[t.bank][t.preset] = t.presetName; })), e; }
|
|
156
156
|
}
|
|
157
157
|
exports.SoundFont = F;
|
|
158
158
|
function M(e) { return Math.pow(2, e / 1200); }
|
|
159
|
-
function
|
|
159
|
+
function L(e) { const t = {}; for (const r in e)
|
|
160
160
|
void 0 !== e[r] && (t[r] = e[r]); return t; }
|
|
161
161
|
exports.default = null;
|
|
162
|
-
//# sourceMappingURL=/sm/
|
|
162
|
+
//# sourceMappingURL=/sm/22d03ff23e99217677631c656a57eea14149d50ae4425cf6cd850760c3e93c1e.map
|