@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.
@@ -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 (0 < event.velocity) {
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
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,13 +570,37 @@ 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
  }
616
600
  stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
617
601
  const channel = this.channels[channelNumber];
618
602
  const promises = [];
619
- const activeNotes = this.getActiveNotes(channel, scheduleTime);
620
- activeNotes.forEach((note) => {
603
+ this.processActiveNotes(channel, scheduleTime, (note) => {
621
604
  const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
622
605
  this.notePromises.push(promise);
623
606
  promises.push(promise);
@@ -628,11 +611,11 @@ export class MidyGMLite {
628
611
  const channel = this.channels[channelNumber];
629
612
  const promises = [];
630
613
  this.processScheduledNotes(channel, (note) => {
631
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
614
+ const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
632
615
  this.notePromises.push(promise);
633
616
  promises.push(promise);
634
617
  });
635
- channel.scheduledNotes.clear();
618
+ channel.scheduledNotes = [];
636
619
  return Promise.all(promises);
637
620
  }
638
621
  stopNotes(velocity, force, scheduleTime) {
@@ -653,9 +636,6 @@ export class MidyGMLite {
653
636
  if (!this.isPlaying)
654
637
  return;
655
638
  this.isStopping = true;
656
- for (let i = 0; i < this.channels.length; i++) {
657
- this.resetAllStates(i);
658
- }
659
639
  }
660
640
  pause() {
661
641
  if (!this.isPlaying || this.isPaused)
@@ -690,37 +670,31 @@ export class MidyGMLite {
690
670
  return this.resumeTime + now - this.startTime - this.startDelay;
691
671
  }
692
672
  processScheduledNotes(channel, callback) {
693
- channel.scheduledNotes.forEach((noteList) => {
694
- for (let i = 0; i < noteList.length; i++) {
695
- const note = noteList[i];
696
- if (!note)
697
- continue;
698
- if (note.ending)
699
- continue;
700
- callback(note);
701
- }
702
- });
703
- }
704
- getActiveNotes(channel, scheduleTime) {
705
- const activeNotes = new SparseMap(128);
706
- channel.scheduledNotes.forEach((noteList) => {
707
- const activeNote = this.getActiveNote(noteList, scheduleTime);
708
- if (activeNote) {
709
- activeNotes.set(activeNote.noteNumber, activeNote);
710
- }
711
- });
712
- 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
+ }
713
682
  }
714
- getActiveNote(noteList, scheduleTime) {
715
- for (let i = noteList.length - 1; i >= 0; i--) {
716
- 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];
717
687
  if (!note)
718
- return;
688
+ continue;
689
+ if (note.ending)
690
+ continue;
691
+ const noteOffEvent = note.noteOffEvent;
692
+ if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
693
+ continue;
719
694
  if (scheduleTime < note.startTime)
720
695
  continue;
721
- return (note.ending) ? null : note;
696
+ callback(note);
722
697
  }
723
- return noteList[0];
724
698
  }
725
699
  cbToRatio(cb) {
726
700
  return Math.pow(10, cb / 200);
@@ -867,6 +841,7 @@ export class MidyGMLite {
867
841
  this.setVolumeEnvelope(note, now);
868
842
  this.setFilterEnvelope(note, now);
869
843
  this.setPitchEnvelope(note, now);
844
+ this.updateDetune(channel, note, now);
870
845
  if (0 < state.modulationDepth) {
871
846
  this.startModulation(channel, note, now);
872
847
  }
@@ -883,7 +858,7 @@ export class MidyGMLite {
883
858
  if (prev) {
884
859
  const [prevNote, prevChannelNumber] = prev;
885
860
  if (prevNote && !prevNote.ending) {
886
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
861
+ this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
887
862
  startTime, true);
888
863
  }
889
864
  }
@@ -899,12 +874,12 @@ export class MidyGMLite {
899
874
  const index = drumExclusiveClass * this.channels.length + channelNumber;
900
875
  const prevNote = this.drumExclusiveClassNotes[index];
901
876
  if (prevNote && !prevNote.ending) {
902
- this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
877
+ this.scheduleNoteOff(channelNumber, prevNote, 0, // velocity,
903
878
  startTime, true);
904
879
  }
905
880
  this.drumExclusiveClassNotes[index] = note;
906
881
  }
907
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
882
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
908
883
  const channel = this.channels[channelNumber];
909
884
  const bankNumber = channel.bank;
910
885
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -916,6 +891,7 @@ export class MidyGMLite {
916
891
  return;
917
892
  const isSF3 = soundFont.parsed.info.version.major === 3;
918
893
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
894
+ note.noteOffEvent = noteOffEvent;
919
895
  note.volumeEnvelopeNode.connect(channel.gainL);
920
896
  note.volumeEnvelopeNode.connect(channel.gainR);
921
897
  if (0.5 <= channel.state.sustainPedal) {
@@ -924,20 +900,13 @@ export class MidyGMLite {
924
900
  this.handleExclusiveClass(note, channelNumber, startTime);
925
901
  this.handleDrumExclusiveClass(note, channelNumber, startTime);
926
902
  const scheduledNotes = channel.scheduledNotes;
927
- let noteList = scheduledNotes.get(noteNumber);
928
- if (noteList) {
929
- noteList.push(note);
930
- }
931
- else {
932
- noteList = [note];
933
- scheduledNotes.set(noteNumber, noteList);
934
- }
903
+ note.index = scheduledNotes.length;
904
+ scheduledNotes.push(note);
935
905
  if (channel.isDrum) {
936
906
  const stopTime = startTime + note.bufferSource.buffer.duration;
937
- const index = noteList.length - 1;
938
907
  const promise = new Promise((resolve) => {
939
908
  note.bufferSource.onended = () => {
940
- noteList[index] = undefined;
909
+ scheduledNotes[note.index] = undefined;
941
910
  this.disconnectNote(note);
942
911
  resolve();
943
912
  };
@@ -945,10 +914,16 @@ export class MidyGMLite {
945
914
  });
946
915
  this.notePromises.push(promise);
947
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
+ }
948
923
  }
949
924
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
950
925
  scheduleTime ??= this.audioContext.currentTime;
951
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
926
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
952
927
  }
953
928
  disconnectNote(note) {
954
929
  note.bufferSource.disconnect();
@@ -960,8 +935,7 @@ export class MidyGMLite {
960
935
  note.modulationLFO.stop();
961
936
  }
962
937
  }
963
- stopNote(endTime, stopTime, noteList, index) {
964
- const note = noteList[index];
938
+ stopNote(channel, note, endTime, stopTime) {
965
939
  note.volumeEnvelopeNode.gain
966
940
  .cancelScheduledValues(endTime)
967
941
  .linearRampToValueAtTime(0, stopTime);
@@ -971,56 +945,52 @@ export class MidyGMLite {
971
945
  }, stopTime);
972
946
  return new Promise((resolve) => {
973
947
  note.bufferSource.onended = () => {
974
- noteList[index] = undefined;
948
+ channel.scheduledNotes[note.index] = undefined;
975
949
  this.disconnectNote(note);
976
950
  resolve();
977
951
  };
978
952
  note.bufferSource.stop(stopTime);
979
953
  });
980
954
  }
981
- findNoteOffTarget(noteList) {
982
- for (let i = 0; i < noteList.length; i++) {
983
- const note = noteList[i];
984
- if (!note)
985
- continue;
986
- if (note.ending)
987
- continue;
988
- return [note, i];
989
- }
990
- }
991
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
955
+ scheduleNoteOff(channelNumber, note, _velocity, endTime, force) {
992
956
  const channel = this.channels[channelNumber];
993
957
  if (channel.isDrum)
994
958
  return;
995
959
  if (!force && 0.5 <= channel.state.sustainPedal)
996
960
  return;
997
- if (!channel.scheduledNotes.has(noteNumber))
998
- return;
999
- const noteList = channel.scheduledNotes.get(noteNumber);
1000
- if (!noteList)
1001
- return; // be careful with drum channel
1002
- const noteOffTarget = this.findNoteOffTarget(noteList, endTime);
1003
- if (!noteOffTarget)
1004
- return;
1005
- const [note, i] = noteOffTarget;
1006
961
  const volRelease = endTime + note.voiceParams.volRelease;
1007
962
  const modRelease = endTime + note.voiceParams.modRelease;
1008
963
  note.filterNode.frequency
1009
964
  .cancelScheduledValues(endTime)
1010
965
  .linearRampToValueAtTime(0, modRelease);
1011
966
  const stopTime = Math.min(volRelease, modRelease);
1012
- return this.stopNote(endTime, stopTime, noteList, i);
967
+ return this.stopNote(channel, note, endTime, stopTime);
968
+ }
969
+ findNoteOffTarget(channel, noteNumber) {
970
+ const scheduledNotes = channel.scheduledNotes;
971
+ for (let i = 0; i < scheduledNotes.length; i++) {
972
+ const note = scheduledNotes[i];
973
+ if (!note)
974
+ continue;
975
+ if (note.ending)
976
+ continue;
977
+ if (note.noteNumber !== noteNumber)
978
+ continue;
979
+ return note;
980
+ }
1013
981
  }
1014
982
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
1015
983
  scheduleTime ??= this.audioContext.currentTime;
1016
- 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);
1017
987
  }
1018
988
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
1019
989
  const velocity = halfVelocity * 2;
1020
990
  const channel = this.channels[channelNumber];
1021
991
  const promises = [];
1022
992
  for (let i = 0; i < channel.sustainNotes.length; i++) {
1023
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
993
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
1024
994
  promises.push(promise);
1025
995
  }
1026
996
  channel.sustainNotes = [];
@@ -1176,20 +1146,20 @@ export class MidyGMLite {
1176
1146
  });
1177
1147
  }
1178
1148
  createControlChangeHandlers() {
1179
- return {
1180
- 1: this.setModulationDepth,
1181
- 6: this.dataEntryMSB,
1182
- 7: this.setVolume,
1183
- 10: this.setPan,
1184
- 11: this.setExpression,
1185
- 38: this.dataEntryLSB,
1186
- 64: this.setSustainPedal,
1187
- 100: this.setRPNLSB,
1188
- 101: this.setRPNMSB,
1189
- 120: this.allSoundOff,
1190
- 121: this.resetAllControllers,
1191
- 123: this.allNotesOff,
1192
- };
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;
1193
1163
  }
1194
1164
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1195
1165
  const handler = this.controlChangeHandlers[controllerType];
@@ -1334,19 +1304,26 @@ export class MidyGMLite {
1334
1304
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1335
1305
  }
1336
1306
  resetAllStates(channelNumber) {
1307
+ const scheduleTime = this.audioContext.currentTime;
1337
1308
  const channel = this.channels[channelNumber];
1338
1309
  const state = channel.state;
1339
- for (const type of Object.keys(defaultControllerState)) {
1340
- 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
+ }
1341
1318
  }
1342
- for (const type of Object.keys(this.constructor.channelSettings)) {
1343
- channel[type] = this.constructor.channelSettings[type];
1319
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1320
+ channel[key] = this.constructor.channelSettings[key];
1344
1321
  }
1345
1322
  this.mode = "GM1";
1346
1323
  }
1347
1324
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1348
- resetAllControllers(channelNumber) {
1349
- const stateTypes = [
1325
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1326
+ const keys = [
1350
1327
  "pitchWheel",
1351
1328
  "expression",
1352
1329
  "modulationDepth",
@@ -1354,10 +1331,17 @@ export class MidyGMLite {
1354
1331
  ];
1355
1332
  const channel = this.channels[channelNumber];
1356
1333
  const state = channel.state;
1357
- for (let i = 0; i < stateTypes.length; i++) {
1358
- const type = stateTypes[i];
1359
- 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
+ }
1360
1343
  }
1344
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1361
1345
  const settingTypes = [
1362
1346
  "rpnMSB",
1363
1347
  "rpnLSB",