@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/script/midy-GM2.js
CHANGED
|
@@ -11,11 +11,11 @@ class Note {
|
|
|
11
11
|
writable: true,
|
|
12
12
|
value: -1
|
|
13
13
|
});
|
|
14
|
-
Object.defineProperty(this, "
|
|
14
|
+
Object.defineProperty(this, "ending", {
|
|
15
15
|
enumerable: true,
|
|
16
16
|
configurable: true,
|
|
17
17
|
writable: true,
|
|
18
|
-
value:
|
|
18
|
+
value: false
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(this, "bufferSource", {
|
|
21
21
|
enumerable: true,
|
|
@@ -458,17 +458,37 @@ class MidyGM2 {
|
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
460
|
}
|
|
461
|
-
async loadSoundFont(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
461
|
+
async loadSoundFont(input) {
|
|
462
|
+
let uint8Array;
|
|
463
|
+
if (typeof input === "string") {
|
|
464
|
+
const response = await fetch(input);
|
|
465
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
466
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
467
|
+
}
|
|
468
|
+
else if (input instanceof Uint8Array) {
|
|
469
|
+
uint8Array = input;
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
473
|
+
}
|
|
474
|
+
const parsed = (0, soundfont_parser_1.parse)(uint8Array);
|
|
465
475
|
const soundFont = new soundfont_parser_1.SoundFont(parsed);
|
|
466
476
|
this.addSoundFont(soundFont);
|
|
467
477
|
}
|
|
468
|
-
async loadMIDI(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
478
|
+
async loadMIDI(input) {
|
|
479
|
+
let uint8Array;
|
|
480
|
+
if (typeof input === "string") {
|
|
481
|
+
const response = await fetch(input);
|
|
482
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
483
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
484
|
+
}
|
|
485
|
+
else if (input instanceof Uint8Array) {
|
|
486
|
+
uint8Array = input;
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
490
|
+
}
|
|
491
|
+
const midi = (0, midi_file_1.parseMidi)(uint8Array);
|
|
472
492
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
473
493
|
const midiData = this.extractMidiData(midi);
|
|
474
494
|
this.instruments = midiData.instruments;
|
|
@@ -493,7 +513,8 @@ class MidyGM2 {
|
|
|
493
513
|
this.resetControlTable(channel.controlTable);
|
|
494
514
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
495
515
|
channel.channelPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
496
|
-
channel.
|
|
516
|
+
channel.polyphonicKeyPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
517
|
+
channel.keyBasedInstrumentControlTable.fill(-1);
|
|
497
518
|
}
|
|
498
519
|
createChannels(audioContext) {
|
|
499
520
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -509,7 +530,7 @@ class MidyGM2 {
|
|
|
509
530
|
controlTable: this.initControlTable(),
|
|
510
531
|
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
511
532
|
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
512
|
-
keyBasedInstrumentControlTable: new Int8Array(128 * 128)
|
|
533
|
+
keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
|
|
513
534
|
};
|
|
514
535
|
});
|
|
515
536
|
return channels;
|
|
@@ -543,10 +564,18 @@ class MidyGM2 {
|
|
|
543
564
|
return audioBuffer;
|
|
544
565
|
}
|
|
545
566
|
}
|
|
546
|
-
|
|
567
|
+
isLoopDrum(channel, noteNumber) {
|
|
568
|
+
const programNumber = channel.programNumber;
|
|
569
|
+
return ((programNumber === 48 && noteNumber === 88) ||
|
|
570
|
+
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84));
|
|
571
|
+
}
|
|
572
|
+
createBufferSource(channel, noteNumber, voiceParams, audioBuffer) {
|
|
547
573
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
548
574
|
bufferSource.buffer = audioBuffer;
|
|
549
575
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
576
|
+
if (channel.isDrum) {
|
|
577
|
+
bufferSource.loop = this.isLoopDrum(channel, noteNumber);
|
|
578
|
+
}
|
|
550
579
|
if (bufferSource.loop) {
|
|
551
580
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
552
581
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -561,12 +590,13 @@ class MidyGM2 {
|
|
|
561
590
|
const delay = this.startDelay - resumeTime;
|
|
562
591
|
const startTime = event.startTime + delay;
|
|
563
592
|
switch (event.type) {
|
|
564
|
-
case "noteOn":
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
593
|
+
case "noteOn":
|
|
594
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
595
|
+
break;
|
|
596
|
+
case "noteOff": {
|
|
597
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
598
|
+
if (notePromise)
|
|
599
|
+
this.notePromises.push(notePromise);
|
|
570
600
|
break;
|
|
571
601
|
}
|
|
572
602
|
case "controller":
|
|
@@ -671,6 +701,7 @@ class MidyGM2 {
|
|
|
671
701
|
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
672
702
|
}
|
|
673
703
|
extractMidiData(midi) {
|
|
704
|
+
this.audioBufferCounter.clear();
|
|
674
705
|
const instruments = new Set();
|
|
675
706
|
const timeline = [];
|
|
676
707
|
const tmpChannels = new Array(this.channels.length);
|
|
@@ -770,38 +801,13 @@ class MidyGM2 {
|
|
|
770
801
|
prevTempoTicks = event.ticks;
|
|
771
802
|
}
|
|
772
803
|
}
|
|
773
|
-
const activeNotes = new Array(this.channels.length * 128);
|
|
774
|
-
for (let i = 0; i < activeNotes.length; i++) {
|
|
775
|
-
activeNotes[i] = [];
|
|
776
|
-
}
|
|
777
|
-
for (let i = 0; i < timeline.length; i++) {
|
|
778
|
-
const event = timeline[i];
|
|
779
|
-
switch (event.type) {
|
|
780
|
-
case "noteOn": {
|
|
781
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
782
|
-
activeNotes[index].push(event);
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
case "noteOff": {
|
|
786
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
787
|
-
const noteOn = activeNotes[index].pop();
|
|
788
|
-
if (noteOn) {
|
|
789
|
-
noteOn.noteOffEvent = event;
|
|
790
|
-
}
|
|
791
|
-
else {
|
|
792
|
-
const eventString = JSON.stringify(event, null, 2);
|
|
793
|
-
console.warn(`noteOff without matching noteOn: ${eventString}`);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
804
|
return { instruments, timeline };
|
|
799
805
|
}
|
|
800
806
|
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
801
807
|
const channel = this.channels[channelNumber];
|
|
802
808
|
const promises = [];
|
|
803
809
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
804
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force
|
|
810
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
805
811
|
this.notePromises.push(promise);
|
|
806
812
|
promises.push(promise);
|
|
807
813
|
});
|
|
@@ -811,7 +817,7 @@ class MidyGM2 {
|
|
|
811
817
|
const channel = this.channels[channelNumber];
|
|
812
818
|
const promises = [];
|
|
813
819
|
this.processScheduledNotes(channel, (note) => {
|
|
814
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
|
|
820
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
815
821
|
this.notePromises.push(promise);
|
|
816
822
|
promises.push(promise);
|
|
817
823
|
});
|
|
@@ -888,9 +894,6 @@ class MidyGM2 {
|
|
|
888
894
|
continue;
|
|
889
895
|
if (note.ending)
|
|
890
896
|
continue;
|
|
891
|
-
const noteOffEvent = note.noteOffEvent;
|
|
892
|
-
if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
|
|
893
|
-
continue;
|
|
894
897
|
if (scheduleTime < note.startTime)
|
|
895
898
|
continue;
|
|
896
899
|
callback(note);
|
|
@@ -1059,8 +1062,7 @@ class MidyGM2 {
|
|
|
1059
1062
|
}
|
|
1060
1063
|
updateDetune(channel, note, scheduleTime) {
|
|
1061
1064
|
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1062
|
-
const
|
|
1063
|
-
const detune = channel.detune + noteDetune + pitchControl;
|
|
1065
|
+
const detune = channel.detune + noteDetune;
|
|
1064
1066
|
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1065
1067
|
const startTime = note.startTime;
|
|
1066
1068
|
const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
|
|
@@ -1200,10 +1202,8 @@ class MidyGM2 {
|
|
|
1200
1202
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
1201
1203
|
}
|
|
1202
1204
|
setPortamentoFilterEnvelope(channel, note, scheduleTime) {
|
|
1203
|
-
const
|
|
1204
|
-
const
|
|
1205
|
-
const softPedalFactor = 1 -
|
|
1206
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1205
|
+
const { voiceParams, startTime } = note;
|
|
1206
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1207
1207
|
const baseCent = voiceParams.initialFilterFc +
|
|
1208
1208
|
this.getFilterCutoffControl(channel);
|
|
1209
1209
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
@@ -1221,10 +1221,8 @@ class MidyGM2 {
|
|
|
1221
1221
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1222
1222
|
}
|
|
1223
1223
|
setFilterEnvelope(channel, note, scheduleTime) {
|
|
1224
|
-
const
|
|
1225
|
-
const
|
|
1226
|
-
const softPedalFactor = 1 -
|
|
1227
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1224
|
+
const { voiceParams, startTime } = note;
|
|
1225
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1228
1226
|
const baseCent = voiceParams.initialFilterFc +
|
|
1229
1227
|
this.getFilterCutoffControl(channel);
|
|
1230
1228
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
@@ -1304,7 +1302,7 @@ class MidyGM2 {
|
|
|
1304
1302
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1305
1303
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1306
1304
|
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1307
|
-
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
1305
|
+
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1308
1306
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1309
1307
|
note.gainL = new GainNode(this.audioContext);
|
|
1310
1308
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1343,10 +1341,10 @@ class MidyGM2 {
|
|
|
1343
1341
|
note.volumeEnvelopeNode.connect(note.volumeNode);
|
|
1344
1342
|
note.volumeNode.connect(note.gainL);
|
|
1345
1343
|
note.volumeNode.connect(note.gainR);
|
|
1346
|
-
if (0 <
|
|
1344
|
+
if (0 < state.chorusSendLevel) {
|
|
1347
1345
|
this.setChorusEffectsSend(channel, note, 0, now);
|
|
1348
1346
|
}
|
|
1349
|
-
if (0 <
|
|
1347
|
+
if (0 < state.reverbSendLevel) {
|
|
1350
1348
|
this.setReverbEffectsSend(channel, note, 0, now);
|
|
1351
1349
|
}
|
|
1352
1350
|
note.bufferSource.start(startTime);
|
|
@@ -1376,7 +1374,7 @@ class MidyGM2 {
|
|
|
1376
1374
|
if (prev) {
|
|
1377
1375
|
const [prevNote, prevChannelNumber] = prev;
|
|
1378
1376
|
if (prevNote && !prevNote.ending) {
|
|
1379
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
|
|
1377
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1380
1378
|
startTime, true);
|
|
1381
1379
|
}
|
|
1382
1380
|
}
|
|
@@ -1396,7 +1394,7 @@ class MidyGM2 {
|
|
|
1396
1394
|
channelNumber;
|
|
1397
1395
|
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1398
1396
|
if (prevNote && !prevNote.ending) {
|
|
1399
|
-
this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
|
|
1397
|
+
this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1400
1398
|
startTime, true);
|
|
1401
1399
|
}
|
|
1402
1400
|
this.drumExclusiveClassNotes[index] = note;
|
|
@@ -1431,31 +1429,6 @@ class MidyGM2 {
|
|
|
1431
1429
|
const scheduledNotes = channel.scheduledNotes;
|
|
1432
1430
|
note.index = scheduledNotes.length;
|
|
1433
1431
|
scheduledNotes.push(note);
|
|
1434
|
-
if (this.isDrumNoteOffException(channel, noteNumber)) {
|
|
1435
|
-
const stopTime = startTime + note.bufferSource.buffer.duration;
|
|
1436
|
-
const promise = new Promise((resolve) => {
|
|
1437
|
-
note.bufferSource.onended = () => {
|
|
1438
|
-
scheduledNotes[note.index] = undefined;
|
|
1439
|
-
this.disconnectNote(note);
|
|
1440
|
-
resolve();
|
|
1441
|
-
};
|
|
1442
|
-
note.bufferSource.stop(stopTime);
|
|
1443
|
-
});
|
|
1444
|
-
this.notePromises.push(promise);
|
|
1445
|
-
}
|
|
1446
|
-
else if (noteOffEvent) {
|
|
1447
|
-
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1448
|
-
const portamentoTime = this.getPortamentoTime(channel, note);
|
|
1449
|
-
const portamentoEndTime = startTime + portamentoTime;
|
|
1450
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, 0, // velocity
|
|
1451
|
-
Math.max(noteOffEvent.startTime, portamentoEndTime), false);
|
|
1452
|
-
this.notePromises.push(notePromise);
|
|
1453
|
-
}
|
|
1454
|
-
else {
|
|
1455
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
|
|
1456
|
-
this.notePromises.push(notePromise);
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
1432
|
}
|
|
1460
1433
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1461
1434
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1484,41 +1457,47 @@ class MidyGM2 {
|
|
|
1484
1457
|
note.chorusEffectsSend.disconnect();
|
|
1485
1458
|
}
|
|
1486
1459
|
}
|
|
1487
|
-
|
|
1460
|
+
releaseNote(channel, note, endTime) {
|
|
1461
|
+
const volRelease = endTime + note.voiceParams.volRelease;
|
|
1462
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1463
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1464
|
+
note.filterNode.frequency
|
|
1465
|
+
.cancelScheduledValues(endTime)
|
|
1466
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1488
1467
|
note.volumeEnvelopeNode.gain
|
|
1489
1468
|
.cancelScheduledValues(endTime)
|
|
1490
|
-
.linearRampToValueAtTime(0,
|
|
1491
|
-
note.ending = true;
|
|
1492
|
-
this.scheduleTask(() => {
|
|
1493
|
-
note.bufferSource.loop = false;
|
|
1494
|
-
}, stopTime);
|
|
1469
|
+
.linearRampToValueAtTime(0, volRelease);
|
|
1495
1470
|
return new Promise((resolve) => {
|
|
1496
|
-
|
|
1497
|
-
|
|
1471
|
+
this.scheduleTask(() => {
|
|
1472
|
+
const bufferSource = note.bufferSource;
|
|
1473
|
+
bufferSource.loop = false;
|
|
1474
|
+
bufferSource.stop(stopTime);
|
|
1498
1475
|
this.disconnectNote(note);
|
|
1476
|
+
channel.scheduledNotes[note.index] = undefined;
|
|
1499
1477
|
resolve();
|
|
1500
|
-
};
|
|
1501
|
-
note.bufferSource.stop(stopTime);
|
|
1478
|
+
}, stopTime);
|
|
1502
1479
|
});
|
|
1503
1480
|
}
|
|
1504
|
-
scheduleNoteOff(channelNumber,
|
|
1481
|
+
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1505
1482
|
const channel = this.channels[channelNumber];
|
|
1506
|
-
if (this.isDrumNoteOffException(channel, note.noteNumber))
|
|
1507
|
-
return;
|
|
1508
1483
|
const state = channel.state;
|
|
1509
1484
|
if (!force) {
|
|
1510
|
-
if (
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1485
|
+
if (channel.isDrum) {
|
|
1486
|
+
if (!this.isLoopDrum(channel, noteNumber))
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
else {
|
|
1490
|
+
if (0.5 <= state.sustainPedal)
|
|
1491
|
+
return;
|
|
1492
|
+
if (0.5 <= state.sostenutoPedal)
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1514
1495
|
}
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
const stopTime = Math.min(volRelease, modRelease);
|
|
1521
|
-
return this.stopNote(channel, note, endTime, stopTime);
|
|
1496
|
+
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1497
|
+
if (!note)
|
|
1498
|
+
return;
|
|
1499
|
+
note.ending = true;
|
|
1500
|
+
this.releaseNote(channel, note, endTime);
|
|
1522
1501
|
}
|
|
1523
1502
|
findNoteOffTarget(channel, noteNumber) {
|
|
1524
1503
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -1535,16 +1514,14 @@ class MidyGM2 {
|
|
|
1535
1514
|
}
|
|
1536
1515
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1537
1516
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1538
|
-
|
|
1539
|
-
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1540
|
-
return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
|
|
1517
|
+
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1541
1518
|
}
|
|
1542
1519
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1543
1520
|
const velocity = halfVelocity * 2;
|
|
1544
1521
|
const channel = this.channels[channelNumber];
|
|
1545
1522
|
const promises = [];
|
|
1546
1523
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1547
|
-
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
|
|
1524
|
+
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1548
1525
|
promises.push(promise);
|
|
1549
1526
|
}
|
|
1550
1527
|
channel.sustainNotes = [];
|
|
@@ -1558,7 +1535,7 @@ class MidyGM2 {
|
|
|
1558
1535
|
channel.state.sostenutoPedal = 0;
|
|
1559
1536
|
for (let i = 0; i < sostenutoNotes.length; i++) {
|
|
1560
1537
|
const note = sostenutoNotes[i];
|
|
1561
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime);
|
|
1538
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1562
1539
|
promises.push(promise);
|
|
1563
1540
|
}
|
|
1564
1541
|
channel.sostenutoNotes = [];
|
|
@@ -1668,10 +1645,13 @@ class MidyGM2 {
|
|
|
1668
1645
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1669
1646
|
}
|
|
1670
1647
|
setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1648
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1649
|
+
let value = note.voiceParams.reverbEffectsSend;
|
|
1650
|
+
if (0 <= keyBasedValue) {
|
|
1651
|
+
value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
|
|
1652
|
+
}
|
|
1671
1653
|
if (0 < prevValue) {
|
|
1672
|
-
if (0 <
|
|
1673
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1674
|
-
const value = note.voiceParams.reverbEffectsSend + keyBasedValue;
|
|
1654
|
+
if (0 < value) {
|
|
1675
1655
|
note.reverbEffectsSend.gain
|
|
1676
1656
|
.cancelScheduledValues(scheduleTime)
|
|
1677
1657
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1681,10 +1661,10 @@ class MidyGM2 {
|
|
|
1681
1661
|
}
|
|
1682
1662
|
}
|
|
1683
1663
|
else {
|
|
1684
|
-
if (0 <
|
|
1664
|
+
if (0 < value) {
|
|
1685
1665
|
if (!note.reverbEffectsSend) {
|
|
1686
1666
|
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1687
|
-
gain:
|
|
1667
|
+
gain: value,
|
|
1688
1668
|
});
|
|
1689
1669
|
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1690
1670
|
}
|
|
@@ -1693,10 +1673,13 @@ class MidyGM2 {
|
|
|
1693
1673
|
}
|
|
1694
1674
|
}
|
|
1695
1675
|
setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1676
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1677
|
+
let value = note.voiceParams.chorusEffectsSend;
|
|
1678
|
+
if (0 <= keyBasedValue) {
|
|
1679
|
+
value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
|
|
1680
|
+
}
|
|
1696
1681
|
if (0 < prevValue) {
|
|
1697
|
-
if (0 <
|
|
1698
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1699
|
-
const value = note.voiceParams.chorusEffectsSend + keyBasedValue;
|
|
1682
|
+
if (0 < vaule) {
|
|
1700
1683
|
note.chorusEffectsSend.gain
|
|
1701
1684
|
.cancelScheduledValues(scheduleTime)
|
|
1702
1685
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1706,10 +1689,10 @@ class MidyGM2 {
|
|
|
1706
1689
|
}
|
|
1707
1690
|
}
|
|
1708
1691
|
else {
|
|
1709
|
-
if (0 <
|
|
1692
|
+
if (0 < value) {
|
|
1710
1693
|
if (!note.chorusEffectsSend) {
|
|
1711
1694
|
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1712
|
-
gain:
|
|
1695
|
+
gain: value,
|
|
1713
1696
|
});
|
|
1714
1697
|
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1715
1698
|
}
|
|
@@ -1935,10 +1918,10 @@ class MidyGM2 {
|
|
|
1935
1918
|
setKeyBasedVolume(channel, scheduleTime) {
|
|
1936
1919
|
this.processScheduledNotes(channel, (note) => {
|
|
1937
1920
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1938
|
-
if (
|
|
1921
|
+
if (0 <= keyBasedValue) {
|
|
1939
1922
|
note.volumeNode.gain
|
|
1940
1923
|
.cancelScheduledValues(scheduleTime)
|
|
1941
|
-
.setValueAtTime(
|
|
1924
|
+
.setValueAtTime(keyBasedValue / 127, scheduleTime);
|
|
1942
1925
|
}
|
|
1943
1926
|
});
|
|
1944
1927
|
}
|
|
@@ -1959,8 +1942,8 @@ class MidyGM2 {
|
|
|
1959
1942
|
setKeyBasedPan(channel, scheduleTime) {
|
|
1960
1943
|
this.processScheduledNotes(channel, (note) => {
|
|
1961
1944
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1962
|
-
if (
|
|
1963
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
1945
|
+
if (0 <= keyBasedValue) {
|
|
1946
|
+
const { gainLeft, gainRight } = this.panToGain(keyBasedValue / 127);
|
|
1964
1947
|
note.gainL.gain
|
|
1965
1948
|
.cancelScheduledValues(scheduleTime)
|
|
1966
1949
|
.setValueAtTime(gainLeft, scheduleTime);
|
|
@@ -2041,6 +2024,9 @@ class MidyGM2 {
|
|
|
2041
2024
|
this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
|
|
2042
2025
|
}
|
|
2043
2026
|
}
|
|
2027
|
+
getSoftPedalFactor(channel, note) {
|
|
2028
|
+
return 1 - (0.1 + (note.noteNumber / 127) * 0.2) * channel.state.softPedal;
|
|
2029
|
+
}
|
|
2044
2030
|
setSoftPedal(channelNumber, softPedal, scheduleTime) {
|
|
2045
2031
|
const channel = this.channels[channelNumber];
|
|
2046
2032
|
if (channel.isDrum)
|
|
@@ -2075,7 +2061,8 @@ class MidyGM2 {
|
|
|
2075
2061
|
this.processScheduledNotes(channel, (note) => {
|
|
2076
2062
|
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2077
2063
|
return false;
|
|
2078
|
-
note.reverbEffectsSend
|
|
2064
|
+
if (note.reverbEffectsSend)
|
|
2065
|
+
note.reverbEffectsSend.disconnect();
|
|
2079
2066
|
});
|
|
2080
2067
|
}
|
|
2081
2068
|
}
|
|
@@ -2107,7 +2094,8 @@ class MidyGM2 {
|
|
|
2107
2094
|
this.processScheduledNotes(channel, (note) => {
|
|
2108
2095
|
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2109
2096
|
return false;
|
|
2110
|
-
note.chorusEffectsSend
|
|
2097
|
+
if (note.chorusEffectsSend)
|
|
2098
|
+
note.chorusEffectsSend.disconnect();
|
|
2111
2099
|
});
|
|
2112
2100
|
}
|
|
2113
2101
|
}
|
|
@@ -2790,7 +2778,7 @@ class MidyGM2 {
|
|
|
2790
2778
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
2791
2779
|
const index = keyNumber * 128 + controllerType;
|
|
2792
2780
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
2793
|
-
return
|
|
2781
|
+
return controlValue;
|
|
2794
2782
|
}
|
|
2795
2783
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
2796
2784
|
const channelNumber = data[4];
|
|
@@ -2803,7 +2791,7 @@ class MidyGM2 {
|
|
|
2803
2791
|
const controllerType = data[i];
|
|
2804
2792
|
const value = data[i + 1];
|
|
2805
2793
|
const index = keyNumber * 128 + controllerType;
|
|
2806
|
-
table[index] = value
|
|
2794
|
+
table[index] = value;
|
|
2807
2795
|
}
|
|
2808
2796
|
this.handleChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
|
|
2809
2797
|
}
|
package/script/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;
|
|
@@ -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"}
|