@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-GMLite.js
CHANGED
|
@@ -8,12 +8,6 @@ export class MidyGMLite {
|
|
|
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,
|
|
@@ -144,10 +138,10 @@ export class MidyGMLite {
|
|
|
144
138
|
const response = await fetch(midiUrl);
|
|
145
139
|
const arrayBuffer = await response.arrayBuffer();
|
|
146
140
|
const midi = parseMidi(new Uint8Array(arrayBuffer));
|
|
141
|
+
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
147
142
|
const midiData = this.extractMidiData(midi);
|
|
148
143
|
this.instruments = midiData.instruments;
|
|
149
144
|
this.timeline = midiData.timeline;
|
|
150
|
-
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
151
145
|
this.totalTime = this.calcTotalTime();
|
|
152
146
|
}
|
|
153
147
|
setChannelAudioNodes(audioContext) {
|
|
@@ -226,28 +220,30 @@ export class MidyGMLite {
|
|
|
226
220
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
227
221
|
while (queueIndex < this.timeline.length) {
|
|
228
222
|
const event = this.timeline[queueIndex];
|
|
229
|
-
|
|
230
|
-
if (time > t + this.lookAhead)
|
|
223
|
+
if (event.startTime > t + this.lookAhead)
|
|
231
224
|
break;
|
|
232
225
|
switch (event.type) {
|
|
233
|
-
case "controller":
|
|
234
|
-
this.handleControlChange(event.channel, event.controllerType, event.value);
|
|
235
|
-
break;
|
|
236
226
|
case "noteOn":
|
|
237
|
-
|
|
238
|
-
|
|
227
|
+
if (event.velocity !== 0) {
|
|
228
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
/* falls through */
|
|
239
232
|
case "noteOff": {
|
|
240
|
-
const notePromise = this.scheduleNoteRelease(event.channel, event.noteNumber, event.velocity,
|
|
233
|
+
const notePromise = this.scheduleNoteRelease(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
241
234
|
if (notePromise) {
|
|
242
235
|
this.notePromises.push(notePromise);
|
|
243
236
|
}
|
|
244
237
|
break;
|
|
245
238
|
}
|
|
239
|
+
case "controller":
|
|
240
|
+
this.handleControlChange(event.channel, event.controllerType, event.value);
|
|
241
|
+
break;
|
|
246
242
|
case "programChange":
|
|
247
243
|
this.handleProgramChange(event.channel, event.programNumber);
|
|
248
244
|
break;
|
|
249
|
-
case "
|
|
250
|
-
this.
|
|
245
|
+
case "pitchBend":
|
|
246
|
+
this.handlePitchBend(event.channel, event.value);
|
|
251
247
|
break;
|
|
252
248
|
case "sysEx":
|
|
253
249
|
this.handleSysEx(event.data);
|
|
@@ -257,9 +253,8 @@ export class MidyGMLite {
|
|
|
257
253
|
return queueIndex;
|
|
258
254
|
}
|
|
259
255
|
getQueueIndex(second) {
|
|
260
|
-
const ticks = this.secondToTicks(second, this.secondsPerBeat);
|
|
261
256
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
262
|
-
if (
|
|
257
|
+
if (second <= this.timeline[i].startTime) {
|
|
263
258
|
return i;
|
|
264
259
|
}
|
|
265
260
|
}
|
|
@@ -367,18 +362,28 @@ export class MidyGMLite {
|
|
|
367
362
|
timeline.push(event);
|
|
368
363
|
});
|
|
369
364
|
});
|
|
365
|
+
const priority = {
|
|
366
|
+
setTempo: 0,
|
|
367
|
+
controller: 1,
|
|
368
|
+
};
|
|
370
369
|
timeline.sort((a, b) => {
|
|
371
|
-
if (a.ticks !== b.ticks)
|
|
370
|
+
if (a.ticks !== b.ticks)
|
|
372
371
|
return a.ticks - b.ticks;
|
|
373
|
-
|
|
374
|
-
if (a.type !== "controller" && b.type === "controller") {
|
|
375
|
-
return -1;
|
|
376
|
-
}
|
|
377
|
-
if (a.type === "controller" && b.type !== "controller") {
|
|
378
|
-
return 1;
|
|
379
|
-
}
|
|
380
|
-
return 0;
|
|
372
|
+
return (priority[a.type] || 2) - (priority[b.type] || 2);
|
|
381
373
|
});
|
|
374
|
+
let prevTempoTime = 0;
|
|
375
|
+
let prevTempoTicks = 0;
|
|
376
|
+
let secondsPerBeat = 0.5;
|
|
377
|
+
for (let i = 0; i < timeline.length; i++) {
|
|
378
|
+
const event = timeline[i];
|
|
379
|
+
const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
380
|
+
event.startTime = prevTempoTime + timeFromPrevTempo;
|
|
381
|
+
if (event.type === "setTempo") {
|
|
382
|
+
prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
|
|
383
|
+
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
384
|
+
prevTempoTicks = event.ticks;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
382
387
|
return { instruments, timeline };
|
|
383
388
|
}
|
|
384
389
|
stopNotes() {
|
|
@@ -430,32 +435,12 @@ export class MidyGMLite {
|
|
|
430
435
|
}
|
|
431
436
|
}
|
|
432
437
|
calcTotalTime() {
|
|
433
|
-
const endOfTracks = [];
|
|
434
|
-
let prevTicks = 0;
|
|
435
438
|
let totalTime = 0;
|
|
436
|
-
let secondsPerBeat = 0.5;
|
|
437
439
|
for (let i = 0; i < this.timeline.length; i++) {
|
|
438
440
|
const event = this.timeline[i];
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const durationTicks = event.ticks - prevTicks;
|
|
442
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
443
|
-
secondsPerBeat = event.microsecondsPerBeat / 1000000;
|
|
444
|
-
prevTicks = event.ticks;
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
case "endOfTrack":
|
|
448
|
-
endOfTracks.push(event);
|
|
449
|
-
}
|
|
441
|
+
if (totalTime < event.startTime)
|
|
442
|
+
totalTime = event.startTime;
|
|
450
443
|
}
|
|
451
|
-
let maxTicks = 0;
|
|
452
|
-
for (let i = 0; i < endOfTracks.length; i++) {
|
|
453
|
-
const event = endOfTracks[i];
|
|
454
|
-
if (maxTicks < event.ticks)
|
|
455
|
-
maxTicks = event.ticks;
|
|
456
|
-
}
|
|
457
|
-
const durationTicks = maxTicks - prevTicks;
|
|
458
|
-
totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
|
|
459
444
|
return totalTime;
|
|
460
445
|
}
|
|
461
446
|
currentTime() {
|
|
@@ -483,43 +468,8 @@ export class MidyGMLite {
|
|
|
483
468
|
const lfo = new OscillatorNode(audioContext, {
|
|
484
469
|
frequency: 5,
|
|
485
470
|
});
|
|
486
|
-
const lfoGain = new GainNode(audioContext);
|
|
487
|
-
lfo.connect(lfoGain);
|
|
488
471
|
return {
|
|
489
472
|
lfo,
|
|
490
|
-
lfoGain,
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
createReverbEffect(audioContext, options = {}) {
|
|
494
|
-
const { decay = 0.8, preDecay = 0, } = options;
|
|
495
|
-
const sampleRate = audioContext.sampleRate;
|
|
496
|
-
const length = sampleRate * decay;
|
|
497
|
-
const impulse = new AudioBuffer({
|
|
498
|
-
numberOfChannels: 2,
|
|
499
|
-
length,
|
|
500
|
-
sampleRate,
|
|
501
|
-
});
|
|
502
|
-
const preDecayLength = Math.min(sampleRate * preDecay, length);
|
|
503
|
-
for (let channel = 0; channel < impulse.numberOfChannels; channel++) {
|
|
504
|
-
const channelData = impulse.getChannelData(channel);
|
|
505
|
-
for (let i = 0; i < preDecayLength; i++) {
|
|
506
|
-
channelData[i] = Math.random() * 2 - 1;
|
|
507
|
-
}
|
|
508
|
-
for (let i = preDecayLength; i < length; i++) {
|
|
509
|
-
const attenuation = Math.exp(-(i - preDecayLength) / sampleRate / decay);
|
|
510
|
-
channelData[i] = (Math.random() * 2 - 1) * attenuation;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
const convolverNode = new ConvolverNode(audioContext, {
|
|
514
|
-
buffer: impulse,
|
|
515
|
-
});
|
|
516
|
-
const dryGain = new GainNode(audioContext);
|
|
517
|
-
const wetGain = new GainNode(audioContext);
|
|
518
|
-
convolverNode.connect(wetGain);
|
|
519
|
-
return {
|
|
520
|
-
convolverNode,
|
|
521
|
-
dryGain,
|
|
522
|
-
wetGain,
|
|
523
473
|
};
|
|
524
474
|
}
|
|
525
475
|
connectNoteEffects(channel, gainNode) {
|
|
@@ -531,19 +481,23 @@ export class MidyGMLite {
|
|
|
531
481
|
centToHz(cent) {
|
|
532
482
|
return 8.176 * Math.pow(2, cent / 1200);
|
|
533
483
|
}
|
|
484
|
+
calcSemitoneOffset(channel) {
|
|
485
|
+
return channel.pitchBend * channel.pitchBendRange + tuning;
|
|
486
|
+
}
|
|
487
|
+
calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
|
|
488
|
+
return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
|
|
489
|
+
}
|
|
534
490
|
async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
|
|
535
|
-
const semitoneOffset = channel.pitchBend * channel.pitchBendRange;
|
|
536
|
-
const playbackRate = noteInfo.playbackRate(noteNumber) *
|
|
537
|
-
Math.pow(2, semitoneOffset / 12);
|
|
538
491
|
const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
|
|
539
|
-
|
|
492
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
493
|
+
bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
540
494
|
// volume envelope
|
|
541
495
|
const gainNode = new GainNode(this.audioContext, {
|
|
542
496
|
gain: 0,
|
|
543
497
|
});
|
|
544
498
|
let volume = (velocity / 127) * channel.volume * channel.expression;
|
|
545
499
|
if (volume === 0)
|
|
546
|
-
volume = 1e-6; // exponentialRampToValueAtTime()
|
|
500
|
+
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
547
501
|
const attackVolume = this.cbToRatio(-noteInfo.initialAttenuation) * volume;
|
|
548
502
|
const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
|
|
549
503
|
const volDelay = startTime + noteInfo.volDelay;
|
|
@@ -555,12 +509,6 @@ export class MidyGMLite {
|
|
|
555
509
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
556
510
|
.setValueAtTime(attackVolume, volHold)
|
|
557
511
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
558
|
-
if (channel.modulation > 0) {
|
|
559
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
560
|
-
lfoGain.connect(bufferSource.detune);
|
|
561
|
-
lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
|
|
562
|
-
lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
|
|
563
|
-
}
|
|
564
512
|
// filter envelope
|
|
565
513
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
566
514
|
const baseFreq = this.centToHz(noteInfo.initialFilterFc);
|
|
@@ -584,10 +532,23 @@ export class MidyGMLite {
|
|
|
584
532
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
585
533
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
586
534
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
535
|
+
let lfoGain;
|
|
536
|
+
if (channel.modulation > 0) {
|
|
537
|
+
const vibratoDelay = startTime + channel.vibratoDelay;
|
|
538
|
+
const vibratoAttack = vibratoDelay + 0.1;
|
|
539
|
+
lfoGain = new GainNode(this.audioContext, {
|
|
540
|
+
gain: 0,
|
|
541
|
+
});
|
|
542
|
+
lfoGain.gain
|
|
543
|
+
.setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
544
|
+
.exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
|
|
545
|
+
channel.modulationEffect.lfo.connect(lfoGain);
|
|
546
|
+
lfoGain.connect(bufferSource.detune);
|
|
547
|
+
}
|
|
587
548
|
bufferSource.connect(filterNode);
|
|
588
549
|
filterNode.connect(gainNode);
|
|
589
550
|
bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
|
|
590
|
-
return { bufferSource, gainNode, filterNode };
|
|
551
|
+
return { bufferSource, gainNode, filterNode, lfoGain };
|
|
591
552
|
}
|
|
592
553
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
593
554
|
const channel = this.channels[channelNumber];
|
|
@@ -600,16 +561,17 @@ export class MidyGMLite {
|
|
|
600
561
|
const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
|
|
601
562
|
if (!noteInfo)
|
|
602
563
|
return;
|
|
603
|
-
const { bufferSource, gainNode, filterNode } = await this
|
|
564
|
+
const { bufferSource, gainNode, filterNode, lfoGain } = await this
|
|
604
565
|
.createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
|
|
605
566
|
this.connectNoteEffects(channel, gainNode);
|
|
606
567
|
const scheduledNotes = channel.scheduledNotes;
|
|
607
568
|
const scheduledNote = {
|
|
608
|
-
gainNode,
|
|
609
|
-
filterNode,
|
|
610
569
|
bufferSource,
|
|
611
|
-
|
|
570
|
+
filterNode,
|
|
571
|
+
gainNode,
|
|
572
|
+
lfoGain,
|
|
612
573
|
noteInfo,
|
|
574
|
+
noteNumber,
|
|
613
575
|
startTime,
|
|
614
576
|
};
|
|
615
577
|
if (scheduledNotes.has(noteNumber)) {
|
|
@@ -636,7 +598,7 @@ export class MidyGMLite {
|
|
|
636
598
|
continue;
|
|
637
599
|
if (targetNote.ending)
|
|
638
600
|
continue;
|
|
639
|
-
const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
|
|
601
|
+
const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
|
|
640
602
|
const velocityRate = (velocity + 127) / 127;
|
|
641
603
|
const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
|
|
642
604
|
gainNode.gain.cancelScheduledValues(stopTime);
|
|
@@ -658,6 +620,8 @@ export class MidyGMLite {
|
|
|
658
620
|
bufferSource.disconnect(0);
|
|
659
621
|
filterNode.disconnect(0);
|
|
660
622
|
gainNode.disconnect(0);
|
|
623
|
+
if (lfoGain)
|
|
624
|
+
lfoGain.disconnect(0);
|
|
661
625
|
resolve();
|
|
662
626
|
};
|
|
663
627
|
bufferSource.stop(volEndTime);
|
|
@@ -668,28 +632,21 @@ export class MidyGMLite {
|
|
|
668
632
|
const now = this.audioContext.currentTime;
|
|
669
633
|
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
670
634
|
}
|
|
671
|
-
releaseSustainPedal(channelNumber) {
|
|
672
|
-
const
|
|
635
|
+
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
636
|
+
const velocity = halfVelocity * 2;
|
|
673
637
|
const channel = this.channels[channelNumber];
|
|
638
|
+
const promises = [];
|
|
674
639
|
channel.sustainPedal = false;
|
|
675
640
|
channel.scheduledNotes.forEach((scheduledNotes) => {
|
|
676
641
|
scheduledNotes.forEach((scheduledNote) => {
|
|
677
642
|
if (scheduledNote) {
|
|
678
|
-
const {
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
gainNode.gain.linearRampToValueAtTime(0, volEndTime);
|
|
682
|
-
const maxFreq = this.audioContext.sampleRate / 2;
|
|
683
|
-
const baseFreq = this.centToHz(noteInfo.initialFilterFc);
|
|
684
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
685
|
-
const modEndTime = now + noteInfo.modRelease;
|
|
686
|
-
filterNode.frequency
|
|
687
|
-
.cancelScheduledValues(stopTime)
|
|
688
|
-
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
689
|
-
bufferSource.stop(volEndTime);
|
|
643
|
+
const { noteNumber } = scheduledNote;
|
|
644
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
645
|
+
promises.push(promise);
|
|
690
646
|
}
|
|
691
647
|
});
|
|
692
648
|
});
|
|
649
|
+
return promises;
|
|
693
650
|
}
|
|
694
651
|
handleMIDIMessage(statusByte, data1, data2) {
|
|
695
652
|
const channelNumber = statusByte & 0x0F;
|
|
@@ -699,46 +656,37 @@ export class MidyGMLite {
|
|
|
699
656
|
return this.releaseNote(channelNumber, data1, data2);
|
|
700
657
|
case 0x90:
|
|
701
658
|
return this.noteOn(channelNumber, data1, data2);
|
|
702
|
-
case 0xA0:
|
|
703
|
-
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
704
659
|
case 0xB0:
|
|
705
660
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
706
661
|
case 0xC0:
|
|
707
662
|
return this.handleProgramChange(channelNumber, data1);
|
|
708
|
-
case 0xD0:
|
|
709
|
-
return this.handleChannelPressure(channelNumber, data1);
|
|
710
663
|
case 0xE0:
|
|
711
|
-
return this.
|
|
664
|
+
return this.handlePitchBendMessage(channelNumber, data1, data2);
|
|
712
665
|
default:
|
|
713
666
|
console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
|
|
714
667
|
}
|
|
715
668
|
}
|
|
716
|
-
handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
|
|
717
|
-
const now = this.audioContext.currentTime;
|
|
718
|
-
const channel = this.channels[channelNumber];
|
|
719
|
-
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
720
|
-
pressure /= 127;
|
|
721
|
-
if (scheduledNotes) {
|
|
722
|
-
scheduledNotes.forEach((scheduledNote) => {
|
|
723
|
-
if (scheduledNote) {
|
|
724
|
-
const { initialAttenuation } = scheduledNote.noteInfo;
|
|
725
|
-
const gain = this.cbToRatio(-initialAttenuation) * pressure;
|
|
726
|
-
scheduledNote.gainNode.gain.cancelScheduledValues(now);
|
|
727
|
-
scheduledNote.gainNode.gain.setValueAtTime(gain, now);
|
|
728
|
-
}
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
669
|
handleProgramChange(channelNumber, program) {
|
|
733
670
|
const channel = this.channels[channelNumber];
|
|
734
671
|
channel.program = program;
|
|
735
672
|
}
|
|
736
|
-
|
|
737
|
-
|
|
673
|
+
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
674
|
+
const pitchBend = msb * 128 + lsb;
|
|
675
|
+
this.handlePitchBend(channelNumber, pitchBend);
|
|
738
676
|
}
|
|
739
|
-
handlePitchBend(channelNumber,
|
|
740
|
-
const
|
|
741
|
-
this.channels[channelNumber]
|
|
677
|
+
handlePitchBend(channelNumber, pitchBend) {
|
|
678
|
+
const now = this.audioContext.currentTime;
|
|
679
|
+
const channel = this.channels[channelNumber];
|
|
680
|
+
channel.pitchBend = (pitchBend - 8192) / 8192;
|
|
681
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
682
|
+
const activeNotes = this.getActiveNotes(channel);
|
|
683
|
+
activeNotes.forEach((activeNote) => {
|
|
684
|
+
const { bufferSource, noteInfo, noteNumber } = activeNote;
|
|
685
|
+
const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
|
|
686
|
+
bufferSource.playbackRate
|
|
687
|
+
.cancelScheduledValues(now)
|
|
688
|
+
.setValueAtTime(playbackRate * pressure, now);
|
|
689
|
+
});
|
|
742
690
|
}
|
|
743
691
|
handleControlChange(channelNumber, controller, value) {
|
|
744
692
|
switch (controller) {
|
|
@@ -771,13 +719,9 @@ export class MidyGMLite {
|
|
|
771
719
|
}
|
|
772
720
|
}
|
|
773
721
|
setModulation(channelNumber, modulation) {
|
|
774
|
-
const now = this.audioContext.currentTime;
|
|
775
722
|
const channel = this.channels[channelNumber];
|
|
776
|
-
channel.modulation = (modulation
|
|
777
|
-
channel.modulationDepthRange;
|
|
778
|
-
const lfoGain = channel.modulationEffect.lfoGain;
|
|
779
|
-
lfoGain.gain.cancelScheduledValues(now);
|
|
780
|
-
lfoGain.gain.setValueAtTime(channel.modulation, now);
|
|
723
|
+
channel.modulation = (modulation / 127) *
|
|
724
|
+
(channel.modulationDepthRange * 100);
|
|
781
725
|
}
|
|
782
726
|
setVolume(channelNumber, volume) {
|
|
783
727
|
const channel = this.channels[channelNumber];
|
|
@@ -806,7 +750,7 @@ export class MidyGMLite {
|
|
|
806
750
|
const isOn = value >= 64;
|
|
807
751
|
this.channels[channelNumber].sustainPedal = isOn;
|
|
808
752
|
if (!isOn) {
|
|
809
|
-
this.releaseSustainPedal(channelNumber);
|
|
753
|
+
this.releaseSustainPedal(channelNumber, value);
|
|
810
754
|
}
|
|
811
755
|
}
|
|
812
756
|
setRPNMSB(channelNumber, value) {
|
|
@@ -902,13 +846,18 @@ export class MidyGMLite {
|
|
|
902
846
|
}
|
|
903
847
|
}
|
|
904
848
|
handleMasterVolumeSysEx(data) {
|
|
905
|
-
const volume = (data[5] * 128 + data[4]
|
|
849
|
+
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
906
850
|
this.handleMasterVolume(volume);
|
|
907
851
|
}
|
|
908
852
|
handleMasterVolume(volume) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
853
|
+
if (volume < 0 && 1 < volume) {
|
|
854
|
+
console.error("Master Volume is out of range");
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
const now = this.audioContext.currentTime;
|
|
858
|
+
this.masterGain.gain.cancelScheduledValues(now);
|
|
859
|
+
this.masterGain.gain.setValueAtTime(volume * volume, now);
|
|
860
|
+
}
|
|
912
861
|
}
|
|
913
862
|
handleExclusiveMessage(data) {
|
|
914
863
|
console.warn(`Unsupported Exclusive Message ${data}`);
|
|
@@ -940,7 +889,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
940
889
|
configurable: true,
|
|
941
890
|
writable: true,
|
|
942
891
|
value: {
|
|
943
|
-
volume:
|
|
892
|
+
volume: 100 / 127,
|
|
944
893
|
pan: 0,
|
|
945
894
|
vibratoRate: 5,
|
|
946
895
|
vibratoDepth: 0.5,
|
|
@@ -950,7 +899,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
950
899
|
dataLSB: 0,
|
|
951
900
|
program: 0,
|
|
952
901
|
pitchBend: 0,
|
|
953
|
-
modulationDepthRange:
|
|
902
|
+
modulationDepthRange: 0.5,
|
|
954
903
|
}
|
|
955
904
|
});
|
|
956
905
|
Object.defineProperty(MidyGMLite, "effectSettings", {
|
package/esm/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/esm/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"}
|