@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.
@@ -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,
@@ -127,7 +81,7 @@ const defaultControllerState = {
127
81
  modulationDepth: { type: 128 + 1, defaultValue: 0 },
128
82
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
129
83
  volume: { type: 128 + 7, defaultValue: 100 / 127 },
130
- pan: { type: 128 + 10, defaultValue: 0.5 },
84
+ pan: { type: 128 + 10, defaultValue: 64 / 127 },
131
85
  expression: { type: 128 + 11, defaultValue: 1 },
132
86
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
133
87
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
@@ -338,7 +292,7 @@ export class MidyGMLite {
338
292
  initSoundFontTable() {
339
293
  const table = new Array(128);
340
294
  for (let i = 0; i < 128; i++) {
341
- table[i] = new SparseMap(128);
295
+ table[i] = new Map();
342
296
  }
343
297
  return table;
344
298
  }
@@ -390,10 +344,10 @@ export class MidyGMLite {
390
344
  return {
391
345
  currentBufferSource: null,
392
346
  isDrum: false,
393
- ...this.constructor.channelSettings,
394
347
  state: new ControllerState(),
348
+ ...this.constructor.channelSettings,
395
349
  ...this.setChannelAudioNodes(audioContext),
396
- scheduledNotes: new SparseMap(128),
350
+ scheduledNotes: [],
397
351
  sustainNotes: [],
398
352
  };
399
353
  });
@@ -438,24 +392,20 @@ export class MidyGMLite {
438
392
  }
439
393
  return bufferSource;
440
394
  }
441
- async scheduleTimelineEvents(t, offset, queueIndex) {
395
+ async scheduleTimelineEvents(t, resumeTime, queueIndex) {
442
396
  while (queueIndex < this.timeline.length) {
443
397
  const event = this.timeline[queueIndex];
444
398
  if (event.startTime > t + this.lookAhead)
445
399
  break;
446
- const startTime = event.startTime + this.startDelay - offset;
400
+ const delay = this.startDelay - resumeTime;
401
+ const startTime = event.startTime + delay;
447
402
  switch (event.type) {
448
- case "noteOn":
449
- if (event.velocity !== 0) {
450
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
451
- break;
452
- }
453
- /* falls through */
454
- case "noteOff": {
455
- const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
456
- if (notePromise) {
457
- this.notePromises.push(notePromise);
458
- }
403
+ case "noteOn": {
404
+ const noteOffEvent = {
405
+ ...event.noteOffEvent,
406
+ startTime: event.noteOffEvent.startTime + delay,
407
+ };
408
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, noteOffEvent);
459
409
  break;
460
410
  }
461
411
  case "controller":
@@ -488,44 +438,53 @@ export class MidyGMLite {
488
438
  this.isPaused = false;
489
439
  this.startTime = this.audioContext.currentTime;
490
440
  let queueIndex = this.getQueueIndex(this.resumeTime);
491
- let offset = this.resumeTime - this.startTime;
441
+ let resumeTime = this.resumeTime - this.startTime;
492
442
  this.notePromises = [];
493
443
  const schedulePlayback = async () => {
494
444
  if (queueIndex >= this.timeline.length) {
495
445
  await Promise.all(this.notePromises);
496
446
  this.notePromises = [];
497
- this.exclusiveClassNotes.flll(undefined);
447
+ this.exclusiveClassNotes.fill(undefined);
448
+ this.drumExclusiveClassNotes.fill(undefined);
498
449
  this.audioBufferCache.clear();
450
+ for (let i = 0; i < this.channels.length; i++) {
451
+ this.resetAllStates(i);
452
+ }
499
453
  resolve();
500
454
  return;
501
455
  }
502
456
  const now = this.audioContext.currentTime;
503
- const t = now + offset;
504
- queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
457
+ const t = now + resumeTime;
458
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
505
459
  if (this.isPausing) {
506
460
  await this.stopNotes(0, true, now);
507
461
  this.notePromises = [];
508
- resolve();
509
462
  this.isPausing = false;
510
463
  this.isPaused = true;
464
+ resolve();
511
465
  return;
512
466
  }
513
467
  else if (this.isStopping) {
514
468
  await this.stopNotes(0, true, now);
515
469
  this.notePromises = [];
516
470
  this.exclusiveClassNotes.fill(undefined);
471
+ this.drumExclusiveClassNotes.fill(undefined);
517
472
  this.audioBufferCache.clear();
518
- resolve();
473
+ for (let i = 0; i < this.channels.length; i++) {
474
+ this.resetAllStates(i);
475
+ }
519
476
  this.isStopping = false;
520
477
  this.isPaused = false;
478
+ resolve();
521
479
  return;
522
480
  }
523
481
  else if (this.isSeeking) {
524
482
  this.stopNotes(0, true, now);
525
483
  this.exclusiveClassNotes.fill(undefined);
484
+ this.drumExclusiveClassNotes.fill(undefined);
526
485
  this.startTime = this.audioContext.currentTime;
527
486
  queueIndex = this.getQueueIndex(this.resumeTime);
528
- offset = this.resumeTime - this.startTime;
487
+ resumeTime = this.resumeTime - this.startTime;
529
488
  this.isSeeking = false;
530
489
  await schedulePlayback();
531
490
  }
@@ -611,17 +570,52 @@ export class MidyGMLite {
611
570
  prevTempoTicks = event.ticks;
612
571
  }
613
572
  }
573
+ const activeNotes = new Array(this.channels.length * 128);
574
+ for (let i = 0; i < activeNotes.length; i++) {
575
+ activeNotes[i] = [];
576
+ }
577
+ for (let i = 0; i < timeline.length; i++) {
578
+ const event = timeline[i];
579
+ switch (event.type) {
580
+ case "noteOn": {
581
+ const index = event.channel * 128 + event.noteNumber;
582
+ activeNotes[index].push(event);
583
+ break;
584
+ }
585
+ case "noteOff": {
586
+ const index = event.channel * 128 + event.noteNumber;
587
+ const noteOn = activeNotes[index].pop();
588
+ if (noteOn) {
589
+ noteOn.noteOffEvent = event;
590
+ }
591
+ else {
592
+ const eventString = JSON.stringify(event, null, 2);
593
+ console.warn(`noteOff without matching noteOn: ${eventString}`);
594
+ }
595
+ }
596
+ }
597
+ }
614
598
  return { instruments, timeline };
615
599
  }
600
+ stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
601
+ const channel = this.channels[channelNumber];
602
+ const promises = [];
603
+ this.processActiveNotes(channel, scheduleTime, (note) => {
604
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
605
+ this.notePromises.push(promise);
606
+ promises.push(promise);
607
+ });
608
+ return Promise.all(promises);
609
+ }
616
610
  stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
617
611
  const channel = this.channels[channelNumber];
618
612
  const promises = [];
619
613
  this.processScheduledNotes(channel, (note) => {
620
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
614
+ const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
621
615
  this.notePromises.push(promise);
622
616
  promises.push(promise);
623
617
  });
624
- channel.scheduledNotes.clear();
618
+ channel.scheduledNotes = [];
625
619
  return Promise.all(promises);
626
620
  }
627
621
  stopNotes(velocity, force, scheduleTime) {
@@ -642,9 +636,6 @@ export class MidyGMLite {
642
636
  if (!this.isPlaying)
643
637
  return;
644
638
  this.isStopping = true;
645
- for (let i = 0; i < this.channels.length; i++) {
646
- this.resetAllStates(i);
647
- }
648
639
  }
649
640
  pause() {
650
641
  if (!this.isPlaying || this.isPaused)
@@ -679,35 +670,31 @@ export class MidyGMLite {
679
670
  return this.resumeTime + now - this.startTime - this.startDelay;
680
671
  }
681
672
  processScheduledNotes(channel, callback) {
682
- channel.scheduledNotes.forEach((noteList) => {
683
- for (let i = 0; i < noteList.length; i++) {
684
- const note = noteList[i];
685
- if (!note)
686
- continue;
687
- callback(note);
688
- }
689
- });
690
- }
691
- getActiveNotes(channel, scheduleTime) {
692
- const activeNotes = new SparseMap(128);
693
- channel.scheduledNotes.forEach((noteList) => {
694
- const activeNote = this.getActiveNote(noteList, scheduleTime);
695
- if (activeNote) {
696
- activeNotes.set(activeNote.noteNumber, activeNote);
697
- }
698
- });
699
- return activeNotes;
673
+ const scheduledNotes = channel.scheduledNotes;
674
+ for (let i = 0; i < scheduledNotes.length; i++) {
675
+ const note = scheduledNotes[i];
676
+ if (!note)
677
+ continue;
678
+ if (note.ending)
679
+ continue;
680
+ callback(note);
681
+ }
700
682
  }
701
- getActiveNote(noteList, scheduleTime) {
702
- for (let i = noteList.length - 1; i >= 0; i--) {
703
- const note = noteList[i];
683
+ processActiveNotes(channel, scheduleTime, callback) {
684
+ const scheduledNotes = channel.scheduledNotes;
685
+ for (let i = 0; i < scheduledNotes.length; i++) {
686
+ const note = scheduledNotes[i];
704
687
  if (!note)
705
- return;
688
+ continue;
689
+ if (note.ending)
690
+ continue;
691
+ const noteOffEvent = note.noteOffEvent;
692
+ if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
693
+ continue;
706
694
  if (scheduleTime < note.startTime)
707
695
  continue;
708
- return (note.ending) ? null : note;
696
+ callback(note);
709
697
  }
710
- return noteList[0];
711
698
  }
712
699
  cbToRatio(cb) {
713
700
  return Math.pow(10, cb / 200);
@@ -854,6 +841,7 @@ export class MidyGMLite {
854
841
  this.setVolumeEnvelope(note, now);
855
842
  this.setFilterEnvelope(note, now);
856
843
  this.setPitchEnvelope(note, now);
844
+ this.updateDetune(channel, note, now);
857
845
  if (0 < state.modulationDepth) {
858
846
  this.startModulation(channel, note, now);
859
847
  }
@@ -870,7 +858,7 @@ export class MidyGMLite {
870
858
  if (prev) {
871
859
  const [prevNote, prevChannelNumber] = prev;
872
860
  if (prevNote && !prevNote.ending) {
873
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
861
+ this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
874
862
  startTime, true);
875
863
  }
876
864
  }
@@ -886,12 +874,12 @@ export class MidyGMLite {
886
874
  const index = drumExclusiveClass * this.channels.length + channelNumber;
887
875
  const prevNote = this.drumExclusiveClassNotes[index];
888
876
  if (prevNote && !prevNote.ending) {
889
- this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
877
+ this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
890
878
  startTime, true);
891
879
  }
892
880
  this.drumExclusiveClassNotes[index] = note;
893
881
  }
894
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
882
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
895
883
  const channel = this.channels[channelNumber];
896
884
  const bankNumber = channel.bank;
897
885
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -903,6 +891,7 @@ export class MidyGMLite {
903
891
  return;
904
892
  const isSF3 = soundFont.parsed.info.version.major === 3;
905
893
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
894
+ note.noteOffEvent = noteOffEvent;
906
895
  note.volumeEnvelopeNode.connect(channel.gainL);
907
896
  note.volumeEnvelopeNode.connect(channel.gainR);
908
897
  if (0.5 <= channel.state.sustainPedal) {
@@ -911,33 +900,32 @@ export class MidyGMLite {
911
900
  this.handleExclusiveClass(note, channelNumber, startTime);
912
901
  this.handleDrumExclusiveClass(note, channelNumber, startTime);
913
902
  const scheduledNotes = channel.scheduledNotes;
914
- let notes = scheduledNotes.get(noteNumber);
915
- if (notes) {
916
- notes.push(note);
917
- }
918
- else {
919
- notes = [note];
920
- scheduledNotes.set(noteNumber, notes);
921
- }
922
- if (this.isDrumNoteOffException(channel, noteNumber)) {
903
+ note.index = scheduledNotes.length;
904
+ scheduledNotes.push(note);
905
+ if (channel.isDrum) {
923
906
  const stopTime = startTime + note.bufferSource.buffer.duration;
924
- const index = notes.length - 1;
925
907
  const promise = new Promise((resolve) => {
926
908
  note.bufferSource.onended = () => {
927
- this.disconnectNote(note, scheduledNotes, index);
909
+ scheduledNotes[note.index] = undefined;
910
+ this.disconnectNote(note);
928
911
  resolve();
929
912
  };
930
913
  note.bufferSource.stop(stopTime);
931
914
  });
932
915
  this.notePromises.push(promise);
933
916
  }
917
+ else if (noteOffEvent) {
918
+ const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
919
+ if (notePromise) {
920
+ this.notePromises.push(notePromise);
921
+ }
922
+ }
934
923
  }
935
924
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
936
925
  scheduleTime ??= this.audioContext.currentTime;
937
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
926
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
938
927
  }
939
- disconnectNote(note, scheduledNotes, index) {
940
- scheduledNotes[index] = null;
928
+ disconnectNote(note) {
941
929
  note.bufferSource.disconnect();
942
930
  note.filterNode.disconnect();
943
931
  note.volumeEnvelopeNode.disconnect();
@@ -947,8 +935,7 @@ export class MidyGMLite {
947
935
  note.modulationLFO.stop();
948
936
  }
949
937
  }
950
- stopNote(endTime, stopTime, scheduledNotes, index) {
951
- const note = scheduledNotes[index];
938
+ stopNote(channel, note, endTime, stopTime) {
952
939
  note.volumeEnvelopeNode.gain
953
940
  .cancelScheduledValues(endTime)
954
941
  .linearRampToValueAtTime(0, stopTime);
@@ -958,46 +945,52 @@ export class MidyGMLite {
958
945
  }, stopTime);
959
946
  return new Promise((resolve) => {
960
947
  note.bufferSource.onended = () => {
961
- this.disconnectNote(note, scheduledNotes, index);
948
+ channel.scheduledNotes[note.index] = undefined;
949
+ this.disconnectNote(note);
962
950
  resolve();
963
951
  };
964
952
  note.bufferSource.stop(stopTime);
965
953
  });
966
954
  }
967
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
955
+ scheduleNoteOff(channelNumber, note, _velocity, endTime, force) {
968
956
  const channel = this.channels[channelNumber];
969
957
  if (channel.isDrum)
970
958
  return;
971
959
  if (!force && 0.5 <= channel.state.sustainPedal)
972
960
  return;
973
- if (!channel.scheduledNotes.has(noteNumber))
974
- return;
975
- const scheduledNotes = channel.scheduledNotes.get(noteNumber);
961
+ const volRelease = endTime + note.voiceParams.volRelease;
962
+ const modRelease = endTime + note.voiceParams.modRelease;
963
+ note.filterNode.frequency
964
+ .cancelScheduledValues(endTime)
965
+ .linearRampToValueAtTime(0, modRelease);
966
+ const stopTime = Math.min(volRelease, modRelease);
967
+ return this.stopNote(channel, note, endTime, stopTime);
968
+ }
969
+ findNoteOffTarget(channel, noteNumber) {
970
+ const scheduledNotes = channel.scheduledNotes;
976
971
  for (let i = 0; i < scheduledNotes.length; i++) {
977
972
  const note = scheduledNotes[i];
978
973
  if (!note)
979
974
  continue;
980
975
  if (note.ending)
981
976
  continue;
982
- const volRelease = endTime + note.voiceParams.volRelease;
983
- const modRelease = endTime + note.voiceParams.modRelease;
984
- note.filterNode.frequency
985
- .cancelScheduledValues(endTime)
986
- .linearRampToValueAtTime(0, modRelease);
987
- const stopTime = Math.min(volRelease, modRelease);
988
- return this.stopNote(endTime, stopTime, scheduledNotes, i);
977
+ if (note.noteNumber !== noteNumber)
978
+ continue;
979
+ return note;
989
980
  }
990
981
  }
991
982
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
992
983
  scheduleTime ??= this.audioContext.currentTime;
993
- return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
984
+ const channel = this.channels[channelNumber];
985
+ const note = this.findNoteOffTarget(channel, noteNumber);
986
+ return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
994
987
  }
995
988
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
996
989
  const velocity = halfVelocity * 2;
997
990
  const channel = this.channels[channelNumber];
998
991
  const promises = [];
999
992
  for (let i = 0; i < channel.sustainNotes.length; i++) {
1000
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
993
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
1001
994
  promises.push(promise);
1002
995
  }
1003
996
  channel.sustainNotes = [];
@@ -1153,20 +1146,20 @@ export class MidyGMLite {
1153
1146
  });
1154
1147
  }
1155
1148
  createControlChangeHandlers() {
1156
- return {
1157
- 1: this.setModulationDepth,
1158
- 6: this.dataEntryMSB,
1159
- 7: this.setVolume,
1160
- 10: this.setPan,
1161
- 11: this.setExpression,
1162
- 38: this.dataEntryLSB,
1163
- 64: this.setSustainPedal,
1164
- 100: this.setRPNLSB,
1165
- 101: this.setRPNMSB,
1166
- 120: this.allSoundOff,
1167
- 121: this.resetAllControllers,
1168
- 123: this.allNotesOff,
1169
- };
1149
+ const handlers = new Array(128);
1150
+ handlers[1] = this.setModulationDepth;
1151
+ handlers[6] = this.dataEntryMSB;
1152
+ handlers[7] = this.setVolume;
1153
+ handlers[10] = this.setPan;
1154
+ handlers[11] = this.setExpression;
1155
+ handlers[38] = this.dataEntryLSB;
1156
+ handlers[64] = this.setSustainPedal;
1157
+ handlers[100] = this.setRPNLSB;
1158
+ handlers[101] = this.setRPNMSB;
1159
+ handlers[120] = this.allSoundOff;
1160
+ handlers[121] = this.resetAllControllers;
1161
+ handlers[123] = this.allNotesOff;
1162
+ return handlers;
1170
1163
  }
1171
1164
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1172
1165
  const handler = this.controlChangeHandlers[controllerType];
@@ -1308,22 +1301,29 @@ export class MidyGMLite {
1308
1301
  }
1309
1302
  allSoundOff(channelNumber, _value, scheduleTime) {
1310
1303
  scheduleTime ??= this.audioContext.currentTime;
1311
- return this.stopChannelNotes(channelNumber, 0, true, scheduleTime);
1304
+ return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1312
1305
  }
1313
1306
  resetAllStates(channelNumber) {
1307
+ const scheduleTime = this.audioContext.currentTime;
1314
1308
  const channel = this.channels[channelNumber];
1315
1309
  const state = channel.state;
1316
- for (const type of Object.keys(defaultControllerState)) {
1317
- state[type] = defaultControllerState[type].defaultValue;
1310
+ const entries = Object.entries(defaultControllerState);
1311
+ for (const [key, { type, defaultValue }] of entries) {
1312
+ if (128 <= type) {
1313
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1314
+ }
1315
+ else {
1316
+ state[key] = defaultValue;
1317
+ }
1318
1318
  }
1319
- for (const type of Object.keys(this.constructor.channelSettings)) {
1320
- channel[type] = this.constructor.channelSettings[type];
1319
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1320
+ channel[key] = this.constructor.channelSettings[key];
1321
1321
  }
1322
1322
  this.mode = "GM1";
1323
1323
  }
1324
1324
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1325
- resetAllControllers(channelNumber) {
1326
- const stateTypes = [
1325
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1326
+ const keys = [
1327
1327
  "pitchWheel",
1328
1328
  "expression",
1329
1329
  "modulationDepth",
@@ -1331,10 +1331,17 @@ export class MidyGMLite {
1331
1331
  ];
1332
1332
  const channel = this.channels[channelNumber];
1333
1333
  const state = channel.state;
1334
- for (let i = 0; i < stateTypes.length; i++) {
1335
- const type = stateTypes[i];
1336
- state[type] = defaultControllerState[type].defaultValue;
1334
+ for (let i = 0; i < keys.length; i++) {
1335
+ const key = keys[i];
1336
+ const { type, defaultValue } = defaultControllerState[key];
1337
+ if (128 <= type) {
1338
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1339
+ }
1340
+ else {
1341
+ state[key] = defaultValue;
1342
+ }
1337
1343
  }
1344
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1338
1345
  const settingTypes = [
1339
1346
  "rpnMSB",
1340
1347
  "rpnLSB",
@@ -1346,7 +1353,7 @@ export class MidyGMLite {
1346
1353
  }
1347
1354
  allNotesOff(channelNumber, _value, scheduleTime) {
1348
1355
  scheduleTime ??= this.audioContext.currentTime;
1349
- return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
1356
+ return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
1350
1357
  }
1351
1358
  handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
1352
1359
  switch (data[2]) {