@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.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,
|
|
@@ -470,17 +470,37 @@ export class Midy {
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
|
-
async loadSoundFont(
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
473
|
+
async loadSoundFont(input) {
|
|
474
|
+
let uint8Array;
|
|
475
|
+
if (typeof input === "string") {
|
|
476
|
+
const response = await fetch(input);
|
|
477
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
478
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
479
|
+
}
|
|
480
|
+
else if (input instanceof Uint8Array) {
|
|
481
|
+
uint8Array = input;
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
485
|
+
}
|
|
486
|
+
const parsed = parse(uint8Array);
|
|
477
487
|
const soundFont = new SoundFont(parsed);
|
|
478
488
|
this.addSoundFont(soundFont);
|
|
479
489
|
}
|
|
480
|
-
async loadMIDI(
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
490
|
+
async loadMIDI(input) {
|
|
491
|
+
let uint8Array;
|
|
492
|
+
if (typeof input === "string") {
|
|
493
|
+
const response = await fetch(input);
|
|
494
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
495
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
496
|
+
}
|
|
497
|
+
else if (input instanceof Uint8Array) {
|
|
498
|
+
uint8Array = input;
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
502
|
+
}
|
|
503
|
+
const midi = parseMidi(uint8Array);
|
|
484
504
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
485
505
|
const midiData = this.extractMidiData(midi);
|
|
486
506
|
this.instruments = midiData.instruments;
|
|
@@ -506,7 +526,7 @@ export class Midy {
|
|
|
506
526
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
507
527
|
channel.channelPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
508
528
|
channel.polyphonicKeyPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
509
|
-
channel.keyBasedInstrumentControlTable.fill(
|
|
529
|
+
channel.keyBasedInstrumentControlTable.fill(-1);
|
|
510
530
|
}
|
|
511
531
|
createChannels(audioContext) {
|
|
512
532
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -523,7 +543,7 @@ export class Midy {
|
|
|
523
543
|
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
524
544
|
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
525
545
|
polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
526
|
-
keyBasedInstrumentControlTable: new Int8Array(128 * 128)
|
|
546
|
+
keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
|
|
527
547
|
};
|
|
528
548
|
});
|
|
529
549
|
return channels;
|
|
@@ -557,10 +577,18 @@ export class Midy {
|
|
|
557
577
|
return audioBuffer;
|
|
558
578
|
}
|
|
559
579
|
}
|
|
560
|
-
|
|
580
|
+
isLoopDrum(channel, noteNumber) {
|
|
581
|
+
const programNumber = channel.programNumber;
|
|
582
|
+
return ((programNumber === 48 && noteNumber === 88) ||
|
|
583
|
+
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84));
|
|
584
|
+
}
|
|
585
|
+
createBufferSource(channel, noteNumber, voiceParams, audioBuffer) {
|
|
561
586
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
562
587
|
bufferSource.buffer = audioBuffer;
|
|
563
588
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
589
|
+
if (channel.isDrum) {
|
|
590
|
+
bufferSource.loop = this.isLoopDrum(channel, noteNumber);
|
|
591
|
+
}
|
|
564
592
|
if (bufferSource.loop) {
|
|
565
593
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
566
594
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -575,12 +603,13 @@ export class Midy {
|
|
|
575
603
|
const delay = this.startDelay - resumeTime;
|
|
576
604
|
const startTime = event.startTime + delay;
|
|
577
605
|
switch (event.type) {
|
|
578
|
-
case "noteOn":
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
606
|
+
case "noteOn":
|
|
607
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
608
|
+
break;
|
|
609
|
+
case "noteOff": {
|
|
610
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
611
|
+
if (notePromise)
|
|
612
|
+
this.notePromises.push(notePromise);
|
|
584
613
|
break;
|
|
585
614
|
}
|
|
586
615
|
case "noteAftertouch":
|
|
@@ -688,6 +717,7 @@ export class Midy {
|
|
|
688
717
|
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
689
718
|
}
|
|
690
719
|
extractMidiData(midi) {
|
|
720
|
+
this.audioBufferCounter.clear();
|
|
691
721
|
const instruments = new Set();
|
|
692
722
|
const timeline = [];
|
|
693
723
|
const tmpChannels = new Array(this.channels.length);
|
|
@@ -787,38 +817,13 @@ export class Midy {
|
|
|
787
817
|
prevTempoTicks = event.ticks;
|
|
788
818
|
}
|
|
789
819
|
}
|
|
790
|
-
const activeNotes = new Array(this.channels.length * 128);
|
|
791
|
-
for (let i = 0; i < activeNotes.length; i++) {
|
|
792
|
-
activeNotes[i] = [];
|
|
793
|
-
}
|
|
794
|
-
for (let i = 0; i < timeline.length; i++) {
|
|
795
|
-
const event = timeline[i];
|
|
796
|
-
switch (event.type) {
|
|
797
|
-
case "noteOn": {
|
|
798
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
799
|
-
activeNotes[index].push(event);
|
|
800
|
-
break;
|
|
801
|
-
}
|
|
802
|
-
case "noteOff": {
|
|
803
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
804
|
-
const noteOn = activeNotes[index].pop();
|
|
805
|
-
if (noteOn) {
|
|
806
|
-
noteOn.noteOffEvent = event;
|
|
807
|
-
}
|
|
808
|
-
else {
|
|
809
|
-
const eventString = JSON.stringify(event, null, 2);
|
|
810
|
-
console.warn(`noteOff without matching noteOn: ${eventString}`);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
820
|
return { instruments, timeline };
|
|
816
821
|
}
|
|
817
822
|
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
818
823
|
const channel = this.channels[channelNumber];
|
|
819
824
|
const promises = [];
|
|
820
825
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
821
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force
|
|
826
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
822
827
|
this.notePromises.push(promise);
|
|
823
828
|
promises.push(promise);
|
|
824
829
|
});
|
|
@@ -828,7 +833,7 @@ export class Midy {
|
|
|
828
833
|
const channel = this.channels[channelNumber];
|
|
829
834
|
const promises = [];
|
|
830
835
|
this.processScheduledNotes(channel, (note) => {
|
|
831
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
|
|
836
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
832
837
|
this.notePromises.push(promise);
|
|
833
838
|
promises.push(promise);
|
|
834
839
|
});
|
|
@@ -905,9 +910,6 @@ export class Midy {
|
|
|
905
910
|
continue;
|
|
906
911
|
if (note.ending)
|
|
907
912
|
continue;
|
|
908
|
-
const noteOffEvent = note.noteOffEvent;
|
|
909
|
-
if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
|
|
910
|
-
continue;
|
|
911
913
|
if (scheduleTime < note.startTime)
|
|
912
914
|
continue;
|
|
913
915
|
callback(note);
|
|
@@ -1220,9 +1222,8 @@ export class Midy {
|
|
|
1220
1222
|
}
|
|
1221
1223
|
setPortamentoFilterEnvelope(channel, note, scheduleTime) {
|
|
1222
1224
|
const state = channel.state;
|
|
1223
|
-
const { voiceParams,
|
|
1224
|
-
const softPedalFactor =
|
|
1225
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1225
|
+
const { voiceParams, startTime } = note;
|
|
1226
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1226
1227
|
const baseCent = voiceParams.initialFilterFc +
|
|
1227
1228
|
this.getFilterCutoffControl(channel, note);
|
|
1228
1229
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
@@ -1242,9 +1243,8 @@ export class Midy {
|
|
|
1242
1243
|
}
|
|
1243
1244
|
setFilterEnvelope(channel, note, scheduleTime) {
|
|
1244
1245
|
const state = channel.state;
|
|
1245
|
-
const { voiceParams,
|
|
1246
|
-
const softPedalFactor =
|
|
1247
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1246
|
+
const { voiceParams, startTime } = note;
|
|
1247
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1248
1248
|
const baseCent = voiceParams.initialFilterFc +
|
|
1249
1249
|
this.getFilterCutoffControl(channel, note);
|
|
1250
1250
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
@@ -1325,7 +1325,7 @@ export class Midy {
|
|
|
1325
1325
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1326
1326
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1327
1327
|
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1328
|
-
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
1328
|
+
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1329
1329
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1330
1330
|
note.gainL = new GainNode(this.audioContext);
|
|
1331
1331
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1364,10 +1364,10 @@ export class Midy {
|
|
|
1364
1364
|
note.volumeEnvelopeNode.connect(note.volumeNode);
|
|
1365
1365
|
note.volumeNode.connect(note.gainL);
|
|
1366
1366
|
note.volumeNode.connect(note.gainR);
|
|
1367
|
-
if (0 <
|
|
1367
|
+
if (0 < state.chorusSendLevel) {
|
|
1368
1368
|
this.setChorusEffectsSend(channel, note, 0, now);
|
|
1369
1369
|
}
|
|
1370
|
-
if (0 <
|
|
1370
|
+
if (0 < state.reverbSendLevel) {
|
|
1371
1371
|
this.setReverbEffectsSend(channel, note, 0, now);
|
|
1372
1372
|
}
|
|
1373
1373
|
note.bufferSource.start(startTime);
|
|
@@ -1397,7 +1397,7 @@ export class Midy {
|
|
|
1397
1397
|
if (prev) {
|
|
1398
1398
|
const [prevNote, prevChannelNumber] = prev;
|
|
1399
1399
|
if (prevNote && !prevNote.ending) {
|
|
1400
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
|
|
1400
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1401
1401
|
startTime, true);
|
|
1402
1402
|
}
|
|
1403
1403
|
}
|
|
@@ -1417,18 +1417,11 @@ export class Midy {
|
|
|
1417
1417
|
channelNumber;
|
|
1418
1418
|
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1419
1419
|
if (prevNote && !prevNote.ending) {
|
|
1420
|
-
this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
|
|
1420
|
+
this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1421
1421
|
startTime, true);
|
|
1422
1422
|
}
|
|
1423
1423
|
this.drumExclusiveClassNotes[index] = note;
|
|
1424
1424
|
}
|
|
1425
|
-
isDrumNoteOffException(channel, noteNumber) {
|
|
1426
|
-
if (!channel.isDrum)
|
|
1427
|
-
return false;
|
|
1428
|
-
const programNumber = channel.programNumber;
|
|
1429
|
-
return !((programNumber === 48 && noteNumber === 88) ||
|
|
1430
|
-
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84));
|
|
1431
|
-
}
|
|
1432
1425
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
|
|
1433
1426
|
const channel = this.channels[channelNumber];
|
|
1434
1427
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
@@ -1452,31 +1445,6 @@ export class Midy {
|
|
|
1452
1445
|
const scheduledNotes = channel.scheduledNotes;
|
|
1453
1446
|
note.index = scheduledNotes.length;
|
|
1454
1447
|
scheduledNotes.push(note);
|
|
1455
|
-
if (this.isDrumNoteOffException(channel, noteNumber)) {
|
|
1456
|
-
const stopTime = startTime + note.bufferSource.buffer.duration;
|
|
1457
|
-
const promise = new Promise((resolve) => {
|
|
1458
|
-
note.bufferSource.onended = () => {
|
|
1459
|
-
scheduledNotes[note.index] = undefined;
|
|
1460
|
-
this.disconnectNote(note);
|
|
1461
|
-
resolve();
|
|
1462
|
-
};
|
|
1463
|
-
note.bufferSource.stop(stopTime);
|
|
1464
|
-
});
|
|
1465
|
-
this.notePromises.push(promise);
|
|
1466
|
-
}
|
|
1467
|
-
else if (noteOffEvent) {
|
|
1468
|
-
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1469
|
-
const portamentoTime = this.getPortamentoTime(channel, note);
|
|
1470
|
-
const portamentoEndTime = startTime + portamentoTime;
|
|
1471
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, 0, // velocity
|
|
1472
|
-
Math.max(noteOffEvent.startTime, portamentoEndTime), false);
|
|
1473
|
-
this.notePromises.push(notePromise);
|
|
1474
|
-
}
|
|
1475
|
-
else {
|
|
1476
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
|
|
1477
|
-
this.notePromises.push(notePromise);
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
1448
|
}
|
|
1481
1449
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1482
1450
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1505,42 +1473,48 @@ export class Midy {
|
|
|
1505
1473
|
note.chorusEffectsSend.disconnect();
|
|
1506
1474
|
}
|
|
1507
1475
|
}
|
|
1508
|
-
|
|
1476
|
+
releaseNote(channel, note, endTime) {
|
|
1477
|
+
const volRelease = endTime +
|
|
1478
|
+
note.voiceParams.volRelease * channel.state.releaseTime * 2;
|
|
1479
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1480
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1481
|
+
note.filterNode.frequency
|
|
1482
|
+
.cancelScheduledValues(endTime)
|
|
1483
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1509
1484
|
note.volumeEnvelopeNode.gain
|
|
1510
1485
|
.cancelScheduledValues(endTime)
|
|
1511
|
-
.linearRampToValueAtTime(0,
|
|
1512
|
-
note.ending = true;
|
|
1513
|
-
this.scheduleTask(() => {
|
|
1514
|
-
note.bufferSource.loop = false;
|
|
1515
|
-
}, stopTime);
|
|
1486
|
+
.linearRampToValueAtTime(0, volRelease);
|
|
1516
1487
|
return new Promise((resolve) => {
|
|
1517
|
-
|
|
1518
|
-
|
|
1488
|
+
this.scheduleTask(() => {
|
|
1489
|
+
const bufferSource = note.bufferSource;
|
|
1490
|
+
bufferSource.loop = false;
|
|
1491
|
+
bufferSource.stop(stopTime);
|
|
1519
1492
|
this.disconnectNote(note);
|
|
1493
|
+
channel.scheduledNotes[note.index] = undefined;
|
|
1520
1494
|
resolve();
|
|
1521
|
-
};
|
|
1522
|
-
note.bufferSource.stop(stopTime);
|
|
1495
|
+
}, stopTime);
|
|
1523
1496
|
});
|
|
1524
1497
|
}
|
|
1525
|
-
scheduleNoteOff(channelNumber,
|
|
1498
|
+
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1526
1499
|
const channel = this.channels[channelNumber];
|
|
1527
|
-
if (this.isDrumNoteOffException(channel, note.noteNumber))
|
|
1528
|
-
return;
|
|
1529
1500
|
const state = channel.state;
|
|
1530
1501
|
if (!force) {
|
|
1531
|
-
if (
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1502
|
+
if (channel.isDrum) {
|
|
1503
|
+
if (!this.isLoopDrum(channel, noteNumber))
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
if (0.5 <= state.sustainPedal)
|
|
1508
|
+
return;
|
|
1509
|
+
if (0.5 <= state.sostenutoPedal)
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1535
1512
|
}
|
|
1536
|
-
const
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
note.
|
|
1540
|
-
|
|
1541
|
-
.linearRampToValueAtTime(0, modRelease);
|
|
1542
|
-
const stopTime = Math.min(volRelease, modRelease);
|
|
1543
|
-
return this.stopNote(channel, note, endTime, stopTime);
|
|
1513
|
+
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1514
|
+
if (!note)
|
|
1515
|
+
return;
|
|
1516
|
+
note.ending = true;
|
|
1517
|
+
this.releaseNote(channel, note, endTime);
|
|
1544
1518
|
}
|
|
1545
1519
|
findNoteOffTarget(channel, noteNumber) {
|
|
1546
1520
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -1557,16 +1531,14 @@ export class Midy {
|
|
|
1557
1531
|
}
|
|
1558
1532
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1559
1533
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1560
|
-
|
|
1561
|
-
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1562
|
-
return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
|
|
1534
|
+
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1563
1535
|
}
|
|
1564
1536
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1565
1537
|
const velocity = halfVelocity * 2;
|
|
1566
1538
|
const channel = this.channels[channelNumber];
|
|
1567
1539
|
const promises = [];
|
|
1568
1540
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1569
|
-
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
|
|
1541
|
+
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1570
1542
|
promises.push(promise);
|
|
1571
1543
|
}
|
|
1572
1544
|
channel.sustainNotes = [];
|
|
@@ -1580,7 +1552,7 @@ export class Midy {
|
|
|
1580
1552
|
channel.state.sostenutoPedal = 0;
|
|
1581
1553
|
for (let i = 0; i < sostenutoNotes.length; i++) {
|
|
1582
1554
|
const note = sostenutoNotes[i];
|
|
1583
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime);
|
|
1555
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1584
1556
|
promises.push(promise);
|
|
1585
1557
|
}
|
|
1586
1558
|
channel.sostenutoNotes = [];
|
|
@@ -1703,10 +1675,13 @@ export class Midy {
|
|
|
1703
1675
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1704
1676
|
}
|
|
1705
1677
|
setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1678
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1679
|
+
let value = note.voiceParams.reverbEffectsSend;
|
|
1680
|
+
if (0 <= keyBasedValue) {
|
|
1681
|
+
value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
|
|
1682
|
+
}
|
|
1706
1683
|
if (0 < prevValue) {
|
|
1707
|
-
if (0 <
|
|
1708
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1709
|
-
const value = note.voiceParams.reverbEffectsSend + keyBasedValue;
|
|
1684
|
+
if (0 < value) {
|
|
1710
1685
|
note.reverbEffectsSend.gain
|
|
1711
1686
|
.cancelScheduledValues(scheduleTime)
|
|
1712
1687
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1716,10 +1691,10 @@ export class Midy {
|
|
|
1716
1691
|
}
|
|
1717
1692
|
}
|
|
1718
1693
|
else {
|
|
1719
|
-
if (0 <
|
|
1694
|
+
if (0 < value) {
|
|
1720
1695
|
if (!note.reverbEffectsSend) {
|
|
1721
1696
|
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1722
|
-
gain:
|
|
1697
|
+
gain: value,
|
|
1723
1698
|
});
|
|
1724
1699
|
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1725
1700
|
}
|
|
@@ -1728,10 +1703,13 @@ export class Midy {
|
|
|
1728
1703
|
}
|
|
1729
1704
|
}
|
|
1730
1705
|
setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1706
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1707
|
+
let value = note.voiceParams.chorusEffectsSend;
|
|
1708
|
+
if (0 <= keyBasedValue) {
|
|
1709
|
+
value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
|
|
1710
|
+
}
|
|
1731
1711
|
if (0 < prevValue) {
|
|
1732
|
-
if (0 <
|
|
1733
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1734
|
-
const value = note.voiceParams.chorusEffectsSend + keyBasedValue;
|
|
1712
|
+
if (0 < vaule) {
|
|
1735
1713
|
note.chorusEffectsSend.gain
|
|
1736
1714
|
.cancelScheduledValues(scheduleTime)
|
|
1737
1715
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1741,10 +1719,10 @@ export class Midy {
|
|
|
1741
1719
|
}
|
|
1742
1720
|
}
|
|
1743
1721
|
else {
|
|
1744
|
-
if (0 <
|
|
1722
|
+
if (0 < value) {
|
|
1745
1723
|
if (!note.chorusEffectsSend) {
|
|
1746
1724
|
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1747
|
-
gain:
|
|
1725
|
+
gain: value,
|
|
1748
1726
|
});
|
|
1749
1727
|
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1750
1728
|
}
|
|
@@ -1981,10 +1959,10 @@ export class Midy {
|
|
|
1981
1959
|
setKeyBasedVolume(channel, scheduleTime) {
|
|
1982
1960
|
this.processScheduledNotes(channel, (note) => {
|
|
1983
1961
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1984
|
-
if (
|
|
1962
|
+
if (0 <= keyBasedValue) {
|
|
1985
1963
|
note.volumeNode.gain
|
|
1986
1964
|
.cancelScheduledValues(scheduleTime)
|
|
1987
|
-
.setValueAtTime(
|
|
1965
|
+
.setValueAtTime(keyBasedValue / 127, scheduleTime);
|
|
1988
1966
|
}
|
|
1989
1967
|
});
|
|
1990
1968
|
}
|
|
@@ -2005,8 +1983,8 @@ export class Midy {
|
|
|
2005
1983
|
setKeyBasedPan(channel, scheduleTime) {
|
|
2006
1984
|
this.processScheduledNotes(channel, (note) => {
|
|
2007
1985
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
2008
|
-
if (
|
|
2009
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
1986
|
+
if (0 <= keyBasedValue) {
|
|
1987
|
+
const { gainLeft, gainRight } = this.panToGain(keyBasedValue / 127);
|
|
2010
1988
|
note.gainL.gain
|
|
2011
1989
|
.cancelScheduledValues(scheduleTime)
|
|
2012
1990
|
.setValueAtTime(gainLeft, scheduleTime);
|
|
@@ -2087,6 +2065,9 @@ export class Midy {
|
|
|
2087
2065
|
this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
|
|
2088
2066
|
}
|
|
2089
2067
|
}
|
|
2068
|
+
getSoftPedalFactor(channel, note) {
|
|
2069
|
+
return 1 - (0.1 + (note.noteNumber / 127) * 0.2) * channel.state.softPedal;
|
|
2070
|
+
}
|
|
2090
2071
|
setSoftPedal(channelNumber, softPedal, scheduleTime) {
|
|
2091
2072
|
const channel = this.channels[channelNumber];
|
|
2092
2073
|
if (channel.isDrum)
|
|
@@ -2220,7 +2201,8 @@ export class Midy {
|
|
|
2220
2201
|
this.processScheduledNotes(channel, (note) => {
|
|
2221
2202
|
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2222
2203
|
return false;
|
|
2223
|
-
note.reverbEffectsSend
|
|
2204
|
+
if (note.reverbEffectsSend)
|
|
2205
|
+
note.reverbEffectsSend.disconnect();
|
|
2224
2206
|
});
|
|
2225
2207
|
}
|
|
2226
2208
|
}
|
|
@@ -2252,7 +2234,8 @@ export class Midy {
|
|
|
2252
2234
|
this.processScheduledNotes(channel, (note) => {
|
|
2253
2235
|
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2254
2236
|
return false;
|
|
2255
|
-
note.chorusEffectsSend
|
|
2237
|
+
if (note.chorusEffectsSend)
|
|
2238
|
+
note.chorusEffectsSend.disconnect();
|
|
2256
2239
|
});
|
|
2257
2240
|
}
|
|
2258
2241
|
}
|
|
@@ -3005,7 +2988,7 @@ export class Midy {
|
|
|
3005
2988
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
3006
2989
|
const index = keyNumber * 128 + controllerType;
|
|
3007
2990
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
3008
|
-
return
|
|
2991
|
+
return controlValue;
|
|
3009
2992
|
}
|
|
3010
2993
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
3011
2994
|
const channelNumber = data[4];
|
|
@@ -3018,7 +3001,7 @@ export class Midy {
|
|
|
3018
3001
|
const controllerType = data[i];
|
|
3019
3002
|
const value = data[i + 1];
|
|
3020
3003
|
const index = keyNumber * 128 + controllerType;
|
|
3021
|
-
table[index] = value
|
|
3004
|
+
table[index] = value;
|
|
3022
3005
|
}
|
|
3023
3006
|
this.handleChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
|
|
3024
3007
|
}
|
package/package.json
CHANGED
package/script/midy-GM1.d.ts
CHANGED
|
@@ -54,8 +54,8 @@ export class MidyGM1 {
|
|
|
54
54
|
channels: any[];
|
|
55
55
|
initSoundFontTable(): any[];
|
|
56
56
|
addSoundFont(soundFont: any): void;
|
|
57
|
-
loadSoundFont(
|
|
58
|
-
loadMIDI(
|
|
57
|
+
loadSoundFont(input: any): Promise<void>;
|
|
58
|
+
loadMIDI(input: any): Promise<void>;
|
|
59
59
|
setChannelAudioNodes(audioContext: any): {
|
|
60
60
|
gainL: any;
|
|
61
61
|
gainR: any;
|
|
@@ -104,12 +104,12 @@ export class MidyGM1 {
|
|
|
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;
|
|
@@ -173,6 +173,7 @@ export class MidyGM1 {
|
|
|
173
173
|
declare class Note {
|
|
174
174
|
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
175
175
|
index: number;
|
|
176
|
+
ending: boolean;
|
|
176
177
|
bufferSource: any;
|
|
177
178
|
filterNode: any;
|
|
178
179
|
filterDepth: any;
|
package/script/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAsFA;IAwBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAlDD,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;IAgBnC,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,4DASC;IAED,+EAkDC;IAED,mCAOC;IAED,0BA8DC;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,qCAMC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,+GA0BC;IAED,gHAyCC;IAED,0EAiBC;IAED,qHAwCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAaC;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,qCAcC;IAED,kGAWC;IAED,wDAUC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,2DAMC;IAED,uDAkBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,iEAKC;IAED,uEAQC;IAED,mEAKC;IAED,yEAQC;IAED,gFAGC;IAED,yCAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA79CD;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"}
|