@marmooo/midy 0.3.8 → 0.4.1

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.
@@ -4,7 +4,19 @@ exports.MidyGM2 = void 0;
4
4
  const midi_file_1 = require("midi-file");
5
5
  const soundfont_parser_1 = require("@marmooo/soundfont-parser");
6
6
  class Note {
7
- constructor(noteNumber, velocity, startTime, voice, voiceParams) {
7
+ constructor(noteNumber, velocity, startTime) {
8
+ Object.defineProperty(this, "voice", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: void 0
13
+ });
14
+ Object.defineProperty(this, "voiceParams", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: void 0
19
+ });
8
20
  Object.defineProperty(this, "index", {
9
21
  enumerable: true,
10
22
  configurable: true,
@@ -17,6 +29,12 @@ class Note {
17
29
  writable: true,
18
30
  value: false
19
31
  });
32
+ Object.defineProperty(this, "pending", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: true
37
+ });
20
38
  Object.defineProperty(this, "bufferSource", {
21
39
  enumerable: true,
22
40
  configurable: true,
@@ -92,8 +110,6 @@ class Note {
92
110
  this.noteNumber = noteNumber;
93
111
  this.velocity = velocity;
94
112
  this.startTime = startTime;
95
- this.voice = voice;
96
- this.voiceParams = voiceParams;
97
113
  }
98
114
  }
99
115
  const drumExclusiveClassesByKit = new Array(57);
@@ -138,12 +154,12 @@ const defaultControllerState = {
138
154
  pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
139
155
  link: { type: 127, defaultValue: 0 },
140
156
  // bankMSB: { type: 128 + 0, defaultValue: 121, },
141
- modulationDepth: { type: 128 + 1, defaultValue: 0 },
142
- portamentoTime: { type: 128 + 5, defaultValue: 0 },
157
+ modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
158
+ portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
143
159
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
144
- volume: { type: 128 + 7, defaultValue: 100 / 127 },
145
- pan: { type: 128 + 10, defaultValue: 64 / 127 },
146
- expression: { type: 128 + 11, defaultValue: 1 },
160
+ volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
161
+ panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
162
+ expressionMSB: { type: 128 + 11, defaultValue: 1 },
147
163
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
148
164
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
149
165
  sustainPedal: { type: 128 + 64, defaultValue: 0 },
@@ -341,6 +357,12 @@ class MidyGM2 {
341
357
  writable: true,
342
358
  value: new Map()
343
359
  });
360
+ Object.defineProperty(this, "realtimeVoiceCache", {
361
+ enumerable: true,
362
+ configurable: true,
363
+ writable: true,
364
+ value: new Map()
365
+ });
344
366
  Object.defineProperty(this, "isPlaying", {
345
367
  enumerable: true,
346
368
  configurable: true,
@@ -527,7 +549,7 @@ class MidyGM2 {
527
549
  return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
528
550
  }
529
551
  createChannelAudioNodes(audioContext) {
530
- const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
552
+ const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
531
553
  const gainL = new GainNode(audioContext, { gain: gainLeft });
532
554
  const gainR = new GainNode(audioContext, { gain: gainRight });
533
555
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
@@ -568,10 +590,9 @@ class MidyGM2 {
568
590
  return channels;
569
591
  }
570
592
  async createAudioBuffer(voiceParams) {
571
- const sample = voiceParams.sample;
572
- const sampleStart = voiceParams.start;
573
- const sampleEnd = sample.data.length + voiceParams.end;
574
- const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
593
+ const { sample, start, end } = voiceParams;
594
+ const sampleEnd = sample.data.length + end;
595
+ const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
575
596
  return audioBuffer;
576
597
  }
577
598
  isLoopDrum(channel, noteNumber) {
@@ -603,12 +624,10 @@ class MidyGM2 {
603
624
  const startTime = event.startTime + schedulingOffset;
604
625
  switch (event.type) {
605
626
  case "noteOn":
606
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
627
+ await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
607
628
  break;
608
629
  case "noteOff": {
609
- const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
610
- if (notePromise)
611
- this.notePromises.push(notePromise);
630
+ this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
612
631
  break;
613
632
  }
614
633
  case "controller":
@@ -642,6 +661,7 @@ class MidyGM2 {
642
661
  this.exclusiveClassNotes.fill(undefined);
643
662
  this.drumExclusiveClassNotes.fill(undefined);
644
663
  this.voiceCache.clear();
664
+ this.realtimeVoiceCache.clear();
645
665
  for (let i = 0; i < this.channels.length; i++) {
646
666
  this.channels[i].scheduledNotes = [];
647
667
  this.resetChannelStates(i);
@@ -686,7 +706,6 @@ class MidyGM2 {
686
706
  finished = true;
687
707
  break;
688
708
  }
689
- queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
690
709
  if (this.isPausing) {
691
710
  await this.stopNotes(0, true, now);
692
711
  await this.audioContext.suspend();
@@ -708,9 +727,16 @@ class MidyGM2 {
708
727
  this.isSeeking = false;
709
728
  continue;
710
729
  }
730
+ queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
711
731
  const waitTime = now + this.noteCheckInterval;
712
732
  await this.scheduleTask(() => { }, waitTime);
713
733
  }
734
+ if (this.timeline.length <= queueIndex) {
735
+ const now = this.audioContext.currentTime;
736
+ await this.stopNotes(0, true, now);
737
+ await this.audioContext.suspend();
738
+ finished = true;
739
+ }
714
740
  if (finished) {
715
741
  this.notePromises = [];
716
742
  this.resetAllStates();
@@ -816,7 +842,7 @@ class MidyGM2 {
816
842
  const channel = this.channels[channelNumber];
817
843
  const promises = [];
818
844
  this.processActiveNotes(channel, scheduleTime, (note) => {
819
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
845
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
820
846
  this.notePromises.push(promise);
821
847
  promises.push(promise);
822
848
  });
@@ -826,7 +852,7 @@ class MidyGM2 {
826
852
  const channel = this.channels[channelNumber];
827
853
  const promises = [];
828
854
  this.processScheduledNotes(channel, (note) => {
829
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
855
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
830
856
  this.notePromises.push(promise);
831
857
  promises.push(promise);
832
858
  });
@@ -859,7 +885,7 @@ class MidyGM2 {
859
885
  if (!this.isPlaying || this.isPaused)
860
886
  return;
861
887
  const now = this.audioContext.currentTime;
862
- this.resumeTime += now - this.startTime - this.startDelay;
888
+ this.resumeTime = now - this.startTime - this.startDelay;
863
889
  this.isPausing = true;
864
890
  await this.playPromise;
865
891
  this.isPausing = false;
@@ -885,11 +911,13 @@ class MidyGM2 {
885
911
  if (totalTime < event.startTime)
886
912
  totalTime = event.startTime;
887
913
  }
888
- return totalTime;
914
+ return totalTime + this.startDelay;
889
915
  }
890
916
  currentTime() {
917
+ if (!this.isPlaying)
918
+ return this.resumeTime;
891
919
  const now = this.audioContext.currentTime;
892
- return this.resumeTime + now - this.startTime - this.startDelay;
920
+ return now + this.resumeTime - this.startTime;
893
921
  }
894
922
  processScheduledNotes(channel, callback) {
895
923
  const scheduledNotes = channel.scheduledNotes;
@@ -1117,7 +1145,7 @@ class MidyGM2 {
1117
1145
  }
1118
1146
  getPortamentoTime(channel, note) {
1119
1147
  const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
1120
- const value = Math.ceil(channel.state.portamentoTime * 127);
1148
+ const value = Math.ceil(channel.state.portamentoTimeMSB * 127);
1121
1149
  return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
1122
1150
  }
1123
1151
  getPitchIncrementSpeed(value) {
@@ -1316,31 +1344,42 @@ class MidyGM2 {
1316
1344
  note.vibratoLFO.connect(note.vibratoDepth);
1317
1345
  note.vibratoDepth.connect(note.bufferSource.detune);
1318
1346
  }
1319
- async getAudioBuffer(channel, noteNumber, velocity, voiceParams) {
1347
+ async getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime) {
1320
1348
  const audioBufferId = this.getVoiceId(channel, noteNumber, velocity);
1321
- const cache = this.voiceCache.get(audioBufferId);
1322
- if (cache) {
1323
- cache.counter += 1;
1324
- if (cache.maxCount <= cache.counter) {
1325
- this.voiceCache.delete(audioBufferId);
1326
- }
1327
- return cache.audioBuffer;
1328
- }
1329
- else {
1330
- const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
1349
+ if (realtime) {
1350
+ const cachedAudioBuffer = this.realtimeVoiceCache.get(audioBufferId);
1351
+ if (cachedAudioBuffer)
1352
+ return cachedAudioBuffer;
1331
1353
  const audioBuffer = await this.createAudioBuffer(voiceParams);
1332
- const cache = { audioBuffer, maxCount, counter: 1 };
1333
- this.voiceCache.set(audioBufferId, cache);
1354
+ this.realtimeVoiceCache.set(audioBufferId, audioBuffer);
1334
1355
  return audioBuffer;
1335
1356
  }
1357
+ else {
1358
+ const cache = this.voiceCache.get(audioBufferId);
1359
+ if (cache) {
1360
+ cache.counter += 1;
1361
+ if (cache.maxCount <= cache.counter) {
1362
+ this.voiceCache.delete(audioBufferId);
1363
+ }
1364
+ return cache.audioBuffer;
1365
+ }
1366
+ else {
1367
+ const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
1368
+ const audioBuffer = await this.createAudioBuffer(voiceParams);
1369
+ const cache = { audioBuffer, maxCount, counter: 1 };
1370
+ this.voiceCache.set(audioBufferId, cache);
1371
+ return audioBuffer;
1372
+ }
1373
+ }
1336
1374
  }
1337
- async createNote(channel, voice, noteNumber, velocity, startTime) {
1375
+ async setNoteAudioNode(channel, note, realtime) {
1338
1376
  const now = this.audioContext.currentTime;
1377
+ const { noteNumber, velocity, startTime } = note;
1339
1378
  const state = channel.state;
1340
1379
  const controllerState = this.getControllerState(channel, noteNumber, velocity);
1341
- const voiceParams = voice.getAllParams(controllerState);
1342
- const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
1343
- const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
1380
+ const voiceParams = note.voice.getAllParams(controllerState);
1381
+ note.voiceParams = voiceParams;
1382
+ const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime);
1344
1383
  note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
1345
1384
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
1346
1385
  note.filterNode = new BiquadFilterNode(this.audioContext, {
@@ -1365,7 +1404,7 @@ class MidyGM2 {
1365
1404
  if (0 < state.vibratoDepth) {
1366
1405
  this.startVibrato(channel, note, now);
1367
1406
  }
1368
- if (0 < state.modulationDepth) {
1407
+ if (0 < state.modulationDepthMSB) {
1369
1408
  this.startModulation(channel, note, now);
1370
1409
  }
1371
1410
  if (channel.mono && channel.currentBufferSource) {
@@ -1376,7 +1415,13 @@ class MidyGM2 {
1376
1415
  note.filterNode.connect(note.volumeEnvelopeNode);
1377
1416
  this.setChorusSend(channel, note, now);
1378
1417
  this.setReverbSend(channel, note, now);
1379
- note.bufferSource.start(startTime);
1418
+ if (voiceParams.sample.type === "compressed") {
1419
+ const offset = voiceParams.start / audioBuffer.sampleRate;
1420
+ note.bufferSource.start(startTime, offset);
1421
+ }
1422
+ else {
1423
+ note.bufferSource.start(startTime);
1424
+ }
1380
1425
  return note;
1381
1426
  }
1382
1427
  handleExclusiveClass(note, channelNumber, startTime) {
@@ -1387,7 +1432,7 @@ class MidyGM2 {
1387
1432
  if (prev) {
1388
1433
  const [prevNote, prevChannelNumber] = prev;
1389
1434
  if (prevNote && !prevNote.ending) {
1390
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
1435
+ this.noteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
1391
1436
  startTime, true);
1392
1437
  }
1393
1438
  }
@@ -1407,27 +1452,14 @@ class MidyGM2 {
1407
1452
  channelNumber;
1408
1453
  const prevNote = this.drumExclusiveClassNotes[index];
1409
1454
  if (prevNote && !prevNote.ending) {
1410
- this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
1455
+ this.noteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
1411
1456
  startTime, true);
1412
1457
  }
1413
1458
  this.drumExclusiveClassNotes[index] = note;
1414
1459
  }
1415
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
1460
+ setNoteRouting(channelNumber, note, startTime) {
1416
1461
  const channel = this.channels[channelNumber];
1417
- const programNumber = channel.programNumber;
1418
- const bankTable = this.soundFontTable[programNumber];
1419
- if (!bankTable)
1420
- return;
1421
- const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
1422
- const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
1423
- const soundFontIndex = bankTable[bank];
1424
- if (soundFontIndex === undefined)
1425
- return;
1426
- const soundFont = this.soundFonts[soundFontIndex];
1427
- const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
1428
- if (!voice)
1429
- return;
1430
- const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
1462
+ const { noteNumber, volumeEnvelopeNode } = note;
1431
1463
  if (channel.isDrum) {
1432
1464
  const { keyBasedGainLs, keyBasedGainRs } = channel;
1433
1465
  let gainL = keyBasedGainLs[noteNumber];
@@ -1437,25 +1469,48 @@ class MidyGM2 {
1437
1469
  gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
1438
1470
  gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
1439
1471
  }
1440
- note.volumeEnvelopeNode.connect(gainL);
1441
- note.volumeEnvelopeNode.connect(gainR);
1472
+ volumeEnvelopeNode.connect(gainL);
1473
+ volumeEnvelopeNode.connect(gainR);
1442
1474
  }
1443
1475
  else {
1444
- note.volumeEnvelopeNode.connect(channel.gainL);
1445
- note.volumeEnvelopeNode.connect(channel.gainR);
1476
+ volumeEnvelopeNode.connect(channel.gainL);
1477
+ volumeEnvelopeNode.connect(channel.gainR);
1446
1478
  }
1447
1479
  if (0.5 <= channel.state.sustainPedal) {
1448
1480
  channel.sustainNotes.push(note);
1449
1481
  }
1450
1482
  this.handleExclusiveClass(note, channelNumber, startTime);
1451
1483
  this.handleDrumExclusiveClass(note, channelNumber, startTime);
1484
+ }
1485
+ async noteOn(channelNumber, noteNumber, velocity, startTime) {
1486
+ const channel = this.channels[channelNumber];
1487
+ const realtime = startTime === undefined;
1488
+ if (realtime)
1489
+ startTime = this.audioContext.currentTime;
1490
+ const note = new Note(noteNumber, velocity, startTime);
1452
1491
  const scheduledNotes = channel.scheduledNotes;
1453
1492
  note.index = scheduledNotes.length;
1454
1493
  scheduledNotes.push(note);
1455
- }
1456
- noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
1457
- scheduleTime ??= this.audioContext.currentTime;
1458
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
1494
+ const programNumber = channel.programNumber;
1495
+ const bankTable = this.soundFontTable[programNumber];
1496
+ if (!bankTable)
1497
+ return;
1498
+ const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
1499
+ const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
1500
+ const soundFontIndex = bankTable[bank];
1501
+ if (soundFontIndex === undefined)
1502
+ return;
1503
+ const soundFont = this.soundFonts[soundFontIndex];
1504
+ note.voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
1505
+ if (!note.voice)
1506
+ return;
1507
+ await this.setNoteAudioNode(channel, note, realtime);
1508
+ this.setNoteRouting(channelNumber, note, startTime);
1509
+ note.pending = false;
1510
+ const off = note.offEvent;
1511
+ if (off) {
1512
+ this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
1513
+ }
1459
1514
  }
1460
1515
  disconnectNote(note) {
1461
1516
  note.bufferSource.disconnect();
@@ -1478,6 +1533,7 @@ class MidyGM2 {
1478
1533
  }
1479
1534
  }
1480
1535
  releaseNote(channel, note, endTime) {
1536
+ endTime ??= this.audioContext.currentTime;
1481
1537
  const volRelease = endTime + note.voiceParams.volRelease;
1482
1538
  const modRelease = endTime + note.voiceParams.modRelease;
1483
1539
  const stopTime = Math.min(volRelease, modRelease);
@@ -1498,7 +1554,7 @@ class MidyGM2 {
1498
1554
  }, stopTime);
1499
1555
  });
1500
1556
  }
1501
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
1557
+ noteOff(channelNumber, noteNumber, velocity, endTime, force) {
1502
1558
  const channel = this.channels[channelNumber];
1503
1559
  const state = channel.state;
1504
1560
  if (!force) {
@@ -1517,9 +1573,15 @@ class MidyGM2 {
1517
1573
  if (index < 0)
1518
1574
  return;
1519
1575
  const note = channel.scheduledNotes[index];
1576
+ if (note.pending) {
1577
+ note.offEvent = { velocity, startTime: endTime };
1578
+ return;
1579
+ }
1520
1580
  note.ending = true;
1521
1581
  this.setNoteIndex(channel, index);
1522
- this.releaseNote(channel, note, endTime);
1582
+ const promise = this.releaseNote(channel, note, endTime);
1583
+ this.notePromises.push(promise);
1584
+ return promise;
1523
1585
  }
1524
1586
  setNoteIndex(channel, index) {
1525
1587
  let allEnds = true;
@@ -1547,16 +1609,12 @@ class MidyGM2 {
1547
1609
  }
1548
1610
  return -1;
1549
1611
  }
1550
- noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
1551
- scheduleTime ??= this.audioContext.currentTime;
1552
- return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
1553
- }
1554
1612
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
1555
1613
  const velocity = halfVelocity * 2;
1556
1614
  const channel = this.channels[channelNumber];
1557
1615
  const promises = [];
1558
1616
  for (let i = 0; i < channel.sustainNotes.length; i++) {
1559
- const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
1617
+ const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
1560
1618
  promises.push(promise);
1561
1619
  }
1562
1620
  channel.sustainNotes = [];
@@ -1570,7 +1628,7 @@ class MidyGM2 {
1570
1628
  channel.state.sostenutoPedal = 0;
1571
1629
  for (let i = 0; i < sostenutoNotes.length; i++) {
1572
1630
  const note = sostenutoNotes[i];
1573
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
1631
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
1574
1632
  promises.push(promise);
1575
1633
  }
1576
1634
  channel.sostenutoNotes = [];
@@ -1655,7 +1713,8 @@ class MidyGM2 {
1655
1713
  if (note.modulationDepth) {
1656
1714
  const modLfoToPitch = note.voiceParams.modLfoToPitch +
1657
1715
  this.getLFOPitchDepth(channel, note);
1658
- const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1716
+ const baseDepth = Math.abs(modLfoToPitch) +
1717
+ channel.state.modulationDepthMSB;
1659
1718
  const depth = baseDepth * Math.sign(modLfoToPitch);
1660
1719
  note.modulationDepth.gain
1661
1720
  .cancelScheduledValues(scheduleTime)
@@ -1787,7 +1846,7 @@ class MidyGM2 {
1787
1846
  createVoiceParamsHandlers() {
1788
1847
  return {
1789
1848
  modLfoToPitch: (channel, note, scheduleTime) => {
1790
- if (0 < channel.state.modulationDepth) {
1849
+ if (0 < channel.state.modulationDepthMSB) {
1791
1850
  this.setModLfoToPitch(channel, note, scheduleTime);
1792
1851
  }
1793
1852
  },
@@ -1797,12 +1856,12 @@ class MidyGM2 {
1797
1856
  }
1798
1857
  },
1799
1858
  modLfoToFilterFc: (channel, note, scheduleTime) => {
1800
- if (0 < channel.state.modulationDepth) {
1859
+ if (0 < channel.state.modulationDepthMSB) {
1801
1860
  this.setModLfoToFilterFc(channel, note, scheduleTime);
1802
1861
  }
1803
1862
  },
1804
1863
  modLfoToVolume: (channel, note, scheduleTime) => {
1805
- if (0 < channel.state.modulationDepth) {
1864
+ if (0 < channel.state.modulationDepthMSB) {
1806
1865
  this.setModLfoToVolume(channel, note, scheduleTime);
1807
1866
  }
1808
1867
  },
@@ -1813,12 +1872,12 @@ class MidyGM2 {
1813
1872
  this.setReverbSend(channel, note, scheduleTime);
1814
1873
  },
1815
1874
  delayModLFO: (_channel, note, _scheduleTime) => {
1816
- if (0 < channel.state.modulationDepth) {
1875
+ if (0 < channel.state.modulationDepthMSB) {
1817
1876
  this.setDelayModLFO(note);
1818
1877
  }
1819
1878
  },
1820
1879
  freqModLFO: (_channel, note, scheduleTime) => {
1821
- if (0 < channel.state.modulationDepth) {
1880
+ if (0 < channel.state.modulationDepthMSB) {
1822
1881
  this.setFreqModLFO(note, scheduleTime);
1823
1882
  }
1824
1883
  },
@@ -1920,7 +1979,8 @@ class MidyGM2 {
1920
1979
  this.channels[channelNumber].bankMSB = msb;
1921
1980
  }
1922
1981
  updateModulation(channel, scheduleTime) {
1923
- const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1982
+ const depth = channel.state.modulationDepthMSB *
1983
+ channel.modulationDepthRange;
1924
1984
  this.processScheduledNotes(channel, (note) => {
1925
1985
  if (note.modulationDepth) {
1926
1986
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
@@ -1935,7 +1995,7 @@ class MidyGM2 {
1935
1995
  if (channel.isDrum)
1936
1996
  return;
1937
1997
  scheduleTime ??= this.audioContext.currentTime;
1938
- channel.state.modulationDepth = modulation / 127;
1998
+ channel.state.modulationDepthMSB = modulation / 127;
1939
1999
  this.updateModulation(channel, scheduleTime);
1940
2000
  }
1941
2001
  updatePortamento(channel, scheduleTime) {
@@ -1957,9 +2017,9 @@ class MidyGM2 {
1957
2017
  });
1958
2018
  }
1959
2019
  setPortamentoTime(channelNumber, portamentoTime, scheduleTime) {
1960
- const channel = this.channels[channelNumber];
1961
2020
  scheduleTime ??= this.audioContext.currentTime;
1962
- channel.state.portamentoTime = portamentoTime / 127;
2021
+ const channel = this.channels[channelNumber];
2022
+ channel.state.portamentoTimeMSB = portamentoTime / 127;
1963
2023
  if (channel.isDrum)
1964
2024
  return;
1965
2025
  this.updatePortamento(channel, scheduleTime);
@@ -1967,7 +2027,7 @@ class MidyGM2 {
1967
2027
  setVolume(channelNumber, volume, scheduleTime) {
1968
2028
  scheduleTime ??= this.audioContext.currentTime;
1969
2029
  const channel = this.channels[channelNumber];
1970
- channel.state.volume = volume / 127;
2030
+ channel.state.volumeMSB = volume / 127;
1971
2031
  if (channel.isDrum) {
1972
2032
  for (let i = 0; i < 128; i++) {
1973
2033
  this.updateKeyBasedVolume(channel, i, scheduleTime);
@@ -1987,7 +2047,7 @@ class MidyGM2 {
1987
2047
  setPan(channelNumber, pan, scheduleTime) {
1988
2048
  scheduleTime ??= this.audioContext.currentTime;
1989
2049
  const channel = this.channels[channelNumber];
1990
- channel.state.pan = pan / 127;
2050
+ channel.state.panMSB = pan / 127;
1991
2051
  if (channel.isDrum) {
1992
2052
  for (let i = 0; i < 128; i++) {
1993
2053
  this.updateKeyBasedVolume(channel, i, scheduleTime);
@@ -2000,7 +2060,7 @@ class MidyGM2 {
2000
2060
  setExpression(channelNumber, expression, scheduleTime) {
2001
2061
  scheduleTime ??= this.audioContext.currentTime;
2002
2062
  const channel = this.channels[channelNumber];
2003
- channel.state.expression = expression / 127;
2063
+ channel.state.expressionMSB = expression / 127;
2004
2064
  this.updateChannelVolume(channel, scheduleTime);
2005
2065
  }
2006
2066
  setBankLSB(channelNumber, lsb) {
@@ -2012,8 +2072,8 @@ class MidyGM2 {
2012
2072
  }
2013
2073
  updateChannelVolume(channel, scheduleTime) {
2014
2074
  const state = channel.state;
2015
- const volume = state.volume * state.expression;
2016
- const { gainLeft, gainRight } = this.panToGain(state.pan);
2075
+ const volume = state.volumeMSB * state.expressionMSB;
2076
+ const { gainLeft, gainRight } = this.panToGain(state.panMSB);
2017
2077
  channel.gainL.gain
2018
2078
  .cancelScheduledValues(scheduleTime)
2019
2079
  .setValueAtTime(volume * gainLeft, scheduleTime);
@@ -2027,8 +2087,8 @@ class MidyGM2 {
2027
2087
  return;
2028
2088
  const gainR = channel.keyBasedGainRs[keyNumber];
2029
2089
  const state = channel.state;
2030
- const defaultVolume = state.volume * state.expression;
2031
- const defaultPan = state.pan;
2090
+ const defaultVolume = state.volumeMSB * state.expressionMSB;
2091
+ const defaultPan = state.panMSB;
2032
2092
  const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
2033
2093
  const volume = (0 <= keyBasedVolume)
2034
2094
  ? defaultVolume * keyBasedVolume / 64
@@ -2279,8 +2339,8 @@ class MidyGM2 {
2279
2339
  const keys = [
2280
2340
  "channelPressure",
2281
2341
  "pitchWheel",
2282
- "expression",
2283
- "modulationDepth",
2342
+ "expressionMSB",
2343
+ "modulationDepthMSB",
2284
2344
  "sustainPedal",
2285
2345
  "portamento",
2286
2346
  "sostenutoPedal",
@@ -23,6 +23,7 @@ export class MidyGMLite {
23
23
  soundFontTable: never[][];
24
24
  voiceCounter: Map<any, any>;
25
25
  voiceCache: Map<any, any>;
26
+ realtimeVoiceCache: Map<any, any>;
26
27
  isPlaying: boolean;
27
28
  isPausing: boolean;
28
29
  isPaused: boolean;
@@ -103,22 +104,21 @@ export class MidyGMLite {
103
104
  clampCutoffFrequency(frequency: any): number;
104
105
  setFilterEnvelope(note: any, scheduleTime: any): void;
105
106
  startModulation(channel: any, note: any, scheduleTime: any): void;
106
- getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any): Promise<any>;
107
- createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any): Promise<Note>;
107
+ getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any, realtime: any): Promise<any>;
108
+ setNoteAudioNode(channel: any, note: any, realtime: any): Promise<any>;
108
109
  handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
109
110
  handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
110
- scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
111
- noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
111
+ setNoteRouting(channelNumber: any, note: any, startTime: any): void;
112
+ noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
112
113
  disconnectNote(note: any): void;
113
114
  releaseNote(channel: any, note: any, endTime: any): Promise<any>;
114
- scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): void;
115
+ noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): Promise<any> | undefined;
115
116
  setNoteIndex(channel: any, index: any): void;
116
117
  findNoteOffIndex(channel: any, noteNumber: any): any;
117
- noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): void;
118
- releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
118
+ releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
119
119
  createMessageHandlers(): any[];
120
120
  handleMessage(data: any, scheduleTime: any): void;
121
- handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<void>;
121
+ handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
122
122
  setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
123
123
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
124
124
  setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
@@ -174,22 +174,4 @@ export class MidyGMLite {
174
174
  handleSysEx(data: any, scheduleTime: any): void;
175
175
  scheduleTask(callback: any, scheduleTime: any): Promise<any>;
176
176
  }
177
- declare class Note {
178
- constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
179
- index: number;
180
- ending: boolean;
181
- bufferSource: any;
182
- filterNode: any;
183
- filterDepth: any;
184
- volumeEnvelopeNode: any;
185
- volumeDepth: any;
186
- modulationLFO: any;
187
- modulationDepth: any;
188
- noteNumber: any;
189
- velocity: any;
190
- startTime: any;
191
- voice: any;
192
- voiceParams: any;
193
- }
194
- export {};
195
177
  //# sourceMappingURL=midy-GMLite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AA0GA;IA4BE;;;;;;;;;MASE;IAEF,+BAeC;IArDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,0BAAuD;IACvD,4BAAyB;IACzB,0BAAuB;IACvB,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IACrC,+BAEE;IAcA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF,uBAAmD;IACnD;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAWC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,0EAUC;IAED,yEAqDC;IAED,mCAOC;IAED,uBAQC;IAED,yDA2BC;IAED,2BAwCC;IAED,uDAEC;IAED,wDAEC;IAED,qCAKC;IAED;;;MAwDC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,sBAKC;IAED,uBAQC;IAED,wBAKC;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,6FAyBC;IAED,oGA2CC;IAED,0EAiBC;IAED,8EAiBC;IAED,kGAiCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAkBC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,+BAmBC;IAED,kDAOC;IAED,uGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAWC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MAiCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDASC;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,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCASC;IAED,4EAaC;IAED,4DAGC;IAED,qDAKC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA9hDD;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"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AA2GA;IA6BE;;;;;;;;;MASE;IAEF,+BAeC;IAtDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,0BAAuD;IACvD,4BAAyB;IACzB,0BAAuB;IACvB,kCAA+B;IAC/B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IACrC,+BAEE;IAcA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF,uBAAmD;IACnD;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAWC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDASC;IAED,0EAUC;IAED,yEAoDC;IAED,mCAOC;IAED,uBASC;IAED,yDA2BC;IAED,2BA8CC;IAED,uDAEC;IAED,wDAEC;IAED,qCAKC;IAED;;;MAwDC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,sBAKC;IAED,uBAQC;IAED,wBAKC;IAED,0BAKC;IAED,wBAOC;IAED,sBAIC;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,4GAkCC;IAED,uEA4CC;IAED,0EAiBC;IAED,8EAiBC;IAED,oEAUC;IAED,0FAwBC;IAED,gCASC;IAED,iEAqBC;IAED,gHAwBC;IAED,6CAUC;IAED,qDAUC;IAED,4GAeC;IAED,+BAmBC;IAED,kDAOC;IAED,sGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAYC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MAiCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDASC;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,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCASC;IAED,4EAaC;IAED,4DAGC;IAED,qDAKC;IAED,gDAYC;IAGD,6DAgBC;CACF"}