@marmooo/midy 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/midy-GM1.js CHANGED
@@ -1,7 +1,19 @@
1
1
  import { parseMidi } from "midi-file";
2
2
  import { parse, SoundFont } from "@marmooo/soundfont-parser";
3
3
  class Note {
4
- constructor(noteNumber, velocity, startTime, voice, voiceParams) {
4
+ constructor(noteNumber, velocity, startTime) {
5
+ Object.defineProperty(this, "voice", {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value: void 0
10
+ });
11
+ Object.defineProperty(this, "voiceParams", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: void 0
16
+ });
5
17
  Object.defineProperty(this, "index", {
6
18
  enumerable: true,
7
19
  configurable: true,
@@ -14,6 +26,12 @@ class Note {
14
26
  writable: true,
15
27
  value: false
16
28
  });
29
+ Object.defineProperty(this, "pending", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: true
34
+ });
17
35
  Object.defineProperty(this, "bufferSource", {
18
36
  enumerable: true,
19
37
  configurable: true,
@@ -59,8 +77,6 @@ class Note {
59
77
  this.noteNumber = noteNumber;
60
78
  this.velocity = velocity;
61
79
  this.startTime = startTime;
62
- this.voice = voice;
63
- this.voiceParams = voiceParams;
64
80
  }
65
81
  }
66
82
  // normalized to 0-1 for use with the SF2 modulator model
@@ -200,7 +216,7 @@ export class MidyGM1 {
200
216
  enumerable: true,
201
217
  configurable: true,
202
218
  writable: true,
203
- value: this.initSoundFontTable()
219
+ value: Array.from({ length: 128 }, () => [])
204
220
  });
205
221
  Object.defineProperty(this, "voiceCounter", {
206
222
  enumerable: true,
@@ -214,6 +230,12 @@ export class MidyGM1 {
214
230
  writable: true,
215
231
  value: new Map()
216
232
  });
233
+ Object.defineProperty(this, "realtimeVoiceCache", {
234
+ enumerable: true,
235
+ configurable: true,
236
+ writable: true,
237
+ value: new Map()
238
+ });
217
239
  Object.defineProperty(this, "isPlaying", {
218
240
  enumerable: true,
219
241
  configurable: true,
@@ -281,6 +303,7 @@ export class MidyGM1 {
281
303
  length: 1,
282
304
  sampleRate: audioContext.sampleRate,
283
305
  });
306
+ this.messageHandlers = this.createMessageHandlers();
284
307
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
285
308
  this.controlChangeHandlers = this.createControlChangeHandlers();
286
309
  this.channels = this.createChannels(audioContext);
@@ -288,21 +311,14 @@ export class MidyGM1 {
288
311
  this.scheduler.connect(audioContext.destination);
289
312
  this.GM1SystemOn();
290
313
  }
291
- initSoundFontTable() {
292
- const table = new Array(128);
293
- for (let i = 0; i < 128; i++) {
294
- table[i] = new Map();
295
- }
296
- return table;
297
- }
298
314
  addSoundFont(soundFont) {
299
315
  const index = this.soundFonts.length;
300
316
  this.soundFonts.push(soundFont);
301
317
  const presetHeaders = soundFont.parsed.presetHeaders;
318
+ const soundFontTable = this.soundFontTable;
302
319
  for (let i = 0; i < presetHeaders.length; i++) {
303
- const presetHeader = presetHeaders[i];
304
- const banks = this.soundFontTable[presetHeader.preset];
305
- banks.set(presetHeader.bank, index);
320
+ const { preset, bank } = presetHeaders[i];
321
+ soundFontTable[preset][bank] = index;
306
322
  }
307
323
  }
308
324
  async toUint8Array(input) {
@@ -380,13 +396,16 @@ export class MidyGM1 {
380
396
  this.GM1SystemOn();
381
397
  }
382
398
  getVoiceId(channel, noteNumber, velocity) {
383
- const bankNumber = this.calcBank(channel);
384
- const soundFontIndex = this.soundFontTable[channel.programNumber]
385
- .get(bankNumber);
399
+ const programNumber = channel.programNumber;
400
+ const bankTable = this.soundFontTable[programNumber];
401
+ if (!bankTable)
402
+ return;
403
+ const bank = channel.isDrum ? 128 : 0;
404
+ const soundFontIndex = bankTable[bank];
386
405
  if (soundFontIndex === undefined)
387
406
  return;
388
407
  const soundFont = this.soundFonts[soundFontIndex];
389
- const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
408
+ const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
390
409
  const { instrument, sampleID } = voice.generators;
391
410
  return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
392
411
  }
@@ -435,19 +454,22 @@ export class MidyGM1 {
435
454
  }
436
455
  return bufferSource;
437
456
  }
438
- async scheduleTimelineEvents(t, resumeTime, queueIndex) {
439
- while (queueIndex < this.timeline.length) {
440
- const event = this.timeline[queueIndex];
441
- if (event.startTime > t + this.lookAhead)
457
+ async scheduleTimelineEvents(scheduleTime, queueIndex) {
458
+ const timeOffset = this.resumeTime - this.startTime;
459
+ const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
460
+ const schedulingOffset = this.startDelay - timeOffset;
461
+ const timeline = this.timeline;
462
+ while (queueIndex < timeline.length) {
463
+ const event = timeline[queueIndex];
464
+ if (lookAheadCheckTime < event.startTime)
442
465
  break;
443
- const delay = this.startDelay - resumeTime;
444
- const startTime = event.startTime + delay;
466
+ const startTime = event.startTime + schedulingOffset;
445
467
  switch (event.type) {
446
468
  case "noteOn":
447
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
469
+ await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
448
470
  break;
449
471
  case "noteOff": {
450
- const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
472
+ const notePromise = this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
451
473
  if (notePromise)
452
474
  this.notePromises.push(notePromise);
453
475
  break;
@@ -480,6 +502,7 @@ export class MidyGM1 {
480
502
  this.exclusiveClassNotes.fill(undefined);
481
503
  this.drumExclusiveClassNotes.fill(undefined);
482
504
  this.voiceCache.clear();
505
+ this.realtimeVoiceCache.clear();
483
506
  for (let i = 0; i < this.channels.length; i++) {
484
507
  this.channels[i].scheduledNotes = [];
485
508
  this.resetChannelStates(i);
@@ -513,13 +536,10 @@ export class MidyGM1 {
513
536
  this.isPaused = false;
514
537
  this.startTime = this.audioContext.currentTime;
515
538
  let queueIndex = this.getQueueIndex(this.resumeTime);
516
- let resumeTime = this.resumeTime - this.startTime;
517
539
  let finished = false;
518
540
  this.notePromises = [];
519
541
  while (queueIndex < this.timeline.length) {
520
542
  const now = this.audioContext.currentTime;
521
- const t = now + resumeTime;
522
- queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
523
543
  if (this.isPausing) {
524
544
  await this.stopNotes(0, true, now);
525
545
  await this.audioContext.suspend();
@@ -538,10 +558,10 @@ export class MidyGM1 {
538
558
  const nextQueueIndex = this.getQueueIndex(this.resumeTime);
539
559
  this.updateStates(queueIndex, nextQueueIndex);
540
560
  queueIndex = nextQueueIndex;
541
- resumeTime = this.resumeTime - this.startTime;
542
561
  this.isSeeking = false;
543
562
  continue;
544
563
  }
564
+ queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
545
565
  const waitTime = now + this.noteCheckInterval;
546
566
  await this.scheduleTask(() => { }, waitTime);
547
567
  }
@@ -557,16 +577,16 @@ export class MidyGM1 {
557
577
  secondToTicks(second, secondsPerBeat) {
558
578
  return second * this.ticksPerBeat / secondsPerBeat;
559
579
  }
580
+ getSoundFontId(channel) {
581
+ const programNumber = channel.programNumber;
582
+ const bank = channel.isDrum ? "128" : "000";
583
+ const program = programNumber.toString().padStart(3, "0");
584
+ return `${bank}:${program}`;
585
+ }
560
586
  extractMidiData(midi) {
561
587
  const instruments = new Set();
562
588
  const timeline = [];
563
- const tmpChannels = new Array(this.channels.length);
564
- for (let i = 0; i < tmpChannels.length; i++) {
565
- tmpChannels[i] = {
566
- programNumber: -1,
567
- bank: this.channels[i].bank,
568
- };
569
- }
589
+ const channels = this.channels;
570
590
  for (let i = 0; i < midi.tracks.length; i++) {
571
591
  const track = midi.tracks[i];
572
592
  let currentTicks = 0;
@@ -576,17 +596,15 @@ export class MidyGM1 {
576
596
  event.ticks = currentTicks;
577
597
  switch (event.type) {
578
598
  case "noteOn": {
579
- const channel = tmpChannels[event.channel];
580
- if (channel.programNumber < 0) {
581
- instruments.add(`${channel.bank}:0`);
582
- channel.programNumber = 0;
583
- }
599
+ const channel = channels[event.channel];
600
+ instruments.add(this.getSoundFontId(channel));
584
601
  break;
585
602
  }
586
603
  case "programChange": {
587
- const channel = tmpChannels[event.channel];
588
- channel.programNumber = event.programNumber;
589
- instruments.add(`${channel.bankNumber}:${channel.programNumber}`);
604
+ const channel = channels[event.channel];
605
+ this.setProgramChange(event.channel, event.programNumber);
606
+ instruments.add(this.getSoundFontId(channel));
607
+ break;
590
608
  }
591
609
  }
592
610
  delete event.deltaTime;
@@ -621,7 +639,7 @@ export class MidyGM1 {
621
639
  const channel = this.channels[channelNumber];
622
640
  const promises = [];
623
641
  this.processActiveNotes(channel, scheduleTime, (note) => {
624
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
642
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
625
643
  this.notePromises.push(promise);
626
644
  promises.push(promise);
627
645
  });
@@ -631,7 +649,7 @@ export class MidyGM1 {
631
649
  const channel = this.channels[channelNumber];
632
650
  const promises = [];
633
651
  this.processScheduledNotes(channel, (note) => {
634
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
652
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
635
653
  this.notePromises.push(promise);
636
654
  promises.push(promise);
637
655
  });
@@ -664,7 +682,7 @@ export class MidyGM1 {
664
682
  if (!this.isPlaying || this.isPaused)
665
683
  return;
666
684
  const now = this.audioContext.currentTime;
667
- this.resumeTime += now - this.startTime - this.startDelay;
685
+ this.resumeTime = now - this.startTime - this.startDelay;
668
686
  this.isPausing = true;
669
687
  await this.playPromise;
670
688
  this.isPausing = false;
@@ -690,11 +708,13 @@ export class MidyGM1 {
690
708
  if (totalTime < event.startTime)
691
709
  totalTime = event.startTime;
692
710
  }
693
- return totalTime;
711
+ return totalTime + this.startDelay;
694
712
  }
695
713
  currentTime() {
714
+ if (!this.isPlaying)
715
+ return this.resumeTime;
696
716
  const now = this.audioContext.currentTime;
697
- return this.resumeTime + now - this.startTime - this.startDelay;
717
+ return now + this.resumeTime - this.startTime;
698
718
  }
699
719
  processScheduledNotes(channel, callback) {
700
720
  const scheduledNotes = channel.scheduledNotes;
@@ -833,31 +853,42 @@ export class MidyGM1 {
833
853
  note.modulationLFO.connect(note.volumeDepth);
834
854
  note.volumeDepth.connect(note.volumeEnvelopeNode.gain);
835
855
  }
836
- async getAudioBuffer(channel, noteNumber, velocity, voiceParams) {
856
+ async getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime) {
837
857
  const audioBufferId = this.getVoiceId(channel, noteNumber, velocity);
838
- const cache = this.voiceCache.get(audioBufferId);
839
- if (cache) {
840
- cache.counter += 1;
841
- if (cache.maxCount <= cache.counter) {
842
- this.voiceCache.delete(audioBufferId);
843
- }
844
- return cache.audioBuffer;
845
- }
846
- else {
847
- const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
858
+ if (realtime) {
859
+ const cachedAudioBuffer = this.realtimeVoiceCache.get(audioBufferId);
860
+ if (cachedAudioBuffer)
861
+ return cachedAudioBuffer;
848
862
  const audioBuffer = await this.createAudioBuffer(voiceParams);
849
- const cache = { audioBuffer, maxCount, counter: 1 };
850
- this.voiceCache.set(audioBufferId, cache);
863
+ this.realtimeVoiceCache.set(audioBufferId, audioBuffer);
851
864
  return audioBuffer;
852
865
  }
866
+ else {
867
+ const cache = this.voiceCache.get(audioBufferId);
868
+ if (cache) {
869
+ cache.counter += 1;
870
+ if (cache.maxCount <= cache.counter) {
871
+ this.voiceCache.delete(audioBufferId);
872
+ }
873
+ return cache.audioBuffer;
874
+ }
875
+ else {
876
+ const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
877
+ const audioBuffer = await this.createAudioBuffer(voiceParams);
878
+ const cache = { audioBuffer, maxCount, counter: 1 };
879
+ this.voiceCache.set(audioBufferId, cache);
880
+ return audioBuffer;
881
+ }
882
+ }
853
883
  }
854
- async createNote(channel, voice, noteNumber, velocity, startTime) {
884
+ async setNoteAudioNode(channel, note, realtime) {
855
885
  const now = this.audioContext.currentTime;
886
+ const { noteNumber, velocity, startTime } = note;
856
887
  const state = channel.state;
857
888
  const controllerState = this.getControllerState(channel, noteNumber, velocity);
858
- const voiceParams = voice.getAllParams(controllerState);
859
- const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
860
- const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
889
+ const voiceParams = note.voice.getAllParams(controllerState);
890
+ note.voiceParams = voiceParams;
891
+ const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime);
861
892
  note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
862
893
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
863
894
  note.filterNode = new BiquadFilterNode(this.audioContext, {
@@ -884,37 +915,50 @@ export class MidyGM1 {
884
915
  if (prev) {
885
916
  const [prevNote, prevChannelNumber] = prev;
886
917
  if (prevNote && !prevNote.ending) {
887
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
918
+ this.noteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
888
919
  startTime, true);
889
920
  }
890
921
  }
891
922
  this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
892
923
  }
893
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
924
+ setNoteRouting(channelNumber, note, startTime) {
894
925
  const channel = this.channels[channelNumber];
895
- const bankNumber = channel.bank;
896
- const soundFontIndex = this.soundFontTable[channel.programNumber]
897
- .get(bankNumber);
898
- if (soundFontIndex === undefined)
899
- return;
900
- const soundFont = this.soundFonts[soundFontIndex];
901
- const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
902
- if (!voice)
903
- return;
904
- const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
905
- note.volumeEnvelopeNode.connect(channel.gainL);
906
- note.volumeEnvelopeNode.connect(channel.gainR);
926
+ const volumeEnvelopeNode = note.volumeEnvelopeNode;
927
+ volumeEnvelopeNode.connect(channel.gainL);
928
+ volumeEnvelopeNode.connect(channel.gainR);
907
929
  if (0.5 <= channel.state.sustainPedal) {
908
930
  channel.sustainNotes.push(note);
909
931
  }
910
932
  this.handleExclusiveClass(note, channelNumber, startTime);
933
+ }
934
+ async noteOn(channelNumber, noteNumber, velocity, startTime) {
935
+ const channel = this.channels[channelNumber];
936
+ const realtime = startTime === undefined;
937
+ if (realtime)
938
+ startTime = this.audioContext.currentTime;
939
+ const note = new Note(noteNumber, velocity, startTime);
911
940
  const scheduledNotes = channel.scheduledNotes;
912
941
  note.index = scheduledNotes.length;
913
942
  scheduledNotes.push(note);
914
- }
915
- noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
916
- scheduleTime ??= this.audioContext.currentTime;
917
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
943
+ const programNumber = channel.programNumber;
944
+ const bankTable = this.soundFontTable[programNumber];
945
+ if (!bankTable)
946
+ return;
947
+ const bank = channel.isDrum ? 128 : 0;
948
+ const soundFontIndex = bankTable[bank];
949
+ if (soundFontIndex === undefined)
950
+ return;
951
+ const soundFont = this.soundFonts[soundFontIndex];
952
+ note.voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
953
+ if (!note.voice)
954
+ return;
955
+ await this.setNoteAudioNode(channel, note, realtime);
956
+ this.setNoteRouting(channelNumber, note, startTime);
957
+ note.pending = false;
958
+ const off = note.offEvent;
959
+ if (off) {
960
+ this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
961
+ }
918
962
  }
919
963
  disconnectNote(note) {
920
964
  note.bufferSource.disconnect();
@@ -927,6 +971,7 @@ export class MidyGM1 {
927
971
  }
928
972
  }
929
973
  releaseNote(channel, note, endTime) {
974
+ endTime ??= this.audioContext.currentTime;
930
975
  const volRelease = endTime + note.voiceParams.volRelease;
931
976
  const modRelease = endTime + note.voiceParams.modRelease;
932
977
  const stopTime = Math.min(volRelease, modRelease);
@@ -947,7 +992,7 @@ export class MidyGM1 {
947
992
  }, stopTime);
948
993
  });
949
994
  }
950
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
995
+ noteOff(channelNumber, noteNumber, velocity, endTime, force) {
951
996
  const channel = this.channels[channelNumber];
952
997
  if (!force && 0.5 <= channel.state.sustainPedal)
953
998
  return;
@@ -955,6 +1000,10 @@ export class MidyGM1 {
955
1000
  if (index < 0)
956
1001
  return;
957
1002
  const note = channel.scheduledNotes[index];
1003
+ if (note.pending) {
1004
+ note.offEvent = { velocity, startTime: endTime };
1005
+ return;
1006
+ }
958
1007
  note.ending = true;
959
1008
  this.setNoteIndex(channel, index);
960
1009
  this.releaseNote(channel, note, endTime);
@@ -985,22 +1034,37 @@ export class MidyGM1 {
985
1034
  }
986
1035
  return -1;
987
1036
  }
988
- noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
989
- scheduleTime ??= this.audioContext.currentTime;
990
- return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
991
- }
992
1037
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
993
1038
  const velocity = halfVelocity * 2;
994
1039
  const channel = this.channels[channelNumber];
995
1040
  const promises = [];
996
1041
  for (let i = 0; i < channel.sustainNotes.length; i++) {
997
- const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
1042
+ const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
998
1043
  promises.push(promise);
999
1044
  }
1000
1045
  channel.sustainNotes = [];
1001
1046
  return promises;
1002
1047
  }
1003
- handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
1048
+ createMessageHandlers() {
1049
+ const handlers = new Array(256);
1050
+ // Channel Message
1051
+ handlers[0x80] = (data, scheduleTime) => this.noteOff(data[0] & 0x0F, data[1], data[2], scheduleTime);
1052
+ handlers[0x90] = (data, scheduleTime) => this.noteOn(data[0] & 0x0F, data[1], data[2], scheduleTime);
1053
+ handlers[0xB0] = (data, scheduleTime) => this.setControlChange(data[0] & 0x0F, data[1], data[2], scheduleTime);
1054
+ handlers[0xC0] = (data, scheduleTime) => this.setProgramChange(data[0] & 0x0F, data[1], scheduleTime);
1055
+ handlers[0xE0] = (data, scheduleTime) => this.handlePitchBendMessage(data[0] & 0x0F, data[1], data[2], scheduleTime);
1056
+ return handlers;
1057
+ }
1058
+ handleMessage(data, scheduleTime) {
1059
+ const status = data[0];
1060
+ if (status === 0xF0) {
1061
+ return this.handleSysEx(data.subarray(1), scheduleTime);
1062
+ }
1063
+ const handler = this.messageHandlers[status];
1064
+ if (handler)
1065
+ handler(data, scheduleTime);
1066
+ }
1067
+ handleChannelMessage(statusByte, data1, data2, scheduleTime) {
1004
1068
  const channelNumber = statusByte & 0x0F;
1005
1069
  const messageType = statusByte & 0xF0;
1006
1070
  switch (messageType) {
@@ -1428,10 +1492,8 @@ export class MidyGM1 {
1428
1492
  for (let i = 0; i < this.channels.length; i++) {
1429
1493
  this.allSoundOff(i, 0, scheduleTime);
1430
1494
  const channel = this.channels[i];
1431
- channel.bank = 0;
1432
1495
  channel.isDrum = false;
1433
1496
  }
1434
- this.channels[9].bank = 128;
1435
1497
  this.channels[9].isDrum = true;
1436
1498
  }
1437
1499
  handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
@@ -1452,16 +1514,11 @@ export class MidyGM1 {
1452
1514
  const volume = (data[5] * 128 + data[4]) / 16383;
1453
1515
  this.setMasterVolume(volume, scheduleTime);
1454
1516
  }
1455
- setMasterVolume(volume, scheduleTime) {
1517
+ setMasterVolume(value, scheduleTime) {
1456
1518
  scheduleTime ??= this.audioContext.currentTime;
1457
- if (volume < 0 && 1 < volume) {
1458
- console.error("Master Volume is out of range");
1459
- }
1460
- else {
1461
- this.masterVolume.gain
1462
- .cancelScheduledValues(scheduleTime)
1463
- .setValueAtTime(volume * volume, scheduleTime);
1464
- }
1519
+ this.masterVolume.gain
1520
+ .cancelScheduledValues(scheduleTime)
1521
+ .setValueAtTime(value * value, scheduleTime);
1465
1522
  }
1466
1523
  handleSysEx(data, scheduleTime) {
1467
1524
  switch (data[0]) {
@@ -1498,9 +1555,9 @@ Object.defineProperty(MidyGM1, "channelSettings", {
1498
1555
  configurable: true,
1499
1556
  writable: true,
1500
1557
  value: {
1558
+ scheduleIndex: 0,
1501
1559
  detune: 0,
1502
1560
  programNumber: 0,
1503
- bank: 0,
1504
1561
  dataMSB: 0,
1505
1562
  dataLSB: 0,
1506
1563
  rpnMSB: 127,
package/esm/midy-GM2.d.ts CHANGED
@@ -3,7 +3,6 @@ export class MidyGM2 {
3
3
  scheduleIndex: number;
4
4
  detune: number;
5
5
  programNumber: number;
6
- bank: number;
7
6
  bankMSB: number;
8
7
  bankLSB: number;
9
8
  dataMSB: number;
@@ -34,15 +33,18 @@ export class MidyGM2 {
34
33
  numChannels: number;
35
34
  ticksPerBeat: number;
36
35
  totalTime: number;
36
+ lastActiveSensing: number;
37
+ activeSensingThreshold: number;
37
38
  noteCheckInterval: number;
38
39
  lookAhead: number;
39
40
  startDelay: number;
40
41
  startTime: number;
41
42
  resumeTime: number;
42
43
  soundFonts: any[];
43
- soundFontTable: any[];
44
+ soundFontTable: never[][];
44
45
  voiceCounter: Map<any, any>;
45
46
  voiceCache: Map<any, any>;
47
+ realtimeVoiceCache: Map<any, any>;
46
48
  isPlaying: boolean;
47
49
  isPausing: boolean;
48
50
  isPaused: boolean;
@@ -58,6 +60,7 @@ export class MidyGM2 {
58
60
  masterVolume: any;
59
61
  scheduler: any;
60
62
  schedulerBuffer: any;
63
+ messageHandlers: any[];
61
64
  voiceParamsHandlers: {
62
65
  modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
63
66
  vibLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
@@ -71,6 +74,7 @@ export class MidyGM2 {
71
74
  freqVibLFO: (channel: any, note: any, scheduleTime: any) => void;
72
75
  };
73
76
  controlChangeHandlers: any[];
77
+ keyBasedControllerHandlers: any[];
74
78
  channels: any[];
75
79
  reverbEffect: {
76
80
  input: any;
@@ -85,7 +89,6 @@ export class MidyGM2 {
85
89
  delayNodes: any[];
86
90
  feedbackGains: any[];
87
91
  };
88
- initSoundFontTable(): any[];
89
92
  addSoundFont(soundFont: any): void;
90
93
  toUint8Array(input: any): Promise<Uint8Array<ArrayBuffer>>;
91
94
  loadSoundFont(input: any): Promise<void>;
@@ -102,13 +105,14 @@ export class MidyGM2 {
102
105
  createAudioBuffer(voiceParams: any): Promise<any>;
103
106
  isLoopDrum(channel: any, noteNumber: any): boolean;
104
107
  createBufferSource(channel: any, noteNumber: any, voiceParams: any, audioBuffer: any): any;
105
- scheduleTimelineEvents(t: any, resumeTime: any, queueIndex: any): Promise<any>;
108
+ scheduleTimelineEvents(scheduleTime: any, queueIndex: any): Promise<any>;
106
109
  getQueueIndex(second: any): number;
107
110
  resetAllStates(): void;
108
111
  updateStates(queueIndex: any, nextQueueIndex: any): void;
109
112
  playNotes(): Promise<void>;
110
113
  ticksToSecond(ticks: any, secondsPerBeat: any): number;
111
114
  secondToTicks(second: any, secondsPerBeat: any): number;
115
+ getSoundFontId(channel: any): string;
112
116
  extractMidiData(midi: any): {
113
117
  instruments: Set<any>;
114
118
  timeline: any[];
@@ -170,22 +174,22 @@ export class MidyGM2 {
170
174
  setFilterEnvelope(channel: any, note: any, scheduleTime: any): void;
171
175
  startModulation(channel: any, note: any, scheduleTime: any): void;
172
176
  startVibrato(channel: any, note: any, scheduleTime: any): void;
173
- getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any): Promise<any>;
174
- createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any): Promise<Note>;
175
- calcBank(channel: any): any;
177
+ getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any, realtime: any): Promise<any>;
178
+ setNoteAudioNode(channel: any, note: any, realtime: any): Promise<any>;
176
179
  handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
177
180
  handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
178
- scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
179
- noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
181
+ setNoteRouting(channelNumber: any, note: any, startTime: any): void;
182
+ noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
180
183
  disconnectNote(note: any): void;
181
184
  releaseNote(channel: any, note: any, endTime: any): Promise<any>;
182
- scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): void;
185
+ noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): void;
183
186
  setNoteIndex(channel: any, index: any): void;
184
187
  findNoteOffIndex(channel: any, noteNumber: any): any;
185
- noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): void;
186
188
  releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
187
189
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
188
- handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<void>;
190
+ createMessageHandlers(): any[];
191
+ handleMessage(data: any, scheduleTime: any): void;
192
+ activeSensing(): void;
189
193
  setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
190
194
  setChannelPressure(channelNumber: any, value: any, scheduleTime: any): void;
191
195
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
@@ -233,6 +237,7 @@ export class MidyGM2 {
233
237
  updateChannelVolume(channel: any, scheduleTime: any): void;
234
238
  updateKeyBasedVolume(channel: any, keyNumber: any, scheduleTime: any): void;
235
239
  setSustainPedal(channelNumber: any, value: any, scheduleTime: any): void;
240
+ isPortamento(channel: any, note: any): boolean;
236
241
  setPortamento(channelNumber: any, value: any, scheduleTime: any): void;
237
242
  setSostenutoPedal(channelNumber: any, value: any, scheduleTime: any): void;
238
243
  getSoftPedalFactor(channel: any, note: any): number;
@@ -266,7 +271,7 @@ export class MidyGM2 {
266
271
  GM2SystemOn(scheduleTime: any): void;
267
272
  handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
268
273
  handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
269
- setMasterVolume(volume: any, scheduleTime: any): void;
274
+ setMasterVolume(value: any, scheduleTime: any): void;
270
275
  handleMasterFineTuningSysEx(data: any, scheduleTime: any): void;
271
276
  setMasterFineTuning(value: any, scheduleTime: any): void;
272
277
  handleMasterCoarseTuningSysEx(data: any, scheduleTime: any): void;
@@ -302,31 +307,9 @@ export class MidyGM2 {
302
307
  setControlChangeEffects(channel: any, controllerType: any, scheduleTime: any): void;
303
308
  handleControlChangeSysEx(data: any, scheduleTime: any): void;
304
309
  getKeyBasedValue(channel: any, keyNumber: any, controllerType: any): any;
310
+ createKeyBasedControllerHandlers(): any[];
305
311
  handleKeyBasedInstrumentControlSysEx(data: any, scheduleTime: any): void;
306
312
  handleSysEx(data: any, scheduleTime: any): void;
307
313
  scheduleTask(callback: any, scheduleTime: any): Promise<any>;
308
314
  }
309
- declare class Note {
310
- constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
311
- index: number;
312
- ending: boolean;
313
- bufferSource: any;
314
- filterNode: any;
315
- filterDepth: any;
316
- volumeEnvelopeNode: any;
317
- volumeDepth: any;
318
- modulationLFO: any;
319
- modulationDepth: any;
320
- vibratoLFO: any;
321
- vibratoDepth: any;
322
- reverbSend: any;
323
- chorusSend: any;
324
- portamentoNoteNumber: number;
325
- noteNumber: any;
326
- velocity: any;
327
- startTime: any;
328
- voice: any;
329
- voiceParams: any;
330
- }
331
- export {};
332
315
  //# sourceMappingURL=midy-GM2.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AAmJA;IA6CE;;;;;;;;;;;;;;MAcE;IAEF,+BAoBC;IAhFD,aAAa;IACb,yBAAqB;IACrB,2BAAuB;IACvB;;;;MAIE;IACF;;;;;;MAME;IACF,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAsB;IACtB,+BAA6B;IAC7B,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;IAmBA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF,uBAAmD;IACnD;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,kCAAyE;IACzE,gBAAiD;IACjD;;;kBAAyD;IACzD;;;;;;;;MAAyD;IAQ3D,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAYC;IAED;;;;MAeC;IAED,sCAKC;IAED,yCAqBC;IAED,kDAUC;IAED,mDAIC;IAED,2FAWC;IAED,yEAwDC;IAED,mCAOC;IAED,uBASC;IAED,yDA2BC;IAED,2BAkDC;IAED,uDAEC;IAED,wDAEC;IAED,qCAMC;IAED;;;MAqFC;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,kFAuBC;IAED;;;;MASC;IAED,gFAUC;IAED,mFAYC;IAED,sGAcC;IAID;;;MA8BC;IAED;;;kBA6BC;IAED;;;;;;;;MA0CC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAkBC;IAED,6CAEC;IAED,2DAIC;IAED,+DAgBC;IAED,mDAIC;IAED,2CAoDC;IAED,8EAWC;IAED,oEAgBC;IAED,+DAKC;IAED,qDAoBC;IAED,6CAIC;IAED,8EAoBC;IAED,oEAwBC;IAED,kEAoBC;IAED,+DAeC;IAED,4GAkCC;IAED,uEA2DC;IAED,0EAiBC;IAED,8EAoBC;IAED,oEAuBC;IAED,0FAyBC;IAED,gCAmBC;IAED,iEAqBC;IAED,4FA2BC;IAED,6CAUC;IAED,qDAUC;IAED,sFAeC;IAED,wFAkBC;IAED,+BAuBC;IAED,kDAOC;IAED,sBAEC;IAED,mFAcC;IAED,4EAgBC;IAED,wFAGC;IAED,sEAWC;IAED,mEAYC;IAED,mEAYC;IAED,sEAMC;IAED,oEAQC;IAED,gEAyBC;IAED,gEAyBC;IAED,gCAKC;IAED,kDAKC;IAED,gEAMC;IAED,8CAOC;IAED;;;;;;;;;;;MAiDC;IAED,oFAOC;IAED,6EA+BC;IAED,qCA2BC;IAED,+FAYC;IAED,+CAEC;IAED,wDASC;IAED,iFAMC;IAED,wDAeC;IAED,oFAMC;IAED,oEAWC;IAED;;;MAMC;IAED,8DAWC;IAED,4EAKC;IAED,+CAEC;IAED,sEAGC;IAED,2DAUC;IAED,4EAoBC;IAED,yEAYC;IAED,+CAEC;IAED,uEAMC;IAED,2EAcC;IAED,oDAEC;IAED,0EAeC;IAED,sFAQC;IAED,sFAQC;IAED,kFAeC;IAED,2DAMC;IAED,uDAqBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAWC;IAED,iEAMC;IAED,uEASC;IAED,mEAKC;IAED,yEASC;IAED,2EAKC;IAED,iFAMC;IAED,gFAGC;IAED,6CAwBC;IAGD,8EAoCC;IAED,gFAGC;IAED,iEAEC;IAED,gEAEC;IAED,gEAIC;IAED,gEAIC;IAED,+EAgCC;IAED,qCAYC;IAED,qCAYC;IAED,4EA4CC;IAED,4DAGC;IAED,qDAKC;IAED,gEAIC;IAED,yDAWC;IAED,kEAGC;IAED,2DAWC;IAED,sEAeC;IAED,4CAOC;IAED,+BAIC;IAED,qDAiBC;IAED,gCAGC;IAED,kCAEC;IA6BD,4CAEC;IAED,+DAaC;IAED,kDAiBC;IAED,2GAKC;IAED,sDAIC;IAED,qCAEC;IAED,uDAMC;IAED,sCAEC;IAED,uDASC;IAED,sCAEC;IAED,2DAqBC;IAED,0CAEC;IAED,mCAeC;IAED,2FAgBC;IAED,6CAMC;IAED,0CAMC;IAED,uCAMC;IAED,wCAMC;IAED,2CAMC;IAED,yEAgBC;IAED,wEAaC;IAED,2CAIC;IAED,oFAOC;IAED,6DAcC;IAED,yEAIC;IAED,0CAmBC;IAED,yEAcC;IAED,gDAYC;IAGD,6DAgBC;CACF"}