@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.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
+ });
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,
@@ -130,7 +90,7 @@ const defaultControllerState = {
130
90
  modulationDepth: { type: 128 + 1, defaultValue: 0 },
131
91
  // dataMSB: { type: 128 + 6, defaultValue: 0, },
132
92
  volume: { type: 128 + 7, defaultValue: 100 / 127 },
133
- pan: { type: 128 + 10, defaultValue: 0.5 },
93
+ pan: { type: 128 + 10, defaultValue: 64 / 127 },
134
94
  expression: { type: 128 + 11, defaultValue: 1 },
135
95
  // bankLSB: { type: 128 + 32, defaultValue: 0, },
136
96
  // dataLSB: { type: 128 + 38, defaultValue: 0, },
@@ -341,7 +301,7 @@ class MidyGMLite {
341
301
  initSoundFontTable() {
342
302
  const table = new Array(128);
343
303
  for (let i = 0; i < 128; i++) {
344
- table[i] = new SparseMap(128);
304
+ table[i] = new Map();
345
305
  }
346
306
  return table;
347
307
  }
@@ -357,17 +317,37 @@ class MidyGMLite {
357
317
  }
358
318
  }
359
319
  }
360
- async loadSoundFont(soundFontUrl) {
361
- const response = await fetch(soundFontUrl);
362
- const arrayBuffer = await response.arrayBuffer();
363
- const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
320
+ async loadSoundFont(input) {
321
+ let uint8Array;
322
+ if (typeof input === "string") {
323
+ const response = await fetch(input);
324
+ const arrayBuffer = await response.arrayBuffer();
325
+ uint8Array = new Uint8Array(arrayBuffer);
326
+ }
327
+ else if (input instanceof Uint8Array) {
328
+ uint8Array = input;
329
+ }
330
+ else {
331
+ throw new TypeError("input must be a URL string or Uint8Array");
332
+ }
333
+ const parsed = (0, soundfont_parser_1.parse)(uint8Array);
364
334
  const soundFont = new soundfont_parser_1.SoundFont(parsed);
365
335
  this.addSoundFont(soundFont);
366
336
  }
367
- async loadMIDI(midiUrl) {
368
- const response = await fetch(midiUrl);
369
- const arrayBuffer = await response.arrayBuffer();
370
- const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
337
+ async loadMIDI(input) {
338
+ let uint8Array;
339
+ if (typeof input === "string") {
340
+ const response = await fetch(input);
341
+ const arrayBuffer = await response.arrayBuffer();
342
+ uint8Array = new Uint8Array(arrayBuffer);
343
+ }
344
+ else if (input instanceof Uint8Array) {
345
+ uint8Array = input;
346
+ }
347
+ else {
348
+ throw new TypeError("input must be a URL string or Uint8Array");
349
+ }
350
+ const midi = (0, midi_file_1.parseMidi)(uint8Array);
371
351
  this.ticksPerBeat = midi.header.ticksPerBeat;
372
352
  const midiData = this.extractMidiData(midi);
373
353
  this.instruments = midiData.instruments;
@@ -393,10 +373,10 @@ class MidyGMLite {
393
373
  return {
394
374
  currentBufferSource: null,
395
375
  isDrum: false,
396
- ...this.constructor.channelSettings,
397
376
  state: new ControllerState(),
377
+ ...this.constructor.channelSettings,
398
378
  ...this.setChannelAudioNodes(audioContext),
399
- scheduledNotes: new SparseMap(128),
379
+ scheduledNotes: [],
400
380
  sustainNotes: [],
401
381
  };
402
382
  });
@@ -431,34 +411,33 @@ class MidyGMLite {
431
411
  return audioBuffer;
432
412
  }
433
413
  }
434
- createBufferSource(voiceParams, audioBuffer) {
414
+ createBufferSource(channel, voiceParams, audioBuffer) {
435
415
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
436
416
  bufferSource.buffer = audioBuffer;
437
417
  bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
418
+ if (channel.isDrum)
419
+ bufferSource.loop = false;
438
420
  if (bufferSource.loop) {
439
421
  bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
440
422
  bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
441
423
  }
442
424
  return bufferSource;
443
425
  }
444
- async scheduleTimelineEvents(t, offset, queueIndex) {
426
+ async scheduleTimelineEvents(t, resumeTime, queueIndex) {
445
427
  while (queueIndex < this.timeline.length) {
446
428
  const event = this.timeline[queueIndex];
447
429
  if (event.startTime > t + this.lookAhead)
448
430
  break;
449
- const startTime = event.startTime + this.startDelay - offset;
431
+ const delay = this.startDelay - resumeTime;
432
+ const startTime = event.startTime + delay;
450
433
  switch (event.type) {
451
434
  case "noteOn":
452
- if (0 < event.velocity) {
453
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
454
- break;
455
- }
456
- /* falls through */
435
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
436
+ break;
457
437
  case "noteOff": {
458
438
  const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
459
- if (notePromise) {
439
+ if (notePromise)
460
440
  this.notePromises.push(notePromise);
461
- }
462
441
  break;
463
442
  }
464
443
  case "controller":
@@ -491,44 +470,53 @@ class MidyGMLite {
491
470
  this.isPaused = false;
492
471
  this.startTime = this.audioContext.currentTime;
493
472
  let queueIndex = this.getQueueIndex(this.resumeTime);
494
- let offset = this.resumeTime - this.startTime;
473
+ let resumeTime = this.resumeTime - this.startTime;
495
474
  this.notePromises = [];
496
475
  const schedulePlayback = async () => {
497
476
  if (queueIndex >= this.timeline.length) {
498
477
  await Promise.all(this.notePromises);
499
478
  this.notePromises = [];
500
479
  this.exclusiveClassNotes.fill(undefined);
480
+ this.drumExclusiveClassNotes.fill(undefined);
501
481
  this.audioBufferCache.clear();
482
+ for (let i = 0; i < this.channels.length; i++) {
483
+ this.resetAllStates(i);
484
+ }
502
485
  resolve();
503
486
  return;
504
487
  }
505
488
  const now = this.audioContext.currentTime;
506
- const t = now + offset;
507
- queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
489
+ const t = now + resumeTime;
490
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
508
491
  if (this.isPausing) {
509
492
  await this.stopNotes(0, true, now);
510
493
  this.notePromises = [];
511
- resolve();
512
494
  this.isPausing = false;
513
495
  this.isPaused = true;
496
+ resolve();
514
497
  return;
515
498
  }
516
499
  else if (this.isStopping) {
517
500
  await this.stopNotes(0, true, now);
518
501
  this.notePromises = [];
519
502
  this.exclusiveClassNotes.fill(undefined);
503
+ this.drumExclusiveClassNotes.fill(undefined);
520
504
  this.audioBufferCache.clear();
521
- resolve();
505
+ for (let i = 0; i < this.channels.length; i++) {
506
+ this.resetAllStates(i);
507
+ }
522
508
  this.isStopping = false;
523
509
  this.isPaused = false;
510
+ resolve();
524
511
  return;
525
512
  }
526
513
  else if (this.isSeeking) {
527
514
  this.stopNotes(0, true, now);
528
515
  this.exclusiveClassNotes.fill(undefined);
516
+ this.drumExclusiveClassNotes.fill(undefined);
529
517
  this.startTime = this.audioContext.currentTime;
530
518
  queueIndex = this.getQueueIndex(this.resumeTime);
531
- offset = this.resumeTime - this.startTime;
519
+ resumeTime = this.resumeTime - this.startTime;
532
520
  this.isSeeking = false;
533
521
  await schedulePlayback();
534
522
  }
@@ -551,6 +539,7 @@ class MidyGMLite {
551
539
  return `${programNumber}:${noteNumber}:${velocity}`;
552
540
  }
553
541
  extractMidiData(midi) {
542
+ this.audioBufferCounter.clear();
554
543
  const instruments = new Set();
555
544
  const timeline = [];
556
545
  const tmpChannels = new Array(this.channels.length);
@@ -619,9 +608,8 @@ class MidyGMLite {
619
608
  stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
620
609
  const channel = this.channels[channelNumber];
621
610
  const promises = [];
622
- const activeNotes = this.getActiveNotes(channel, scheduleTime);
623
- activeNotes.forEach((note) => {
624
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
611
+ this.processActiveNotes(channel, scheduleTime, (note) => {
612
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
625
613
  this.notePromises.push(promise);
626
614
  promises.push(promise);
627
615
  });
@@ -635,7 +623,7 @@ class MidyGMLite {
635
623
  this.notePromises.push(promise);
636
624
  promises.push(promise);
637
625
  });
638
- channel.scheduledNotes.clear();
626
+ channel.scheduledNotes = [];
639
627
  return Promise.all(promises);
640
628
  }
641
629
  stopNotes(velocity, force, scheduleTime) {
@@ -656,9 +644,6 @@ class MidyGMLite {
656
644
  if (!this.isPlaying)
657
645
  return;
658
646
  this.isStopping = true;
659
- for (let i = 0; i < this.channels.length; i++) {
660
- this.resetAllStates(i);
661
- }
662
647
  }
663
648
  pause() {
664
649
  if (!this.isPlaying || this.isPaused)
@@ -693,37 +678,28 @@ class MidyGMLite {
693
678
  return this.resumeTime + now - this.startTime - this.startDelay;
694
679
  }
695
680
  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;
681
+ const scheduledNotes = channel.scheduledNotes;
682
+ for (let i = 0; i < scheduledNotes.length; i++) {
683
+ const note = scheduledNotes[i];
684
+ if (!note)
685
+ continue;
686
+ if (note.ending)
687
+ continue;
688
+ callback(note);
689
+ }
716
690
  }
717
- getActiveNote(noteList, scheduleTime) {
718
- for (let i = noteList.length - 1; i >= 0; i--) {
719
- const note = noteList[i];
691
+ processActiveNotes(channel, scheduleTime, callback) {
692
+ const scheduledNotes = channel.scheduledNotes;
693
+ for (let i = 0; i < scheduledNotes.length; i++) {
694
+ const note = scheduledNotes[i];
720
695
  if (!note)
721
- return;
696
+ continue;
697
+ if (note.ending)
698
+ continue;
722
699
  if (scheduleTime < note.startTime)
723
700
  continue;
724
- return (note.ending) ? null : note;
701
+ callback(note);
725
702
  }
726
- return noteList[0];
727
703
  }
728
704
  cbToRatio(cb) {
729
705
  return Math.pow(10, cb / 200);
@@ -861,7 +837,7 @@ class MidyGMLite {
861
837
  const voiceParams = voice.getAllParams(controllerState);
862
838
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
863
839
  const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
864
- note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
840
+ note.bufferSource = this.createBufferSource(channel, voiceParams, audioBuffer);
865
841
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
866
842
  note.filterNode = new BiquadFilterNode(this.audioContext, {
867
843
  type: "lowpass",
@@ -870,6 +846,7 @@ class MidyGMLite {
870
846
  this.setVolumeEnvelope(note, now);
871
847
  this.setFilterEnvelope(note, now);
872
848
  this.setPitchEnvelope(note, now);
849
+ this.updateDetune(channel, note, now);
873
850
  if (0 < state.modulationDepth) {
874
851
  this.startModulation(channel, note, now);
875
852
  }
@@ -907,7 +884,7 @@ class MidyGMLite {
907
884
  }
908
885
  this.drumExclusiveClassNotes[index] = note;
909
886
  }
910
- async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
887
+ async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, noteOffEvent) {
911
888
  const channel = this.channels[channelNumber];
912
889
  const bankNumber = channel.bank;
913
890
  const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
@@ -919,6 +896,7 @@ class MidyGMLite {
919
896
  return;
920
897
  const isSF3 = soundFont.parsed.info.version.major === 3;
921
898
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
899
+ note.noteOffEvent = noteOffEvent;
922
900
  note.volumeEnvelopeNode.connect(channel.gainL);
923
901
  note.volumeEnvelopeNode.connect(channel.gainR);
924
902
  if (0.5 <= channel.state.sustainPedal) {
@@ -927,31 +905,12 @@ class MidyGMLite {
927
905
  this.handleExclusiveClass(note, channelNumber, startTime);
928
906
  this.handleDrumExclusiveClass(note, channelNumber, startTime);
929
907
  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
- }
938
- if (channel.isDrum) {
939
- const stopTime = startTime + note.bufferSource.buffer.duration;
940
- const index = noteList.length - 1;
941
- const promise = new Promise((resolve) => {
942
- note.bufferSource.onended = () => {
943
- noteList[index] = undefined;
944
- this.disconnectNote(note);
945
- resolve();
946
- };
947
- note.bufferSource.stop(stopTime);
948
- });
949
- this.notePromises.push(promise);
950
- }
908
+ note.index = scheduledNotes.length;
909
+ scheduledNotes.push(note);
951
910
  }
952
911
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
953
912
  scheduleTime ??= this.audioContext.currentTime;
954
- return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
913
+ return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, undefined);
955
914
  }
956
915
  disconnectNote(note) {
957
916
  note.bufferSource.disconnect();
@@ -963,57 +922,54 @@ class MidyGMLite {
963
922
  note.modulationLFO.stop();
964
923
  }
965
924
  }
966
- stopNote(endTime, stopTime, noteList, index) {
967
- const note = noteList[index];
925
+ releaseNote(channel, note, endTime) {
926
+ const volRelease = endTime + note.voiceParams.volRelease;
927
+ const modRelease = endTime + note.voiceParams.modRelease;
928
+ const stopTime = Math.min(volRelease, modRelease);
929
+ note.filterNode.frequency
930
+ .cancelScheduledValues(endTime)
931
+ .linearRampToValueAtTime(0, modRelease);
968
932
  note.volumeEnvelopeNode.gain
969
933
  .cancelScheduledValues(endTime)
970
- .linearRampToValueAtTime(0, stopTime);
971
- note.ending = true;
972
- this.scheduleTask(() => {
973
- note.bufferSource.loop = false;
974
- }, stopTime);
934
+ .linearRampToValueAtTime(0, volRelease);
975
935
  return new Promise((resolve) => {
976
- note.bufferSource.onended = () => {
977
- noteList[index] = undefined;
936
+ this.scheduleTask(() => {
937
+ const bufferSource = note.bufferSource;
938
+ bufferSource.loop = false;
939
+ bufferSource.stop(stopTime);
978
940
  this.disconnectNote(note);
941
+ channel.scheduledNotes[note.index] = undefined;
979
942
  resolve();
980
- };
981
- note.bufferSource.stop(stopTime);
943
+ }, stopTime);
982
944
  });
983
945
  }
984
- findNoteOffTarget(noteList) {
985
- for (let i = 0; i < noteList.length; i++) {
986
- const note = noteList[i];
946
+ scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
947
+ const channel = this.channels[channelNumber];
948
+ if (!force) {
949
+ if (channel.isDrum)
950
+ return;
951
+ if (0.5 <= channel.state.sustainPedal)
952
+ return;
953
+ }
954
+ const note = this.findNoteOffTarget(channel, noteNumber);
955
+ if (!note)
956
+ return;
957
+ note.ending = true;
958
+ this.releaseNote(channel, note, endTime);
959
+ }
960
+ findNoteOffTarget(channel, noteNumber) {
961
+ const scheduledNotes = channel.scheduledNotes;
962
+ for (let i = 0; i < scheduledNotes.length; i++) {
963
+ const note = scheduledNotes[i];
987
964
  if (!note)
988
965
  continue;
989
966
  if (note.ending)
990
967
  continue;
991
- return [note, i];
968
+ if (note.noteNumber !== noteNumber)
969
+ continue;
970
+ return note;
992
971
  }
993
972
  }
994
- scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
995
- const channel = this.channels[channelNumber];
996
- if (channel.isDrum)
997
- return;
998
- if (!force && 0.5 <= channel.state.sustainPedal)
999
- 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
- const volRelease = endTime + note.voiceParams.volRelease;
1010
- const modRelease = endTime + note.voiceParams.modRelease;
1011
- note.filterNode.frequency
1012
- .cancelScheduledValues(endTime)
1013
- .linearRampToValueAtTime(0, modRelease);
1014
- const stopTime = Math.min(volRelease, modRelease);
1015
- return this.stopNote(endTime, stopTime, noteList, i);
1016
- }
1017
973
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
1018
974
  scheduleTime ??= this.audioContext.currentTime;
1019
975
  return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
@@ -1023,7 +979,7 @@ class MidyGMLite {
1023
979
  const channel = this.channels[channelNumber];
1024
980
  const promises = [];
1025
981
  for (let i = 0; i < channel.sustainNotes.length; i++) {
1026
- const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
982
+ const promise = this.scheduleNoteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
1027
983
  promises.push(promise);
1028
984
  }
1029
985
  channel.sustainNotes = [];
@@ -1179,20 +1135,20 @@ class MidyGMLite {
1179
1135
  });
1180
1136
  }
1181
1137
  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
- };
1138
+ const handlers = new Array(128);
1139
+ handlers[1] = this.setModulationDepth;
1140
+ handlers[6] = this.dataEntryMSB;
1141
+ handlers[7] = this.setVolume;
1142
+ handlers[10] = this.setPan;
1143
+ handlers[11] = this.setExpression;
1144
+ handlers[38] = this.dataEntryLSB;
1145
+ handlers[64] = this.setSustainPedal;
1146
+ handlers[100] = this.setRPNLSB;
1147
+ handlers[101] = this.setRPNMSB;
1148
+ handlers[120] = this.allSoundOff;
1149
+ handlers[121] = this.resetAllControllers;
1150
+ handlers[123] = this.allNotesOff;
1151
+ return handlers;
1196
1152
  }
1197
1153
  handleControlChange(channelNumber, controllerType, value, scheduleTime) {
1198
1154
  const handler = this.controlChangeHandlers[controllerType];
@@ -1337,19 +1293,26 @@ class MidyGMLite {
1337
1293
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1338
1294
  }
1339
1295
  resetAllStates(channelNumber) {
1296
+ const scheduleTime = this.audioContext.currentTime;
1340
1297
  const channel = this.channels[channelNumber];
1341
1298
  const state = channel.state;
1342
- for (const type of Object.keys(defaultControllerState)) {
1343
- state[type] = defaultControllerState[type].defaultValue;
1299
+ const entries = Object.entries(defaultControllerState);
1300
+ for (const [key, { type, defaultValue }] of entries) {
1301
+ if (128 <= type) {
1302
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1303
+ }
1304
+ else {
1305
+ state[key] = defaultValue;
1306
+ }
1344
1307
  }
1345
- for (const type of Object.keys(this.constructor.channelSettings)) {
1346
- channel[type] = this.constructor.channelSettings[type];
1308
+ for (const key of Object.keys(this.constructor.channelSettings)) {
1309
+ channel[key] = this.constructor.channelSettings[key];
1347
1310
  }
1348
1311
  this.mode = "GM1";
1349
1312
  }
1350
1313
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1351
- resetAllControllers(channelNumber) {
1352
- const stateTypes = [
1314
+ resetAllControllers(channelNumber, _value, scheduleTime) {
1315
+ const keys = [
1353
1316
  "pitchWheel",
1354
1317
  "expression",
1355
1318
  "modulationDepth",
@@ -1357,10 +1320,17 @@ class MidyGMLite {
1357
1320
  ];
1358
1321
  const channel = this.channels[channelNumber];
1359
1322
  const state = channel.state;
1360
- for (let i = 0; i < stateTypes.length; i++) {
1361
- const type = stateTypes[i];
1362
- state[type] = defaultControllerState[type].defaultValue;
1323
+ for (let i = 0; i < keys.length; i++) {
1324
+ const key = keys[i];
1325
+ const { type, defaultValue } = defaultControllerState[key];
1326
+ if (128 <= type) {
1327
+ this.handleControlChange(channelNumber, type - 128, Math.ceil(defaultValue * 127), scheduleTime);
1328
+ }
1329
+ else {
1330
+ state[key] = defaultValue;
1331
+ }
1363
1332
  }
1333
+ this.setPitchBend(channelNumber, 8192, scheduleTime);
1364
1334
  const settingTypes = [
1365
1335
  "rpnMSB",
1366
1336
  "rpnLSB",