@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-GMLite.js
CHANGED
|
@@ -11,12 +11,6 @@ class MidyGMLite {
|
|
|
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,
|
|
@@ -147,10 +141,10 @@ class MidyGMLite {
|
|
|
147
141
|
const response = await fetch(midiUrl);
|
|
148
142
|
const arrayBuffer = await response.arrayBuffer();
|
|
149
143
|
const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
|
|
144
|
+
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
150
145
|
const midiData = this.extractMidiData(midi);
|
|
151
146
|
this.instruments = midiData.instruments;
|
|
152
147
|
this.timeline = midiData.timeline;
|
|
153
|
-
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
154
148
|
this.totalTime = this.calcTotalTime();
|
|
155
149
|
}
|
|
156
150
|
setChannelAudioNodes(audioContext) {
|
|
@@ -229,28 +223,30 @@ class MidyGMLite {
|
|
|
229
223
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
230
224
|
while (queueIndex < this.timeline.length) {
|
|
231
225
|
const event = this.timeline[queueIndex];
|
|
232
|
-
|
|
233
|
-
if (time > t + this.lookAhead)
|
|
226
|
+
if (event.startTime > t + this.lookAhead)
|
|
234
227
|
break;
|
|
235
228
|
switch (event.type) {
|
|
236
|
-
case "controller":
|
|
237
|
-
this.handleControlChange(event.channel, event.controllerType, event.value);
|
|
238
|
-
break;
|
|
239
229
|
case "noteOn":
|
|
240
|
-
|
|
241
|
-
|
|
230
|
+
if (event.velocity !== 0) {
|
|
231
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
/* falls through */
|
|
242
235
|
case "noteOff": {
|
|
243
|
-
const notePromise = this.scheduleNoteRelease(event.channel, event.noteNumber, event.velocity,
|
|
236
|
+
const notePromise = this.scheduleNoteRelease(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
244
237
|
if (notePromise) {
|
|
245
238
|
this.notePromises.push(notePromise);
|
|
246
239
|
}
|
|
247
240
|
break;
|
|
248
241
|
}
|
|
242
|
+
case "controller":
|
|
243
|
+
this.handleControlChange(event.channel, event.controllerType, event.value);
|
|
244
|
+
break;
|
|
249
245
|
case "programChange":
|
|
250
246
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
251
247
|
break;
|
|
252
|
-
case "
|
|
253
|
-
this.
|
|
248
|
+
case "pitchBend":
|
|
249
|
+
this.handlePitchBend(event.channel, event.value);
|
|
254
250
|
break;
|
|
255
251
|
case "sysEx":
|
|
256
252
|
this.handleSysEx(event.data);
|
|
@@ -260,9 +256,8 @@ class MidyGMLite {
|
|
|
260
256
|
return queueIndex;
|
|
261
257
|
}
|
|
262
258
|
getQueueIndex(second) {
|
|
263
|
-
const ticks = this.secondToTicks(second, this.secondsPerBeat);
|
|
264
259
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
265
|
-
if (
|
|
260
|
+
if (second <= this.timeline[i].startTime) {
|
|
266
261
|
return i;
|
|
267
262
|
}
|
|
268
263
|
}
|
|
@@ -370,18 +365,28 @@ class MidyGMLite {
|
|
|
370
365
|
timeline.push(event);
|
|
371
366
|
});
|
|
372
367
|
});
|
|
368
|
+
const priority = {
|
|
369
|
+
setTempo: 0,
|
|
370
|
+
controller: 1,
|
|
371
|
+
};
|
|
373
372
|
timeline.sort((a, b) => {
|
|
374
|
-
if (a.ticks !== b.ticks)
|
|
373
|
+
if (a.ticks !== b.ticks)
|
|
375
374
|
return a.ticks - b.ticks;
|
|
376
|
-
|
|
377
|
-
if (a.type !== "controller" && b.type === "controller") {
|
|
378
|
-
return -1;
|
|
379
|
-
}
|
|
380
|
-
if (a.type === "controller" && b.type !== "controller") {
|
|
381
|
-
return 1;
|
|
382
|
-
}
|
|
383
|
-
return 0;
|
|
375
|
+
return (priority[a.type] || 2) - (priority[b.type] || 2);
|
|
384
376
|
});
|
|
377
|
+
let prevTempoTime = 0;
|
|
378
|
+
let prevTempoTicks = 0;
|
|
379
|
+
let secondsPerBeat = 0.5;
|
|
380
|
+
for (let i = 0; i < timeline.length; i++) {
|
|
381
|
+
const event = timeline[i];
|
|
382
|
+
const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
383
|
+
event.startTime = prevTempoTime + timeFromPrevTempo;
|
|
384
|
+
if (event.type === "setTempo") {
|
|
385
|
+
prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
386
|
+
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
387
|
+
prevTempoTicks = event.ticks;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
385
390
|
return { instruments, timeline };
|
|
386
391
|
}
|
|
387
392
|
stopNotes() {
|
|
@@ -433,32 +438,12 @@ class MidyGMLite {
|
|
|
433
438
|
}
|
|
434
439
|
}
|
|
435
440
|
calcTotalTime() {
|
|
436
|
-
const endOfTracks = [];
|
|
437
|
-
let prevTicks = 0;
|
|
438
441
|
let totalTime = 0;
|
|
439
|
-
let secondsPerBeat = 0.5;
|
|
440
442
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
441
443
|
const event = this.timeline[i];
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const durationTicks = event.ticks - prevTicks;
|
|
445
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
446
|
-
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
447
|
-
prevTicks = event.ticks;
|
|
448
|
-
break;
|
|
449
|
-
}
|
|
450
|
-
case "endOfTrack":
|
|
451
|
-
endOfTracks.push(event);
|
|
452
|
-
}
|
|
444
|
+
if (totalTime < event.startTime)
|
|
445
|
+
totalTime = event.startTime;
|
|
453
446
|
}
|
|
454
|
-
let maxTicks = 0;
|
|
455
|
-
for (let i = 0; i < endOfTracks.length; i++) {
|
|
456
|
-
const event = endOfTracks[i];
|
|
457
|
-
if (maxTicks < event.ticks)
|
|
458
|
-
maxTicks = event.ticks;
|
|
459
|
-
}
|
|
460
|
-
const durationTicks = maxTicks - prevTicks;
|
|
461
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
462
447
|
return totalTime;
|
|
463
448
|
}
|
|
464
449
|
currentTime() {
|
|
@@ -486,43 +471,8 @@ class MidyGMLite {
|
|
|
486
471
|
const lfo = new OscillatorNode(audioContext, {
|
|
487
472
|
frequency: 5,
|
|
488
473
|
});
|
|
489
|
-
const lfoGain = new GainNode(audioContext);
|
|
490
|
-
lfo.connect(lfoGain);
|
|
491
474
|
return {
|
|
492
475
|
lfo,
|
|
493
|
-
lfoGain,
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
createReverbEffect(audioContext, options = {}) {
|
|
497
|
-
const { decay = 0.8, preDecay = 0, } = options;
|
|
498
|
-
const sampleRate = audioContext.sampleRate;
|
|
499
|
-
const length = sampleRate * decay;
|
|
500
|
-
const impulse = new AudioBuffer({
|
|
501
|
-
numberOfChannels: 2,
|
|
502
|
-
length,
|
|
503
|
-
sampleRate,
|
|
504
|
-
});
|
|
505
|
-
const preDecayLength = Math.min(sampleRate * preDecay, length);
|
|
506
|
-
for (let channel = 0; channel < impulse.numberOfChannels; channel++) {
|
|
507
|
-
const channelData = impulse.getChannelData(channel);
|
|
508
|
-
for (let i = 0; i < preDecayLength; i++) {
|
|
509
|
-
channelData[i] = Math.random() * 2 - 1;
|
|
510
|
-
}
|
|
511
|
-
for (let i = preDecayLength; i < length; i++) {
|
|
512
|
-
const attenuation = Math.exp(-(i - preDecayLength) / sampleRate / decay);
|
|
513
|
-
channelData[i] = (Math.random() * 2 - 1) * attenuation;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
const convolverNode = new ConvolverNode(audioContext, {
|
|
517
|
-
buffer: impulse,
|
|
518
|
-
});
|
|
519
|
-
const dryGain = new GainNode(audioContext);
|
|
520
|
-
const wetGain = new GainNode(audioContext);
|
|
521
|
-
convolverNode.connect(wetGain);
|
|
522
|
-
return {
|
|
523
|
-
convolverNode,
|
|
524
|
-
dryGain,
|
|
525
|
-
wetGain,
|
|
526
476
|
};
|
|
527
477
|
}
|
|
528
478
|
connectNoteEffects(channel, gainNode) {
|
|
@@ -534,19 +484,23 @@ class MidyGMLite {
|
|
|
534
484
|
centToHz(cent) {
|
|
535
485
|
return 8.176 * Math.pow(2, cent / 1200);
|
|
536
486
|
}
|
|
487
|
+
calcSemitoneOffset(channel) {
|
|
488
|
+
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
489
|
+
}
|
|
490
|
+
calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
|
|
491
|
+
return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
|
|
492
|
+
}
|
|
537
493
|
async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
|
|
538
|
-
const semitoneOffset = channel.pitchBend * channel.pitchBendRange;
|
|
539
|
-
const playbackRate = noteInfo.playbackRate(noteNumber) *
|
|
540
|
-
Math.pow(2, semitoneOffset / 12);
|
|
541
494
|
const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
|
|
542
|
-
|
|
495
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
496
|
+
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
543
497
|
// volume envelope
|
|
544
498
|
const gainNode = new GainNode(this.audioContext, {
|
|
545
499
|
gain: 0,
|
|
546
500
|
});
|
|
547
501
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
548
502
|
if (volume === 0)
|
|
549
|
-
volume = 1e-6; // exponentialRampToValueAtTime()
|
|
503
|
+
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
550
504
|
const attackVolume = this.cbToRatio(-noteInfo.initialAttenuation) * volume;
|
|
551
505
|
const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
|
|
552
506
|
const volDelay = startTime + noteInfo.volDelay;
|
|
@@ -558,12 +512,6 @@ class MidyGMLite {
|
|
|
558
512
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
559
513
|
.setValueAtTime(attackVolume, volHold)
|
|
560
514
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
561
|
-
if (channel.modulation > 0) {
|
|
562
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
563
|
-
lfoGain.connect(bufferSource.detune);
|
|
564
|
-
lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
|
|
565
|
-
lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
|
|
566
|
-
}
|
|
567
515
|
// filter envelope
|
|
568
516
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
569
517
|
const baseFreq = this.centToHz(noteInfo.initialFilterFc);
|
|
@@ -587,10 +535,23 @@ class MidyGMLite {
|
|
|
587
535
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
588
536
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
589
537
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
538
|
+
let lfoGain;
|
|
539
|
+
if (channel.modulation > 0) {
|
|
540
|
+
const vibratoDelay = startTime + channel.vibratoDelay;
|
|
541
|
+
const vibratoAttack = vibratoDelay + 0.1;
|
|
542
|
+
lfoGain = new GainNode(this.audioContext, {
|
|
543
|
+
gain: 0,
|
|
544
|
+
});
|
|
545
|
+
lfoGain.gain
|
|
546
|
+
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
547
|
+
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
548
|
+
channel.modulationEffect.lfo.connect(lfoGain);
|
|
549
|
+
lfoGain.connect(bufferSource.detune);
|
|
550
|
+
}
|
|
590
551
|
bufferSource.connect(filterNode);
|
|
591
552
|
filterNode.connect(gainNode);
|
|
592
553
|
bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
|
|
593
|
-
return { bufferSource, gainNode, filterNode };
|
|
554
|
+
return { bufferSource, gainNode, filterNode, lfoGain };
|
|
594
555
|
}
|
|
595
556
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
596
557
|
const channel = this.channels[channelNumber];
|
|
@@ -603,16 +564,17 @@ class MidyGMLite {
|
|
|
603
564
|
const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
604
565
|
if (!noteInfo)
|
|
605
566
|
return;
|
|
606
|
-
const { bufferSource, gainNode, filterNode } = await this
|
|
567
|
+
const { bufferSource, gainNode, filterNode, lfoGain } = await this
|
|
607
568
|
.createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
|
|
608
569
|
this.connectNoteEffects(channel, gainNode);
|
|
609
570
|
const scheduledNotes = channel.scheduledNotes;
|
|
610
571
|
const scheduledNote = {
|
|
611
|
-
gainNode,
|
|
612
|
-
filterNode,
|
|
613
572
|
bufferSource,
|
|
614
|
-
|
|
573
|
+
filterNode,
|
|
574
|
+
gainNode,
|
|
575
|
+
lfoGain,
|
|
615
576
|
noteInfo,
|
|
577
|
+
noteNumber,
|
|
616
578
|
startTime,
|
|
617
579
|
};
|
|
618
580
|
if (scheduledNotes.has(noteNumber)) {
|
|
@@ -639,7 +601,7 @@ class MidyGMLite {
|
|
|
639
601
|
continue;
|
|
640
602
|
if (targetNote.ending)
|
|
641
603
|
continue;
|
|
642
|
-
const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
|
|
604
|
+
const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
|
|
643
605
|
const velocityRate = (velocity + 127) / 127;
|
|
644
606
|
const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
|
|
645
607
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
@@ -661,6 +623,8 @@ class MidyGMLite {
|
|
|
661
623
|
bufferSource.disconnect(0);
|
|
662
624
|
filterNode.disconnect(0);
|
|
663
625
|
gainNode.disconnect(0);
|
|
626
|
+
if (lfoGain)
|
|
627
|
+
lfoGain.disconnect(0);
|
|
664
628
|
resolve();
|
|
665
629
|
};
|
|
666
630
|
bufferSource.stop(volEndTime);
|
|
@@ -671,28 +635,21 @@ class MidyGMLite {
|
|
|
671
635
|
const now = this.audioContext.currentTime;
|
|
672
636
|
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
673
637
|
}
|
|
674
|
-
releaseSustainPedal(channelNumber) {
|
|
675
|
-
const
|
|
638
|
+
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
639
|
+
const velocity = halfVelocity * 2;
|
|
676
640
|
const channel = this.channels[channelNumber];
|
|
641
|
+
const promises = [];
|
|
677
642
|
channel.sustainPedal = false;
|
|
678
643
|
channel.scheduledNotes.forEach((scheduledNotes) => {
|
|
679
644
|
scheduledNotes.forEach((scheduledNote) => {
|
|
680
645
|
if (scheduledNote) {
|
|
681
|
-
const {
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
685
|
-
const maxFreq = this.audioContext.sampleRate / 2;
|
|
686
|
-
const baseFreq = this.centToHz(noteInfo.initialFilterFc);
|
|
687
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
688
|
-
const modEndTime = now + noteInfo.modRelease;
|
|
689
|
-
filterNode.frequency
|
|
690
|
-
.cancelScheduledValues(stopTime)
|
|
691
|
-
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
692
|
-
bufferSource.stop(volEndTime);
|
|
646
|
+
const { noteNumber } = scheduledNote;
|
|
647
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
648
|
+
promises.push(promise);
|
|
693
649
|
}
|
|
694
650
|
});
|
|
695
651
|
});
|
|
652
|
+
return promises;
|
|
696
653
|
}
|
|
697
654
|
handleMIDIMessage(statusByte, data1, data2) {
|
|
698
655
|
const channelNumber = statusByte & 0x0F;
|
|
@@ -702,46 +659,37 @@ class MidyGMLite {
|
|
|
702
659
|
return this.releaseNote(channelNumber, data1, data2);
|
|
703
660
|
case 0x90:
|
|
704
661
|
return this.noteOn(channelNumber, data1, data2);
|
|
705
|
-
case 0xA0:
|
|
706
|
-
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
707
662
|
case 0xB0:
|
|
708
663
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
709
664
|
case 0xC0:
|
|
710
665
|
return this.handleProgramChange(channelNumber, data1);
|
|
711
|
-
case 0xD0:
|
|
712
|
-
return this.handleChannelPressure(channelNumber, data1);
|
|
713
666
|
case 0xE0:
|
|
714
|
-
return this.
|
|
667
|
+
return this.handlePitchBendMessage(channelNumber, data1, data2);
|
|
715
668
|
default:
|
|
716
669
|
console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
|
|
717
670
|
}
|
|
718
671
|
}
|
|
719
|
-
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
720
|
-
const now = this.audioContext.currentTime;
|
|
721
|
-
const channel = this.channels[channelNumber];
|
|
722
|
-
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
723
|
-
pressure /= 127;
|
|
724
|
-
if (scheduledNotes) {
|
|
725
|
-
scheduledNotes.forEach((scheduledNote) => {
|
|
726
|
-
if (scheduledNote) {
|
|
727
|
-
const { initialAttenuation } = scheduledNote.noteInfo;
|
|
728
|
-
const gain = this.cbToRatio(-initialAttenuation) * pressure;
|
|
729
|
-
scheduledNote.gainNode.gain.cancelScheduledValues(now);
|
|
730
|
-
scheduledNote.gainNode.gain.setValueAtTime(gain, now);
|
|
731
|
-
}
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
672
|
handleProgramChange(channelNumber, program) {
|
|
736
673
|
const channel = this.channels[channelNumber];
|
|
737
674
|
channel.program = program;
|
|
738
675
|
}
|
|
739
|
-
|
|
740
|
-
|
|
676
|
+
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
677
|
+
const pitchBend = msb * 128 + lsb;
|
|
678
|
+
this.handlePitchBend(channelNumber, pitchBend);
|
|
741
679
|
}
|
|
742
|
-
handlePitchBend(channelNumber,
|
|
743
|
-
const
|
|
744
|
-
this.channels[channelNumber]
|
|
680
|
+
handlePitchBend(channelNumber, pitchBend) {
|
|
681
|
+
const now = this.audioContext.currentTime;
|
|
682
|
+
const channel = this.channels[channelNumber];
|
|
683
|
+
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
684
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
685
|
+
const activeNotes = this.getActiveNotes(channel);
|
|
686
|
+
activeNotes.forEach((activeNote) => {
|
|
687
|
+
const { bufferSource, noteInfo, noteNumber } = activeNote;
|
|
688
|
+
const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
689
|
+
bufferSource.playbackRate
|
|
690
|
+
.cancelScheduledValues(now)
|
|
691
|
+
.setValueAtTime(playbackRate * pressure, now);
|
|
692
|
+
});
|
|
745
693
|
}
|
|
746
694
|
handleControlChange(channelNumber, controller, value) {
|
|
747
695
|
switch (controller) {
|
|
@@ -774,13 +722,9 @@ class MidyGMLite {
|
|
|
774
722
|
}
|
|
775
723
|
}
|
|
776
724
|
setModulation(channelNumber, modulation) {
|
|
777
|
-
const now = this.audioContext.currentTime;
|
|
778
725
|
const channel = this.channels[channelNumber];
|
|
779
|
-
channel.modulation = (modulation
|
|
780
|
-
channel.modulationDepthRange;
|
|
781
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
782
|
-
lfoGain.gain.cancelScheduledValues(now);
|
|
783
|
-
lfoGain.gain.setValueAtTime(channel.modulation, now);
|
|
726
|
+
channel.modulation = (modulation / 127) *
|
|
727
|
+
(channel.modulationDepthRange * 100);
|
|
784
728
|
}
|
|
785
729
|
setVolume(channelNumber, volume) {
|
|
786
730
|
const channel = this.channels[channelNumber];
|
|
@@ -809,7 +753,7 @@ class MidyGMLite {
|
|
|
809
753
|
const isOn = value >= 64;
|
|
810
754
|
this.channels[channelNumber].sustainPedal = isOn;
|
|
811
755
|
if (!isOn) {
|
|
812
|
-
this.releaseSustainPedal(channelNumber);
|
|
756
|
+
this.releaseSustainPedal(channelNumber, value);
|
|
813
757
|
}
|
|
814
758
|
}
|
|
815
759
|
setRPNMSB(channelNumber, value) {
|
|
@@ -905,13 +849,18 @@ class MidyGMLite {
|
|
|
905
849
|
}
|
|
906
850
|
}
|
|
907
851
|
handleMasterVolumeSysEx(data) {
|
|
908
|
-
const volume = (data[5] * 128 + data[4]
|
|
852
|
+
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
909
853
|
this.handleMasterVolume(volume);
|
|
910
854
|
}
|
|
911
855
|
handleMasterVolume(volume) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
856
|
+
if (volume < 0 && 1 < volume) {
|
|
857
|
+
console.error("Master Volume is out of range");
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
const now = this.audioContext.currentTime;
|
|
861
|
+
this.masterGain.gain.cancelScheduledValues(now);
|
|
862
|
+
this.masterGain.gain.setValueAtTime(volume * volume, now);
|
|
863
|
+
}
|
|
915
864
|
}
|
|
916
865
|
handleExclusiveMessage(data) {
|
|
917
866
|
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
@@ -944,7 +893,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
944
893
|
configurable: true,
|
|
945
894
|
writable: true,
|
|
946
895
|
value: {
|
|
947
|
-
volume:
|
|
896
|
+
volume: 100 / 127,
|
|
948
897
|
pan: 0,
|
|
949
898
|
vibratoRate: 5,
|
|
950
899
|
vibratoDepth: 0.5,
|
|
@@ -954,7 +903,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
954
903
|
dataLSB: 0,
|
|
955
904
|
program: 0,
|
|
956
905
|
pitchBend: 0,
|
|
957
|
-
modulationDepthRange:
|
|
906
|
+
modulationDepthRange: 0.5,
|
|
958
907
|
}
|
|
959
908
|
});
|
|
960
909
|
Object.defineProperty(MidyGMLite, "effectSettings", {
|
package/script/midy.d.ts
CHANGED
|
@@ -32,9 +32,16 @@ export class Midy {
|
|
|
32
32
|
channelPressure: number;
|
|
33
33
|
pitchBendRange: number;
|
|
34
34
|
};
|
|
35
|
+
static controllerDestinationSettings: {
|
|
36
|
+
pitchControl: number;
|
|
37
|
+
filterCutoffControl: number;
|
|
38
|
+
amplitudeControl: number;
|
|
39
|
+
lfoPitchDepth: number;
|
|
40
|
+
lfoFilterDepth: number;
|
|
41
|
+
lfoAmplitudeDepth: number;
|
|
42
|
+
};
|
|
35
43
|
constructor(audioContext: any);
|
|
36
44
|
ticksPerBeat: number;
|
|
37
|
-
secondsPerBeat: number;
|
|
38
45
|
totalTime: number;
|
|
39
46
|
reverbFactor: number;
|
|
40
47
|
masterFineTuning: number;
|
|
@@ -61,11 +68,26 @@ export class Midy {
|
|
|
61
68
|
channels: {
|
|
62
69
|
scheduledNotes: Map<any, any>;
|
|
63
70
|
sostenutoNotes: Map<any, any>;
|
|
71
|
+
polyphonicKeyPressure: {
|
|
72
|
+
pitchControl: number;
|
|
73
|
+
filterCutoffControl: number;
|
|
74
|
+
amplitudeControl: number;
|
|
75
|
+
lfoPitchDepth: number;
|
|
76
|
+
lfoFilterDepth: number;
|
|
77
|
+
lfoAmplitudeDepth: number;
|
|
78
|
+
};
|
|
79
|
+
channelPressure: {
|
|
80
|
+
pitchControl: number;
|
|
81
|
+
filterCutoffControl: number;
|
|
82
|
+
amplitudeControl: number;
|
|
83
|
+
lfoPitchDepth: number;
|
|
84
|
+
lfoFilterDepth: number;
|
|
85
|
+
lfoAmplitudeDepth: number;
|
|
86
|
+
};
|
|
64
87
|
gainNode: any;
|
|
65
88
|
pannerNode: any;
|
|
66
89
|
modulationEffect: {
|
|
67
90
|
lfo: any;
|
|
68
|
-
lfoGain: any;
|
|
69
91
|
};
|
|
70
92
|
reverbEffect: {
|
|
71
93
|
convolverNode: any;
|
|
@@ -86,7 +108,6 @@ export class Midy {
|
|
|
86
108
|
softPedal: number;
|
|
87
109
|
rpnMSB: number;
|
|
88
110
|
rpnLSB: number;
|
|
89
|
-
channelPressure: number;
|
|
90
111
|
pitchBendRange: number;
|
|
91
112
|
currentBufferSource: null;
|
|
92
113
|
volume: number;
|
|
@@ -117,7 +138,6 @@ export class Midy {
|
|
|
117
138
|
pannerNode: any;
|
|
118
139
|
modulationEffect: {
|
|
119
140
|
lfo: any;
|
|
120
|
-
lfoGain: any;
|
|
121
141
|
};
|
|
122
142
|
reverbEffect: {
|
|
123
143
|
convolverNode: any;
|
|
@@ -134,11 +154,26 @@ export class Midy {
|
|
|
134
154
|
createChannels(audioContext: any): {
|
|
135
155
|
scheduledNotes: Map<any, any>;
|
|
136
156
|
sostenutoNotes: Map<any, any>;
|
|
157
|
+
polyphonicKeyPressure: {
|
|
158
|
+
pitchControl: number;
|
|
159
|
+
filterCutoffControl: number;
|
|
160
|
+
amplitudeControl: number;
|
|
161
|
+
lfoPitchDepth: number;
|
|
162
|
+
lfoFilterDepth: number;
|
|
163
|
+
lfoAmplitudeDepth: number;
|
|
164
|
+
};
|
|
165
|
+
channelPressure: {
|
|
166
|
+
pitchControl: number;
|
|
167
|
+
filterCutoffControl: number;
|
|
168
|
+
amplitudeControl: number;
|
|
169
|
+
lfoPitchDepth: number;
|
|
170
|
+
lfoFilterDepth: number;
|
|
171
|
+
lfoAmplitudeDepth: number;
|
|
172
|
+
};
|
|
137
173
|
gainNode: any;
|
|
138
174
|
pannerNode: any;
|
|
139
175
|
modulationEffect: {
|
|
140
176
|
lfo: any;
|
|
141
|
-
lfoGain: any;
|
|
142
177
|
};
|
|
143
178
|
reverbEffect: {
|
|
144
179
|
convolverNode: any;
|
|
@@ -159,7 +194,6 @@ export class Midy {
|
|
|
159
194
|
softPedal: number;
|
|
160
195
|
rpnMSB: number;
|
|
161
196
|
rpnLSB: number;
|
|
162
|
-
channelPressure: number;
|
|
163
197
|
pitchBendRange: number;
|
|
164
198
|
currentBufferSource: null;
|
|
165
199
|
volume: number;
|
|
@@ -205,7 +239,6 @@ export class Midy {
|
|
|
205
239
|
getActiveChannelNotes(scheduledNotes: any): any;
|
|
206
240
|
createModulationEffect(audioContext: any): {
|
|
207
241
|
lfo: any;
|
|
208
|
-
lfoGain: any;
|
|
209
242
|
};
|
|
210
243
|
createReverbEffect(audioContext: any, options?: {}): {
|
|
211
244
|
convolverNode: any;
|
|
@@ -221,23 +254,27 @@ export class Midy {
|
|
|
221
254
|
connectNoteEffects(channel: any, gainNode: any): void;
|
|
222
255
|
cbToRatio(cb: any): number;
|
|
223
256
|
centToHz(cent: any): number;
|
|
257
|
+
calcSemitoneOffset(channel: any): any;
|
|
258
|
+
calcPlaybackRate(noteInfo: any, noteNumber: any, semitoneOffset: any): number;
|
|
224
259
|
createNoteAudioChain(channel: any, noteInfo: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<{
|
|
225
260
|
bufferSource: any;
|
|
226
261
|
gainNode: any;
|
|
227
262
|
filterNode: any;
|
|
263
|
+
lfoGain: any;
|
|
228
264
|
}>;
|
|
229
265
|
calcBank(channel: any, channelNumber: any): any;
|
|
230
266
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
231
267
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
232
268
|
scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
|
|
233
269
|
releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
|
|
234
|
-
releaseSustainPedal(channelNumber: any):
|
|
235
|
-
|
|
270
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
271
|
+
releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
|
|
236
272
|
handleMIDIMessage(statusByte: any, data1: any, data2: any): any;
|
|
237
273
|
handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
|
|
238
274
|
handleProgramChange(channelNumber: any, program: any): void;
|
|
239
275
|
handleChannelPressure(channelNumber: any, pressure: any): void;
|
|
240
|
-
|
|
276
|
+
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
|
|
277
|
+
handlePitchBend(channelNumber: any, pitchBend: any): void;
|
|
241
278
|
handleControlChange(channelNumber: any, controller: any, value: any): any;
|
|
242
279
|
setBankMSB(channelNumber: any, msb: any): void;
|
|
243
280
|
setModulation(channelNumber: any, modulation: any): void;
|
package/script/midy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAMA;
|
|
1
|
+
{"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAMA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,cAAa;IACb,cAAa;IACb,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;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;;;;MAuBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA2GC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4CASC;IAED,gDAKC;IAED;;MAOC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,8EAEC;IAED;;;;;OAqFC;IAED,gDAQC;IAED,kGAgDC;IAED,0EAGC;IAED,sIA6CC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,0DAiBC;IAED,0EAkEC;IAED,+CAEC;IAED,yDAIC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAUC;IAED,uDAGC;IAED,2DAOC;IAED,6DAOC;IAED,6DASC;IAED,4CAkBC;IAED,4CAkBC;IAED,gDAEC;IAED,gDAEC;IAGD,+DAuBC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,sCAQC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
|