@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.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,
|
|
@@ -473,17 +473,37 @@ class Midy {
|
|
|
473
473
|
}
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
|
-
async loadSoundFont(
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
476
|
+
async loadSoundFont(input) {
|
|
477
|
+
let uint8Array;
|
|
478
|
+
if (typeof input === "string") {
|
|
479
|
+
const response = await fetch(input);
|
|
480
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
481
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
482
|
+
}
|
|
483
|
+
else if (input instanceof Uint8Array) {
|
|
484
|
+
uint8Array = input;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
488
|
+
}
|
|
489
|
+
const parsed = (0, soundfont_parser_1.parse)(uint8Array);
|
|
480
490
|
const soundFont = new soundfont_parser_1.SoundFont(parsed);
|
|
481
491
|
this.addSoundFont(soundFont);
|
|
482
492
|
}
|
|
483
|
-
async loadMIDI(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
493
|
+
async loadMIDI(input) {
|
|
494
|
+
let uint8Array;
|
|
495
|
+
if (typeof input === "string") {
|
|
496
|
+
const response = await fetch(input);
|
|
497
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
498
|
+
uint8Array = new Uint8Array(arrayBuffer);
|
|
499
|
+
}
|
|
500
|
+
else if (input instanceof Uint8Array) {
|
|
501
|
+
uint8Array = input;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
throw new TypeError("input must be a URL string or Uint8Array");
|
|
505
|
+
}
|
|
506
|
+
const midi = (0, midi_file_1.parseMidi)(uint8Array);
|
|
487
507
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
488
508
|
const midiData = this.extractMidiData(midi);
|
|
489
509
|
this.instruments = midiData.instruments;
|
|
@@ -509,7 +529,7 @@ class Midy {
|
|
|
509
529
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
510
530
|
channel.channelPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
511
531
|
channel.polyphonicKeyPressureTable.set([64, 64, 64, 0, 0, 0]);
|
|
512
|
-
channel.keyBasedInstrumentControlTable.fill(
|
|
532
|
+
channel.keyBasedInstrumentControlTable.fill(-1);
|
|
513
533
|
}
|
|
514
534
|
createChannels(audioContext) {
|
|
515
535
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -526,7 +546,7 @@ class Midy {
|
|
|
526
546
|
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
527
547
|
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
528
548
|
polyphonicKeyPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
529
|
-
keyBasedInstrumentControlTable: new Int8Array(128 * 128)
|
|
549
|
+
keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
|
|
530
550
|
};
|
|
531
551
|
});
|
|
532
552
|
return channels;
|
|
@@ -560,10 +580,18 @@ class Midy {
|
|
|
560
580
|
return audioBuffer;
|
|
561
581
|
}
|
|
562
582
|
}
|
|
563
|
-
|
|
583
|
+
isLoopDrum(channel, noteNumber) {
|
|
584
|
+
const programNumber = channel.programNumber;
|
|
585
|
+
return ((programNumber === 48 && noteNumber === 88) ||
|
|
586
|
+
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84));
|
|
587
|
+
}
|
|
588
|
+
createBufferSource(channel, noteNumber, voiceParams, audioBuffer) {
|
|
564
589
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
565
590
|
bufferSource.buffer = audioBuffer;
|
|
566
591
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
592
|
+
if (channel.isDrum) {
|
|
593
|
+
bufferSource.loop = this.isLoopDrum(channel, noteNumber);
|
|
594
|
+
}
|
|
567
595
|
if (bufferSource.loop) {
|
|
568
596
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
569
597
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -578,12 +606,13 @@ class Midy {
|
|
|
578
606
|
const delay = this.startDelay - resumeTime;
|
|
579
607
|
const startTime = event.startTime + delay;
|
|
580
608
|
switch (event.type) {
|
|
581
|
-
case "noteOn":
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
609
|
+
case "noteOn":
|
|
610
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
611
|
+
break;
|
|
612
|
+
case "noteOff": {
|
|
613
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
614
|
+
if (notePromise)
|
|
615
|
+
this.notePromises.push(notePromise);
|
|
587
616
|
break;
|
|
588
617
|
}
|
|
589
618
|
case "noteAftertouch":
|
|
@@ -691,6 +720,7 @@ class Midy {
|
|
|
691
720
|
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
692
721
|
}
|
|
693
722
|
extractMidiData(midi) {
|
|
723
|
+
this.audioBufferCounter.clear();
|
|
694
724
|
const instruments = new Set();
|
|
695
725
|
const timeline = [];
|
|
696
726
|
const tmpChannels = new Array(this.channels.length);
|
|
@@ -790,38 +820,13 @@ class Midy {
|
|
|
790
820
|
prevTempoTicks = event.ticks;
|
|
791
821
|
}
|
|
792
822
|
}
|
|
793
|
-
const activeNotes = new Array(this.channels.length * 128);
|
|
794
|
-
for (let i = 0; i < activeNotes.length; i++) {
|
|
795
|
-
activeNotes[i] = [];
|
|
796
|
-
}
|
|
797
|
-
for (let i = 0; i < timeline.length; i++) {
|
|
798
|
-
const event = timeline[i];
|
|
799
|
-
switch (event.type) {
|
|
800
|
-
case "noteOn": {
|
|
801
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
802
|
-
activeNotes[index].push(event);
|
|
803
|
-
break;
|
|
804
|
-
}
|
|
805
|
-
case "noteOff": {
|
|
806
|
-
const index = event.channel * 128 + event.noteNumber;
|
|
807
|
-
const noteOn = activeNotes[index].pop();
|
|
808
|
-
if (noteOn) {
|
|
809
|
-
noteOn.noteOffEvent = event;
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
const eventString = JSON.stringify(event, null, 2);
|
|
813
|
-
console.warn(`noteOff without matching noteOn: ${eventString}`);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
823
|
return { instruments, timeline };
|
|
819
824
|
}
|
|
820
825
|
stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
|
|
821
826
|
const channel = this.channels[channelNumber];
|
|
822
827
|
const promises = [];
|
|
823
828
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
824
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force
|
|
829
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
825
830
|
this.notePromises.push(promise);
|
|
826
831
|
promises.push(promise);
|
|
827
832
|
});
|
|
@@ -831,7 +836,7 @@ class Midy {
|
|
|
831
836
|
const channel = this.channels[channelNumber];
|
|
832
837
|
const promises = [];
|
|
833
838
|
this.processScheduledNotes(channel, (note) => {
|
|
834
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
|
|
839
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
835
840
|
this.notePromises.push(promise);
|
|
836
841
|
promises.push(promise);
|
|
837
842
|
});
|
|
@@ -908,9 +913,6 @@ class Midy {
|
|
|
908
913
|
continue;
|
|
909
914
|
if (note.ending)
|
|
910
915
|
continue;
|
|
911
|
-
const noteOffEvent = note.noteOffEvent;
|
|
912
|
-
if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
|
|
913
|
-
continue;
|
|
914
916
|
if (scheduleTime < note.startTime)
|
|
915
917
|
continue;
|
|
916
918
|
callback(note);
|
|
@@ -1223,9 +1225,8 @@ class Midy {
|
|
|
1223
1225
|
}
|
|
1224
1226
|
setPortamentoFilterEnvelope(channel, note, scheduleTime) {
|
|
1225
1227
|
const state = channel.state;
|
|
1226
|
-
const { voiceParams,
|
|
1227
|
-
const softPedalFactor =
|
|
1228
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1228
|
+
const { voiceParams, startTime } = note;
|
|
1229
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1229
1230
|
const baseCent = voiceParams.initialFilterFc +
|
|
1230
1231
|
this.getFilterCutoffControl(channel, note);
|
|
1231
1232
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
@@ -1245,9 +1246,8 @@ class Midy {
|
|
|
1245
1246
|
}
|
|
1246
1247
|
setFilterEnvelope(channel, note, scheduleTime) {
|
|
1247
1248
|
const state = channel.state;
|
|
1248
|
-
const { voiceParams,
|
|
1249
|
-
const softPedalFactor =
|
|
1250
|
-
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1249
|
+
const { voiceParams, startTime } = note;
|
|
1250
|
+
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1251
1251
|
const baseCent = voiceParams.initialFilterFc +
|
|
1252
1252
|
this.getFilterCutoffControl(channel, note);
|
|
1253
1253
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor *
|
|
@@ -1328,7 +1328,7 @@ class Midy {
|
|
|
1328
1328
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1329
1329
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1330
1330
|
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1331
|
-
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
1331
|
+
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1332
1332
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1333
1333
|
note.gainL = new GainNode(this.audioContext);
|
|
1334
1334
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1367,10 +1367,10 @@ class Midy {
|
|
|
1367
1367
|
note.volumeEnvelopeNode.connect(note.volumeNode);
|
|
1368
1368
|
note.volumeNode.connect(note.gainL);
|
|
1369
1369
|
note.volumeNode.connect(note.gainR);
|
|
1370
|
-
if (0 <
|
|
1370
|
+
if (0 < state.chorusSendLevel) {
|
|
1371
1371
|
this.setChorusEffectsSend(channel, note, 0, now);
|
|
1372
1372
|
}
|
|
1373
|
-
if (0 <
|
|
1373
|
+
if (0 < state.reverbSendLevel) {
|
|
1374
1374
|
this.setReverbEffectsSend(channel, note, 0, now);
|
|
1375
1375
|
}
|
|
1376
1376
|
note.bufferSource.start(startTime);
|
|
@@ -1400,7 +1400,7 @@ class Midy {
|
|
|
1400
1400
|
if (prev) {
|
|
1401
1401
|
const [prevNote, prevChannelNumber] = prev;
|
|
1402
1402
|
if (prevNote && !prevNote.ending) {
|
|
1403
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
|
|
1403
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1404
1404
|
startTime, true);
|
|
1405
1405
|
}
|
|
1406
1406
|
}
|
|
@@ -1420,18 +1420,11 @@ class Midy {
|
|
|
1420
1420
|
channelNumber;
|
|
1421
1421
|
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1422
1422
|
if (prevNote && !prevNote.ending) {
|
|
1423
|
-
this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
|
|
1423
|
+
this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1424
1424
|
startTime, true);
|
|
1425
1425
|
}
|
|
1426
1426
|
this.drumExclusiveClassNotes[index] = note;
|
|
1427
1427
|
}
|
|
1428
|
-
isDrumNoteOffException(channel, noteNumber) {
|
|
1429
|
-
if (!channel.isDrum)
|
|
1430
|
-
return false;
|
|
1431
|
-
const programNumber = channel.programNumber;
|
|
1432
|
-
return !((programNumber === 48 && noteNumber === 88) ||
|
|
1433
|
-
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84));
|
|
1434
|
-
}
|
|
1435
1428
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
|
|
1436
1429
|
const channel = this.channels[channelNumber];
|
|
1437
1430
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
@@ -1455,31 +1448,6 @@ class Midy {
|
|
|
1455
1448
|
const scheduledNotes = channel.scheduledNotes;
|
|
1456
1449
|
note.index = scheduledNotes.length;
|
|
1457
1450
|
scheduledNotes.push(note);
|
|
1458
|
-
if (this.isDrumNoteOffException(channel, noteNumber)) {
|
|
1459
|
-
const stopTime = startTime + note.bufferSource.buffer.duration;
|
|
1460
|
-
const promise = new Promise((resolve) => {
|
|
1461
|
-
note.bufferSource.onended = () => {
|
|
1462
|
-
scheduledNotes[note.index] = undefined;
|
|
1463
|
-
this.disconnectNote(note);
|
|
1464
|
-
resolve();
|
|
1465
|
-
};
|
|
1466
|
-
note.bufferSource.stop(stopTime);
|
|
1467
|
-
});
|
|
1468
|
-
this.notePromises.push(promise);
|
|
1469
|
-
}
|
|
1470
|
-
else if (noteOffEvent) {
|
|
1471
|
-
if (0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber) {
|
|
1472
|
-
const portamentoTime = this.getPortamentoTime(channel, note);
|
|
1473
|
-
const portamentoEndTime = startTime + portamentoTime;
|
|
1474
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, 0, // velocity
|
|
1475
|
-
Math.max(noteOffEvent.startTime, portamentoEndTime), false);
|
|
1476
|
-
this.notePromises.push(notePromise);
|
|
1477
|
-
}
|
|
1478
|
-
else {
|
|
1479
|
-
const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
|
|
1480
|
-
this.notePromises.push(notePromise);
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
1451
|
}
|
|
1484
1452
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1485
1453
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1508,42 +1476,48 @@ class Midy {
|
|
|
1508
1476
|
note.chorusEffectsSend.disconnect();
|
|
1509
1477
|
}
|
|
1510
1478
|
}
|
|
1511
|
-
|
|
1479
|
+
releaseNote(channel, note, endTime) {
|
|
1480
|
+
const volRelease = endTime +
|
|
1481
|
+
note.voiceParams.volRelease * channel.state.releaseTime * 2;
|
|
1482
|
+
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1483
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1484
|
+
note.filterNode.frequency
|
|
1485
|
+
.cancelScheduledValues(endTime)
|
|
1486
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1512
1487
|
note.volumeEnvelopeNode.gain
|
|
1513
1488
|
.cancelScheduledValues(endTime)
|
|
1514
|
-
.linearRampToValueAtTime(0,
|
|
1515
|
-
note.ending = true;
|
|
1516
|
-
this.scheduleTask(() => {
|
|
1517
|
-
note.bufferSource.loop = false;
|
|
1518
|
-
}, stopTime);
|
|
1489
|
+
.linearRampToValueAtTime(0, volRelease);
|
|
1519
1490
|
return new Promise((resolve) => {
|
|
1520
|
-
|
|
1521
|
-
|
|
1491
|
+
this.scheduleTask(() => {
|
|
1492
|
+
const bufferSource = note.bufferSource;
|
|
1493
|
+
bufferSource.loop = false;
|
|
1494
|
+
bufferSource.stop(stopTime);
|
|
1522
1495
|
this.disconnectNote(note);
|
|
1496
|
+
channel.scheduledNotes[note.index] = undefined;
|
|
1523
1497
|
resolve();
|
|
1524
|
-
};
|
|
1525
|
-
note.bufferSource.stop(stopTime);
|
|
1498
|
+
}, stopTime);
|
|
1526
1499
|
});
|
|
1527
1500
|
}
|
|
1528
|
-
scheduleNoteOff(channelNumber,
|
|
1501
|
+
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1529
1502
|
const channel = this.channels[channelNumber];
|
|
1530
|
-
if (this.isDrumNoteOffException(channel, note.noteNumber))
|
|
1531
|
-
return;
|
|
1532
1503
|
const state = channel.state;
|
|
1533
1504
|
if (!force) {
|
|
1534
|
-
if (
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1505
|
+
if (channel.isDrum) {
|
|
1506
|
+
if (!this.isLoopDrum(channel, noteNumber))
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
else {
|
|
1510
|
+
if (0.5 <= state.sustainPedal)
|
|
1511
|
+
return;
|
|
1512
|
+
if (0.5 <= state.sostenutoPedal)
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1538
1515
|
}
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
note.
|
|
1543
|
-
|
|
1544
|
-
.linearRampToValueAtTime(0, modRelease);
|
|
1545
|
-
const stopTime = Math.min(volRelease, modRelease);
|
|
1546
|
-
return this.stopNote(channel, note, endTime, stopTime);
|
|
1516
|
+
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1517
|
+
if (!note)
|
|
1518
|
+
return;
|
|
1519
|
+
note.ending = true;
|
|
1520
|
+
this.releaseNote(channel, note, endTime);
|
|
1547
1521
|
}
|
|
1548
1522
|
findNoteOffTarget(channel, noteNumber) {
|
|
1549
1523
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -1560,16 +1534,14 @@ class Midy {
|
|
|
1560
1534
|
}
|
|
1561
1535
|
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1562
1536
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1563
|
-
|
|
1564
|
-
const note = this.findNoteOffTarget(channel, noteNumber);
|
|
1565
|
-
return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
|
|
1537
|
+
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1566
1538
|
}
|
|
1567
1539
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1568
1540
|
const velocity = halfVelocity * 2;
|
|
1569
1541
|
const channel = this.channels[channelNumber];
|
|
1570
1542
|
const promises = [];
|
|
1571
1543
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1572
|
-
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
|
|
1544
|
+
const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1573
1545
|
promises.push(promise);
|
|
1574
1546
|
}
|
|
1575
1547
|
channel.sustainNotes = [];
|
|
@@ -1583,7 +1555,7 @@ class Midy {
|
|
|
1583
1555
|
channel.state.sostenutoPedal = 0;
|
|
1584
1556
|
for (let i = 0; i < sostenutoNotes.length; i++) {
|
|
1585
1557
|
const note = sostenutoNotes[i];
|
|
1586
|
-
const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime);
|
|
1558
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1587
1559
|
promises.push(promise);
|
|
1588
1560
|
}
|
|
1589
1561
|
channel.sostenutoNotes = [];
|
|
@@ -1706,10 +1678,13 @@ class Midy {
|
|
|
1706
1678
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1707
1679
|
}
|
|
1708
1680
|
setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1681
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1682
|
+
let value = note.voiceParams.reverbEffectsSend;
|
|
1683
|
+
if (0 <= keyBasedValue) {
|
|
1684
|
+
value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
|
|
1685
|
+
}
|
|
1709
1686
|
if (0 < prevValue) {
|
|
1710
|
-
if (0 <
|
|
1711
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 91);
|
|
1712
|
-
const value = note.voiceParams.reverbEffectsSend + keyBasedValue;
|
|
1687
|
+
if (0 < value) {
|
|
1713
1688
|
note.reverbEffectsSend.gain
|
|
1714
1689
|
.cancelScheduledValues(scheduleTime)
|
|
1715
1690
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1719,10 +1694,10 @@ class Midy {
|
|
|
1719
1694
|
}
|
|
1720
1695
|
}
|
|
1721
1696
|
else {
|
|
1722
|
-
if (0 <
|
|
1697
|
+
if (0 < value) {
|
|
1723
1698
|
if (!note.reverbEffectsSend) {
|
|
1724
1699
|
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1725
|
-
gain:
|
|
1700
|
+
gain: value,
|
|
1726
1701
|
});
|
|
1727
1702
|
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1728
1703
|
}
|
|
@@ -1731,10 +1706,13 @@ class Midy {
|
|
|
1731
1706
|
}
|
|
1732
1707
|
}
|
|
1733
1708
|
setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
|
|
1709
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1710
|
+
let value = note.voiceParams.chorusEffectsSend;
|
|
1711
|
+
if (0 <= keyBasedValue) {
|
|
1712
|
+
value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
|
|
1713
|
+
}
|
|
1734
1714
|
if (0 < prevValue) {
|
|
1735
|
-
if (0 <
|
|
1736
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 93);
|
|
1737
|
-
const value = note.voiceParams.chorusEffectsSend + keyBasedValue;
|
|
1715
|
+
if (0 < vaule) {
|
|
1738
1716
|
note.chorusEffectsSend.gain
|
|
1739
1717
|
.cancelScheduledValues(scheduleTime)
|
|
1740
1718
|
.setValueAtTime(value, scheduleTime);
|
|
@@ -1744,10 +1722,10 @@ class Midy {
|
|
|
1744
1722
|
}
|
|
1745
1723
|
}
|
|
1746
1724
|
else {
|
|
1747
|
-
if (0 <
|
|
1725
|
+
if (0 < value) {
|
|
1748
1726
|
if (!note.chorusEffectsSend) {
|
|
1749
1727
|
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1750
|
-
gain:
|
|
1728
|
+
gain: value,
|
|
1751
1729
|
});
|
|
1752
1730
|
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1753
1731
|
}
|
|
@@ -1984,10 +1962,10 @@ class Midy {
|
|
|
1984
1962
|
setKeyBasedVolume(channel, scheduleTime) {
|
|
1985
1963
|
this.processScheduledNotes(channel, (note) => {
|
|
1986
1964
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1987
|
-
if (
|
|
1965
|
+
if (0 <= keyBasedValue) {
|
|
1988
1966
|
note.volumeNode.gain
|
|
1989
1967
|
.cancelScheduledValues(scheduleTime)
|
|
1990
|
-
.setValueAtTime(
|
|
1968
|
+
.setValueAtTime(keyBasedValue / 127, scheduleTime);
|
|
1991
1969
|
}
|
|
1992
1970
|
});
|
|
1993
1971
|
}
|
|
@@ -2008,8 +1986,8 @@ class Midy {
|
|
|
2008
1986
|
setKeyBasedPan(channel, scheduleTime) {
|
|
2009
1987
|
this.processScheduledNotes(channel, (note) => {
|
|
2010
1988
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
2011
|
-
if (
|
|
2012
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
1989
|
+
if (0 <= keyBasedValue) {
|
|
1990
|
+
const { gainLeft, gainRight } = this.panToGain(keyBasedValue / 127);
|
|
2013
1991
|
note.gainL.gain
|
|
2014
1992
|
.cancelScheduledValues(scheduleTime)
|
|
2015
1993
|
.setValueAtTime(gainLeft, scheduleTime);
|
|
@@ -2090,6 +2068,9 @@ class Midy {
|
|
|
2090
2068
|
this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
|
|
2091
2069
|
}
|
|
2092
2070
|
}
|
|
2071
|
+
getSoftPedalFactor(channel, note) {
|
|
2072
|
+
return 1 - (0.1 + (note.noteNumber / 127) * 0.2) * channel.state.softPedal;
|
|
2073
|
+
}
|
|
2093
2074
|
setSoftPedal(channelNumber, softPedal, scheduleTime) {
|
|
2094
2075
|
const channel = this.channels[channelNumber];
|
|
2095
2076
|
if (channel.isDrum)
|
|
@@ -2223,7 +2204,8 @@ class Midy {
|
|
|
2223
2204
|
this.processScheduledNotes(channel, (note) => {
|
|
2224
2205
|
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2225
2206
|
return false;
|
|
2226
|
-
note.reverbEffectsSend
|
|
2207
|
+
if (note.reverbEffectsSend)
|
|
2208
|
+
note.reverbEffectsSend.disconnect();
|
|
2227
2209
|
});
|
|
2228
2210
|
}
|
|
2229
2211
|
}
|
|
@@ -2255,7 +2237,8 @@ class Midy {
|
|
|
2255
2237
|
this.processScheduledNotes(channel, (note) => {
|
|
2256
2238
|
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2257
2239
|
return false;
|
|
2258
|
-
note.chorusEffectsSend
|
|
2240
|
+
if (note.chorusEffectsSend)
|
|
2241
|
+
note.chorusEffectsSend.disconnect();
|
|
2259
2242
|
});
|
|
2260
2243
|
}
|
|
2261
2244
|
}
|
|
@@ -3008,7 +2991,7 @@ class Midy {
|
|
|
3008
2991
|
getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
|
|
3009
2992
|
const index = keyNumber * 128 + controllerType;
|
|
3010
2993
|
const controlValue = channel.keyBasedInstrumentControlTable[index];
|
|
3011
|
-
return
|
|
2994
|
+
return controlValue;
|
|
3012
2995
|
}
|
|
3013
2996
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
3014
2997
|
const channelNumber = data[4];
|
|
@@ -3021,7 +3004,7 @@ class Midy {
|
|
|
3021
3004
|
const controllerType = data[i];
|
|
3022
3005
|
const value = data[i + 1];
|
|
3023
3006
|
const index = keyNumber * 128 + controllerType;
|
|
3024
|
-
table[index] = value
|
|
3007
|
+
table[index] = value;
|
|
3025
3008
|
}
|
|
3026
3009
|
this.handleChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
|
|
3027
3010
|
}
|