@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.
@@ -11,11 +11,11 @@ class Note {
11
11
  writable: true,
12
12
  value: -1
13
13
  });
14
- Object.defineProperty(this, "noteOffEvent", {
14
+ Object.defineProperty(this, "ending", {
15
15
  enumerable: true,
16
16
  configurable: true,
17
17
  writable: true,
18
- value: void 0
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(soundFontUrl) {
462
- const response = await fetch(soundFontUrl);
463
- const arrayBuffer = await response.arrayBuffer();
464
- const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
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(midiUrl) {
469
- const response = await fetch(midiUrl);
470
- const arrayBuffer = await response.arrayBuffer();
471
- const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
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.keyBasedInstrumentControlTable.fill(0); // [-64, 63]
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), // [-64, 63]
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
- createBufferSource(voiceParams, audioBuffer) {
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
- const noteOffEvent = {
566
- ...event.noteOffEvent,
567
- startTime: event.noteOffEvent.startTime + delay,
568
- };
569
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, noteOffEvent);
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, undefined);
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 pitchControl = this.getPitchControl(channel, note);
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 state = channel.state;
1204
- const { voiceParams, noteNumber, startTime } = note;
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 state = channel.state;
1225
- const { voiceParams, noteNumber, startTime } = note;
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 < channel.chorusSendLevel) {
1344
+ if (0 < state.chorusSendLevel) {
1347
1345
  this.setChorusEffectsSend(channel, note, 0, now);
1348
1346
  }
1349
- if (0 < channel.reverbSendLevel) {
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
- stopNote(channel, note, endTime, stopTime) {
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, stopTime);
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
- note.bufferSource.onended = () => {
1497
- channel.scheduledNotes[note.index] = undefined;
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, note, _velocity, endTime, force) {
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 (0.5 <= state.sustainPedal)
1511
- return;
1512
- if (0.5 <= channel.state.sostenutoPedal)
1513
- return;
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 volRelease = endTime + note.voiceParams.volRelease;
1516
- const modRelease = endTime + note.voiceParams.modRelease;
1517
- note.filterNode.frequency
1518
- .cancelScheduledValues(endTime)
1519
- .linearRampToValueAtTime(0, modRelease);
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
- const channel = this.channels[channelNumber];
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 < note.voiceParams.reverbEffectsSend) {
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 < note.voiceParams.reverbEffectsSend) {
1664
+ if (0 < value) {
1685
1665
  if (!note.reverbEffectsSend) {
1686
1666
  note.reverbEffectsSend = new GainNode(this.audioContext, {
1687
- gain: note.voiceParams.reverbEffectsSend,
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 < note.voiceParams.chorusEffectsSend) {
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 < note.voiceParams.chorusEffectsSend) {
1692
+ if (0 < value) {
1710
1693
  if (!note.chorusEffectsSend) {
1711
1694
  note.chorusEffectsSend = new GainNode(this.audioContext, {
1712
- gain: note.voiceParams.chorusEffectsSend,
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 (keyBasedValue !== 0) {
1921
+ if (0 <= keyBasedValue) {
1939
1922
  note.volumeNode.gain
1940
1923
  .cancelScheduledValues(scheduleTime)
1941
- .setValueAtTime(1 + keyBasedValue, scheduleTime);
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 (keyBasedValue !== 0) {
1963
- const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
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.disconnect();
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.disconnect();
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 (controlValue + 64) / 64;
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 - 64;
2794
+ table[index] = value;
2807
2795
  }
2808
2796
  this.handleChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
2809
2797
  }
@@ -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"}