@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.
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 (0 < event.velocity) {
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,7 +419,7 @@ 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) {
@@ -477,18 +427,21 @@ export class MidyGM1 {
477
427
  this.notePromises = [];
478
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,13 +548,37 @@ 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
  }
597
578
  stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
598
579
  const channel = this.channels[channelNumber];
599
580
  const promises = [];
600
- const activeNotes = this.getActiveNotes(channel, scheduleTime);
601
- activeNotes.forEach((note) => {
581
+ this.processActiveNotes(channel, scheduleTime, (note) => {
602
582
  const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
603
583
  this.notePromises.push(promise);
604
584
  promises.push(promise);
@@ -609,11 +589,11 @@ export class MidyGM1 {
609
589
  const channel = this.channels[channelNumber];
610
590
  const promises = [];
611
591
  this.processScheduledNotes(channel, (note) => {
612
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
592
+ const promise = this.scheduleNoteOff(channelNumber, note, velocity, scheduleTime, force);
613
593
  this.notePromises.push(promise);
614
594
  promises.push(promise);
615
595
  });
616
- channel.scheduledNotes.clear();
596
+ channel.scheduledNotes = [];
617
597
  return Promise.all(promises);
618
598
  }
619
599
  stopNotes(velocity, force, scheduleTime) {
@@ -634,9 +614,6 @@ export class MidyGM1 {
634
614
  if (!this.isPlaying)
635
615
  return;
636
616
  this.isStopping = true;
637
- for (let i = 0; i < this.channels.length; i++) {
638
- this.resetAllStates(i);
639
- }
640
617
  }
641
618
  pause() {
642
619
  if (!this.isPlaying || this.isPaused)
@@ -671,37 +648,31 @@ export class MidyGM1 {
671
648
  return this.resumeTime + now - this.startTime - this.startDelay;
672
649
  }
673
650
  processScheduledNotes(channel, callback) {
674
- channel.scheduledNotes.forEach((noteList) => {
675
- for (let i = 0; i < noteList.length; i++) {
676
- const note = noteList[i];
677
- if (!note)
678
- continue;
679
- if (note.ending)
680
- continue;
681
- callback(note);
682
- }
683
- });
684
- }
685
- getActiveNotes(channel, scheduleTime) {
686
- const activeNotes = new SparseMap(128);
687
- channel.scheduledNotes.forEach((noteList) => {
688
- const activeNote = this.getActiveNote(noteList, scheduleTime);
689
- if (activeNote) {
690
- activeNotes.set(activeNote.noteNumber, activeNote);
691
- }
692
- });
693
- 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
+ }
694
660
  }
695
- getActiveNote(noteList, scheduleTime) {
696
- for (let i = noteList.length - 1; i >= 0; i--) {
697
- 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];
698
665
  if (!note)
699
- return;
666
+ continue;
667
+ if (note.ending)
668
+ continue;
669
+ const noteOffEvent = note.noteOffEvent;
670
+ if (noteOffEvent && noteOffEvent.startTime < scheduleTime)
671
+ continue;
700
672
  if (scheduleTime < note.startTime)
701
673
  continue;
702
- return (note.ending) ? null : note;
674
+ callback(note);
703
675
  }
704
- return noteList[0];
705
676
  }
706
677
  cbToRatio(cb) {
707
678
  return Math.pow(10, cb / 200);
@@ -841,7 +812,7 @@ export class MidyGM1 {
841
812
  const voiceParams = voice.getAllParams(controllerState);
842
813
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
843
814
  const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
844
- note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
815
+ note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
845
816
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
846
817
  note.filterNode = new BiquadFilterNode(this.audioContext, {
847
818
  type: "lowpass",
@@ -850,6 +821,7 @@ export class MidyGM1 {
850
821
  this.setVolumeEnvelope(note, now);
851
822
  this.setFilterEnvelope(note, now);
852
823
  this.setPitchEnvelope(note, now);
824
+ this.updateDetune(channel, note, now);
853
825
  if (0 < state.modulationDepth) {
854
826
  this.startModulation(channel, note, now);
855
827
  }
@@ -866,13 +838,13 @@ export class MidyGM1 {
866
838
  if (prev) {
867
839
  const [prevNote, prevChannelNumber] = prev;
868
840
  if (prevNote && !prevNote.ending) {
869
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
841
+ this.scheduleNoteOff(prevChannelNumber, prevNote, 0, // velocity,
870
842
  startTime, true);
871
843
  }
872
844
  }
873
845
  this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
874
846
  }
875
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
847
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
876
848
  const channel = this.channels[channelNumber];
877
849
  const bankNumber = channel.bank;
878
850
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -884,6 +856,7 @@ export class MidyGM1 {
884
856
  return;
885
857
  const isSF3 = soundFont.parsed.info.version.major === 3;
886
858
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
859
+ note.noteOffEvent = noteOffEvent;
887
860
  note.volumeEnvelopeNode.connect(channel.gainL);
888
861
  note.volumeEnvelopeNode.connect(channel.gainR);
889
862
  if (0.5 <= channel.state.sustainPedal) {
@@ -891,18 +864,18 @@ export class MidyGM1 {
891
864
  }
892
865
  this.handleExclusiveClass(note, channelNumber, startTime);
893
866
  const scheduledNotes = channel.scheduledNotes;
894
- let noteList = scheduledNotes.get(noteNumber);
895
- if (noteList) {
896
- noteList.push(note);
897
- }
898
- else {
899
- noteList = [note];
900
- scheduledNotes.set(noteNumber, noteList);
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
+ }
901
874
  }
902
875
  }
903
876
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
904
877
  scheduleTime ??= this.audioContext.currentTime;
905
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
878
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
906
879
  }
907
880
  disconnectNote(note) {
908
881
  note.bufferSource.disconnect();
@@ -914,8 +887,7 @@ export class MidyGM1 {
914
887
  note.modulationLFO.stop();
915
888
  }
916
889
  }
917
- stopNote(endTime, stopTime, noteList, index) {
918
- const note = noteList[index];
890
+ stopNote(channel, note, endTime, stopTime) {
919
891
  note.volumeEnvelopeNode.gain
920
892
  .cancelScheduledValues(endTime)
921
893
  .linearRampToValueAtTime(0, stopTime);
@@ -925,54 +897,50 @@ export class MidyGM1 {
925
897
  }, stopTime);
926
898
  return new Promise((resolve) => {
927
899
  note.bufferSource.onended = () => {
928
- noteList[index] = undefined;
900
+ channel.scheduledNotes[note.index] = undefined;
929
901
  this.disconnectNote(note);
930
902
  resolve();
931
903
  };
932
904
  note.bufferSource.stop(stopTime);
933
905
  });
934
906
  }
935
- findNoteOffTarget(noteList) {
936
- for (let i = 0; i < noteList.length; i++) {
937
- const note = noteList[i];
938
- if (!note)
939
- continue;
940
- if (note.ending)
941
- continue;
942
- return [note, i];
943
- }
944
- }
945
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
907
+ scheduleNoteOff(channelNumber, note, _velocity, endTime, force) {
946
908
  const channel = this.channels[channelNumber];
947
909
  if (!force && 0.5 <= channel.state.sustainPedal)
948
910
  return;
949
- if (!channel.scheduledNotes.has(noteNumber))
950
- return;
951
- const noteList = channel.scheduledNotes.get(noteNumber);
952
- if (!noteList)
953
- return; // be careful with drum channel
954
- const noteOffTarget = this.findNoteOffTarget(noteList, endTime);
955
- if (!noteOffTarget)
956
- return;
957
- const [note, i] = noteOffTarget;
958
911
  const volRelease = endTime + note.voiceParams.volRelease;
959
912
  const modRelease = endTime + note.voiceParams.modRelease;
960
913
  note.filterNode.frequency
961
914
  .cancelScheduledValues(endTime)
962
915
  .linearRampToValueAtTime(0, modRelease);
963
916
  const stopTime = Math.min(volRelease, modRelease);
964
- return this.stopNote(endTime, stopTime, noteList, i);
917
+ return this.stopNote(channel, note, endTime, stopTime);
918
+ }
919
+ findNoteOffTarget(channel, noteNumber) {
920
+ const scheduledNotes = channel.scheduledNotes;
921
+ for (let i = 0; i < scheduledNotes.length; i++) {
922
+ const note = scheduledNotes[i];
923
+ if (!note)
924
+ continue;
925
+ if (note.ending)
926
+ continue;
927
+ if (note.noteNumber !== noteNumber)
928
+ continue;
929
+ return note;
930
+ }
965
931
  }
966
932
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
967
933
  scheduleTime ??= this.audioContext.currentTime;
968
- 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);
969
937
  }
970
938
  releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
971
939
  const velocity = halfVelocity * 2;
972
940
  const channel = this.channels[channelNumber];
973
941
  const promises = [];
974
942
  for (let i = 0; i < channel.sustainNotes.length; i++) {
975
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
943
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i], velocity, scheduleTime);
976
944
  promises.push(promise);
977
945
  }
978
946
  channel.sustainNotes = [];
@@ -1128,20 +1096,19 @@ export class MidyGM1 {
1128
1096
  });
1129
1097
  }
1130
1098
  createControlChangeHandlers() {
1131
- return {
1132
- 1: this.setModulationDepth,
1133
- 6: this.dataEntryMSB,
1134
- 7: this.setVolume,
1135
- 10: this.setPan,
1136
- 11: this.setExpression,
1137
- 38: this.dataEntryLSB,
1138
- 64: this.setSustainPedal,
1139
- 100: this.setRPNLSB,
1140
- 101: this.setRPNMSB,
1141
- 120: this.allSoundOff,
1142
- 121: this.resetAllControllers,
1143
- 123: this.allNotesOff,
1144
- };
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;
1145
1112
  }
1146
1113
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1147
1114
  const handler = this.controlChangeHandlers[controllerType];
@@ -1330,19 +1297,26 @@ export class MidyGM1 {
1330
1297
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1331
1298
  }
1332
1299
  resetAllStates(channelNumber) {
1300
+ const scheduleTime = this.audioContext.currentTime;
1333
1301
  const channel = this.channels[channelNumber];
1334
1302
  const state = channel.state;
1335
- for (const type of Object.keys(defaultControllerState)) {
1336
- 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
+ }
1337
1311
  }
1338
- for (const type of Object.keys(this.constructor.channelSettings)) {
1339
- channel[type] = this.constructor.channelSettings[type];
1312
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1313
+ channel[key] = this.constructor.channelSettings[key];
1340
1314
  }
1341
1315
  this.mode = "GM1";
1342
1316
  }
1343
1317
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1344
- resetAllControllers(channelNumber) {
1345
- const stateTypes = [
1318
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1319
+ const keys = [
1346
1320
  "pitchWheel",
1347
1321
  "expression",
1348
1322
  "modulationDepth",
@@ -1350,10 +1324,17 @@ export class MidyGM1 {
1350
1324
  ];
1351
1325
  const channel = this.channels[channelNumber];
1352
1326
  const state = channel.state;
1353
- for (let i = 0; i < stateTypes.length; i++) {
1354
- const type = stateTypes[i];
1355
- 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
+ }
1356
1336
  }
1337
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1357
1338
  const settingTypes = [
1358
1339
  "rpnMSB",
1359
1340
  "rpnLSB",