@marmooo/midy 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/midy-GM1.d.ts +6 -14
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +104 -155
- package/esm/midy-GM2.d.ts +17 -107
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +152 -133
- package/esm/midy-GMLite.d.ts +6 -14
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +104 -155
- package/esm/midy.d.ts +47 -10
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +173 -129
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +6 -14
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +104 -155
- package/script/midy-GM2.d.ts +17 -107
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +152 -133
- package/script/midy-GMLite.d.ts +6 -14
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +104 -155
- package/script/midy.d.ts +47 -10
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +173 -129
package/esm/midy-GM2.js
CHANGED
|
@@ -8,12 +8,6 @@ export class MidyGM2 {
|
|
|
8
8
|
writable: true,
|
|
9
9
|
value: 120
|
|
10
10
|
});
|
|
11
|
-
Object.defineProperty(this, "secondsPerBeat", {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
configurable: true,
|
|
14
|
-
writable: true,
|
|
15
|
-
value: 0.5
|
|
16
|
-
});
|
|
17
11
|
Object.defineProperty(this, "totalTime", {
|
|
18
12
|
enumerable: true,
|
|
19
13
|
configurable: true,
|
|
@@ -174,10 +168,10 @@ export class MidyGM2 {
|
|
|
174
168
|
const response = await fetch(midiUrl);
|
|
175
169
|
const arrayBuffer = await response.arrayBuffer();
|
|
176
170
|
const midi = parseMidi(new Uint8Array(arrayBuffer));
|
|
171
|
+
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
177
172
|
const midiData = this.extractMidiData(midi);
|
|
178
173
|
this.instruments = midiData.instruments;
|
|
179
174
|
this.timeline = midiData.timeline;
|
|
180
|
-
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
181
175
|
this.totalTime = this.calcTotalTime();
|
|
182
176
|
}
|
|
183
177
|
setChannelAudioNodes(audioContext) {
|
|
@@ -207,11 +201,14 @@ export class MidyGM2 {
|
|
|
207
201
|
createChannels(audioContext) {
|
|
208
202
|
const channels = Array.from({ length: 16 }, () => {
|
|
209
203
|
return {
|
|
210
|
-
...
|
|
211
|
-
...
|
|
204
|
+
...Midy.channelSettings,
|
|
205
|
+
...Midy.effectSettings,
|
|
212
206
|
...this.setChannelAudioNodes(audioContext),
|
|
213
207
|
scheduledNotes: new Map(),
|
|
214
208
|
sostenutoNotes: new Map(),
|
|
209
|
+
channelPressure: {
|
|
210
|
+
...Midy.controllerDestinationSettings,
|
|
211
|
+
},
|
|
215
212
|
};
|
|
216
213
|
});
|
|
217
214
|
return channels;
|
|
@@ -263,28 +260,33 @@ export class MidyGM2 {
|
|
|
263
260
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
264
261
|
while (queueIndex < this.timeline.length) {
|
|
265
262
|
const event = this.timeline[queueIndex];
|
|
266
|
-
|
|
267
|
-
if (time > t + this.lookAhead)
|
|
263
|
+
if (event.startTime > t + this.lookAhead)
|
|
268
264
|
break;
|
|
269
265
|
switch (event.type) {
|
|
270
|
-
case "controller":
|
|
271
|
-
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
272
|
-
break;
|
|
273
266
|
case "noteOn":
|
|
274
|
-
|
|
275
|
-
|
|
267
|
+
if (event.velocity !== 0) {
|
|
268
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
/* falls through */
|
|
276
272
|
case "noteOff": {
|
|
277
|
-
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity,
|
|
273
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
278
274
|
if (notePromise) {
|
|
279
275
|
this.notePromises.push(notePromise);
|
|
280
276
|
}
|
|
281
277
|
break;
|
|
282
278
|
}
|
|
279
|
+
case "controller":
|
|
280
|
+
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
281
|
+
break;
|
|
283
282
|
case "programChange":
|
|
284
283
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
285
284
|
break;
|
|
286
|
-
case "
|
|
287
|
-
this.
|
|
285
|
+
case "channelAftertouch":
|
|
286
|
+
this.handleChannelPressure(event.channel, event.amount);
|
|
287
|
+
break;
|
|
288
|
+
case "pitchBend":
|
|
289
|
+
this.handlePitchBend(event.channel, event.value);
|
|
288
290
|
break;
|
|
289
291
|
case "sysEx":
|
|
290
292
|
this.handleSysEx(event.data);
|
|
@@ -294,9 +296,8 @@ export class MidyGM2 {
|
|
|
294
296
|
return queueIndex;
|
|
295
297
|
}
|
|
296
298
|
getQueueIndex(second) {
|
|
297
|
-
const ticks = this.secondToTicks(second, this.secondsPerBeat);
|
|
298
299
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
299
|
-
if (
|
|
300
|
+
if (second <= this.timeline[i].startTime) {
|
|
300
301
|
return i;
|
|
301
302
|
}
|
|
302
303
|
}
|
|
@@ -438,18 +439,28 @@ export class MidyGM2 {
|
|
|
438
439
|
timeline.push(event);
|
|
439
440
|
});
|
|
440
441
|
});
|
|
442
|
+
const priority = {
|
|
443
|
+
setTempo: 0,
|
|
444
|
+
controller: 1,
|
|
445
|
+
};
|
|
441
446
|
timeline.sort((a, b) => {
|
|
442
|
-
if (a.ticks !== b.ticks)
|
|
447
|
+
if (a.ticks !== b.ticks)
|
|
443
448
|
return a.ticks - b.ticks;
|
|
444
|
-
|
|
445
|
-
if (a.type !== "controller" && b.type === "controller") {
|
|
446
|
-
return -1;
|
|
447
|
-
}
|
|
448
|
-
if (a.type === "controller" && b.type !== "controller") {
|
|
449
|
-
return 1;
|
|
450
|
-
}
|
|
451
|
-
return 0;
|
|
449
|
+
return (priority[a.type] || 2) - (priority[b.type] || 2);
|
|
452
450
|
});
|
|
451
|
+
let prevTempoTime = 0;
|
|
452
|
+
let prevTempoTicks = 0;
|
|
453
|
+
let secondsPerBeat = 0.5;
|
|
454
|
+
for (let i = 0; i < timeline.length; i++) {
|
|
455
|
+
const event = timeline[i];
|
|
456
|
+
const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
457
|
+
event.startTime = prevTempoTime + timeFromPrevTempo;
|
|
458
|
+
if (event.type === "setTempo") {
|
|
459
|
+
prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
460
|
+
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
461
|
+
prevTempoTicks = event.ticks;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
453
464
|
return { instruments, timeline };
|
|
454
465
|
}
|
|
455
466
|
stopNotes() {
|
|
@@ -501,32 +512,12 @@ export class MidyGM2 {
|
|
|
501
512
|
}
|
|
502
513
|
}
|
|
503
514
|
calcTotalTime() {
|
|
504
|
-
const endOfTracks = [];
|
|
505
|
-
let prevTicks = 0;
|
|
506
515
|
let totalTime = 0;
|
|
507
|
-
let secondsPerBeat = 0.5;
|
|
508
516
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
509
517
|
const event = this.timeline[i];
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const durationTicks = event.ticks - prevTicks;
|
|
513
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
514
|
-
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
515
|
-
prevTicks = event.ticks;
|
|
516
|
-
break;
|
|
517
|
-
}
|
|
518
|
-
case "endOfTrack":
|
|
519
|
-
endOfTracks.push(event);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
let maxTicks = 0;
|
|
523
|
-
for (let i = 0; i < endOfTracks.length; i++) {
|
|
524
|
-
const event = endOfTracks[i];
|
|
525
|
-
if (maxTicks < event.ticks)
|
|
526
|
-
maxTicks = event.ticks;
|
|
518
|
+
if (totalTime < event.startTime)
|
|
519
|
+
totalTime = event.startTime;
|
|
527
520
|
}
|
|
528
|
-
const durationTicks = maxTicks - prevTicks;
|
|
529
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
530
521
|
return totalTime;
|
|
531
522
|
}
|
|
532
523
|
currentTime() {
|
|
@@ -554,11 +545,8 @@ export class MidyGM2 {
|
|
|
554
545
|
const lfo = new OscillatorNode(audioContext, {
|
|
555
546
|
frequency: 5,
|
|
556
547
|
});
|
|
557
|
-
const lfoGain = new GainNode(audioContext);
|
|
558
|
-
lfo.connect(lfoGain);
|
|
559
548
|
return {
|
|
560
549
|
lfo,
|
|
561
|
-
lfoGain,
|
|
562
550
|
};
|
|
563
551
|
}
|
|
564
552
|
createReverbEffect(audioContext, options = {}) {
|
|
@@ -663,15 +651,19 @@ export class MidyGM2 {
|
|
|
663
651
|
centToHz(cent) {
|
|
664
652
|
return 8.176 * Math.pow(2, cent / 1200);
|
|
665
653
|
}
|
|
666
|
-
|
|
654
|
+
calcSemitoneOffset(channel) {
|
|
667
655
|
const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
|
|
668
656
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
669
657
|
const tuning = masterTuning + channelTuning;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
658
|
+
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
659
|
+
}
|
|
660
|
+
calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
|
|
661
|
+
return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
|
|
662
|
+
}
|
|
663
|
+
async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
|
|
673
664
|
const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
|
|
674
|
-
|
|
665
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
666
|
+
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
675
667
|
// volume envelope
|
|
676
668
|
const gainNode = new GainNode(this.audioContext, {
|
|
677
669
|
gain: 0,
|
|
@@ -690,12 +682,6 @@ export class MidyGM2 {
|
|
|
690
682
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
691
683
|
.setValueAtTime(attackVolume, volHold)
|
|
692
684
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
693
|
-
if (channel.modulation > 0) {
|
|
694
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
695
|
-
lfoGain.connect(bufferSource.detune);
|
|
696
|
-
lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
|
|
697
|
-
lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
|
|
698
|
-
}
|
|
699
685
|
// filter envelope
|
|
700
686
|
const softPedalFactor = 1 -
|
|
701
687
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
@@ -721,6 +707,19 @@ export class MidyGM2 {
|
|
|
721
707
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
722
708
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
723
709
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
710
|
+
let lfoGain;
|
|
711
|
+
if (channel.modulation > 0) {
|
|
712
|
+
const vibratoDelay = startTime + channel.vibratoDelay;
|
|
713
|
+
const vibratoAttack = vibratoDelay + 0.1;
|
|
714
|
+
lfoGain = new GainNode(this.audioContext, {
|
|
715
|
+
gain: 0,
|
|
716
|
+
});
|
|
717
|
+
lfoGain.gain
|
|
718
|
+
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
719
|
+
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
720
|
+
channel.modulationEffect.lfo.connect(lfoGain);
|
|
721
|
+
lfoGain.connect(bufferSource.detune);
|
|
722
|
+
}
|
|
724
723
|
bufferSource.connect(filterNode);
|
|
725
724
|
filterNode.connect(gainNode);
|
|
726
725
|
if (this.mono && channel.currentBufferSource) {
|
|
@@ -728,7 +727,7 @@ export class MidyGM2 {
|
|
|
728
727
|
channel.currentBufferSource = bufferSource;
|
|
729
728
|
}
|
|
730
729
|
bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
|
|
731
|
-
return { bufferSource, gainNode, filterNode };
|
|
730
|
+
return { bufferSource, gainNode, filterNode, lfoGain };
|
|
732
731
|
}
|
|
733
732
|
calcBank(channel, channelNumber) {
|
|
734
733
|
if (channel.bankMSB === 121) {
|
|
@@ -750,7 +749,7 @@ export class MidyGM2 {
|
|
|
750
749
|
const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
751
750
|
if (!noteInfo)
|
|
752
751
|
return;
|
|
753
|
-
const { bufferSource, gainNode, filterNode } = await this
|
|
752
|
+
const { bufferSource, gainNode, filterNode, lfoGain } = await this
|
|
754
753
|
.createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
|
|
755
754
|
this.connectNoteEffects(channel, gainNode);
|
|
756
755
|
if (channel.sostenutoPedal) {
|
|
@@ -764,11 +763,12 @@ export class MidyGM2 {
|
|
|
764
763
|
}
|
|
765
764
|
const scheduledNotes = channel.scheduledNotes;
|
|
766
765
|
const scheduledNote = {
|
|
767
|
-
gainNode,
|
|
768
|
-
filterNode,
|
|
769
766
|
bufferSource,
|
|
770
|
-
|
|
767
|
+
filterNode,
|
|
768
|
+
gainNode,
|
|
769
|
+
lfoGain,
|
|
771
770
|
noteInfo,
|
|
771
|
+
noteNumber,
|
|
772
772
|
startTime,
|
|
773
773
|
};
|
|
774
774
|
if (scheduledNotes.has(noteNumber)) {
|
|
@@ -797,7 +797,7 @@ export class MidyGM2 {
|
|
|
797
797
|
continue;
|
|
798
798
|
if (targetNote.ending)
|
|
799
799
|
continue;
|
|
800
|
-
const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
|
|
800
|
+
const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
|
|
801
801
|
const velocityRate = (velocity + 127) / 127;
|
|
802
802
|
const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
|
|
803
803
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
@@ -819,6 +819,8 @@ export class MidyGM2 {
|
|
|
819
819
|
bufferSource.disconnect(0);
|
|
820
820
|
filterNode.disconnect(0);
|
|
821
821
|
gainNode.disconnect(0);
|
|
822
|
+
if (lfoGain)
|
|
823
|
+
lfoGain.disconnect(0);
|
|
822
824
|
resolve();
|
|
823
825
|
};
|
|
824
826
|
bufferSource.stop(volEndTime);
|
|
@@ -829,41 +831,34 @@ export class MidyGM2 {
|
|
|
829
831
|
const now = this.audioContext.currentTime;
|
|
830
832
|
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
831
833
|
}
|
|
832
|
-
releaseSustainPedal(channelNumber) {
|
|
833
|
-
const
|
|
834
|
+
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
835
|
+
const velocity = halfVelocity * 2;
|
|
834
836
|
const channel = this.channels[channelNumber];
|
|
837
|
+
const promises = [];
|
|
835
838
|
channel.sustainPedal = false;
|
|
836
839
|
channel.scheduledNotes.forEach((scheduledNotes) => {
|
|
837
840
|
scheduledNotes.forEach((scheduledNote) => {
|
|
838
841
|
if (scheduledNote) {
|
|
839
|
-
const {
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
843
|
-
const maxFreq = this.audioContext.sampleRate / 2;
|
|
844
|
-
const baseFreq = this.centToHz(noteInfo.initialFilterFc);
|
|
845
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
846
|
-
const modEndTime = now + noteInfo.modRelease;
|
|
847
|
-
filterNode.frequency
|
|
848
|
-
.cancelScheduledValues(stopTime)
|
|
849
|
-
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
850
|
-
bufferSource.stop(volEndTime);
|
|
842
|
+
const { noteNumber } = scheduledNote;
|
|
843
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
844
|
+
promises.push(promise);
|
|
851
845
|
}
|
|
852
846
|
});
|
|
853
847
|
});
|
|
848
|
+
return promises;
|
|
854
849
|
}
|
|
855
|
-
|
|
856
|
-
const
|
|
850
|
+
releaseSostenutoPedal(channelNumber, halfVelocity) {
|
|
851
|
+
const velocity = halfVelocity * 2;
|
|
857
852
|
const channel = this.channels[channelNumber];
|
|
853
|
+
const promises = [];
|
|
858
854
|
channel.sostenutoPedal = false;
|
|
859
855
|
channel.sostenutoNotes.forEach((activeNote) => {
|
|
860
|
-
const {
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
gainNode.gain.linearRampToValueAtTime(0, now + fadeTime);
|
|
864
|
-
bufferSource.stop(now + fadeTime);
|
|
856
|
+
const { noteNumber } = activeNote;
|
|
857
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
858
|
+
promises.push(promise);
|
|
865
859
|
});
|
|
866
860
|
channel.sostenutoNotes.clear();
|
|
861
|
+
return promises;
|
|
867
862
|
}
|
|
868
863
|
handleMIDIMessage(statusByte, data1, data2) {
|
|
869
864
|
const channelNumber = omni ? 0 : statusByte & 0x0F;
|
|
@@ -873,8 +868,6 @@ export class MidyGM2 {
|
|
|
873
868
|
return this.releaseNote(channelNumber, data1, data2);
|
|
874
869
|
case 0x90:
|
|
875
870
|
return this.noteOn(channelNumber, data1, data2);
|
|
876
|
-
case 0xA0:
|
|
877
|
-
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
878
871
|
case 0xB0:
|
|
879
872
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
880
873
|
case 0xC0:
|
|
@@ -882,38 +875,48 @@ export class MidyGM2 {
|
|
|
882
875
|
case 0xD0:
|
|
883
876
|
return this.handleChannelPressure(channelNumber, data1);
|
|
884
877
|
case 0xE0:
|
|
885
|
-
return this.
|
|
878
|
+
return this.handlePitchBendMessage(channelNumber, data1, data2);
|
|
886
879
|
default:
|
|
887
880
|
console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
|
|
888
881
|
}
|
|
889
882
|
}
|
|
890
|
-
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
891
|
-
const now = this.audioContext.currentTime;
|
|
892
|
-
const channel = this.channels[channelNumber];
|
|
893
|
-
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
894
|
-
pressure /= 127;
|
|
895
|
-
if (scheduledNotes) {
|
|
896
|
-
scheduledNotes.forEach((scheduledNote) => {
|
|
897
|
-
if (scheduledNote) {
|
|
898
|
-
const { initialAttenuation } = scheduledNote.noteInfo;
|
|
899
|
-
const gain = this.cbToRatio(-initialAttenuation) * pressure;
|
|
900
|
-
scheduledNote.gainNode.gain.cancelScheduledValues(now);
|
|
901
|
-
scheduledNote.gainNode.gain.setValueAtTime(gain, now);
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
883
|
handleProgramChange(channelNumber, program) {
|
|
907
884
|
const channel = this.channels[channelNumber];
|
|
908
885
|
channel.bank = channel.bankMSB * 128 + channel.bankLSB;
|
|
909
886
|
channel.program = program;
|
|
910
887
|
}
|
|
911
888
|
handleChannelPressure(channelNumber, pressure) {
|
|
912
|
-
this.
|
|
889
|
+
const now = this.audioContext.currentTime;
|
|
890
|
+
const channel = this.channels[channelNumber];
|
|
891
|
+
pressure /= 64;
|
|
892
|
+
channel.channelPressure = pressure;
|
|
893
|
+
const activeNotes = this.getActiveNotes(channel);
|
|
894
|
+
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
895
|
+
activeNotes.forEach((activeNote) => {
|
|
896
|
+
const gain = activeNote.gainNode.gain.value;
|
|
897
|
+
activeNote.gainNode.gain
|
|
898
|
+
.cancelScheduledValues(now)
|
|
899
|
+
.setValueAtTime(gain * pressure, now);
|
|
900
|
+
});
|
|
901
|
+
}
|
|
913
902
|
}
|
|
914
|
-
|
|
915
|
-
const pitchBend =
|
|
916
|
-
this.
|
|
903
|
+
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
904
|
+
const pitchBend = msb * 128 + lsb;
|
|
905
|
+
this.handlePitchBend(channelNumber, pitchBend);
|
|
906
|
+
}
|
|
907
|
+
handlePitchBend(channelNumber, pitchBend) {
|
|
908
|
+
const now = this.audioContext.currentTime;
|
|
909
|
+
const channel = this.channels[channelNumber];
|
|
910
|
+
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
911
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
912
|
+
const activeNotes = this.getActiveNotes(channel);
|
|
913
|
+
activeNotes.forEach((activeNote) => {
|
|
914
|
+
const { bufferSource, noteInfo, noteNumber } = activeNote;
|
|
915
|
+
const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
916
|
+
bufferSource.playbackRate
|
|
917
|
+
.cancelScheduledValues(now)
|
|
918
|
+
.setValueAtTime(playbackRate * pressure, now);
|
|
919
|
+
});
|
|
917
920
|
}
|
|
918
921
|
handleControlChange(channelNumber, controller, value) {
|
|
919
922
|
switch (controller) {
|
|
@@ -973,13 +976,9 @@ export class MidyGM2 {
|
|
|
973
976
|
this.channels[channelNumber].bankMSB = msb;
|
|
974
977
|
}
|
|
975
978
|
setModulation(channelNumber, modulation) {
|
|
976
|
-
const now = this.audioContext.currentTime;
|
|
977
979
|
const channel = this.channels[channelNumber];
|
|
978
|
-
channel.modulation = (modulation
|
|
979
|
-
channel.modulationDepthRange;
|
|
980
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
981
|
-
lfoGain.gain.cancelScheduledValues(now);
|
|
982
|
-
lfoGain.gain.setValueAtTime(channel.modulation, now);
|
|
980
|
+
channel.modulation = (modulation / 127) *
|
|
981
|
+
(channel.modulationDepthRange * 100);
|
|
983
982
|
}
|
|
984
983
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
985
984
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1014,7 +1013,7 @@ export class MidyGM2 {
|
|
|
1014
1013
|
const isOn = value >= 64;
|
|
1015
1014
|
this.channels[channelNumber].sustainPedal = isOn;
|
|
1016
1015
|
if (!isOn) {
|
|
1017
|
-
this.releaseSustainPedal(channelNumber);
|
|
1016
|
+
this.releaseSustainPedal(channelNumber, value);
|
|
1018
1017
|
}
|
|
1019
1018
|
}
|
|
1020
1019
|
setPortamento(channelNumber, value) {
|
|
@@ -1043,11 +1042,13 @@ export class MidyGM2 {
|
|
|
1043
1042
|
const activeNotes = this.getActiveNotes(channel);
|
|
1044
1043
|
channel.sostenutoNotes = new Map(activeNotes);
|
|
1045
1044
|
}
|
|
1045
|
+
else {
|
|
1046
|
+
this.releaseSostenutoPedal(channelNumber, value);
|
|
1047
|
+
}
|
|
1046
1048
|
}
|
|
1047
1049
|
setSoftPedal(channelNumber, softPedal) {
|
|
1048
1050
|
const channel = this.channels[channelNumber];
|
|
1049
1051
|
channel.softPedal = softPedal / 127;
|
|
1050
|
-
this.updateChannelGain(channel);
|
|
1051
1052
|
}
|
|
1052
1053
|
setRPNMSB(channelNumber, value) {
|
|
1053
1054
|
this.channels[channelNumber].rpnMSB = value;
|
|
@@ -1188,10 +1189,10 @@ export class MidyGM2 {
|
|
|
1188
1189
|
switch (data[3]) {
|
|
1189
1190
|
// case 1:
|
|
1190
1191
|
// // TODO
|
|
1191
|
-
// return this.
|
|
1192
|
+
// return this.setChannelPressure();
|
|
1192
1193
|
// case 3:
|
|
1193
1194
|
// // TODO
|
|
1194
|
-
// return this.
|
|
1195
|
+
// return this.setControlChange();
|
|
1195
1196
|
default:
|
|
1196
1197
|
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1197
1198
|
}
|
|
@@ -1210,20 +1211,25 @@ export class MidyGM2 {
|
|
|
1210
1211
|
}
|
|
1211
1212
|
}
|
|
1212
1213
|
handleMasterVolumeSysEx(data) {
|
|
1213
|
-
const volume = (data[5] * 128 + data[4]
|
|
1214
|
+
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
1214
1215
|
this.handleMasterVolume(volume);
|
|
1215
1216
|
}
|
|
1216
1217
|
handleMasterVolume(volume) {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1218
|
+
if (volume < 0 && 1 < volume) {
|
|
1219
|
+
console.error("Master Volume is out of range");
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
const now = this.audioContext.currentTime;
|
|
1223
|
+
this.masterGain.gain.cancelScheduledValues(now);
|
|
1224
|
+
this.masterGain.gain.setValueAtTime(volume * volume, now);
|
|
1225
|
+
}
|
|
1220
1226
|
}
|
|
1221
1227
|
handleMasterFineTuningSysEx(data) {
|
|
1222
1228
|
const fineTuning = (data[5] * 128 + data[4] - 8192) / 8192;
|
|
1223
1229
|
this.handleMasterFineTuning(fineTuning);
|
|
1224
1230
|
}
|
|
1225
1231
|
handleMasterFineTuning(fineTuning) {
|
|
1226
|
-
if (fineTuning <
|
|
1232
|
+
if (fineTuning < -1 && 1 < fineTuning) {
|
|
1227
1233
|
console.error("Master Fine Tuning value is out of range");
|
|
1228
1234
|
}
|
|
1229
1235
|
else {
|
|
@@ -1273,7 +1279,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1273
1279
|
writable: true,
|
|
1274
1280
|
value: {
|
|
1275
1281
|
currentBufferSource: null,
|
|
1276
|
-
volume:
|
|
1282
|
+
volume: 100 / 127,
|
|
1277
1283
|
pan: 0,
|
|
1278
1284
|
portamentoTime: 0,
|
|
1279
1285
|
reverb: 0,
|
|
@@ -1290,7 +1296,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1290
1296
|
pitchBend: 0,
|
|
1291
1297
|
fineTuning: 0,
|
|
1292
1298
|
coarseTuning: 0,
|
|
1293
|
-
modulationDepthRange:
|
|
1299
|
+
modulationDepthRange: 0.5,
|
|
1294
1300
|
}
|
|
1295
1301
|
});
|
|
1296
1302
|
Object.defineProperty(MidyGM2, "effectSettings", {
|
|
@@ -1310,3 +1316,16 @@ Object.defineProperty(MidyGM2, "effectSettings", {
|
|
|
1310
1316
|
pitchBendRange: 2,
|
|
1311
1317
|
}
|
|
1312
1318
|
});
|
|
1319
|
+
Object.defineProperty(MidyGM2, "controllerDestinationSettings", {
|
|
1320
|
+
enumerable: true,
|
|
1321
|
+
configurable: true,
|
|
1322
|
+
writable: true,
|
|
1323
|
+
value: {
|
|
1324
|
+
pitchControl: 0,
|
|
1325
|
+
filterCutoffControl: 0,
|
|
1326
|
+
amplitudeControl: 1,
|
|
1327
|
+
lfoPitchDepth: 0,
|
|
1328
|
+
lfoFilterDepth: 0,
|
|
1329
|
+
lfoAmplitudeDepth: 0,
|
|
1330
|
+
}
|
|
1331
|
+
});
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -22,7 +22,6 @@ export class MidyGMLite {
|
|
|
22
22
|
};
|
|
23
23
|
constructor(audioContext: any);
|
|
24
24
|
ticksPerBeat: number;
|
|
25
|
-
secondsPerBeat: number;
|
|
26
25
|
totalTime: number;
|
|
27
26
|
noteCheckInterval: number;
|
|
28
27
|
lookAhead: number;
|
|
@@ -48,7 +47,6 @@ export class MidyGMLite {
|
|
|
48
47
|
pannerNode: any;
|
|
49
48
|
modulationEffect: {
|
|
50
49
|
lfo: any;
|
|
51
|
-
lfoGain: any;
|
|
52
50
|
};
|
|
53
51
|
expression: number;
|
|
54
52
|
modulation: number;
|
|
@@ -77,7 +75,6 @@ export class MidyGMLite {
|
|
|
77
75
|
pannerNode: any;
|
|
78
76
|
modulationEffect: {
|
|
79
77
|
lfo: any;
|
|
80
|
-
lfoGain: any;
|
|
81
78
|
};
|
|
82
79
|
};
|
|
83
80
|
createChannels(audioContext: any): {
|
|
@@ -87,7 +84,6 @@ export class MidyGMLite {
|
|
|
87
84
|
pannerNode: any;
|
|
88
85
|
modulationEffect: {
|
|
89
86
|
lfo: any;
|
|
90
|
-
lfoGain: any;
|
|
91
87
|
};
|
|
92
88
|
expression: number;
|
|
93
89
|
modulation: number;
|
|
@@ -131,31 +127,27 @@ export class MidyGMLite {
|
|
|
131
127
|
getActiveChannelNotes(scheduledNotes: any): any;
|
|
132
128
|
createModulationEffect(audioContext: any): {
|
|
133
129
|
lfo: any;
|
|
134
|
-
lfoGain: any;
|
|
135
|
-
};
|
|
136
|
-
createReverbEffect(audioContext: any, options?: {}): {
|
|
137
|
-
convolverNode: any;
|
|
138
|
-
dryGain: any;
|
|
139
|
-
wetGain: any;
|
|
140
130
|
};
|
|
141
131
|
connectNoteEffects(channel: any, gainNode: any): void;
|
|
142
132
|
cbToRatio(cb: any): number;
|
|
143
133
|
centToHz(cent: any): number;
|
|
134
|
+
calcSemitoneOffset(channel: any): any;
|
|
135
|
+
calcPlaybackRate(noteInfo: any, noteNumber: any, semitoneOffset: any): number;
|
|
144
136
|
createNoteAudioChain(channel: any, noteInfo: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<{
|
|
145
137
|
bufferSource: any;
|
|
146
138
|
gainNode: any;
|
|
147
139
|
filterNode: any;
|
|
140
|
+
lfoGain: any;
|
|
148
141
|
}>;
|
|
149
142
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
150
143
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
151
144
|
scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
|
|
152
145
|
releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
|
|
153
|
-
releaseSustainPedal(channelNumber: any):
|
|
146
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
154
147
|
handleMIDIMessage(statusByte: any, data1: any, data2: any): void | any[] | Promise<any>;
|
|
155
|
-
handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
|
|
156
148
|
handleProgramChange(channelNumber: any, program: any): void;
|
|
157
|
-
|
|
158
|
-
handlePitchBend(channelNumber: any,
|
|
149
|
+
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
|
|
150
|
+
handlePitchBend(channelNumber: any, pitchBend: any): void;
|
|
159
151
|
handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
|
|
160
152
|
setModulation(channelNumber: any, modulation: any): void;
|
|
161
153
|
setVolume(channelNumber: any, volume: any): void;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAMA;
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAMA;IAmBE;;;;;;;;;;;;MAYE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA/CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IA0BhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;MAgBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;QAWC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAyEC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4CASC;IAED,gDAKC;IAED;;MAOC;IAED,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED,sCAEC;IAED,8EAEC;IAED;;;;;OA8EC;IAED,kGAuCC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,0DAiBC;IAED,mFA+BC;IAED,yDAIC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,sCAKC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAcC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,sCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
|