@marmooo/midy 0.0.2 → 0.0.3
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 +2 -11
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +64 -118
- package/esm/midy-GM2.d.ts +3 -7
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +74 -94
- package/esm/midy-GMLite.d.ts +2 -11
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +65 -119
- package/esm/midy.d.ts +2 -7
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +78 -96
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +2 -11
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +64 -118
- package/script/midy-GM2.d.ts +3 -7
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +74 -94
- package/script/midy-GMLite.d.ts +2 -11
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +65 -119
- package/script/midy.d.ts +2 -7
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +78 -96
package/script/midy.js
CHANGED
|
@@ -11,12 +11,6 @@ class Midy {
|
|
|
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 Midy {
|
|
|
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) {
|
|
@@ -266,18 +260,20 @@ class Midy {
|
|
|
266
260
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
267
261
|
while (queueIndex < this.timeline.length) {
|
|
268
262
|
const event = this.timeline[queueIndex];
|
|
269
|
-
|
|
270
|
-
if (time > t + this.lookAhead)
|
|
263
|
+
if (event.startTime > t + this.lookAhead)
|
|
271
264
|
break;
|
|
272
265
|
switch (event.type) {
|
|
273
266
|
case "controller":
|
|
274
267
|
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
275
268
|
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
|
}
|
|
@@ -286,9 +282,6 @@ class Midy {
|
|
|
286
282
|
case "programChange":
|
|
287
283
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
288
284
|
break;
|
|
289
|
-
case "setTempo":
|
|
290
|
-
this.secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
291
|
-
break;
|
|
292
285
|
case "sysEx":
|
|
293
286
|
this.handleSysEx(event.data);
|
|
294
287
|
}
|
|
@@ -297,9 +290,8 @@ class Midy {
|
|
|
297
290
|
return queueIndex;
|
|
298
291
|
}
|
|
299
292
|
getQueueIndex(second) {
|
|
300
|
-
const ticks = this.secondToTicks(second, this.secondsPerBeat);
|
|
301
293
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
302
|
-
if (
|
|
294
|
+
if (second <= this.timeline[i].startTime) {
|
|
303
295
|
return i;
|
|
304
296
|
}
|
|
305
297
|
}
|
|
@@ -441,18 +433,28 @@ class Midy {
|
|
|
441
433
|
timeline.push(event);
|
|
442
434
|
});
|
|
443
435
|
});
|
|
436
|
+
const priority = {
|
|
437
|
+
setTempo: 0,
|
|
438
|
+
controller: 1,
|
|
439
|
+
};
|
|
444
440
|
timeline.sort((a, b) => {
|
|
445
|
-
if (a.ticks !== b.ticks)
|
|
441
|
+
if (a.ticks !== b.ticks)
|
|
446
442
|
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;
|
|
443
|
+
return (priority[a.type] || 2) - (priority[b.type] || 2);
|
|
455
444
|
});
|
|
445
|
+
let prevTempoTime = 0;
|
|
446
|
+
let prevTempoTicks = 0;
|
|
447
|
+
let secondsPerBeat = 0.5;
|
|
448
|
+
for (let i = 0; i < timeline.length; i++) {
|
|
449
|
+
const event = timeline[i];
|
|
450
|
+
const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
451
|
+
event.startTime = prevTempoTime + timeFromPrevTempo;
|
|
452
|
+
if (event.type === "setTempo") {
|
|
453
|
+
prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
454
|
+
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
455
|
+
prevTempoTicks = event.ticks;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
456
458
|
return { instruments, timeline };
|
|
457
459
|
}
|
|
458
460
|
stopNotes() {
|
|
@@ -504,32 +506,12 @@ class Midy {
|
|
|
504
506
|
}
|
|
505
507
|
}
|
|
506
508
|
calcTotalTime() {
|
|
507
|
-
const endOfTracks = [];
|
|
508
|
-
let prevTicks = 0;
|
|
509
509
|
let totalTime = 0;
|
|
510
|
-
let secondsPerBeat = 0.5;
|
|
511
510
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
512
511
|
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;
|
|
512
|
+
if (totalTime < event.startTime)
|
|
513
|
+
totalTime = event.startTime;
|
|
530
514
|
}
|
|
531
|
-
const durationTicks = maxTicks - prevTicks;
|
|
532
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
533
515
|
return totalTime;
|
|
534
516
|
}
|
|
535
517
|
currentTime() {
|
|
@@ -557,11 +539,8 @@ class Midy {
|
|
|
557
539
|
const lfo = new OscillatorNode(audioContext, {
|
|
558
540
|
frequency: 5,
|
|
559
541
|
});
|
|
560
|
-
const lfoGain = new GainNode(audioContext);
|
|
561
|
-
lfo.connect(lfoGain);
|
|
562
542
|
return {
|
|
563
543
|
lfo,
|
|
564
|
-
lfoGain,
|
|
565
544
|
};
|
|
566
545
|
}
|
|
567
546
|
createReverbEffect(audioContext, options = {}) {
|
|
@@ -693,12 +672,6 @@ class Midy {
|
|
|
693
672
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
694
673
|
.setValueAtTime(attackVolume, volHold)
|
|
695
674
|
.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
675
|
// filter envelope
|
|
703
676
|
const softPedalFactor = 1 -
|
|
704
677
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
@@ -724,6 +697,19 @@ class Midy {
|
|
|
724
697
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
725
698
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
726
699
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
700
|
+
let lfoGain;
|
|
701
|
+
if (channel.modulation > 0) {
|
|
702
|
+
const vibratoDelay = startTime + channel.vibratoDelay;
|
|
703
|
+
const vibratoAttack = vibratoDelay + 0.1;
|
|
704
|
+
lfoGain = new GainNode(this.audioContext, {
|
|
705
|
+
gain: 0,
|
|
706
|
+
});
|
|
707
|
+
lfoGain.gain
|
|
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);
|
|
712
|
+
}
|
|
727
713
|
bufferSource.connect(filterNode);
|
|
728
714
|
filterNode.connect(gainNode);
|
|
729
715
|
if (this.mono && channel.currentBufferSource) {
|
|
@@ -767,11 +753,12 @@ class Midy {
|
|
|
767
753
|
}
|
|
768
754
|
const scheduledNotes = channel.scheduledNotes;
|
|
769
755
|
const scheduledNote = {
|
|
770
|
-
gainNode,
|
|
771
|
-
filterNode,
|
|
772
756
|
bufferSource,
|
|
773
|
-
|
|
757
|
+
filterNode,
|
|
758
|
+
gainNode,
|
|
759
|
+
lfoGain,
|
|
774
760
|
noteInfo,
|
|
761
|
+
noteNumber,
|
|
775
762
|
startTime,
|
|
776
763
|
};
|
|
777
764
|
if (scheduledNotes.has(noteNumber)) {
|
|
@@ -800,7 +787,7 @@ class Midy {
|
|
|
800
787
|
continue;
|
|
801
788
|
if (targetNote.ending)
|
|
802
789
|
continue;
|
|
803
|
-
const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
|
|
790
|
+
const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
|
|
804
791
|
const velocityRate = (velocity + 127) / 127;
|
|
805
792
|
const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
|
|
806
793
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
@@ -822,6 +809,8 @@ class Midy {
|
|
|
822
809
|
bufferSource.disconnect(0);
|
|
823
810
|
filterNode.disconnect(0);
|
|
824
811
|
gainNode.disconnect(0);
|
|
812
|
+
if (lfoGain)
|
|
813
|
+
lfoGain.disconnect(0);
|
|
825
814
|
resolve();
|
|
826
815
|
};
|
|
827
816
|
bufferSource.stop(volEndTime);
|
|
@@ -832,41 +821,34 @@ class Midy {
|
|
|
832
821
|
const now = this.audioContext.currentTime;
|
|
833
822
|
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
834
823
|
}
|
|
835
|
-
releaseSustainPedal(channelNumber) {
|
|
836
|
-
const
|
|
824
|
+
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
825
|
+
const velocity = halfVelocity * 2;
|
|
837
826
|
const channel = this.channels[channelNumber];
|
|
827
|
+
const promises = [];
|
|
838
828
|
channel.sustainPedal = false;
|
|
839
829
|
channel.scheduledNotes.forEach((scheduledNotes) => {
|
|
840
830
|
scheduledNotes.forEach((scheduledNote) => {
|
|
841
831
|
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);
|
|
832
|
+
const { noteNumber } = scheduledNote;
|
|
833
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
834
|
+
promises.push(promise);
|
|
854
835
|
}
|
|
855
836
|
});
|
|
856
837
|
});
|
|
838
|
+
return promises;
|
|
857
839
|
}
|
|
858
|
-
|
|
859
|
-
const
|
|
840
|
+
releaseSostenutoPedal(channelNumber, halfVelocity) {
|
|
841
|
+
const velocity = halfVelocity * 2;
|
|
860
842
|
const channel = this.channels[channelNumber];
|
|
843
|
+
const promises = [];
|
|
861
844
|
channel.sostenutoPedal = false;
|
|
862
845
|
channel.sostenutoNotes.forEach((activeNote) => {
|
|
863
|
-
const {
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
gainNode.gain.linearRampToValueAtTime(0, now + fadeTime);
|
|
867
|
-
bufferSource.stop(now + fadeTime);
|
|
846
|
+
const { noteNumber } = activeNote;
|
|
847
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
848
|
+
promises.push(promise);
|
|
868
849
|
});
|
|
869
850
|
channel.sostenutoNotes.clear();
|
|
851
|
+
return promises;
|
|
870
852
|
}
|
|
871
853
|
handleMIDIMessage(statusByte, data1, data2) {
|
|
872
854
|
const channelNumber = omni ? 0 : statusByte & 0x0F;
|
|
@@ -987,13 +969,9 @@ class Midy {
|
|
|
987
969
|
this.channels[channelNumber].bankMSB = msb;
|
|
988
970
|
}
|
|
989
971
|
setModulation(channelNumber, modulation) {
|
|
990
|
-
const now = this.audioContext.currentTime;
|
|
991
972
|
const channel = this.channels[channelNumber];
|
|
992
|
-
channel.modulation = (modulation
|
|
993
|
-
channel.modulationDepthRange;
|
|
994
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
995
|
-
lfoGain.gain.cancelScheduledValues(now);
|
|
996
|
-
lfoGain.gain.setValueAtTime(channel.modulation, now);
|
|
973
|
+
channel.modulation = (modulation / 127) *
|
|
974
|
+
(channel.modulationDepthRange * 100);
|
|
997
975
|
}
|
|
998
976
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
999
977
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
@@ -1028,7 +1006,7 @@ class Midy {
|
|
|
1028
1006
|
const isOn = value >= 64;
|
|
1029
1007
|
this.channels[channelNumber].sustainPedal = isOn;
|
|
1030
1008
|
if (!isOn) {
|
|
1031
|
-
this.releaseSustainPedal(channelNumber);
|
|
1009
|
+
this.releaseSustainPedal(channelNumber, value);
|
|
1032
1010
|
}
|
|
1033
1011
|
}
|
|
1034
1012
|
setPortamento(channelNumber, value) {
|
|
@@ -1057,25 +1035,29 @@ class Midy {
|
|
|
1057
1035
|
const activeNotes = this.getActiveNotes(channel);
|
|
1058
1036
|
channel.sostenutoNotes = new Map(activeNotes);
|
|
1059
1037
|
}
|
|
1038
|
+
else {
|
|
1039
|
+
this.releaseSostenutoPedal(channelNumber, value);
|
|
1040
|
+
}
|
|
1060
1041
|
}
|
|
1061
1042
|
setSoftPedal(channelNumber, softPedal) {
|
|
1062
1043
|
const channel = this.channels[channelNumber];
|
|
1063
1044
|
channel.softPedal = softPedal / 127;
|
|
1064
|
-
this.updateChannelGain(channel);
|
|
1065
1045
|
}
|
|
1066
1046
|
setVibratoRate(channelNumber, vibratoRate) {
|
|
1067
1047
|
const now = this.audioContext.currentTime;
|
|
1068
1048
|
const channel = this.channels[channelNumber];
|
|
1069
1049
|
channel.vibratoRate = vibratoRate / 127 * 4 + 3; // 3-7Hz
|
|
1070
|
-
channel.modulationEffect.lfo.frequency
|
|
1071
|
-
|
|
1050
|
+
channel.modulationEffect.lfo.frequency
|
|
1051
|
+
.cancelScheduledValues(now)
|
|
1052
|
+
.setValueAtTime(channel.vibratoRate, now);
|
|
1072
1053
|
}
|
|
1073
1054
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1074
1055
|
const now = this.audioContext.currentTime;
|
|
1075
1056
|
const channel = this.channels[channelNumber];
|
|
1076
1057
|
channel.vibratoDepth = vibratoDepth / 127;
|
|
1077
|
-
channel.modulationEffect.lfoGain.gain
|
|
1078
|
-
|
|
1058
|
+
channel.modulationEffect.lfoGain.gain
|
|
1059
|
+
.cancelScheduledValues(now)
|
|
1060
|
+
.setValueAtTime(channel.vibratoDepth, now);
|
|
1079
1061
|
}
|
|
1080
1062
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1081
1063
|
// Access Virus: 0-10sec
|
|
@@ -1347,7 +1329,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1347
1329
|
writable: true,
|
|
1348
1330
|
value: {
|
|
1349
1331
|
currentBufferSource: null,
|
|
1350
|
-
volume:
|
|
1332
|
+
volume: 100 / 127,
|
|
1351
1333
|
pan: 0,
|
|
1352
1334
|
portamentoTime: 0,
|
|
1353
1335
|
reverb: 0,
|
|
@@ -1364,7 +1346,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1364
1346
|
pitchBend: 0,
|
|
1365
1347
|
fineTuning: 0,
|
|
1366
1348
|
coarseTuning: 0,
|
|
1367
|
-
modulationDepthRange:
|
|
1349
|
+
modulationDepthRange: 0.5,
|
|
1368
1350
|
}
|
|
1369
1351
|
});
|
|
1370
1352
|
Object.defineProperty(Midy, "effectSettings", {
|