@marmooo/midy 0.3.1 → 0.3.3

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,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGM1 = 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
+ });
14
+ Object.defineProperty(this, "ending", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: false
19
+ });
60
20
  Object.defineProperty(this, "bufferSource", {
61
21
  enumerable: true,
62
22
  configurable: true,
@@ -117,7 +77,7 @@ const defaultControllerState = {
117
77
  modulationDepth: { type: 128 + 1, defaultValue: 0 },
118
78
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
119
79
  volume: { type: 128 + 7, defaultValue: 100 / 127 },
120
- pan: { type: 128 + 10, defaultValue: 0.5 },
80
+ pan: { type: 128 + 10, defaultValue: 64 / 127 },
121
81
  expression: { type: 128 + 11, defaultValue: 1 },
122
82
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
123
83
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
@@ -322,7 +282,7 @@ class MidyGM1 {
322
282
  initSoundFontTable() {
323
283
  const table = new Array(128);
324
284
  for (let i = 0; i < 128; i++) {
325
- table[i] = new SparseMap(128);
285
+ table[i] = new Map();
326
286
  }
327
287
  return table;
328
288
  }
@@ -338,17 +298,37 @@ class MidyGM1 {
338
298
  }
339
299
  }
340
300
  }
341
- async loadSoundFont(soundFontUrl) {
342
- const response = await fetch(soundFontUrl);
343
- const arrayBuffer = await response.arrayBuffer();
344
- const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
301
+ async loadSoundFont(input) {
302
+ let uint8Array;
303
+ if (typeof input === "string") {
304
+ const response = await fetch(input);
305
+ const arrayBuffer = await response.arrayBuffer();
306
+ uint8Array = new Uint8Array(arrayBuffer);
307
+ }
308
+ else if (input instanceof Uint8Array) {
309
+ uint8Array = input;
310
+ }
311
+ else {
312
+ throw new TypeError("input must be a URL string or Uint8Array");
313
+ }
314
+ const parsed = (0, soundfont_parser_1.parse)(uint8Array);
345
315
  const soundFont = new soundfont_parser_1.SoundFont(parsed);
346
316
  this.addSoundFont(soundFont);
347
317
  }
348
- async loadMIDI(midiUrl) {
349
- const response = await fetch(midiUrl);
350
- const arrayBuffer = await response.arrayBuffer();
351
- const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
318
+ async loadMIDI(input) {
319
+ let uint8Array;
320
+ if (typeof input === "string") {
321
+ const response = await fetch(input);
322
+ const arrayBuffer = await response.arrayBuffer();
323
+ uint8Array = new Uint8Array(arrayBuffer);
324
+ }
325
+ else if (input instanceof Uint8Array) {
326
+ uint8Array = input;
327
+ }
328
+ else {
329
+ throw new TypeError("input must be a URL string or Uint8Array");
330
+ }
331
+ const midi = (0, midi_file_1.parseMidi)(uint8Array);
352
332
  this.ticksPerBeat = midi.header.ticksPerBeat;
353
333
  const midiData = this.extractMidiData(midi);
354
334
  this.instruments = midiData.instruments;
@@ -374,10 +354,10 @@ class MidyGM1 {
374
354
  return {
375
355
  currentBufferSource: null,
376
356
  isDrum: false,
377
- ...this.constructor.channelSettings,
378
357
  state: new ControllerState(),
358
+ ...this.constructor.channelSettings,
379
359
  ...this.setChannelAudioNodes(audioContext),
380
- scheduledNotes: new SparseMap(128),
360
+ scheduledNotes: [],
381
361
  sustainNotes: [],
382
362
  };
383
363
  });
@@ -412,7 +392,7 @@ class MidyGM1 {
412
392
  return audioBuffer;
413
393
  }
414
394
  }
415
- createBufferSource(audioBuffer, voiceParams) {
395
+ createBufferSource(voiceParams, audioBuffer) {
416
396
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
417
397
  bufferSource.buffer = audioBuffer;
418
398
  bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
@@ -422,24 +402,21 @@ class MidyGM1 {
422
402
  }
423
403
  return bufferSource;
424
404
  }
425
- async scheduleTimelineEvents(t, offset, queueIndex) {
405
+ async scheduleTimelineEvents(t, resumeTime, queueIndex) {
426
406
  while (queueIndex < this.timeline.length) {
427
407
  const event = this.timeline[queueIndex];
428
408
  if (event.startTime > t + this.lookAhead)
429
409
  break;
430
- const startTime = event.startTime + this.startDelay - offset;
410
+ const delay = this.startDelay - resumeTime;
411
+ const startTime = event.startTime + delay;
431
412
  switch (event.type) {
432
413
  case "noteOn":
433
- if (0 < event.velocity) {
434
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
435
- break;
436
- }
437
- /* falls through */
414
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
415
+ break;
438
416
  case "noteOff": {
439
417
  const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
440
- if (notePromise) {
418
+ if (notePromise)
441
419
  this.notePromises.push(notePromise);
442
- }
443
420
  break;
444
421
  }
445
422
  case "controller":
@@ -472,7 +449,7 @@ class MidyGM1 {
472
449
  this.isPaused = false;
473
450
  this.startTime = this.audioContext.currentTime;
474
451
  let queueIndex = this.getQueueIndex(this.resumeTime);
475
- let offset = this.resumeTime - this.startTime;
452
+ let resumeTime = this.resumeTime - this.startTime;
476
453
  this.notePromises = [];
477
454
  const schedulePlayback = async () => {
478
455
  if (queueIndex >= this.timeline.length) {
@@ -480,18 +457,21 @@ class MidyGM1 {
480
457
  this.notePromises = [];
481
458
  this.exclusiveClassNotes.fill(undefined);
482
459
  this.audioBufferCache.clear();
460
+ for (let i = 0; i < this.channels.length; i++) {
461
+ this.resetAllStates(i);
462
+ }
483
463
  resolve();
484
464
  return;
485
465
  }
486
466
  const now = this.audioContext.currentTime;
487
- const t = now + offset;
488
- queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
467
+ const t = now + resumeTime;
468
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
489
469
  if (this.isPausing) {
490
470
  await this.stopNotes(0, true, now);
491
471
  this.notePromises = [];
492
- resolve();
493
472
  this.isPausing = false;
494
473
  this.isPaused = true;
474
+ resolve();
495
475
  return;
496
476
  }
497
477
  else if (this.isStopping) {
@@ -499,9 +479,12 @@ class MidyGM1 {
499
479
  this.notePromises = [];
500
480
  this.exclusiveClassNotes.fill(undefined);
501
481
  this.audioBufferCache.clear();
502
- resolve();
482
+ for (let i = 0; i < this.channels.length; i++) {
483
+ this.resetAllStates(i);
484
+ }
503
485
  this.isStopping = false;
504
486
  this.isPaused = false;
487
+ resolve();
505
488
  return;
506
489
  }
507
490
  else if (this.isSeeking) {
@@ -509,7 +492,7 @@ class MidyGM1 {
509
492
  this.exclusiveClassNotes.fill(undefined);
510
493
  this.startTime = this.audioContext.currentTime;
511
494
  queueIndex = this.getQueueIndex(this.resumeTime);
512
- offset = this.resumeTime - this.startTime;
495
+ resumeTime = this.resumeTime - this.startTime;
513
496
  this.isSeeking = false;
514
497
  await schedulePlayback();
515
498
  }
@@ -532,6 +515,7 @@ class MidyGM1 {
532
515
  return `${programNumber}:${noteNumber}:${velocity}`;
533
516
  }
534
517
  extractMidiData(midi) {
518
+ this.audioBufferCounter.clear();
535
519
  const instruments = new Set();
536
520
  const timeline = [];
537
521
  const tmpChannels = new Array(this.channels.length);
@@ -600,9 +584,8 @@ class MidyGM1 {
600
584
  stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
601
585
  const channel = this.channels[channelNumber];
602
586
  const promises = [];
603
- const activeNotes = this.getActiveNotes(channel, scheduleTime);
604
- activeNotes.forEach((note) => {
605
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
587
+ this.processActiveNotes(channel, scheduleTime, (note) => {
588
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
606
589
  this.notePromises.push(promise);
607
590
  promises.push(promise);
608
591
  });
@@ -616,7 +599,7 @@ class MidyGM1 {
616
599
  this.notePromises.push(promise);
617
600
  promises.push(promise);
618
601
  });
619
- channel.scheduledNotes.clear();
602
+ channel.scheduledNotes = [];
620
603
  return Promise.all(promises);
621
604
  }
622
605
  stopNotes(velocity, force, scheduleTime) {
@@ -637,9 +620,6 @@ class MidyGM1 {
637
620
  if (!this.isPlaying)
638
621
  return;
639
622
  this.isStopping = true;
640
- for (let i = 0; i < this.channels.length; i++) {
641
- this.resetAllStates(i);
642
- }
643
623
  }
644
624
  pause() {
645
625
  if (!this.isPlaying || this.isPaused)
@@ -674,37 +654,28 @@ class MidyGM1 {
674
654
  return this.resumeTime + now - this.startTime - this.startDelay;
675
655
  }
676
656
  processScheduledNotes(channel, callback) {
677
- channel.scheduledNotes.forEach((noteList) => {
678
- for (let i = 0; i < noteList.length; i++) {
679
- const note = noteList[i];
680
- if (!note)
681
- continue;
682
- if (note.ending)
683
- continue;
684
- callback(note);
685
- }
686
- });
687
- }
688
- getActiveNotes(channel, scheduleTime) {
689
- const activeNotes = new SparseMap(128);
690
- channel.scheduledNotes.forEach((noteList) => {
691
- const activeNote = this.getActiveNote(noteList, scheduleTime);
692
- if (activeNote) {
693
- activeNotes.set(activeNote.noteNumber, activeNote);
694
- }
695
- });
696
- return activeNotes;
657
+ const scheduledNotes = channel.scheduledNotes;
658
+ for (let i = 0; i < scheduledNotes.length; i++) {
659
+ const note = scheduledNotes[i];
660
+ if (!note)
661
+ continue;
662
+ if (note.ending)
663
+ continue;
664
+ callback(note);
665
+ }
697
666
  }
698
- getActiveNote(noteList, scheduleTime) {
699
- for (let i = noteList.length - 1; i >= 0; i--) {
700
- const note = noteList[i];
667
+ processActiveNotes(channel, scheduleTime, callback) {
668
+ const scheduledNotes = channel.scheduledNotes;
669
+ for (let i = 0; i < scheduledNotes.length; i++) {
670
+ const note = scheduledNotes[i];
701
671
  if (!note)
702
- return;
672
+ continue;
673
+ if (note.ending)
674
+ continue;
703
675
  if (scheduleTime < note.startTime)
704
676
  continue;
705
- return (note.ending) ? null : note;
677
+ callback(note);
706
678
  }
707
- return noteList[0];
708
679
  }
709
680
  cbToRatio(cb) {
710
681
  return Math.pow(10, cb / 200);
@@ -844,7 +815,7 @@ class MidyGM1 {
844
815
  const voiceParams = voice.getAllParams(controllerState);
845
816
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
846
817
  const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
847
- note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
818
+ note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
848
819
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
849
820
  note.filterNode = new BiquadFilterNode(this.audioContext, {
850
821
  type: "lowpass",
@@ -853,6 +824,7 @@ class MidyGM1 {
853
824
  this.setVolumeEnvelope(note, now);
854
825
  this.setFilterEnvelope(note, now);
855
826
  this.setPitchEnvelope(note, now);
827
+ this.updateDetune(channel, note, now);
856
828
  if (0 < state.modulationDepth) {
857
829
  this.startModulation(channel, note, now);
858
830
  }
@@ -875,7 +847,7 @@ class MidyGM1 {
875
847
  }
876
848
  this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
877
849
  }
878
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
850
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
879
851
  const channel = this.channels[channelNumber];
880
852
  const bankNumber = channel.bank;
881
853
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -887,6 +859,7 @@ class MidyGM1 {
887
859
  return;
888
860
  const isSF3 = soundFont.parsed.info.version.major === 3;
889
861
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
862
+ note.noteOffEvent = noteOffEvent;
890
863
  note.volumeEnvelopeNode.connect(channel.gainL);
891
864
  note.volumeEnvelopeNode.connect(channel.gainR);
892
865
  if (0.5 <= channel.state.sustainPedal) {
@@ -894,18 +867,12 @@ class MidyGM1 {
894
867
  }
895
868
  this.handleExclusiveClass(note, channelNumber, startTime);
896
869
  const scheduledNotes = channel.scheduledNotes;
897
- let noteList = scheduledNotes.get(noteNumber);
898
- if (noteList) {
899
- noteList.push(note);
900
- }
901
- else {
902
- noteList = [note];
903
- scheduledNotes.set(noteNumber, noteList);
904
- }
870
+ note.index = scheduledNotes.length;
871
+ scheduledNotes.push(note);
905
872
  }
906
873
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
907
874
  scheduleTime ??= this.audioContext.currentTime;
908
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
875
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
909
876
  }
910
877
  disconnectNote(note) {
911
878
  note.bufferSource.disconnect();
@@ -917,54 +884,49 @@ class MidyGM1 {
917
884
  note.modulationLFO.stop();
918
885
  }
919
886
  }
920
- stopNote(endTime, stopTime, noteList, index) {
921
- const note = noteList[index];
887
+ releaseNote(channel, note, endTime) {
888
+ const volRelease = endTime + note.voiceParams.volRelease;
889
+ const modRelease = endTime + note.voiceParams.modRelease;
890
+ const stopTime = Math.min(volRelease, modRelease);
891
+ note.filterNode.frequency
892
+ .cancelScheduledValues(endTime)
893
+ .linearRampToValueAtTime(0, modRelease);
922
894
  note.volumeEnvelopeNode.gain
923
895
  .cancelScheduledValues(endTime)
924
- .linearRampToValueAtTime(0, stopTime);
925
- note.ending = true;
926
- this.scheduleTask(() => {
927
- note.bufferSource.loop = false;
928
- }, stopTime);
896
+ .linearRampToValueAtTime(0, volRelease);
929
897
  return new Promise((resolve) => {
930
- note.bufferSource.onended = () => {
931
- noteList[index] = undefined;
898
+ this.scheduleTask(() => {
899
+ const bufferSource = note.bufferSource;
900
+ bufferSource.loop = false;
901
+ bufferSource.stop(stopTime);
932
902
  this.disconnectNote(note);
903
+ channel.scheduledNotes[note.index] = undefined;
933
904
  resolve();
934
- };
935
- note.bufferSource.stop(stopTime);
905
+ }, stopTime);
936
906
  });
937
907
  }
938
- findNoteOffTarget(noteList) {
939
- for (let i = 0; i < noteList.length; i++) {
940
- const note = noteList[i];
941
- if (!note)
942
- continue;
943
- if (note.ending)
944
- continue;
945
- return [note, i];
946
- }
947
- }
948
908
  scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
949
909
  const channel = this.channels[channelNumber];
950
910
  if (!force && 0.5 <= channel.state.sustainPedal)
951
911
  return;
952
- if (!channel.scheduledNotes.has(noteNumber))
912
+ const note = this.findNoteOffTarget(channel, noteNumber);
913
+ if (!note)
953
914
  return;
954
- const noteList = channel.scheduledNotes.get(noteNumber);
955
- if (!noteList)
956
- return; // be careful with drum channel
957
- const noteOffTarget = this.findNoteOffTarget(noteList, endTime);
958
- if (!noteOffTarget)
959
- return;
960
- const [note, i] = noteOffTarget;
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(endTime, stopTime, noteList, i);
915
+ note.ending = true;
916
+ this.releaseNote(channel, note, endTime);
917
+ }
918
+ findNoteOffTarget(channel, noteNumber) {
919
+ const scheduledNotes = channel.scheduledNotes;
920
+ for (let i = 0; i < scheduledNotes.length; i++) {
921
+ const note = scheduledNotes[i];
922
+ if (!note)
923
+ continue;
924
+ if (note.ending)
925
+ continue;
926
+ if (note.noteNumber !== noteNumber)
927
+ continue;
928
+ return note;
929
+ }
968
930
  }
969
931
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
970
932
  scheduleTime ??= this.audioContext.currentTime;
@@ -975,7 +937,7 @@ class MidyGM1 {
975
937
  const channel = this.channels[channelNumber];
976
938
  const promises = [];
977
939
  for (let i = 0; i < channel.sustainNotes.length; i++) {
978
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
940
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
979
941
  promises.push(promise);
980
942
  }
981
943
  channel.sustainNotes = [];
@@ -1131,20 +1093,19 @@ class MidyGM1 {
1131
1093
  });
1132
1094
  }
1133
1095
  createControlChangeHandlers() {
1134
- return {
1135
- 1: this.setModulationDepth,
1136
- 6: this.dataEntryMSB,
1137
- 7: this.setVolume,
1138
- 10: this.setPan,
1139
- 11: this.setExpression,
1140
- 38: this.dataEntryLSB,
1141
- 64: this.setSustainPedal,
1142
- 100: this.setRPNLSB,
1143
- 101: this.setRPNMSB,
1144
- 120: this.allSoundOff,
1145
- 121: this.resetAllControllers,
1146
- 123: this.allNotesOff,
1147
- };
1096
+ const handlers = new Array(128);
1097
+ handlers[1] = this.setModulationDepth;
1098
+ handlers[6] = this.dataEntryMSB;
1099
+ handlers[7] = this.setVolume;
1100
+ handlers[10] = this.setPan;
1101
+ handlers[11] = this.setExpression;
1102
+ handlers[38] = this.dataEntryLSB;
1103
+ handlers[64] = this.setSustainPedal;
1104
+ handlers[100] = this.setRPNLSB;
1105
+ handlers[101] = this.setRPNMSB;
1106
+ handlers[120] = this.allSoundOff;
1107
+ handlers[121] = this.resetAllControllers;
1108
+ return handlers;
1148
1109
  }
1149
1110
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1150
1111
  const handler = this.controlChangeHandlers[controllerType];
@@ -1333,19 +1294,26 @@ class MidyGM1 {
1333
1294
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1334
1295
  }
1335
1296
  resetAllStates(channelNumber) {
1297
+ const scheduleTime = this.audioContext.currentTime;
1336
1298
  const channel = this.channels[channelNumber];
1337
1299
  const state = channel.state;
1338
- for (const type of Object.keys(defaultControllerState)) {
1339
- state[type] = defaultControllerState[type].defaultValue;
1300
+ const entries = Object.entries(defaultControllerState);
1301
+ for (const [key, { type, defaultValue }] of entries) {
1302
+ if (128 <= type) {
1303
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1304
+ }
1305
+ else {
1306
+ state[key] = defaultValue;
1307
+ }
1340
1308
  }
1341
- for (const type of Object.keys(this.constructor.channelSettings)) {
1342
- channel[type] = this.constructor.channelSettings[type];
1309
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1310
+ channel[key] = this.constructor.channelSettings[key];
1343
1311
  }
1344
1312
  this.mode = "GM1";
1345
1313
  }
1346
1314
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1347
- resetAllControllers(channelNumber) {
1348
- const stateTypes = [
1315
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1316
+ const keys = [
1349
1317
  "pitchWheel",
1350
1318
  "expression",
1351
1319
  "modulationDepth",
@@ -1353,10 +1321,17 @@ class MidyGM1 {
1353
1321
  ];
1354
1322
  const channel = this.channels[channelNumber];
1355
1323
  const state = channel.state;
1356
- for (let i = 0; i < stateTypes.length; i++) {
1357
- const type = stateTypes[i];
1358
- state[type] = defaultControllerState[type].defaultValue;
1324
+ for (let i = 0; i < keys.length; i++) {
1325
+ const key = keys[i];
1326
+ const { type, defaultValue } = defaultControllerState[key];
1327
+ if (128 <= type) {
1328
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1329
+ }
1330
+ else {
1331
+ state[key] = defaultValue;
1332
+ }
1359
1333
  }
1334
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1360
1335
  const settingTypes = [
1361
1336
  "rpnMSB",
1362
1337
  "rpnLSB",