@marmooo/midy 0.3.0 → 0.3.2

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.
@@ -3,60 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGM1 = void 0;
4
4
  const midi_file_1 = require("midi-file");
5
5
  const soundfont_parser_1 = require("@marmooo/soundfont-parser");
6
- // 2-3 times faster than Map
7
- class SparseMap {
8
- constructor(size) {
9
- this.data = new Array(size);
10
- this.activeIndices = [];
11
- }
12
- set(key, value) {
13
- if (this.data[key] === undefined) {
14
- this.activeIndices.push(key);
15
- }
16
- this.data[key] = value;
17
- }
18
- get(key) {
19
- return this.data[key];
20
- }
21
- delete(key) {
22
- if (this.data[key] !== undefined) {
23
- this.data[key] = undefined;
24
- const index = this.activeIndices.indexOf(key);
25
- if (index !== -1) {
26
- this.activeIndices.splice(index, 1);
27
- }
28
- return true;
29
- }
30
- return false;
31
- }
32
- has(key) {
33
- return this.data[key] !== undefined;
34
- }
35
- get size() {
36
- return this.activeIndices.length;
37
- }
38
- clear() {
39
- for (let i = 0; i < this.activeIndices.length; i++) {
40
- const key = this.activeIndices[i];
41
- this.data[key] = undefined;
42
- }
43
- this.activeIndices = [];
44
- }
45
- *[Symbol.iterator]() {
46
- for (let i = 0; i < this.activeIndices.length; i++) {
47
- const key = this.activeIndices[i];
48
- yield [key, this.data[key]];
49
- }
50
- }
51
- forEach(callback) {
52
- for (let i = 0; i < this.activeIndices.length; i++) {
53
- const key = this.activeIndices[i];
54
- callback(this.data[key], key, this);
55
- }
56
- }
57
- }
58
6
  class Note {
59
7
  constructor(noteNumber, velocity, startTime, voice, voiceParams) {
8
+ Object.defineProperty(this, "index", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: -1
13
+ });
60
14
  Object.defineProperty(this, "bufferSource", {
61
15
  enumerable: true,
62
16
  configurable: true,
@@ -117,7 +71,7 @@ const defaultControllerState = {
117
71
  modulationDepth: { type: 128 + 1, defaultValue: 0 },
118
72
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
119
73
  volume: { type: 128 + 7, defaultValue: 100 / 127 },
120
- pan: { type: 128 + 10, defaultValue: 0.5 },
74
+ pan: { type: 128 + 10, defaultValue: 64 / 127 },
121
75
  expression: { type: 128 + 11, defaultValue: 1 },
122
76
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
123
77
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
@@ -322,7 +276,7 @@ class MidyGM1 {
322
276
  initSoundFontTable() {
323
277
  const table = new Array(128);
324
278
  for (let i = 0; i < 128; i++) {
325
- table[i] = new SparseMap(128);
279
+ table[i] = new Map();
326
280
  }
327
281
  return table;
328
282
  }
@@ -374,10 +328,10 @@ class MidyGM1 {
374
328
  return {
375
329
  currentBufferSource: null,
376
330
  isDrum: false,
377
- ...this.constructor.channelSettings,
378
331
  state: new ControllerState(),
332
+ ...this.constructor.channelSettings,
379
333
  ...this.setChannelAudioNodes(audioContext),
380
- scheduledNotes: new SparseMap(128),
334
+ scheduledNotes: [],
381
335
  sustainNotes: [],
382
336
  };
383
337
  });
@@ -412,7 +366,7 @@ class MidyGM1 {
412
366
  return audioBuffer;
413
367
  }
414
368
  }
415
- createBufferSource(audioBuffer, voiceParams) {
369
+ createBufferSource(voiceParams, audioBuffer) {
416
370
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
417
371
  bufferSource.buffer = audioBuffer;
418
372
  bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
@@ -422,24 +376,20 @@ class MidyGM1 {
422
376
  }
423
377
  return bufferSource;
424
378
  }
425
- async scheduleTimelineEvents(t, offset, queueIndex) {
379
+ async scheduleTimelineEvents(t, resumeTime, queueIndex) {
426
380
  while (queueIndex < this.timeline.length) {
427
381
  const event = this.timeline[queueIndex];
428
382
  if (event.startTime > t + this.lookAhead)
429
383
  break;
430
- const startTime = event.startTime + this.startDelay - offset;
384
+ const delay = this.startDelay - resumeTime;
385
+ const startTime = event.startTime + delay;
431
386
  switch (event.type) {
432
- case "noteOn":
433
- if (event.velocity !== 0) {
434
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
435
- break;
436
- }
437
- /* falls through */
438
- case "noteOff": {
439
- const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
440
- if (notePromise) {
441
- this.notePromises.push(notePromise);
442
- }
387
+ case "noteOn": {
388
+ const noteOffEvent = {
389
+ ...event.noteOffEvent,
390
+ startTime: event.noteOffEvent.startTime + delay,
391
+ };
392
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, noteOffEvent);
443
393
  break;
444
394
  }
445
395
  case "controller":
@@ -472,26 +422,29 @@ class MidyGM1 {
472
422
  this.isPaused = false;
473
423
  this.startTime = this.audioContext.currentTime;
474
424
  let queueIndex = this.getQueueIndex(this.resumeTime);
475
- let offset = this.resumeTime - this.startTime;
425
+ let resumeTime = this.resumeTime - this.startTime;
476
426
  this.notePromises = [];
477
427
  const schedulePlayback = async () => {
478
428
  if (queueIndex >= this.timeline.length) {
479
429
  await Promise.all(this.notePromises);
480
430
  this.notePromises = [];
481
- this.exclusiveClassNotes.flll(undefined);
431
+ this.exclusiveClassNotes.fill(undefined);
482
432
  this.audioBufferCache.clear();
433
+ for (let i = 0; i < this.channels.length; i++) {
434
+ this.resetAllStates(i);
435
+ }
483
436
  resolve();
484
437
  return;
485
438
  }
486
439
  const now = this.audioContext.currentTime;
487
- const t = now + offset;
488
- queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
440
+ const t = now + resumeTime;
441
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
489
442
  if (this.isPausing) {
490
443
  await this.stopNotes(0, true, now);
491
444
  this.notePromises = [];
492
- resolve();
493
445
  this.isPausing = false;
494
446
  this.isPaused = true;
447
+ resolve();
495
448
  return;
496
449
  }
497
450
  else if (this.isStopping) {
@@ -499,9 +452,12 @@ class MidyGM1 {
499
452
  this.notePromises = [];
500
453
  this.exclusiveClassNotes.fill(undefined);
501
454
  this.audioBufferCache.clear();
502
- resolve();
455
+ for (let i = 0; i < this.channels.length; i++) {
456
+ this.resetAllStates(i);
457
+ }
503
458
  this.isStopping = false;
504
459
  this.isPaused = false;
460
+ resolve();
505
461
  return;
506
462
  }
507
463
  else if (this.isSeeking) {
@@ -509,7 +465,7 @@ class MidyGM1 {
509
465
  this.exclusiveClassNotes.fill(undefined);
510
466
  this.startTime = this.audioContext.currentTime;
511
467
  queueIndex = this.getQueueIndex(this.resumeTime);
512
- offset = this.resumeTime - this.startTime;
468
+ resumeTime = this.resumeTime - this.startTime;
513
469
  this.isSeeking = false;
514
470
  await schedulePlayback();
515
471
  }
@@ -595,17 +551,52 @@ class MidyGM1 {
595
551
  prevTempoTicks = event.ticks;
596
552
  }
597
553
  }
554
+ const activeNotes = new Array(this.channels.length * 128);
555
+ for (let i = 0; i < activeNotes.length; i++) {
556
+ activeNotes[i] = [];
557
+ }
558
+ for (let i = 0; i < timeline.length; i++) {
559
+ const event = timeline[i];
560
+ switch (event.type) {
561
+ case "noteOn": {
562
+ const index = event.channel * 128 + event.noteNumber;
563
+ activeNotes[index].push(event);
564
+ break;
565
+ }
566
+ case "noteOff": {
567
+ const index = event.channel * 128 + event.noteNumber;
568
+ const noteOn = activeNotes[index].pop();
569
+ if (noteOn) {
570
+ noteOn.noteOffEvent = event;
571
+ }
572
+ else {
573
+ const eventString = JSON.stringify(event, null, 2);
574
+ console.warn(`noteOff without matching noteOn: ${eventString}`);
575
+ }
576
+ }
577
+ }
578
+ }
598
579
  return { instruments, timeline };
599
580
  }
581
+ stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
582
+ const channel = this.channels[channelNumber];
583
+ const promises = [];
584
+ this.processActiveNotes(channel, scheduleTime, (note) => {
585
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
586
+ this.notePromises.push(promise);
587
+ promises.push(promise);
588
+ });
589
+ return Promise.all(promises);
590
+ }
600
591
  stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
601
592
  const channel = this.channels[channelNumber];
602
593
  const promises = [];
603
594
  this.processScheduledNotes(channel, (note) => {
604
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
595
+ const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
605
596
  this.notePromises.push(promise);
606
597
  promises.push(promise);
607
598
  });
608
- channel.scheduledNotes.clear();
599
+ channel.scheduledNotes = [];
609
600
  return Promise.all(promises);
610
601
  }
611
602
  stopNotes(velocity, force, scheduleTime) {
@@ -626,9 +617,6 @@ class MidyGM1 {
626
617
  if (!this.isPlaying)
627
618
  return;
628
619
  this.isStopping = true;
629
- for (let i = 0; i < this.channels.length; i++) {
630
- this.resetAllStates(i);
631
- }
632
620
  }
633
621
  pause() {
634
622
  if (!this.isPlaying || this.isPaused)
@@ -663,35 +651,31 @@ class MidyGM1 {
663
651
  return this.resumeTime + now - this.startTime - this.startDelay;
664
652
  }
665
653
  processScheduledNotes(channel, callback) {
666
- channel.scheduledNotes.forEach((noteList) => {
667
- for (let i = 0; i < noteList.length; i++) {
668
- const note = noteList[i];
669
- if (!note)
670
- continue;
671
- callback(note);
672
- }
673
- });
674
- }
675
- getActiveNotes(channel, scheduleTime) {
676
- const activeNotes = new SparseMap(128);
677
- channel.scheduledNotes.forEach((noteList) => {
678
- const activeNote = this.getActiveNote(noteList, scheduleTime);
679
- if (activeNote) {
680
- activeNotes.set(activeNote.noteNumber, activeNote);
681
- }
682
- });
683
- return activeNotes;
654
+ const scheduledNotes = channel.scheduledNotes;
655
+ for (let i = 0; i < scheduledNotes.length; i++) {
656
+ const note = scheduledNotes[i];
657
+ if (!note)
658
+ continue;
659
+ if (note.ending)
660
+ continue;
661
+ callback(note);
662
+ }
684
663
  }
685
- getActiveNote(noteList, scheduleTime) {
686
- for (let i = noteList.length - 1; i >= 0; i--) {
687
- const note = noteList[i];
664
+ processActiveNotes(channel, scheduleTime, callback) {
665
+ const scheduledNotes = channel.scheduledNotes;
666
+ for (let i = 0; i < scheduledNotes.length; i++) {
667
+ const note = scheduledNotes[i];
688
668
  if (!note)
689
- return;
669
+ continue;
670
+ if (note.ending)
671
+ continue;
672
+ const noteOffEvent = note.noteOffEvent;
673
+ if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
674
+ continue;
690
675
  if (scheduleTime < note.startTime)
691
676
  continue;
692
- return (note.ending) ? null : note;
677
+ callback(note);
693
678
  }
694
- return noteList[0];
695
679
  }
696
680
  cbToRatio(cb) {
697
681
  return Math.pow(10, cb / 200);
@@ -831,7 +815,7 @@ class MidyGM1 {
831
815
  const voiceParams = voice.getAllParams(controllerState);
832
816
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
833
817
  const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
834
- note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
818
+ note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
835
819
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
836
820
  note.filterNode = new BiquadFilterNode(this.audioContext, {
837
821
  type: "lowpass",
@@ -840,6 +824,7 @@ class MidyGM1 {
840
824
  this.setVolumeEnvelope(note, now);
841
825
  this.setFilterEnvelope(note, now);
842
826
  this.setPitchEnvelope(note, now);
827
+ this.updateDetune(channel, note, now);
843
828
  if (0 < state.modulationDepth) {
844
829
  this.startModulation(channel, note, now);
845
830
  }
@@ -856,13 +841,13 @@ class MidyGM1 {
856
841
  if (prev) {
857
842
  const [prevNote, prevChannelNumber] = prev;
858
843
  if (prevNote && !prevNote.ending) {
859
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
844
+ this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
860
845
  startTime, true);
861
846
  }
862
847
  }
863
848
  this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
864
849
  }
865
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
850
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
866
851
  const channel = this.channels[channelNumber];
867
852
  const bankNumber = channel.bank;
868
853
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -874,27 +859,28 @@ class MidyGM1 {
874
859
  return;
875
860
  const isSF3 = soundFont.parsed.info.version.major === 3;
876
861
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
862
+ note.noteOffEvent = noteOffEvent;
877
863
  note.volumeEnvelopeNode.connect(channel.gainL);
878
864
  note.volumeEnvelopeNode.connect(channel.gainR);
879
865
  if (0.5 <= channel.state.sustainPedal) {
880
866
  channel.sustainNotes.push(note);
881
867
  }
882
868
  this.handleExclusiveClass(note, channelNumber, startTime);
883
- let notes = scheduledNotes.get(noteNumber);
884
- if (notes) {
885
- notes.push(note);
886
- }
887
- else {
888
- notes = [note];
889
- scheduledNotes.set(noteNumber, notes);
869
+ const scheduledNotes = channel.scheduledNotes;
870
+ note.index = scheduledNotes.length;
871
+ scheduledNotes.push(note);
872
+ if (noteOffEvent) {
873
+ const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
874
+ if (notePromise) {
875
+ this.notePromises.push(notePromise);
876
+ }
890
877
  }
891
878
  }
892
879
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
893
880
  scheduleTime ??= this.audioContext.currentTime;
894
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
881
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
895
882
  }
896
- disconnectNote(note, scheduledNotes, index) {
897
- scheduledNotes[index] = null;
883
+ disconnectNote(note) {
898
884
  note.bufferSource.disconnect();
899
885
  note.filterNode.disconnect();
900
886
  note.volumeEnvelopeNode.disconnect();
@@ -904,8 +890,7 @@ class MidyGM1 {
904
890
  note.modulationLFO.stop();
905
891
  }
906
892
  }
907
- stopNote(endTime, stopTime, scheduledNotes, index) {
908
- const note = scheduledNotes[index];
893
+ stopNote(channel, note, endTime, stopTime) {
909
894
  note.volumeEnvelopeNode.gain
910
895
  .cancelScheduledValues(endTime)
911
896
  .linearRampToValueAtTime(0, stopTime);
@@ -915,44 +900,50 @@ class MidyGM1 {
915
900
  }, stopTime);
916
901
  return new Promise((resolve) => {
917
902
  note.bufferSource.onended = () => {
918
- this.disconnectNote(note, scheduledNotes, index);
903
+ channel.scheduledNotes[note.index] = undefined;
904
+ this.disconnectNote(note);
919
905
  resolve();
920
906
  };
921
907
  note.bufferSource.stop(stopTime);
922
908
  });
923
909
  }
924
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
910
+ scheduleNoteOff(channelNumber, note, _velocity, endTime, force) {
925
911
  const channel = this.channels[channelNumber];
926
912
  if (!force && 0.5 <= channel.state.sustainPedal)
927
913
  return;
928
- if (!channel.scheduledNotes.has(noteNumber))
929
- return;
930
- const scheduledNotes = channel.scheduledNotes.get(noteNumber);
914
+ const volRelease = endTime + note.voiceParams.volRelease;
915
+ const modRelease = endTime + note.voiceParams.modRelease;
916
+ note.filterNode.frequency
917
+ .cancelScheduledValues(endTime)
918
+ .linearRampToValueAtTime(0, modRelease);
919
+ const stopTime = Math.min(volRelease, modRelease);
920
+ return this.stopNote(channel, note, endTime, stopTime);
921
+ }
922
+ findNoteOffTarget(channel, noteNumber) {
923
+ const scheduledNotes = channel.scheduledNotes;
931
924
  for (let i = 0; i < scheduledNotes.length; i++) {
932
925
  const note = scheduledNotes[i];
933
926
  if (!note)
934
927
  continue;
935
928
  if (note.ending)
936
929
  continue;
937
- const volRelease = endTime + note.voiceParams.volRelease;
938
- const modRelease = endTime + note.voiceParams.modRelease;
939
- note.filterNode.frequency
940
- .cancelScheduledValues(endTime)
941
- .linearRampToValueAtTime(0, modRelease);
942
- const stopTime = Math.min(volRelease, modRelease);
943
- return this.stopNote(endTime, stopTime, scheduledNotes, i);
930
+ if (note.noteNumber !== noteNumber)
931
+ continue;
932
+ return note;
944
933
  }
945
934
  }
946
935
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
947
936
  scheduleTime ??= this.audioContext.currentTime;
948
- return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
937
+ const channel = this.channels[channelNumber];
938
+ const note = this.findNoteOffTarget(channel, noteNumber);
939
+ return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
949
940
  }
950
941
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
951
942
  const velocity = halfVelocity * 2;
952
943
  const channel = this.channels[channelNumber];
953
944
  const promises = [];
954
945
  for (let i = 0; i < channel.sustainNotes.length; i++) {
955
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
946
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
956
947
  promises.push(promise);
957
948
  }
958
949
  channel.sustainNotes = [];
@@ -1108,20 +1099,19 @@ class MidyGM1 {
1108
1099
  });
1109
1100
  }
1110
1101
  createControlChangeHandlers() {
1111
- return {
1112
- 1: this.setModulationDepth,
1113
- 6: this.dataEntryMSB,
1114
- 7: this.setVolume,
1115
- 10: this.setPan,
1116
- 11: this.setExpression,
1117
- 38: this.dataEntryLSB,
1118
- 64: this.setSustainPedal,
1119
- 100: this.setRPNLSB,
1120
- 101: this.setRPNMSB,
1121
- 120: this.allSoundOff,
1122
- 121: this.resetAllControllers,
1123
- 123: this.allNotesOff,
1124
- };
1102
+ const handlers = new Array(128);
1103
+ handlers[1] = this.setModulationDepth;
1104
+ handlers[6] = this.dataEntryMSB;
1105
+ handlers[7] = this.setVolume;
1106
+ handlers[10] = this.setPan;
1107
+ handlers[11] = this.setExpression;
1108
+ handlers[38] = this.dataEntryLSB;
1109
+ handlers[64] = this.setSustainPedal;
1110
+ handlers[100] = this.setRPNLSB;
1111
+ handlers[101] = this.setRPNMSB;
1112
+ handlers[120] = this.allSoundOff;
1113
+ handlers[121] = this.resetAllControllers;
1114
+ return handlers;
1125
1115
  }
1126
1116
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1127
1117
  const handler = this.controlChangeHandlers[controllerType];
@@ -1307,22 +1297,29 @@ class MidyGM1 {
1307
1297
  }
1308
1298
  allSoundOff(channelNumber, _value, scheduleTime) {
1309
1299
  scheduleTime ??= this.audioContext.currentTime;
1310
- return this.stopChannelNotes(channelNumber, 0, true, scheduleTime);
1300
+ return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1311
1301
  }
1312
1302
  resetAllStates(channelNumber) {
1303
+ const scheduleTime = this.audioContext.currentTime;
1313
1304
  const channel = this.channels[channelNumber];
1314
1305
  const state = channel.state;
1315
- for (const type of Object.keys(defaultControllerState)) {
1316
- state[type] = defaultControllerState[type].defaultValue;
1306
+ const entries = Object.entries(defaultControllerState);
1307
+ for (const [key, { type, defaultValue }] of entries) {
1308
+ if (128 <= type) {
1309
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1310
+ }
1311
+ else {
1312
+ state[key] = defaultValue;
1313
+ }
1317
1314
  }
1318
- for (const type of Object.keys(this.constructor.channelSettings)) {
1319
- channel[type] = this.constructor.channelSettings[type];
1315
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1316
+ channel[key] = this.constructor.channelSettings[key];
1320
1317
  }
1321
1318
  this.mode = "GM1";
1322
1319
  }
1323
1320
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1324
- resetAllControllers(channelNumber) {
1325
- const stateTypes = [
1321
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1322
+ const keys = [
1326
1323
  "pitchWheel",
1327
1324
  "expression",
1328
1325
  "modulationDepth",
@@ -1330,10 +1327,17 @@ class MidyGM1 {
1330
1327
  ];
1331
1328
  const channel = this.channels[channelNumber];
1332
1329
  const state = channel.state;
1333
- for (let i = 0; i < stateTypes.length; i++) {
1334
- const type = stateTypes[i];
1335
- state[type] = defaultControllerState[type].defaultValue;
1330
+ for (let i = 0; i < keys.length; i++) {
1331
+ const key = keys[i];
1332
+ const { type, defaultValue } = defaultControllerState[key];
1333
+ if (128 <= type) {
1334
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1335
+ }
1336
+ else {
1337
+ state[key] = defaultValue;
1338
+ }
1336
1339
  }
1340
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1337
1341
  const settingTypes = [
1338
1342
  "rpnMSB",
1339
1343
  "rpnLSB",
@@ -1345,7 +1349,7 @@ class MidyGM1 {
1345
1349
  }
1346
1350
  allNotesOff(channelNumber, _value, scheduleTime) {
1347
1351
  scheduleTime ??= this.audioContext.currentTime;
1348
- return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
1352
+ return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
1349
1353
  }
1350
1354
  handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
1351
1355
  switch (data[2]) {