@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/script/midy-GM2.js
CHANGED
|
@@ -11,12 +11,6 @@ class MidyGM2 {
|
|
|
11
11
|
writable: true,
|
|
12
12
|
value: 120
|
|
13
13
|
});
|
|
14
|
-
Object.defineProperty(this, "secondsPerBeat", {
|
|
15
|
-
enumerable: true,
|
|
16
|
-
configurable: true,
|
|
17
|
-
writable: true,
|
|
18
|
-
value: 0.5
|
|
19
|
-
});
|
|
20
14
|
Object.defineProperty(this, "totalTime", {
|
|
21
15
|
enumerable: true,
|
|
22
16
|
configurable: true,
|
|
@@ -177,10 +171,10 @@ class MidyGM2 {
|
|
|
177
171
|
const response = await fetch(midiUrl);
|
|
178
172
|
const arrayBuffer = await response.arrayBuffer();
|
|
179
173
|
const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
|
|
174
|
+
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
180
175
|
const midiData = this.extractMidiData(midi);
|
|
181
176
|
this.instruments = midiData.instruments;
|
|
182
177
|
this.timeline = midiData.timeline;
|
|
183
|
-
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
184
178
|
this.totalTime = this.calcTotalTime();
|
|
185
179
|
}
|
|
186
180
|
setChannelAudioNodes(audioContext) {
|
|
@@ -210,11 +204,14 @@ class MidyGM2 {
|
|
|
210
204
|
createChannels(audioContext) {
|
|
211
205
|
const channels = Array.from({ length: 16 }, () => {
|
|
212
206
|
return {
|
|
213
|
-
...
|
|
214
|
-
...
|
|
207
|
+
...Midy.channelSettings,
|
|
208
|
+
...Midy.effectSettings,
|
|
215
209
|
...this.setChannelAudioNodes(audioContext),
|
|
216
210
|
scheduledNotes: new Map(),
|
|
217
211
|
sostenutoNotes: new Map(),
|
|
212
|
+
channelPressure: {
|
|
213
|
+
...Midy.controllerDestinationSettings,
|
|
214
|
+
},
|
|
218
215
|
};
|
|
219
216
|
});
|
|
220
217
|
return channels;
|
|
@@ -266,28 +263,33 @@ class MidyGM2 {
|
|
|
266
263
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
267
264
|
while (queueIndex < this.timeline.length) {
|
|
268
265
|
const event = this.timeline[queueIndex];
|
|
269
|
-
|
|
270
|
-
if (time > t + this.lookAhead)
|
|
266
|
+
if (event.startTime > t + this.lookAhead)
|
|
271
267
|
break;
|
|
272
268
|
switch (event.type) {
|
|
273
|
-
case "controller":
|
|
274
|
-
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
275
|
-
break;
|
|
276
269
|
case "noteOn":
|
|
277
|
-
|
|
278
|
-
|
|
270
|
+
if (event.velocity !== 0) {
|
|
271
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
/* falls through */
|
|
279
275
|
case "noteOff": {
|
|
280
|
-
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity,
|
|
276
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
281
277
|
if (notePromise) {
|
|
282
278
|
this.notePromises.push(notePromise);
|
|
283
279
|
}
|
|
284
280
|
break;
|
|
285
281
|
}
|
|
282
|
+
case "controller":
|
|
283
|
+
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
284
|
+
break;
|
|
286
285
|
case "programChange":
|
|
287
286
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
288
287
|
break;
|
|
289
|
-
case "
|
|
290
|
-
this.
|
|
288
|
+
case "channelAftertouch":
|
|
289
|
+
this.handleChannelPressure(event.channel, event.amount);
|
|
290
|
+
break;
|
|
291
|
+
case "pitchBend":
|
|
292
|
+
this.handlePitchBend(event.channel, event.value);
|
|
291
293
|
break;
|
|
292
294
|
case "sysEx":
|
|
293
295
|
this.handleSysEx(event.data);
|
|
@@ -297,9 +299,8 @@ class MidyGM2 {
|
|
|
297
299
|
return queueIndex;
|
|
298
300
|
}
|
|
299
301
|
getQueueIndex(second) {
|
|
300
|
-
const ticks = this.secondToTicks(second, this.secondsPerBeat);
|
|
301
302
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
302
|
-
if (
|
|
303
|
+
if (second <= this.timeline[i].startTime) {
|
|
303
304
|
return i;
|
|
304
305
|
}
|
|
305
306
|
}
|
|
@@ -441,18 +442,28 @@ class MidyGM2 {
|
|
|
441
442
|
timeline.push(event);
|
|
442
443
|
});
|
|
443
444
|
});
|
|
445
|
+
const priority = {
|
|
446
|
+
setTempo: 0,
|
|
447
|
+
controller: 1,
|
|
448
|
+
};
|
|
444
449
|
timeline.sort((a, b) => {
|
|
445
|
-
if (a.ticks !== b.ticks)
|
|
450
|
+
if (a.ticks !== b.ticks)
|
|
446
451
|
return a.ticks - b.ticks;
|
|
447
|
-
|
|
448
|
-
if (a.type !== "controller" && b.type === "controller") {
|
|
449
|
-
return -1;
|
|
450
|
-
}
|
|
451
|
-
if (a.type === "controller" && b.type !== "controller") {
|
|
452
|
-
return 1;
|
|
453
|
-
}
|
|
454
|
-
return 0;
|
|
452
|
+
return (priority[a.type] || 2) - (priority[b.type] || 2);
|
|
455
453
|
});
|
|
454
|
+
let prevTempoTime = 0;
|
|
455
|
+
let prevTempoTicks = 0;
|
|
456
|
+
let secondsPerBeat = 0.5;
|
|
457
|
+
for (let i = 0; i < timeline.length; i++) {
|
|
458
|
+
const event = timeline[i];
|
|
459
|
+
const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
460
|
+
event.startTime = prevTempoTime + timeFromPrevTempo;
|
|
461
|
+
if (event.type === "setTempo") {
|
|
462
|
+
prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
463
|
+
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
464
|
+
prevTempoTicks = event.ticks;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
456
467
|
return { instruments, timeline };
|
|
457
468
|
}
|
|
458
469
|
stopNotes() {
|
|
@@ -504,32 +515,12 @@ class MidyGM2 {
|
|
|
504
515
|
}
|
|
505
516
|
}
|
|
506
517
|
calcTotalTime() {
|
|
507
|
-
const endOfTracks = [];
|
|
508
|
-
let prevTicks = 0;
|
|
509
518
|
let totalTime = 0;
|
|
510
|
-
let secondsPerBeat = 0.5;
|
|
511
519
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
512
520
|
const event = this.timeline[i];
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const durationTicks = event.ticks - prevTicks;
|
|
516
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
517
|
-
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
518
|
-
prevTicks = event.ticks;
|
|
519
|
-
break;
|
|
520
|
-
}
|
|
521
|
-
case "endOfTrack":
|
|
522
|
-
endOfTracks.push(event);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
let maxTicks = 0;
|
|
526
|
-
for (let i = 0; i < endOfTracks.length; i++) {
|
|
527
|
-
const event = endOfTracks[i];
|
|
528
|
-
if (maxTicks < event.ticks)
|
|
529
|
-
maxTicks = event.ticks;
|
|
521
|
+
if (totalTime < event.startTime)
|
|
522
|
+
totalTime = event.startTime;
|
|
530
523
|
}
|
|
531
|
-
const durationTicks = maxTicks - prevTicks;
|
|
532
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
533
524
|
return totalTime;
|
|
534
525
|
}
|
|
535
526
|
currentTime() {
|
|
@@ -557,11 +548,8 @@ class MidyGM2 {
|
|
|
557
548
|
const lfo = new OscillatorNode(audioContext, {
|
|
558
549
|
frequency: 5,
|
|
559
550
|
});
|
|
560
|
-
const lfoGain = new GainNode(audioContext);
|
|
561
|
-
lfo.connect(lfoGain);
|
|
562
551
|
return {
|
|
563
552
|
lfo,
|
|
564
|
-
lfoGain,
|
|
565
553
|
};
|
|
566
554
|
}
|
|
567
555
|
createReverbEffect(audioContext, options = {}) {
|
|
@@ -666,15 +654,19 @@ class MidyGM2 {
|
|
|
666
654
|
centToHz(cent) {
|
|
667
655
|
return 8.176 * Math.pow(2, cent / 1200);
|
|
668
656
|
}
|
|
669
|
-
|
|
657
|
+
calcSemitoneOffset(channel) {
|
|
670
658
|
const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
|
|
671
659
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
672
660
|
const tuning = masterTuning + channelTuning;
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
661
|
+
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
662
|
+
}
|
|
663
|
+
calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
|
|
664
|
+
return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
|
|
665
|
+
}
|
|
666
|
+
async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
|
|
676
667
|
const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
|
|
677
|
-
|
|
668
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
669
|
+
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
678
670
|
// volume envelope
|
|
679
671
|
const gainNode = new GainNode(this.audioContext, {
|
|
680
672
|
gain: 0,
|
|
@@ -693,12 +685,6 @@ class MidyGM2 {
|
|
|
693
685
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
694
686
|
.setValueAtTime(attackVolume, volHold)
|
|
695
687
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
696
|
-
if (channel.modulation > 0) {
|
|
697
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
698
|
-
lfoGain.connect(bufferSource.detune);
|
|
699
|
-
lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
|
|
700
|
-
lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
|
|
701
|
-
}
|
|
702
688
|
// filter envelope
|
|
703
689
|
const softPedalFactor = 1 -
|
|
704
690
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
@@ -724,6 +710,19 @@ class MidyGM2 {
|
|
|
724
710
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
725
711
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
726
712
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
713
|
+
let lfoGain;
|
|
714
|
+
if (channel.modulation > 0) {
|
|
715
|
+
const vibratoDelay = startTime + channel.vibratoDelay;
|
|
716
|
+
const vibratoAttack = vibratoDelay + 0.1;
|
|
717
|
+
lfoGain = new GainNode(this.audioContext, {
|
|
718
|
+
gain: 0,
|
|
719
|
+
});
|
|
720
|
+
lfoGain.gain
|
|
721
|
+
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
722
|
+
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
723
|
+
channel.modulationEffect.lfo.connect(lfoGain);
|
|
724
|
+
lfoGain.connect(bufferSource.detune);
|
|
725
|
+
}
|
|
727
726
|
bufferSource.connect(filterNode);
|
|
728
727
|
filterNode.connect(gainNode);
|
|
729
728
|
if (this.mono && channel.currentBufferSource) {
|
|
@@ -731,7 +730,7 @@ class MidyGM2 {
|
|
|
731
730
|
channel.currentBufferSource = bufferSource;
|
|
732
731
|
}
|
|
733
732
|
bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
|
|
734
|
-
return { bufferSource, gainNode, filterNode };
|
|
733
|
+
return { bufferSource, gainNode, filterNode, lfoGain };
|
|
735
734
|
}
|
|
736
735
|
calcBank(channel, channelNumber) {
|
|
737
736
|
if (channel.bankMSB === 121) {
|
|
@@ -753,7 +752,7 @@ class MidyGM2 {
|
|
|
753
752
|
const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
754
753
|
if (!noteInfo)
|
|
755
754
|
return;
|
|
756
|
-
const { bufferSource, gainNode, filterNode } = await this
|
|
755
|
+
const { bufferSource, gainNode, filterNode, lfoGain } = await this
|
|
757
756
|
.createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
|
|
758
757
|
this.connectNoteEffects(channel, gainNode);
|
|
759
758
|
if (channel.sostenutoPedal) {
|
|
@@ -767,11 +766,12 @@ class MidyGM2 {
|
|
|
767
766
|
}
|
|
768
767
|
const scheduledNotes = channel.scheduledNotes;
|
|
769
768
|
const scheduledNote = {
|
|
770
|
-
gainNode,
|
|
771
|
-
filterNode,
|
|
772
769
|
bufferSource,
|
|
773
|
-
|
|
770
|
+
filterNode,
|
|
771
|
+
gainNode,
|
|
772
|
+
lfoGain,
|
|
774
773
|
noteInfo,
|
|
774
|
+
noteNumber,
|
|
775
775
|
startTime,
|
|
776
776
|
};
|
|
777
777
|
if (scheduledNotes.has(noteNumber)) {
|
|
@@ -800,7 +800,7 @@ class MidyGM2 {
|
|
|
800
800
|
continue;
|
|
801
801
|
if (targetNote.ending)
|
|
802
802
|
continue;
|
|
803
|
-
const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
|
|
803
|
+
const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
|
|
804
804
|
const velocityRate = (velocity + 127) / 127;
|
|
805
805
|
const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
|
|
806
806
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
@@ -822,6 +822,8 @@ class MidyGM2 {
|
|
|
822
822
|
bufferSource.disconnect(0);
|
|
823
823
|
filterNode.disconnect(0);
|
|
824
824
|
gainNode.disconnect(0);
|
|
825
|
+
if (lfoGain)
|
|
826
|
+
lfoGain.disconnect(0);
|
|
825
827
|
resolve();
|
|
826
828
|
};
|
|
827
829
|
bufferSource.stop(volEndTime);
|
|
@@ -832,41 +834,34 @@ class MidyGM2 {
|
|
|
832
834
|
const now = this.audioContext.currentTime;
|
|
833
835
|
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
834
836
|
}
|
|
835
|
-
releaseSustainPedal(channelNumber) {
|
|
836
|
-
const
|
|
837
|
+
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
838
|
+
const velocity = halfVelocity * 2;
|
|
837
839
|
const channel = this.channels[channelNumber];
|
|
840
|
+
const promises = [];
|
|
838
841
|
channel.sustainPedal = false;
|
|
839
842
|
channel.scheduledNotes.forEach((scheduledNotes) => {
|
|
840
843
|
scheduledNotes.forEach((scheduledNote) => {
|
|
841
844
|
if (scheduledNote) {
|
|
842
|
-
const {
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
846
|
-
const maxFreq = this.audioContext.sampleRate / 2;
|
|
847
|
-
const baseFreq = this.centToHz(noteInfo.initialFilterFc);
|
|
848
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
849
|
-
const modEndTime = now + noteInfo.modRelease;
|
|
850
|
-
filterNode.frequency
|
|
851
|
-
.cancelScheduledValues(stopTime)
|
|
852
|
-
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
853
|
-
bufferSource.stop(volEndTime);
|
|
845
|
+
const { noteNumber } = scheduledNote;
|
|
846
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
847
|
+
promises.push(promise);
|
|
854
848
|
}
|
|
855
849
|
});
|
|
856
850
|
});
|
|
851
|
+
return promises;
|
|
857
852
|
}
|
|
858
|
-
|
|
859
|
-
const
|
|
853
|
+
releaseSostenutoPedal(channelNumber, halfVelocity) {
|
|
854
|
+
const velocity = halfVelocity * 2;
|
|
860
855
|
const channel = this.channels[channelNumber];
|
|
856
|
+
const promises = [];
|
|
861
857
|
channel.sostenutoPedal = false;
|
|
862
858
|
channel.sostenutoNotes.forEach((activeNote) => {
|
|
863
|
-
const {
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
gainNode.gain.linearRampToValueAtTime(0, now + fadeTime);
|
|
867
|
-
bufferSource.stop(now + fadeTime);
|
|
859
|
+
const { noteNumber } = activeNote;
|
|
860
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
861
|
+
promises.push(promise);
|
|
868
862
|
});
|
|
869
863
|
channel.sostenutoNotes.clear();
|
|
864
|
+
return promises;
|
|
870
865
|
}
|
|
871
866
|
handleMIDIMessage(statusByte, data1, data2) {
|
|
872
867
|
const channelNumber = omni ? 0 : statusByte & 0x0F;
|
|
@@ -876,8 +871,6 @@ class MidyGM2 {
|
|
|
876
871
|
return this.releaseNote(channelNumber, data1, data2);
|
|
877
872
|
case 0x90:
|
|
878
873
|
return this.noteOn(channelNumber, data1, data2);
|
|
879
|
-
case 0xA0:
|
|
880
|
-
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
881
874
|
case 0xB0:
|
|
882
875
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
883
876
|
case 0xC0:
|
|
@@ -885,38 +878,48 @@ class MidyGM2 {
|
|
|
885
878
|
case 0xD0:
|
|
886
879
|
return this.handleChannelPressure(channelNumber, data1);
|
|
887
880
|
case 0xE0:
|
|
888
|
-
return this.
|
|
881
|
+
return this.handlePitchBendMessage(channelNumber, data1, data2);
|
|
889
882
|
default:
|
|
890
883
|
console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
|
|
891
884
|
}
|
|
892
885
|
}
|
|
893
|
-
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
894
|
-
const now = this.audioContext.currentTime;
|
|
895
|
-
const channel = this.channels[channelNumber];
|
|
896
|
-
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
897
|
-
pressure /= 127;
|
|
898
|
-
if (scheduledNotes) {
|
|
899
|
-
scheduledNotes.forEach((scheduledNote) => {
|
|
900
|
-
if (scheduledNote) {
|
|
901
|
-
const { initialAttenuation } = scheduledNote.noteInfo;
|
|
902
|
-
const gain = this.cbToRatio(-initialAttenuation) * pressure;
|
|
903
|
-
scheduledNote.gainNode.gain.cancelScheduledValues(now);
|
|
904
|
-
scheduledNote.gainNode.gain.setValueAtTime(gain, now);
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
886
|
handleProgramChange(channelNumber, program) {
|
|
910
887
|
const channel = this.channels[channelNumber];
|
|
911
888
|
channel.bank = channel.bankMSB * 128 + channel.bankLSB;
|
|
912
889
|
channel.program = program;
|
|
913
890
|
}
|
|
914
891
|
handleChannelPressure(channelNumber, pressure) {
|
|
915
|
-
this.
|
|
892
|
+
const now = this.audioContext.currentTime;
|
|
893
|
+
const channel = this.channels[channelNumber];
|
|
894
|
+
pressure /= 64;
|
|
895
|
+
channel.channelPressure = pressure;
|
|
896
|
+
const activeNotes = this.getActiveNotes(channel);
|
|
897
|
+
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
898
|
+
activeNotes.forEach((activeNote) => {
|
|
899
|
+
const gain = activeNote.gainNode.gain.value;
|
|
900
|
+
activeNote.gainNode.gain
|
|
901
|
+
.cancelScheduledValues(now)
|
|
902
|
+
.setValueAtTime(gain * pressure, now);
|
|
903
|
+
});
|
|
904
|
+
}
|
|
916
905
|
}
|
|
917
|
-
|
|
918
|
-
const pitchBend =
|
|
919
|
-
this.
|
|
906
|
+
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
907
|
+
const pitchBend = msb * 128 + lsb;
|
|
908
|
+
this.handlePitchBend(channelNumber, pitchBend);
|
|
909
|
+
}
|
|
910
|
+
handlePitchBend(channelNumber, pitchBend) {
|
|
911
|
+
const now = this.audioContext.currentTime;
|
|
912
|
+
const channel = this.channels[channelNumber];
|
|
913
|
+
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
914
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
915
|
+
const activeNotes = this.getActiveNotes(channel);
|
|
916
|
+
activeNotes.forEach((activeNote) => {
|
|
917
|
+
const { bufferSource, noteInfo, noteNumber } = activeNote;
|
|
918
|
+
const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
919
|
+
bufferSource.playbackRate
|
|
920
|
+
.cancelScheduledValues(now)
|
|
921
|
+
.setValueAtTime(playbackRate * pressure, now);
|
|
922
|
+
});
|
|
920
923
|
}
|
|
921
924
|
handleControlChange(channelNumber, controller, value) {
|
|
922
925
|
switch (controller) {
|
|
@@ -976,13 +979,9 @@ class MidyGM2 {
|
|
|
976
979
|
this.channels[channelNumber].bankMSB = msb;
|
|
977
980
|
}
|
|
978
981
|
setModulation(channelNumber, modulation) {
|
|
979
|
-
const now = this.audioContext.currentTime;
|
|
980
982
|
const channel = this.channels[channelNumber];
|
|
981
|
-
channel.modulation = (modulation
|
|
982
|
-
channel.modulationDepthRange;
|
|
983
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
984
|
-
lfoGain.gain.cancelScheduledValues(now);
|
|
985
|
-
lfoGain.gain.setValueAtTime(channel.modulation, now);
|
|
983
|
+
channel.modulation = (modulation / 127) *
|
|
984
|
+
(channel.modulationDepthRange * 100);
|
|
986
985
|
}
|
|
987
986
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
988
987
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1017,7 +1016,7 @@ class MidyGM2 {
|
|
|
1017
1016
|
const isOn = value >= 64;
|
|
1018
1017
|
this.channels[channelNumber].sustainPedal = isOn;
|
|
1019
1018
|
if (!isOn) {
|
|
1020
|
-
this.releaseSustainPedal(channelNumber);
|
|
1019
|
+
this.releaseSustainPedal(channelNumber, value);
|
|
1021
1020
|
}
|
|
1022
1021
|
}
|
|
1023
1022
|
setPortamento(channelNumber, value) {
|
|
@@ -1046,11 +1045,13 @@ class MidyGM2 {
|
|
|
1046
1045
|
const activeNotes = this.getActiveNotes(channel);
|
|
1047
1046
|
channel.sostenutoNotes = new Map(activeNotes);
|
|
1048
1047
|
}
|
|
1048
|
+
else {
|
|
1049
|
+
this.releaseSostenutoPedal(channelNumber, value);
|
|
1050
|
+
}
|
|
1049
1051
|
}
|
|
1050
1052
|
setSoftPedal(channelNumber, softPedal) {
|
|
1051
1053
|
const channel = this.channels[channelNumber];
|
|
1052
1054
|
channel.softPedal = softPedal / 127;
|
|
1053
|
-
this.updateChannelGain(channel);
|
|
1054
1055
|
}
|
|
1055
1056
|
setRPNMSB(channelNumber, value) {
|
|
1056
1057
|
this.channels[channelNumber].rpnMSB = value;
|
|
@@ -1191,10 +1192,10 @@ class MidyGM2 {
|
|
|
1191
1192
|
switch (data[3]) {
|
|
1192
1193
|
// case 1:
|
|
1193
1194
|
// // TODO
|
|
1194
|
-
// return this.
|
|
1195
|
+
// return this.setChannelPressure();
|
|
1195
1196
|
// case 3:
|
|
1196
1197
|
// // TODO
|
|
1197
|
-
// return this.
|
|
1198
|
+
// return this.setControlChange();
|
|
1198
1199
|
default:
|
|
1199
1200
|
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
1200
1201
|
}
|
|
@@ -1213,20 +1214,25 @@ class MidyGM2 {
|
|
|
1213
1214
|
}
|
|
1214
1215
|
}
|
|
1215
1216
|
handleMasterVolumeSysEx(data) {
|
|
1216
|
-
const volume = (data[5] * 128 + data[4]
|
|
1217
|
+
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
1217
1218
|
this.handleMasterVolume(volume);
|
|
1218
1219
|
}
|
|
1219
1220
|
handleMasterVolume(volume) {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1221
|
+
if (volume < 0 && 1 < volume) {
|
|
1222
|
+
console.error("Master Volume is out of range");
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
const now = this.audioContext.currentTime;
|
|
1226
|
+
this.masterGain.gain.cancelScheduledValues(now);
|
|
1227
|
+
this.masterGain.gain.setValueAtTime(volume * volume, now);
|
|
1228
|
+
}
|
|
1223
1229
|
}
|
|
1224
1230
|
handleMasterFineTuningSysEx(data) {
|
|
1225
1231
|
const fineTuning = (data[5] * 128 + data[4] - 8192) / 8192;
|
|
1226
1232
|
this.handleMasterFineTuning(fineTuning);
|
|
1227
1233
|
}
|
|
1228
1234
|
handleMasterFineTuning(fineTuning) {
|
|
1229
|
-
if (fineTuning <
|
|
1235
|
+
if (fineTuning < -1 && 1 < fineTuning) {
|
|
1230
1236
|
console.error("Master Fine Tuning value is out of range");
|
|
1231
1237
|
}
|
|
1232
1238
|
else {
|
|
@@ -1277,7 +1283,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1277
1283
|
writable: true,
|
|
1278
1284
|
value: {
|
|
1279
1285
|
currentBufferSource: null,
|
|
1280
|
-
volume:
|
|
1286
|
+
volume: 100 / 127,
|
|
1281
1287
|
pan: 0,
|
|
1282
1288
|
portamentoTime: 0,
|
|
1283
1289
|
reverb: 0,
|
|
@@ -1294,7 +1300,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1294
1300
|
pitchBend: 0,
|
|
1295
1301
|
fineTuning: 0,
|
|
1296
1302
|
coarseTuning: 0,
|
|
1297
|
-
modulationDepthRange:
|
|
1303
|
+
modulationDepthRange: 0.5,
|
|
1298
1304
|
}
|
|
1299
1305
|
});
|
|
1300
1306
|
Object.defineProperty(MidyGM2, "effectSettings", {
|
|
@@ -1314,3 +1320,16 @@ Object.defineProperty(MidyGM2, "effectSettings", {
|
|
|
1314
1320
|
pitchBendRange: 2,
|
|
1315
1321
|
}
|
|
1316
1322
|
});
|
|
1323
|
+
Object.defineProperty(MidyGM2, "controllerDestinationSettings", {
|
|
1324
|
+
enumerable: true,
|
|
1325
|
+
configurable: true,
|
|
1326
|
+
writable: true,
|
|
1327
|
+
value: {
|
|
1328
|
+
pitchControl: 0,
|
|
1329
|
+
filterCutoffControl: 0,
|
|
1330
|
+
amplitudeControl: 1,
|
|
1331
|
+
lfoPitchDepth: 0,
|
|
1332
|
+
lfoFilterDepth: 0,
|
|
1333
|
+
lfoAmplitudeDepth: 0,
|
|
1334
|
+
}
|
|
1335
|
+
});
|
package/script/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;
|
|
@@ -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"}
|