@marmooo/midy 0.3.2 → 0.3.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 +8 -7
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +67 -73
- package/esm/midy-GM2.d.ts +13 -11
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +124 -136
- package/esm/midy-GMLite.d.ts +9 -8
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +77 -91
- package/esm/midy.d.ts +13 -12
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +123 -140
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +8 -7
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +67 -73
- package/script/midy-GM2.d.ts +13 -11
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +124 -136
- package/script/midy-GMLite.d.ts +9 -8
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +77 -91
- package/script/midy.d.ts +13 -12
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +123 -140
package/esm/midy-GM2.js
CHANGED
|
@@ -8,11 +8,11 @@ class Note {
|
|
|
8
8
|
writable: true,
|
|
9
9
|
value: -1
|
|
10
10
|
});
|
|
11
|
-
Object.defineProperty(this, "
|
|
11
|
+
Object.defineProperty(this, "ending", {
|
|
12
12
|
enumerable: true,
|
|
13
13
|
configurable: true,
|
|
14
14
|
writable: true,
|
|
15
|
-
value:
|
|
15
|
+
value: false
|
|
16
16
|
});
|
|
17
17
|
Object.defineProperty(this, "bufferSource", {
|
|
18
18
|
enumerable: true,
|
|
@@ -455,17 +455,37 @@ export class MidyGM2 {
|
|
|
455
455
|
}
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
|
-
async loadSoundFont(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
458
|
+
async loadSoundFont(input) {
|
|
459
|
+
let uint8Array;
|
|
460
|
+
if (typeof input === "string") {
|
|
461
|
+
const response = await fetch(input);
|
|
462
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
463
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
464
|
+
}
|
|
465
|
+
else if (input instanceof Uint8Array) {
|
|
466
|
+
uint8Array = input;
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
470
|
+
}
|
|
471
|
+
const parsed = parse(uint8Array);
|
|
462
472
|
const soundFont = new SoundFont(parsed);
|
|
463
473
|
this.addSoundFont(soundFont);
|
|
464
474
|
}
|
|
465
|
-
async loadMIDI(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
475
|
+
async loadMIDI(input) {
|
|
476
|
+
let uint8Array;
|
|
477
|
+
if (typeof input === "string") {
|
|
478
|
+
const response = await fetch(input);
|
|
479
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
480
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
481
|
+
}
|
|
482
|
+
else if (input instanceof Uint8Array) {
|
|
483
|
+
uint8Array = input;
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
487
|
+
}
|
|
488
|
+
const midi = parseMidi(uint8Array);
|
|
469
489
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
470
490
|
const midiData = this.extractMidiData(midi);
|
|
471
491
|
this.instruments = midiData.instruments;
|
|
@@ -490,7 +510,8 @@ export class MidyGM2 {
|
|
|
490
510
|
this.resetControlTable(channel.controlTable);
|
|
491
511
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
492
512
|
channel.channelPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
493
|
-
channel.
|
|
513
|
+
channel.polyphonicKeyPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
514
|
+
channel.keyBasedInstrumentControlTable.fill(-1);
|
|
494
515
|
}
|
|
495
516
|
createChannels(audioContext) {
|
|
496
517
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -506,7 +527,7 @@ export class MidyGM2 {
|
|
|
506
527
|
controlTable: this.initControlTable(),
|
|
507
528
|
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
508
529
|
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
509
|
-
keyBasedInstrumentControlTable: new Int8Array(128 * 128)
|
|
530
|
+
keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
|
|
510
531
|
};
|
|
511
532
|
});
|
|
512
533
|
return channels;
|
|
@@ -540,10 +561,18 @@ export class MidyGM2 {
|
|
|
540
561
|
return audioBuffer;
|
|
541
562
|
}
|
|
542
563
|
}
|
|
543
|
-
|
|
564
|
+
isLoopDrum(channel, noteNumber) {
|
|
565
|
+
const programNumber = channel.programNumber;
|
|
566
|
+
return ((programNumber === 48 && noteNumber === 88) ||
|
|
567
|
+
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84));
|
|
568
|
+
}
|
|
569
|
+
createBufferSource(channel, noteNumber, voiceParams, audioBuffer) {
|
|
544
570
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
545
571
|
bufferSource.buffer = audioBuffer;
|
|
546
572
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
573
|
+
if (channel.isDrum) {
|
|
574
|
+
bufferSource.loop = this.isLoopDrum(channel, noteNumber);
|
|
575
|
+
}
|
|
547
576
|
if (bufferSource.loop) {
|
|
548
577
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
549
578
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -558,12 +587,13 @@ export class MidyGM2 {
|
|
|
558
587
|
const delay = this.startDelay - resumeTime;
|
|
559
588
|
const startTime = event.startTime + delay;
|
|
560
589
|
switch (event.type) {
|
|
561
|
-
case "noteOn":
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
590
|
+
case "noteOn":
|
|
591
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
592
|
+
break;
|
|
593
|
+
case "noteOff": {
|
|
594
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
595
|
+
if (notePromise)
|
|
596
|
+
this.notePromises.push(notePromise);
|
|
567
597
|
break;
|
|
568
598
|
}
|
|
569
599
|
case "controller":
|
|
@@ -668,6 +698,7 @@ export class MidyGM2 {
|
|
|
668
698
|
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
669
699
|
}
|
|
670
700
|
extractMidiData(midi) {
|
|
701
|
+
this.audioBufferCounter.clear();
|
|
671
702
|
const instruments = new Set();
|
|
672
703
|
const timeline = [];
|
|
673
704
|
const tmpChannels = new Array(this.channels.length);
|
|
@@ -767,38 +798,13 @@ export class MidyGM2 {
|
|
|
767
798
|
prevTempoTicks = event.ticks;
|
|
768
799
|
}
|
|
769
800
|
}
|
|
770
|
-
const activeNotes = new Array(this.channels.length * 128);
|
|
771
|
-
for (let i = 0; i < activeNotes.length; i++) {
|
|
772
|
-
activeNotes[i] = [];
|
|
773
|
-
}
|
|
774
|
-
for (let i = 0; i < timeline.length; i++) {
|
|
775
|
-
const event = timeline[i];
|
|
776
|
-
switch (event.type) {
|
|
777
|
-
case "noteOn": {
|
|
778
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
779
|
-
activeNotes[index].push(event);
|
|
780
|
-
break;
|
|
781
|
-
}
|
|
782
|
-
case "noteOff": {
|
|
783
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
784
|
-
const noteOn = activeNotes[index].pop();
|
|
785
|
-
if (noteOn) {
|
|
786
|
-
noteOn.noteOffEvent = event;
|
|
787
|
-
}
|
|
788
|
-
else {
|
|
789
|
-
const eventString = JSON.stringify(event, null, 2);
|
|
790
|
-
console.warn(`noteOff without matching noteOn: ${eventString}`);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
801
|
return { instruments, timeline };
|
|
796
802
|
}
|
|
797
803
|
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
798
804
|
const channel = this.channels[channelNumber];
|
|
799
805
|
const promises = [];
|
|
800
806
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
801
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force
|
|
807
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
802
808
|
this.notePromises.push(promise);
|
|
803
809
|
promises.push(promise);
|
|
804
810
|
});
|
|
@@ -808,7 +814,7 @@ export class MidyGM2 {
|
|
|
808
814
|
const channel = this.channels[channelNumber];
|
|
809
815
|
const promises = [];
|
|
810
816
|
this.processScheduledNotes(channel, (note) => {
|
|
811
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
|
|
817
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
812
818
|
this.notePromises.push(promise);
|
|
813
819
|
promises.push(promise);
|
|
814
820
|
});
|
|
@@ -885,9 +891,6 @@ export class MidyGM2 {
|
|
|
885
891
|
continue;
|
|
886
892
|
if (note.ending)
|
|
887
893
|
continue;
|
|
888
|
-
const noteOffEvent = note.noteOffEvent;
|
|
889
|
-
if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
|
|
890
|
-
continue;
|
|
891
894
|
if (scheduleTime < note.startTime)
|
|
892
895
|
continue;
|
|
893
896
|
callback(note);
|
|
@@ -1056,8 +1059,7 @@ export class MidyGM2 {
|
|
|
1056
1059
|
}
|
|
1057
1060
|
updateDetune(channel, note, scheduleTime) {
|
|
1058
1061
|
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1059
|
-
const
|
|
1060
|
-
const detune = channel.detune + noteDetune + pitchControl;
|
|
1062
|
+
const detune = channel.detune + noteDetune;
|
|
1061
1063
|
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1062
1064
|
const startTime = note.startTime;
|
|
1063
1065
|
const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
|
|
@@ -1197,10 +1199,8 @@ export class MidyGM2 {
|
|
|
1197
1199
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
1198
1200
|
}
|
|
1199
1201
|
setPortamentoFilterEnvelope(channel, note, scheduleTime) {
|
|
1200
|
-
const
|
|
1201
|
-
const
|
|
1202
|
-
const softPedalFactor = 1 -
|
|
1203
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1202
|
+
const { voiceParams, startTime } = note;
|
|
1203
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1204
1204
|
const baseCent = voiceParams.initialFilterFc +
|
|
1205
1205
|
this.getFilterCutoffControl(channel);
|
|
1206
1206
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
@@ -1218,10 +1218,8 @@ export class MidyGM2 {
|
|
|
1218
1218
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1219
1219
|
}
|
|
1220
1220
|
setFilterEnvelope(channel, note, scheduleTime) {
|
|
1221
|
-
const
|
|
1222
|
-
const
|
|
1223
|
-
const softPedalFactor = 1 -
|
|
1224
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1221
|
+
const { voiceParams, startTime } = note;
|
|
1222
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1225
1223
|
const baseCent = voiceParams.initialFilterFc +
|
|
1226
1224
|
this.getFilterCutoffControl(channel);
|
|
1227
1225
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
@@ -1301,7 +1299,7 @@ export class MidyGM2 {
|
|
|
1301
1299
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1302
1300
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1303
1301
|
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1304
|
-
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
1302
|
+
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1305
1303
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1306
1304
|
note.gainL = new GainNode(this.audioContext);
|
|
1307
1305
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1340,10 +1338,10 @@ export class MidyGM2 {
|
|
|
1340
1338
|
note.volumeEnvelopeNode.connect(note.volumeNode);
|
|
1341
1339
|
note.volumeNode.connect(note.gainL);
|
|
1342
1340
|
note.volumeNode.connect(note.gainR);
|
|
1343
|
-
if (0 <
|
|
1341
|
+
if (0 < state.chorusSendLevel) {
|
|
1344
1342
|
this.setChorusEffectsSend(channel, note, 0, now);
|
|
1345
1343
|
}
|
|
1346
|
-
if (0 <
|
|
1344
|
+
if (0 < state.reverbSendLevel) {
|
|
1347
1345
|
this.setReverbEffectsSend(channel, note, 0, now);
|
|
1348
1346
|
}
|
|
1349
1347
|
note.bufferSource.start(startTime);
|
|
@@ -1373,7 +1371,7 @@ export class MidyGM2 {
|
|
|
1373
1371
|
if (prev) {
|
|
1374
1372
|
const [prevNote, prevChannelNumber] = prev;
|
|
1375
1373
|
if (prevNote && !prevNote.ending) {
|
|
1376
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
|
|
1374
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1377
1375
|
startTime, true);
|
|
1378
1376
|
}
|
|
1379
1377
|
}
|
|
@@ -1393,7 +1391,7 @@ export class MidyGM2 {
|
|
|
1393
1391
|
channelNumber;
|
|
1394
1392
|
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1395
1393
|
if (prevNote && !prevNote.ending) {
|
|
1396
|
-
this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
|
|
1394
|
+
this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1397
1395
|
startTime, true);
|
|
1398
1396
|
}
|
|
1399
1397
|
this.drumExclusiveClassNotes[index] = note;
|
|
@@ -1428,31 +1426,6 @@ export class MidyGM2 {
|
|
|
1428
1426
|
const scheduledNotes = channel.scheduledNotes;
|
|
1429
1427
|
note.index = scheduledNotes.length;
|
|
1430
1428
|
scheduledNotes.push(note);
|
|
1431
|
-
if (this.isDrumNoteOffException(channel, noteNumber)) {
|
|
1432
|
-
const stopTime = startTime + note.bufferSource.buffer.duration;
|
|
1433
|
-
const promise = new Promise((resolve) => {
|
|
1434
|
-
note.bufferSource.onended = () => {
|
|
1435
|
-
scheduledNotes[note.index] = undefined;
|
|
1436
|
-
this.disconnectNote(note);
|
|
1437
|
-
resolve();
|
|
1438
|
-
};
|
|
1439
|
-
note.bufferSource.stop(stopTime);
|
|
1440
|
-
});
|
|
1441
|
-
this.notePromises.push(promise);
|
|
1442
|
-
}
|
|
1443
|
-
else if (noteOffEvent) {
|
|
1444
|
-
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1445
|
-
const portamentoTime = this.getPortamentoTime(channel, note);
|
|
1446
|
-
const portamentoEndTime = startTime + portamentoTime;
|
|
1447
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, 0, // velocity
|
|
1448
|
-
Math.max(noteOffEvent.startTime, portamentoEndTime), false);
|
|
1449
|
-
this.notePromises.push(notePromise);
|
|
1450
|
-
}
|
|
1451
|
-
else {
|
|
1452
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
|
|
1453
|
-
this.notePromises.push(notePromise);
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
1429
|
}
|
|
1457
1430
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1458
1431
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1481,41 +1454,47 @@ export class MidyGM2 {
|
|
|
1481
1454
|
note.chorusEffectsSend.disconnect();
|
|
1482
1455
|
}
|
|
1483
1456
|
}
|
|
1484
|
-
|
|
1457
|
+
releaseNote(channel, note, endTime) {
|
|
1458
|
+
const volRelease = endTime + note.voiceParams.volRelease;
|
|
1459
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1460
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1461
|
+
note.filterNode.frequency
|
|
1462
|
+
.cancelScheduledValues(endTime)
|
|
1463
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1485
1464
|
note.volumeEnvelopeNode.gain
|
|
1486
1465
|
.cancelScheduledValues(endTime)
|
|
1487
|
-
.linearRampToValueAtTime(0,
|
|
1488
|
-
note.ending = true;
|
|
1489
|
-
this.scheduleTask(() => {
|
|
1490
|
-
note.bufferSource.loop = false;
|
|
1491
|
-
}, stopTime);
|
|
1466
|
+
.linearRampToValueAtTime(0, volRelease);
|
|
1492
1467
|
return new Promise((resolve) => {
|
|
1493
|
-
|
|
1494
|
-
|
|
1468
|
+
this.scheduleTask(() => {
|
|
1469
|
+
const bufferSource = note.bufferSource;
|
|
1470
|
+
bufferSource.loop = false;
|
|
1471
|
+
bufferSource.stop(stopTime);
|
|
1495
1472
|
this.disconnectNote(note);
|
|
1473
|
+
channel.scheduledNotes[note.index] = undefined;
|
|
1496
1474
|
resolve();
|
|
1497
|
-
};
|
|
1498
|
-
note.bufferSource.stop(stopTime);
|
|
1475
|
+
}, stopTime);
|
|
1499
1476
|
});
|
|
1500
1477
|
}
|
|
1501
|
-
scheduleNoteOff(channelNumber,
|
|
1478
|
+
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1502
1479
|
const channel = this.channels[channelNumber];
|
|
1503
|
-
if (this.isDrumNoteOffException(channel, note.noteNumber))
|
|
1504
|
-
return;
|
|
1505
1480
|
const state = channel.state;
|
|
1506
1481
|
if (!force) {
|
|
1507
|
-
if (
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1482
|
+
if (channel.isDrum) {
|
|
1483
|
+
if (!this.isLoopDrum(channel, noteNumber))
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
else {
|
|
1487
|
+
if (0.5 <= state.sustainPedal)
|
|
1488
|
+
return;
|
|
1489
|
+
if (0.5 <= state.sostenutoPedal)
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1511
1492
|
}
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
const stopTime = Math.min(volRelease, modRelease);
|
|
1518
|
-
return this.stopNote(channel, note, endTime, stopTime);
|
|
1493
|
+
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1494
|
+
if (!note)
|
|
1495
|
+
return;
|
|
1496
|
+
note.ending = true;
|
|
1497
|
+
this.releaseNote(channel, note, endTime);
|
|
1519
1498
|
}
|
|
1520
1499
|
findNoteOffTarget(channel, noteNumber) {
|
|
1521
1500
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -1532,16 +1511,14 @@ export class MidyGM2 {
|
|
|
1532
1511
|
}
|
|
1533
1512
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1534
1513
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1535
|
-
|
|
1536
|
-
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1537
|
-
return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
|
|
1514
|
+
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1538
1515
|
}
|
|
1539
1516
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1540
1517
|
const velocity = halfVelocity * 2;
|
|
1541
1518
|
const channel = this.channels[channelNumber];
|
|
1542
1519
|
const promises = [];
|
|
1543
1520
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1544
|
-
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
|
|
1521
|
+
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1545
1522
|
promises.push(promise);
|
|
1546
1523
|
}
|
|
1547
1524
|
channel.sustainNotes = [];
|
|
@@ -1555,7 +1532,7 @@ export class MidyGM2 {
|
|
|
1555
1532
|
channel.state.sostenutoPedal = 0;
|
|
1556
1533
|
for (let i = 0; i < sostenutoNotes.length; i++) {
|
|
1557
1534
|
const note = sostenutoNotes[i];
|
|
1558
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime);
|
|
1535
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1559
1536
|
promises.push(promise);
|
|
1560
1537
|
}
|
|
1561
1538
|
channel.sostenutoNotes = [];
|
|
@@ -1665,10 +1642,13 @@ export class MidyGM2 {
|
|
|
1665
1642
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1666
1643
|
}
|
|
1667
1644
|
setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1645
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1646
|
+
let value = note.voiceParams.reverbEffectsSend;
|
|
1647
|
+
if (0 <= keyBasedValue) {
|
|
1648
|
+
value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
|
|
1649
|
+
}
|
|
1668
1650
|
if (0 < prevValue) {
|
|
1669
|
-
if (0 <
|
|
1670
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1671
|
-
const value = note.voiceParams.reverbEffectsSend + keyBasedValue;
|
|
1651
|
+
if (0 < value) {
|
|
1672
1652
|
note.reverbEffectsSend.gain
|
|
1673
1653
|
.cancelScheduledValues(scheduleTime)
|
|
1674
1654
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1678,10 +1658,10 @@ export class MidyGM2 {
|
|
|
1678
1658
|
}
|
|
1679
1659
|
}
|
|
1680
1660
|
else {
|
|
1681
|
-
if (0 <
|
|
1661
|
+
if (0 < value) {
|
|
1682
1662
|
if (!note.reverbEffectsSend) {
|
|
1683
1663
|
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1684
|
-
gain:
|
|
1664
|
+
gain: value,
|
|
1685
1665
|
});
|
|
1686
1666
|
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1687
1667
|
}
|
|
@@ -1690,10 +1670,13 @@ export class MidyGM2 {
|
|
|
1690
1670
|
}
|
|
1691
1671
|
}
|
|
1692
1672
|
setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1673
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1674
|
+
let value = note.voiceParams.chorusEffectsSend;
|
|
1675
|
+
if (0 <= keyBasedValue) {
|
|
1676
|
+
value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
|
|
1677
|
+
}
|
|
1693
1678
|
if (0 < prevValue) {
|
|
1694
|
-
if (0 <
|
|
1695
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1696
|
-
const value = note.voiceParams.chorusEffectsSend + keyBasedValue;
|
|
1679
|
+
if (0 < vaule) {
|
|
1697
1680
|
note.chorusEffectsSend.gain
|
|
1698
1681
|
.cancelScheduledValues(scheduleTime)
|
|
1699
1682
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1703,10 +1686,10 @@ export class MidyGM2 {
|
|
|
1703
1686
|
}
|
|
1704
1687
|
}
|
|
1705
1688
|
else {
|
|
1706
|
-
if (0 <
|
|
1689
|
+
if (0 < value) {
|
|
1707
1690
|
if (!note.chorusEffectsSend) {
|
|
1708
1691
|
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1709
|
-
gain:
|
|
1692
|
+
gain: value,
|
|
1710
1693
|
});
|
|
1711
1694
|
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1712
1695
|
}
|
|
@@ -1932,10 +1915,10 @@ export class MidyGM2 {
|
|
|
1932
1915
|
setKeyBasedVolume(channel, scheduleTime) {
|
|
1933
1916
|
this.processScheduledNotes(channel, (note) => {
|
|
1934
1917
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1935
|
-
if (
|
|
1918
|
+
if (0 <= keyBasedValue) {
|
|
1936
1919
|
note.volumeNode.gain
|
|
1937
1920
|
.cancelScheduledValues(scheduleTime)
|
|
1938
|
-
.setValueAtTime(
|
|
1921
|
+
.setValueAtTime(keyBasedValue / 127, scheduleTime);
|
|
1939
1922
|
}
|
|
1940
1923
|
});
|
|
1941
1924
|
}
|
|
@@ -1956,8 +1939,8 @@ export class MidyGM2 {
|
|
|
1956
1939
|
setKeyBasedPan(channel, scheduleTime) {
|
|
1957
1940
|
this.processScheduledNotes(channel, (note) => {
|
|
1958
1941
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1959
|
-
if (
|
|
1960
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
1942
|
+
if (0 <= keyBasedValue) {
|
|
1943
|
+
const { gainLeft, gainRight } = this.panToGain(keyBasedValue / 127);
|
|
1961
1944
|
note.gainL.gain
|
|
1962
1945
|
.cancelScheduledValues(scheduleTime)
|
|
1963
1946
|
.setValueAtTime(gainLeft, scheduleTime);
|
|
@@ -2038,6 +2021,9 @@ export class MidyGM2 {
|
|
|
2038
2021
|
this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
|
|
2039
2022
|
}
|
|
2040
2023
|
}
|
|
2024
|
+
getSoftPedalFactor(channel, note) {
|
|
2025
|
+
return 1 - (0.1 + (note.noteNumber / 127) * 0.2) * channel.state.softPedal;
|
|
2026
|
+
}
|
|
2041
2027
|
setSoftPedal(channelNumber, softPedal, scheduleTime) {
|
|
2042
2028
|
const channel = this.channels[channelNumber];
|
|
2043
2029
|
if (channel.isDrum)
|
|
@@ -2072,7 +2058,8 @@ export class MidyGM2 {
|
|
|
2072
2058
|
this.processScheduledNotes(channel, (note) => {
|
|
2073
2059
|
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2074
2060
|
return false;
|
|
2075
|
-
note.reverbEffectsSend
|
|
2061
|
+
if (note.reverbEffectsSend)
|
|
2062
|
+
note.reverbEffectsSend.disconnect();
|
|
2076
2063
|
});
|
|
2077
2064
|
}
|
|
2078
2065
|
}
|
|
@@ -2104,7 +2091,8 @@ export class MidyGM2 {
|
|
|
2104
2091
|
this.processScheduledNotes(channel, (note) => {
|
|
2105
2092
|
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2106
2093
|
return false;
|
|
2107
|
-
note.chorusEffectsSend
|
|
2094
|
+
if (note.chorusEffectsSend)
|
|
2095
|
+
note.chorusEffectsSend.disconnect();
|
|
2108
2096
|
});
|
|
2109
2097
|
}
|
|
2110
2098
|
}
|
|
@@ -2787,7 +2775,7 @@ export class MidyGM2 {
|
|
|
2787
2775
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
2788
2776
|
const index = keyNumber * 128 + controllerType;
|
|
2789
2777
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
2790
|
-
return
|
|
2778
|
+
return controlValue;
|
|
2791
2779
|
}
|
|
2792
2780
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
2793
2781
|
const channelNumber = data[4];
|
|
@@ -2800,7 +2788,7 @@ export class MidyGM2 {
|
|
|
2800
2788
|
const controllerType = data[i];
|
|
2801
2789
|
const value = data[i + 1];
|
|
2802
2790
|
const index = keyNumber * 128 + controllerType;
|
|
2803
|
-
table[index] = value
|
|
2791
|
+
table[index] = value;
|
|
2804
2792
|
}
|
|
2805
2793
|
this.handleChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
|
|
2806
2794
|
}
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -53,8 +53,8 @@ export class MidyGMLite {
|
|
|
53
53
|
channels: any[];
|
|
54
54
|
initSoundFontTable(): any[];
|
|
55
55
|
addSoundFont(soundFont: any): void;
|
|
56
|
-
loadSoundFont(
|
|
57
|
-
loadMIDI(
|
|
56
|
+
loadSoundFont(input: any): Promise<void>;
|
|
57
|
+
loadMIDI(input: any): Promise<void>;
|
|
58
58
|
setChannelAudioNodes(audioContext: any): {
|
|
59
59
|
gainL: any;
|
|
60
60
|
gainR: any;
|
|
@@ -62,7 +62,7 @@ export class MidyGMLite {
|
|
|
62
62
|
};
|
|
63
63
|
createChannels(audioContext: any): any[];
|
|
64
64
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
65
|
-
createBufferSource(voiceParams: any, audioBuffer: any): any;
|
|
65
|
+
createBufferSource(channel: any, voiceParams: any, audioBuffer: any): any;
|
|
66
66
|
scheduleTimelineEvents(t: any, resumeTime: any, queueIndex: any): Promise<any>;
|
|
67
67
|
getQueueIndex(second: any): number;
|
|
68
68
|
playNotes(): Promise<any>;
|
|
@@ -104,12 +104,12 @@ export class MidyGMLite {
|
|
|
104
104
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, noteOffEvent: any): Promise<void>;
|
|
105
105
|
noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
|
|
106
106
|
disconnectNote(note: any): void;
|
|
107
|
-
|
|
108
|
-
scheduleNoteOff(channelNumber: any,
|
|
107
|
+
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
108
|
+
scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): void;
|
|
109
109
|
findNoteOffTarget(channel: any, noteNumber: any): any;
|
|
110
|
-
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any):
|
|
111
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any):
|
|
112
|
-
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<
|
|
110
|
+
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): void;
|
|
111
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
112
|
+
handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<void>;
|
|
113
113
|
handleProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
114
114
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
115
115
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
@@ -168,6 +168,7 @@ export class MidyGMLite {
|
|
|
168
168
|
declare class Note {
|
|
169
169
|
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
170
170
|
index: number;
|
|
171
|
+
ending: boolean;
|
|
171
172
|
bufferSource: any;
|
|
172
173
|
filterNode: any;
|
|
173
174
|
filterDepth: any;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAoGA;IA2BE;;;;;;;;;MASE;IAEF,+BAcC;IAnDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,2BAAqC;IACrC,+BAEE;IAcA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCAWC;IAED,yCAcC;IAED,oCAiBC;IAED;;;;MAeC;IAED,yCAaC;IAED,6DA2BC;IAED,0EAUC;IAED,+EAkDC;IAED,mCAOC;IAED,0BAiEC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA6EC;IAED,kGAeC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEASC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,+GA0BC;IAED,gHA6CC;IAED,0EAiBC;IAED,8EAiBC;IAED,qHAyCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAgBC;IAED,sDASC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;IAED,sFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED,qCAeC;IAED,kGAWC;IAED,wDAUC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,yCAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA19CD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|