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