@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-GM2.js CHANGED
@@ -8,11 +8,11 @@ class Note {
8
8
  writable: true,
9
9
  value: -1
10
10
  });
11
- Object.defineProperty(this, "noteOffEvent", {
11
+ Object.defineProperty(this, "ending", {
12
12
  enumerable: true,
13
13
  configurable: true,
14
14
  writable: true,
15
- value: void 0
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(soundFontUrl) {
459
- const response = await fetch(soundFontUrl);
460
- const arrayBuffer = await response.arrayBuffer();
461
- const parsed = parse(new Uint8Array(arrayBuffer));
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(midiUrl) {
466
- const response = await fetch(midiUrl);
467
- const arrayBuffer = await response.arrayBuffer();
468
- const midi = parseMidi(new Uint8Array(arrayBuffer));
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.keyBasedInstrumentControlTable.fill(0); // [-64, 63]
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), // [-64, 63]
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
- createBufferSource(voiceParams, audioBuffer) {
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
- const noteOffEvent = {
563
- ...event.noteOffEvent,
564
- startTime: event.noteOffEvent.startTime + delay,
565
- };
566
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, noteOffEvent);
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, undefined);
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 pitchControl = this.getPitchControl(channel, note);
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 state = channel.state;
1201
- const { voiceParams, noteNumber, startTime } = note;
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 state = channel.state;
1222
- const { voiceParams, noteNumber, startTime } = note;
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 < channel.chorusSendLevel) {
1341
+ if (0 < state.chorusSendLevel) {
1344
1342
  this.setChorusEffectsSend(channel, note, 0, now);
1345
1343
  }
1346
- if (0 < channel.reverbSendLevel) {
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
- stopNote(channel, note, endTime, stopTime) {
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, stopTime);
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
- note.bufferSource.onended = () => {
1494
- channel.scheduledNotes[note.index] = undefined;
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, note, _velocity, endTime, force) {
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 (0.5 <= state.sustainPedal)
1508
- return;
1509
- if (0.5 <= channel.state.sostenutoPedal)
1510
- return;
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 volRelease = endTime + note.voiceParams.volRelease;
1513
- const modRelease = endTime + note.voiceParams.modRelease;
1514
- note.filterNode.frequency
1515
- .cancelScheduledValues(endTime)
1516
- .linearRampToValueAtTime(0, modRelease);
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
- const channel = this.channels[channelNumber];
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 < note.voiceParams.reverbEffectsSend) {
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 < note.voiceParams.reverbEffectsSend) {
1661
+ if (0 < value) {
1682
1662
  if (!note.reverbEffectsSend) {
1683
1663
  note.reverbEffectsSend = new GainNode(this.audioContext, {
1684
- gain: note.voiceParams.reverbEffectsSend,
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 < note.voiceParams.chorusEffectsSend) {
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 < note.voiceParams.chorusEffectsSend) {
1689
+ if (0 < value) {
1707
1690
  if (!note.chorusEffectsSend) {
1708
1691
  note.chorusEffectsSend = new GainNode(this.audioContext, {
1709
- gain: note.voiceParams.chorusEffectsSend,
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 (keyBasedValue !== 0) {
1918
+ if (0 <= keyBasedValue) {
1936
1919
  note.volumeNode.gain
1937
1920
  .cancelScheduledValues(scheduleTime)
1938
- .setValueAtTime(1 + keyBasedValue, scheduleTime);
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 (keyBasedValue !== 0) {
1960
- const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
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.disconnect();
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.disconnect();
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 (controlValue + 64) / 64;
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 - 64;
2791
+ table[index] = value;
2804
2792
  }
2805
2793
  this.handleChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
2806
2794
  }
@@ -53,8 +53,8 @@ export class MidyGMLite {
53
53
  channels: any[];
54
54
  initSoundFontTable(): any[];
55
55
  addSoundFont(soundFont: any): void;
56
- loadSoundFont(soundFontUrl: any): Promise<void>;
57
- loadMIDI(midiUrl: any): Promise<void>;
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
- stopNote(channel: any, note: any, endTime: any, stopTime: any): Promise<any>;
108
- scheduleNoteOff(channelNumber: any, note: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
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): Promise<any> | undefined;
111
- releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
112
- handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
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":"AAmGA;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,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAaC;IAED,6DA2BC;IAED,4DASC;IAED,+EA6CC;IAED,mCAOC;IAED,0BAiEC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MAoGC;IAED,kGAgBC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEAWC;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,gHAyCC;IAED,0EAiBC;IAED,8EAiBC;IAED,qHAgEC;IAED,6FASC;IAED,gCASC;IAED,6EAgBC;IAED,mHAiBC;IAED,sDASC;IAED,yGAWC;IAED,4GAeC;IAED,mGA2BC;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;AA/+CD;IAUE,0FAMC;IAfD,cAAW;IACX,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
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"}