@marmooo/midy 0.3.1 → 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.MidyGMLite = 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,
@@ -130,7 +84,7 @@ const defaultControllerState = {
130
84
  modulationDepth: { type: 128 + 1, defaultValue: 0 },
131
85
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
132
86
  volume: { type: 128 + 7, defaultValue: 100 / 127 },
133
- pan: { type: 128 + 10, defaultValue: 0.5 },
87
+ pan: { type: 128 + 10, defaultValue: 64 / 127 },
134
88
  expression: { type: 128 + 11, defaultValue: 1 },
135
89
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
136
90
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
@@ -341,7 +295,7 @@ class MidyGMLite {
341
295
  initSoundFontTable() {
342
296
  const table = new Array(128);
343
297
  for (let i = 0; i < 128; i++) {
344
- table[i] = new SparseMap(128);
298
+ table[i] = new Map();
345
299
  }
346
300
  return table;
347
301
  }
@@ -393,10 +347,10 @@ class MidyGMLite {
393
347
  return {
394
348
  currentBufferSource: null,
395
349
  isDrum: false,
396
- ...this.constructor.channelSettings,
397
350
  state: new ControllerState(),
351
+ ...this.constructor.channelSettings,
398
352
  ...this.setChannelAudioNodes(audioContext),
399
- scheduledNotes: new SparseMap(128),
353
+ scheduledNotes: [],
400
354
  sustainNotes: [],
401
355
  };
402
356
  });
@@ -441,24 +395,20 @@ class MidyGMLite {
441
395
  }
442
396
  return bufferSource;
443
397
  }
444
- async scheduleTimelineEvents(t, offset, queueIndex) {
398
+ async scheduleTimelineEvents(t, resumeTime, queueIndex) {
445
399
  while (queueIndex < this.timeline.length) {
446
400
  const event = this.timeline[queueIndex];
447
401
  if (event.startTime > t + this.lookAhead)
448
402
  break;
449
- const startTime = event.startTime + this.startDelay - offset;
403
+ const delay = this.startDelay - resumeTime;
404
+ const startTime = event.startTime + delay;
450
405
  switch (event.type) {
451
- case "noteOn":
452
- if (0 < event.velocity) {
453
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
454
- break;
455
- }
456
- /* falls through */
457
- case "noteOff": {
458
- const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
459
- if (notePromise) {
460
- this.notePromises.push(notePromise);
461
- }
406
+ case "noteOn": {
407
+ const noteOffEvent = {
408
+ ...event.noteOffEvent,
409
+ startTime: event.noteOffEvent.startTime + delay,
410
+ };
411
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, noteOffEvent);
462
412
  break;
463
413
  }
464
414
  case "controller":
@@ -491,44 +441,53 @@ class MidyGMLite {
491
441
  this.isPaused = false;
492
442
  this.startTime = this.audioContext.currentTime;
493
443
  let queueIndex = this.getQueueIndex(this.resumeTime);
494
- let offset = this.resumeTime - this.startTime;
444
+ let resumeTime = this.resumeTime - this.startTime;
495
445
  this.notePromises = [];
496
446
  const schedulePlayback = async () => {
497
447
  if (queueIndex >= this.timeline.length) {
498
448
  await Promise.all(this.notePromises);
499
449
  this.notePromises = [];
500
450
  this.exclusiveClassNotes.fill(undefined);
451
+ this.drumExclusiveClassNotes.fill(undefined);
501
452
  this.audioBufferCache.clear();
453
+ for (let i = 0; i < this.channels.length; i++) {
454
+ this.resetAllStates(i);
455
+ }
502
456
  resolve();
503
457
  return;
504
458
  }
505
459
  const now = this.audioContext.currentTime;
506
- const t = now + offset;
507
- queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
460
+ const t = now + resumeTime;
461
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
508
462
  if (this.isPausing) {
509
463
  await this.stopNotes(0, true, now);
510
464
  this.notePromises = [];
511
- resolve();
512
465
  this.isPausing = false;
513
466
  this.isPaused = true;
467
+ resolve();
514
468
  return;
515
469
  }
516
470
  else if (this.isStopping) {
517
471
  await this.stopNotes(0, true, now);
518
472
  this.notePromises = [];
519
473
  this.exclusiveClassNotes.fill(undefined);
474
+ this.drumExclusiveClassNotes.fill(undefined);
520
475
  this.audioBufferCache.clear();
521
- resolve();
476
+ for (let i = 0; i < this.channels.length; i++) {
477
+ this.resetAllStates(i);
478
+ }
522
479
  this.isStopping = false;
523
480
  this.isPaused = false;
481
+ resolve();
524
482
  return;
525
483
  }
526
484
  else if (this.isSeeking) {
527
485
  this.stopNotes(0, true, now);
528
486
  this.exclusiveClassNotes.fill(undefined);
487
+ this.drumExclusiveClassNotes.fill(undefined);
529
488
  this.startTime = this.audioContext.currentTime;
530
489
  queueIndex = this.getQueueIndex(this.resumeTime);
531
- offset = this.resumeTime - this.startTime;
490
+ resumeTime = this.resumeTime - this.startTime;
532
491
  this.isSeeking = false;
533
492
  await schedulePlayback();
534
493
  }
@@ -614,13 +573,37 @@ class MidyGMLite {
614
573
  prevTempoTicks = event.ticks;
615
574
  }
616
575
  }
576
+ const activeNotes = new Array(this.channels.length * 128);
577
+ for (let i = 0; i < activeNotes.length; i++) {
578
+ activeNotes[i] = [];
579
+ }
580
+ for (let i = 0; i < timeline.length; i++) {
581
+ const event = timeline[i];
582
+ switch (event.type) {
583
+ case "noteOn": {
584
+ const index = event.channel * 128 + event.noteNumber;
585
+ activeNotes[index].push(event);
586
+ break;
587
+ }
588
+ case "noteOff": {
589
+ const index = event.channel * 128 + event.noteNumber;
590
+ const noteOn = activeNotes[index].pop();
591
+ if (noteOn) {
592
+ noteOn.noteOffEvent = event;
593
+ }
594
+ else {
595
+ const eventString = JSON.stringify(event, null, 2);
596
+ console.warn(`noteOff without matching noteOn: ${eventString}`);
597
+ }
598
+ }
599
+ }
600
+ }
617
601
  return { instruments, timeline };
618
602
  }
619
603
  stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
620
604
  const channel = this.channels[channelNumber];
621
605
  const promises = [];
622
- const activeNotes = this.getActiveNotes(channel, scheduleTime);
623
- activeNotes.forEach((note) => {
606
+ this.processActiveNotes(channel, scheduleTime, (note) => {
624
607
  const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
625
608
  this.notePromises.push(promise);
626
609
  promises.push(promise);
@@ -631,11 +614,11 @@ class MidyGMLite {
631
614
  const channel = this.channels[channelNumber];
632
615
  const promises = [];
633
616
  this.processScheduledNotes(channel, (note) => {
634
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
617
+ const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
635
618
  this.notePromises.push(promise);
636
619
  promises.push(promise);
637
620
  });
638
- channel.scheduledNotes.clear();
621
+ channel.scheduledNotes = [];
639
622
  return Promise.all(promises);
640
623
  }
641
624
  stopNotes(velocity, force, scheduleTime) {
@@ -656,9 +639,6 @@ class MidyGMLite {
656
639
  if (!this.isPlaying)
657
640
  return;
658
641
  this.isStopping = true;
659
- for (let i = 0; i < this.channels.length; i++) {
660
- this.resetAllStates(i);
661
- }
662
642
  }
663
643
  pause() {
664
644
  if (!this.isPlaying || this.isPaused)
@@ -693,37 +673,31 @@ class MidyGMLite {
693
673
  return this.resumeTime + now - this.startTime - this.startDelay;
694
674
  }
695
675
  processScheduledNotes(channel, callback) {
696
- channel.scheduledNotes.forEach((noteList) => {
697
- for (let i = 0; i < noteList.length; i++) {
698
- const note = noteList[i];
699
- if (!note)
700
- continue;
701
- if (note.ending)
702
- continue;
703
- callback(note);
704
- }
705
- });
706
- }
707
- getActiveNotes(channel, scheduleTime) {
708
- const activeNotes = new SparseMap(128);
709
- channel.scheduledNotes.forEach((noteList) => {
710
- const activeNote = this.getActiveNote(noteList, scheduleTime);
711
- if (activeNote) {
712
- activeNotes.set(activeNote.noteNumber, activeNote);
713
- }
714
- });
715
- return activeNotes;
676
+ const scheduledNotes = channel.scheduledNotes;
677
+ for (let i = 0; i < scheduledNotes.length; i++) {
678
+ const note = scheduledNotes[i];
679
+ if (!note)
680
+ continue;
681
+ if (note.ending)
682
+ continue;
683
+ callback(note);
684
+ }
716
685
  }
717
- getActiveNote(noteList, scheduleTime) {
718
- for (let i = noteList.length - 1; i >= 0; i--) {
719
- const note = noteList[i];
686
+ processActiveNotes(channel, scheduleTime, callback) {
687
+ const scheduledNotes = channel.scheduledNotes;
688
+ for (let i = 0; i < scheduledNotes.length; i++) {
689
+ const note = scheduledNotes[i];
720
690
  if (!note)
721
- return;
691
+ continue;
692
+ if (note.ending)
693
+ continue;
694
+ const noteOffEvent = note.noteOffEvent;
695
+ if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
696
+ continue;
722
697
  if (scheduleTime < note.startTime)
723
698
  continue;
724
- return (note.ending) ? null : note;
699
+ callback(note);
725
700
  }
726
- return noteList[0];
727
701
  }
728
702
  cbToRatio(cb) {
729
703
  return Math.pow(10, cb / 200);
@@ -870,6 +844,7 @@ class MidyGMLite {
870
844
  this.setVolumeEnvelope(note, now);
871
845
  this.setFilterEnvelope(note, now);
872
846
  this.setPitchEnvelope(note, now);
847
+ this.updateDetune(channel, note, now);
873
848
  if (0 < state.modulationDepth) {
874
849
  this.startModulation(channel, note, now);
875
850
  }
@@ -886,7 +861,7 @@ class MidyGMLite {
886
861
  if (prev) {
887
862
  const [prevNote, prevChannelNumber] = prev;
888
863
  if (prevNote && !prevNote.ending) {
889
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
864
+ this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
890
865
  startTime, true);
891
866
  }
892
867
  }
@@ -902,12 +877,12 @@ class MidyGMLite {
902
877
  const index = drumExclusiveClass * this.channels.length + channelNumber;
903
878
  const prevNote = this.drumExclusiveClassNotes[index];
904
879
  if (prevNote && !prevNote.ending) {
905
- this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
880
+ this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
906
881
  startTime, true);
907
882
  }
908
883
  this.drumExclusiveClassNotes[index] = note;
909
884
  }
910
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
885
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
911
886
  const channel = this.channels[channelNumber];
912
887
  const bankNumber = channel.bank;
913
888
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -919,6 +894,7 @@ class MidyGMLite {
919
894
  return;
920
895
  const isSF3 = soundFont.parsed.info.version.major === 3;
921
896
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
897
+ note.noteOffEvent = noteOffEvent;
922
898
  note.volumeEnvelopeNode.connect(channel.gainL);
923
899
  note.volumeEnvelopeNode.connect(channel.gainR);
924
900
  if (0.5 <= channel.state.sustainPedal) {
@@ -927,20 +903,13 @@ class MidyGMLite {
927
903
  this.handleExclusiveClass(note, channelNumber, startTime);
928
904
  this.handleDrumExclusiveClass(note, channelNumber, startTime);
929
905
  const scheduledNotes = channel.scheduledNotes;
930
- let noteList = scheduledNotes.get(noteNumber);
931
- if (noteList) {
932
- noteList.push(note);
933
- }
934
- else {
935
- noteList = [note];
936
- scheduledNotes.set(noteNumber, noteList);
937
- }
906
+ note.index = scheduledNotes.length;
907
+ scheduledNotes.push(note);
938
908
  if (channel.isDrum) {
939
909
  const stopTime = startTime + note.bufferSource.buffer.duration;
940
- const index = noteList.length - 1;
941
910
  const promise = new Promise((resolve) => {
942
911
  note.bufferSource.onended = () => {
943
- noteList[index] = undefined;
912
+ scheduledNotes[note.index] = undefined;
944
913
  this.disconnectNote(note);
945
914
  resolve();
946
915
  };
@@ -948,10 +917,16 @@ class MidyGMLite {
948
917
  });
949
918
  this.notePromises.push(promise);
950
919
  }
920
+ else if (noteOffEvent) {
921
+ const notePromise = this.scheduleNoteOff(channelNumber, note, noteOffEvent.velocity, noteOffEvent.startTime, false);
922
+ if (notePromise) {
923
+ this.notePromises.push(notePromise);
924
+ }
925
+ }
951
926
  }
952
927
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
953
928
  scheduleTime ??= this.audioContext.currentTime;
954
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
929
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
955
930
  }
956
931
  disconnectNote(note) {
957
932
  note.bufferSource.disconnect();
@@ -963,8 +938,7 @@ class MidyGMLite {
963
938
  note.modulationLFO.stop();
964
939
  }
965
940
  }
966
- stopNote(endTime, stopTime, noteList, index) {
967
- const note = noteList[index];
941
+ stopNote(channel, note, endTime, stopTime) {
968
942
  note.volumeEnvelopeNode.gain
969
943
  .cancelScheduledValues(endTime)
970
944
  .linearRampToValueAtTime(0, stopTime);
@@ -974,56 +948,52 @@ class MidyGMLite {
974
948
  }, stopTime);
975
949
  return new Promise((resolve) => {
976
950
  note.bufferSource.onended = () => {
977
- noteList[index] = undefined;
951
+ channel.scheduledNotes[note.index] = undefined;
978
952
  this.disconnectNote(note);
979
953
  resolve();
980
954
  };
981
955
  note.bufferSource.stop(stopTime);
982
956
  });
983
957
  }
984
- findNoteOffTarget(noteList) {
985
- for (let i = 0; i < noteList.length; i++) {
986
- const note = noteList[i];
987
- if (!note)
988
- continue;
989
- if (note.ending)
990
- continue;
991
- return [note, i];
992
- }
993
- }
994
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
958
+ scheduleNoteOff(channelNumber, note, _velocity, endTime, force) {
995
959
  const channel = this.channels[channelNumber];
996
960
  if (channel.isDrum)
997
961
  return;
998
962
  if (!force && 0.5 <= channel.state.sustainPedal)
999
963
  return;
1000
- if (!channel.scheduledNotes.has(noteNumber))
1001
- return;
1002
- const noteList = channel.scheduledNotes.get(noteNumber);
1003
- if (!noteList)
1004
- return; // be careful with drum channel
1005
- const noteOffTarget = this.findNoteOffTarget(noteList, endTime);
1006
- if (!noteOffTarget)
1007
- return;
1008
- const [note, i] = noteOffTarget;
1009
964
  const volRelease = endTime + note.voiceParams.volRelease;
1010
965
  const modRelease = endTime + note.voiceParams.modRelease;
1011
966
  note.filterNode.frequency
1012
967
  .cancelScheduledValues(endTime)
1013
968
  .linearRampToValueAtTime(0, modRelease);
1014
969
  const stopTime = Math.min(volRelease, modRelease);
1015
- return this.stopNote(endTime, stopTime, noteList, i);
970
+ return this.stopNote(channel, note, endTime, stopTime);
971
+ }
972
+ findNoteOffTarget(channel, noteNumber) {
973
+ const scheduledNotes = channel.scheduledNotes;
974
+ for (let i = 0; i < scheduledNotes.length; i++) {
975
+ const note = scheduledNotes[i];
976
+ if (!note)
977
+ continue;
978
+ if (note.ending)
979
+ continue;
980
+ if (note.noteNumber !== noteNumber)
981
+ continue;
982
+ return note;
983
+ }
1016
984
  }
1017
985
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
1018
986
  scheduleTime ??= this.audioContext.currentTime;
1019
- return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
987
+ const channel = this.channels[channelNumber];
988
+ const note = this.findNoteOffTarget(channel, noteNumber);
989
+ return this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, false);
1020
990
  }
1021
991
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
1022
992
  const velocity = halfVelocity * 2;
1023
993
  const channel = this.channels[channelNumber];
1024
994
  const promises = [];
1025
995
  for (let i = 0; i < channel.sustainNotes.length; i++) {
1026
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
996
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
1027
997
  promises.push(promise);
1028
998
  }
1029
999
  channel.sustainNotes = [];
@@ -1179,20 +1149,20 @@ class MidyGMLite {
1179
1149
  });
1180
1150
  }
1181
1151
  createControlChangeHandlers() {
1182
- return {
1183
- 1: this.setModulationDepth,
1184
- 6: this.dataEntryMSB,
1185
- 7: this.setVolume,
1186
- 10: this.setPan,
1187
- 11: this.setExpression,
1188
- 38: this.dataEntryLSB,
1189
- 64: this.setSustainPedal,
1190
- 100: this.setRPNLSB,
1191
- 101: this.setRPNMSB,
1192
- 120: this.allSoundOff,
1193
- 121: this.resetAllControllers,
1194
- 123: this.allNotesOff,
1195
- };
1152
+ const handlers = new Array(128);
1153
+ handlers[1] = this.setModulationDepth;
1154
+ handlers[6] = this.dataEntryMSB;
1155
+ handlers[7] = this.setVolume;
1156
+ handlers[10] = this.setPan;
1157
+ handlers[11] = this.setExpression;
1158
+ handlers[38] = this.dataEntryLSB;
1159
+ handlers[64] = this.setSustainPedal;
1160
+ handlers[100] = this.setRPNLSB;
1161
+ handlers[101] = this.setRPNMSB;
1162
+ handlers[120] = this.allSoundOff;
1163
+ handlers[121] = this.resetAllControllers;
1164
+ handlers[123] = this.allNotesOff;
1165
+ return handlers;
1196
1166
  }
1197
1167
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1198
1168
  const handler = this.controlChangeHandlers[controllerType];
@@ -1337,19 +1307,26 @@ class MidyGMLite {
1337
1307
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1338
1308
  }
1339
1309
  resetAllStates(channelNumber) {
1310
+ const scheduleTime = this.audioContext.currentTime;
1340
1311
  const channel = this.channels[channelNumber];
1341
1312
  const state = channel.state;
1342
- for (const type of Object.keys(defaultControllerState)) {
1343
- state[type] = defaultControllerState[type].defaultValue;
1313
+ const entries = Object.entries(defaultControllerState);
1314
+ for (const [key, { type, defaultValue }] of entries) {
1315
+ if (128 <= type) {
1316
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1317
+ }
1318
+ else {
1319
+ state[key] = defaultValue;
1320
+ }
1344
1321
  }
1345
- for (const type of Object.keys(this.constructor.channelSettings)) {
1346
- channel[type] = this.constructor.channelSettings[type];
1322
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1323
+ channel[key] = this.constructor.channelSettings[key];
1347
1324
  }
1348
1325
  this.mode = "GM1";
1349
1326
  }
1350
1327
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1351
- resetAllControllers(channelNumber) {
1352
- const stateTypes = [
1328
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1329
+ const keys = [
1353
1330
  "pitchWheel",
1354
1331
  "expression",
1355
1332
  "modulationDepth",
@@ -1357,10 +1334,17 @@ class MidyGMLite {
1357
1334
  ];
1358
1335
  const channel = this.channels[channelNumber];
1359
1336
  const state = channel.state;
1360
- for (let i = 0; i < stateTypes.length; i++) {
1361
- const type = stateTypes[i];
1362
- state[type] = defaultControllerState[type].defaultValue;
1337
+ for (let i = 0; i < keys.length; i++) {
1338
+ const key = keys[i];
1339
+ const { type, defaultValue } = defaultControllerState[key];
1340
+ if (128 <= type) {
1341
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1342
+ }
1343
+ else {
1344
+ state[key] = defaultValue;
1345
+ }
1363
1346
  }
1347
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1364
1348
  const settingTypes = [
1365
1349
  "rpnMSB",
1366
1350
  "rpnLSB",