@marmooo/midy 0.3.6 → 0.3.8

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.
@@ -203,7 +203,7 @@ class MidyGM1 {
203
203
  enumerable: true,
204
204
  configurable: true,
205
205
  writable: true,
206
- value: this.initSoundFontTable()
206
+ value: Array.from({ length: 128 }, () => [])
207
207
  });
208
208
  Object.defineProperty(this, "voiceCounter", {
209
209
  enumerable: true,
@@ -247,6 +247,12 @@ class MidyGM1 {
247
247
  writable: true,
248
248
  value: false
249
249
  });
250
+ Object.defineProperty(this, "playPromise", {
251
+ enumerable: true,
252
+ configurable: true,
253
+ writable: true,
254
+ value: void 0
255
+ });
250
256
  Object.defineProperty(this, "timeline", {
251
257
  enumerable: true,
252
258
  configurable: true,
@@ -278,6 +284,7 @@ class MidyGM1 {
278
284
  length: 1,
279
285
  sampleRate: audioContext.sampleRate,
280
286
  });
287
+ this.messageHandlers = this.createMessageHandlers();
281
288
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
282
289
  this.controlChangeHandlers = this.createControlChangeHandlers();
283
290
  this.channels = this.createChannels(audioContext);
@@ -285,21 +292,14 @@ class MidyGM1 {
285
292
  this.scheduler.connect(audioContext.destination);
286
293
  this.GM1SystemOn();
287
294
  }
288
- initSoundFontTable() {
289
- const table = new Array(128);
290
- for (let i = 0; i < 128; i++) {
291
- table[i] = new Map();
292
- }
293
- return table;
294
- }
295
295
  addSoundFont(soundFont) {
296
296
  const index = this.soundFonts.length;
297
297
  this.soundFonts.push(soundFont);
298
298
  const presetHeaders = soundFont.parsed.presetHeaders;
299
+ const soundFontTable = this.soundFontTable;
299
300
  for (let i = 0; i < presetHeaders.length; i++) {
300
- const presetHeader = presetHeaders[i];
301
- const banks = this.soundFontTable[presetHeader.preset];
302
- banks.set(presetHeader.bank, index);
301
+ const { preset, bank } = presetHeaders[i];
302
+ soundFontTable[preset][bank] = index;
303
303
  }
304
304
  }
305
305
  async toUint8Array(input) {
@@ -377,13 +377,16 @@ class MidyGM1 {
377
377
  this.GM1SystemOn();
378
378
  }
379
379
  getVoiceId(channel, noteNumber, velocity) {
380
- const bankNumber = this.calcBank(channel);
381
- const soundFontIndex = this.soundFontTable[channel.programNumber]
382
- .get(bankNumber);
380
+ const programNumber = channel.programNumber;
381
+ const bankTable = this.soundFontTable[programNumber];
382
+ if (!bankTable)
383
+ return;
384
+ const bank = channel.isDrum ? 128 : 0;
385
+ const soundFontIndex = bankTable[bank];
383
386
  if (soundFontIndex === undefined)
384
387
  return;
385
388
  const soundFont = this.soundFonts[soundFontIndex];
386
- const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
389
+ const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
387
390
  const { instrument, sampleID } = voice.generators;
388
391
  return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
389
392
  }
@@ -432,13 +435,16 @@ class MidyGM1 {
432
435
  }
433
436
  return bufferSource;
434
437
  }
435
- async scheduleTimelineEvents(t, resumeTime, queueIndex) {
436
- while (queueIndex < this.timeline.length) {
437
- const event = this.timeline[queueIndex];
438
- if (event.startTime > t + this.lookAhead)
438
+ async scheduleTimelineEvents(scheduleTime, queueIndex) {
439
+ const timeOffset = this.resumeTime - this.startTime;
440
+ const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
441
+ const schedulingOffset = this.startDelay - timeOffset;
442
+ const timeline = this.timeline;
443
+ while (queueIndex < timeline.length) {
444
+ const event = timeline[queueIndex];
445
+ if (lookAheadCheckTime < event.startTime)
439
446
  break;
440
- const delay = this.startDelay - resumeTime;
441
- const startTime = event.startTime + delay;
447
+ const startTime = event.startTime + schedulingOffset;
442
448
  switch (event.type) {
443
449
  case "noteOn":
444
450
  await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
@@ -473,69 +479,77 @@ class MidyGM1 {
473
479
  }
474
480
  return 0;
475
481
  }
476
- playNotes() {
477
- return new Promise((resolve) => {
478
- this.isPlaying = true;
479
- this.isPaused = false;
480
- this.startTime = this.audioContext.currentTime;
481
- let queueIndex = this.getQueueIndex(this.resumeTime);
482
- let resumeTime = this.resumeTime - this.startTime;
482
+ resetAllStates() {
483
+ this.exclusiveClassNotes.fill(undefined);
484
+ this.drumExclusiveClassNotes.fill(undefined);
485
+ this.voiceCache.clear();
486
+ for (let i = 0; i < this.channels.length; i++) {
487
+ this.channels[i].scheduledNotes = [];
488
+ this.resetChannelStates(i);
489
+ }
490
+ }
491
+ updateStates(queueIndex, nextQueueIndex) {
492
+ if (nextQueueIndex < queueIndex)
493
+ queueIndex = 0;
494
+ for (let i = queueIndex; i < nextQueueIndex; i++) {
495
+ const event = this.timeline[i];
496
+ switch (event.type) {
497
+ case "controller":
498
+ this.setControlChange(event.channel, event.controllerType, event.value, 0);
499
+ break;
500
+ case "programChange":
501
+ this.setProgramChange(event.channel, event.programNumber, 0);
502
+ break;
503
+ case "pitchBend":
504
+ this.setPitchBend(event.channel, event.value + 8192, 0);
505
+ break;
506
+ case "sysEx":
507
+ this.handleSysEx(event.data, 0);
508
+ }
509
+ }
510
+ }
511
+ async playNotes() {
512
+ if (this.audioContext.state === "suspended") {
513
+ await this.audioContext.resume();
514
+ }
515
+ this.isPlaying = true;
516
+ this.isPaused = false;
517
+ this.startTime = this.audioContext.currentTime;
518
+ let queueIndex = this.getQueueIndex(this.resumeTime);
519
+ let finished = false;
520
+ this.notePromises = [];
521
+ while (queueIndex < this.timeline.length) {
522
+ const now = this.audioContext.currentTime;
523
+ queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
524
+ if (this.isPausing) {
525
+ await this.stopNotes(0, true, now);
526
+ await this.audioContext.suspend();
527
+ this.notePromises = [];
528
+ break;
529
+ }
530
+ else if (this.isStopping) {
531
+ await this.stopNotes(0, true, now);
532
+ await this.audioContext.suspend();
533
+ finished = true;
534
+ break;
535
+ }
536
+ else if (this.isSeeking) {
537
+ await this.stopNotes(0, true, now);
538
+ this.startTime = this.audioContext.currentTime;
539
+ const nextQueueIndex = this.getQueueIndex(this.resumeTime);
540
+ this.updateStates(queueIndex, nextQueueIndex);
541
+ queueIndex = nextQueueIndex;
542
+ this.isSeeking = false;
543
+ continue;
544
+ }
545
+ const waitTime = now + this.noteCheckInterval;
546
+ await this.scheduleTask(() => { }, waitTime);
547
+ }
548
+ if (finished) {
483
549
  this.notePromises = [];
484
- const schedulePlayback = async () => {
485
- if (queueIndex >= this.timeline.length) {
486
- await Promise.all(this.notePromises);
487
- this.notePromises = [];
488
- this.exclusiveClassNotes.fill(undefined);
489
- this.voiceCache.clear();
490
- for (let i = 0; i < this.channels.length; i++) {
491
- this.channels[i].scheduledNotes = [];
492
- this.resetAllStates(i);
493
- }
494
- resolve();
495
- return;
496
- }
497
- const now = this.audioContext.currentTime;
498
- const t = now + resumeTime;
499
- queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
500
- if (this.isPausing) {
501
- await this.stopNotes(0, true, now);
502
- this.notePromises = [];
503
- this.isPausing = false;
504
- this.isPaused = true;
505
- resolve();
506
- return;
507
- }
508
- else if (this.isStopping) {
509
- await this.stopNotes(0, true, now);
510
- this.notePromises = [];
511
- this.exclusiveClassNotes.fill(undefined);
512
- this.voiceCache.clear();
513
- for (let i = 0; i < this.channels.length; i++) {
514
- this.channels[i].scheduledNotes = [];
515
- this.resetAllStates(i);
516
- }
517
- this.isStopping = false;
518
- this.isPaused = false;
519
- resolve();
520
- return;
521
- }
522
- else if (this.isSeeking) {
523
- this.stopNotes(0, true, now);
524
- this.exclusiveClassNotes.fill(undefined);
525
- this.startTime = this.audioContext.currentTime;
526
- queueIndex = this.getQueueIndex(this.resumeTime);
527
- resumeTime = this.resumeTime - this.startTime;
528
- this.isSeeking = false;
529
- await schedulePlayback();
530
- }
531
- else {
532
- const waitTime = now + this.noteCheckInterval;
533
- await this.scheduleTask(() => { }, waitTime);
534
- await schedulePlayback();
535
- }
536
- };
537
- schedulePlayback();
538
- });
550
+ this.resetAllStates();
551
+ }
552
+ this.isPlaying = false;
539
553
  }
540
554
  ticksToSecond(ticks, secondsPerBeat) {
541
555
  return ticks * secondsPerBeat / this.ticksPerBeat;
@@ -543,16 +557,16 @@ class MidyGM1 {
543
557
  secondToTicks(second, secondsPerBeat) {
544
558
  return second * this.ticksPerBeat / secondsPerBeat;
545
559
  }
560
+ getSoundFontId(channel) {
561
+ const programNumber = channel.programNumber;
562
+ const bank = channel.isDrum ? "128" : "000";
563
+ const program = programNumber.toString().padStart(3, "0");
564
+ return `${bank}:${program}`;
565
+ }
546
566
  extractMidiData(midi) {
547
567
  const instruments = new Set();
548
568
  const timeline = [];
549
- const tmpChannels = new Array(this.channels.length);
550
- for (let i = 0; i < tmpChannels.length; i++) {
551
- tmpChannels[i] = {
552
- programNumber: -1,
553
- bank: this.channels[i].bank,
554
- };
555
- }
569
+ const channels = this.channels;
556
570
  for (let i = 0; i < midi.tracks.length; i++) {
557
571
  const track = midi.tracks[i];
558
572
  let currentTicks = 0;
@@ -562,17 +576,15 @@ class MidyGM1 {
562
576
  event.ticks = currentTicks;
563
577
  switch (event.type) {
564
578
  case "noteOn": {
565
- const channel = tmpChannels[event.channel];
566
- if (channel.programNumber < 0) {
567
- instruments.add(`${channel.bank}:0`);
568
- channel.programNumber = 0;
569
- }
579
+ const channel = channels[event.channel];
580
+ instruments.add(this.getSoundFontId(channel));
570
581
  break;
571
582
  }
572
583
  case "programChange": {
573
- const channel = tmpChannels[event.channel];
574
- channel.programNumber = event.programNumber;
575
- instruments.add(`${channel.bankNumber}:${channel.programNumber}`);
584
+ const channel = channels[event.channel];
585
+ this.setProgramChange(event.channel, event.programNumber);
586
+ instruments.add(this.getSoundFontId(channel));
587
+ break;
576
588
  }
577
589
  }
578
590
  delete event.deltaTime;
@@ -636,26 +648,32 @@ class MidyGM1 {
636
648
  this.resumeTime = 0;
637
649
  if (this.voiceCounter.size === 0)
638
650
  this.cacheVoiceIds();
639
- await this.playNotes();
640
- this.isPlaying = false;
651
+ this.playPromise = this.playNotes();
652
+ await this.playPromise;
641
653
  }
642
- stop() {
654
+ async stop() {
643
655
  if (!this.isPlaying)
644
656
  return;
645
657
  this.isStopping = true;
658
+ await this.playPromise;
659
+ this.isStopping = false;
646
660
  }
647
- pause() {
661
+ async pause() {
648
662
  if (!this.isPlaying || this.isPaused)
649
663
  return;
650
664
  const now = this.audioContext.currentTime;
651
665
  this.resumeTime += now - this.startTime - this.startDelay;
652
666
  this.isPausing = true;
667
+ await this.playPromise;
668
+ this.isPausing = false;
669
+ this.isPaused = true;
653
670
  }
654
671
  async resume() {
655
672
  if (!this.isPaused)
656
673
  return;
657
- await this.playNotes();
658
- this.isPlaying = false;
674
+ this.playPromise = this.playNotes();
675
+ await this.playPromise;
676
+ this.isPaused = false;
659
677
  }
660
678
  seekTo(second) {
661
679
  this.resumeTime = second;
@@ -872,13 +890,16 @@ class MidyGM1 {
872
890
  }
873
891
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
874
892
  const channel = this.channels[channelNumber];
875
- const bankNumber = channel.bank;
876
- const soundFontIndex = this.soundFontTable[channel.programNumber]
877
- .get(bankNumber);
893
+ const programNumber = channel.programNumber;
894
+ const bankTable = this.soundFontTable[programNumber];
895
+ if (!bankTable)
896
+ return;
897
+ const bank = channel.isDrum ? 128 : 0;
898
+ const soundFontIndex = bankTable[bank];
878
899
  if (soundFontIndex === undefined)
879
900
  return;
880
901
  const soundFont = this.soundFonts[soundFontIndex];
881
- const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
902
+ const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
882
903
  if (!voice)
883
904
  return;
884
905
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
@@ -980,7 +1001,26 @@ class MidyGM1 {
980
1001
  channel.sustainNotes = [];
981
1002
  return promises;
982
1003
  }
983
- handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
1004
+ createMessageHandlers() {
1005
+ const handlers = new Array(256);
1006
+ // Channel Message
1007
+ handlers[0x80] = (data, scheduleTime) => this.noteOff(data[0] & 0x0F, data[1], data[2], scheduleTime);
1008
+ handlers[0x90] = (data, scheduleTime) => this.noteOn(data[0] & 0x0F, data[1], data[2], scheduleTime);
1009
+ handlers[0xB0] = (data, scheduleTime) => this.setControlChange(data[0] & 0x0F, data[1], data[2], scheduleTime);
1010
+ handlers[0xC0] = (data, scheduleTime) => this.setProgramChange(data[0] & 0x0F, data[1], scheduleTime);
1011
+ handlers[0xE0] = (data, scheduleTime) => this.handlePitchBendMessage(data[0] & 0x0F, data[1], data[2], scheduleTime);
1012
+ return handlers;
1013
+ }
1014
+ handleMessage(data, scheduleTime) {
1015
+ const status = data[0];
1016
+ if (status === 0xF0) {
1017
+ return this.handleSysEx(data.subarray(1), scheduleTime);
1018
+ }
1019
+ const handler = this.messageHandlers[status];
1020
+ if (handler)
1021
+ handler(data, scheduleTime);
1022
+ }
1023
+ handleChannelMessage(statusByte, data1, data2, scheduleTime) {
984
1024
  const channelNumber = statusByte & 0x0F;
985
1025
  const messageType = statusByte & 0xF0;
986
1026
  switch (messageType) {
@@ -1060,28 +1100,36 @@ class MidyGM1 {
1060
1100
  }
1061
1101
  createVoiceParamsHandlers() {
1062
1102
  return {
1063
- modLfoToPitch: (channel, note, _prevValue, scheduleTime) => {
1103
+ modLfoToPitch: (channel, note, scheduleTime) => {
1064
1104
  if (0 < channel.state.modulationDepth) {
1065
1105
  this.setModLfoToPitch(channel, note, scheduleTime);
1066
1106
  }
1067
1107
  },
1068
- vibLfoToPitch: (_channel, _note, _prevValue, _scheduleTime) => { },
1069
- modLfoToFilterFc: (channel, note, _prevValue, scheduleTime) => {
1108
+ vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
1109
+ modLfoToFilterFc: (channel, note, scheduleTime) => {
1070
1110
  if (0 < channel.state.modulationDepth) {
1071
1111
  this.setModLfoToFilterFc(note, scheduleTime);
1072
1112
  }
1073
1113
  },
1074
- modLfoToVolume: (channel, note, _prevValue, scheduleTime) => {
1114
+ modLfoToVolume: (channel, note, scheduleTime) => {
1075
1115
  if (0 < channel.state.modulationDepth) {
1076
1116
  this.setModLfoToVolume(note, scheduleTime);
1077
1117
  }
1078
1118
  },
1079
- chorusEffectsSend: (_channel, _note, _prevValue, _scheduleTime) => { },
1080
- reverbEffectsSend: (_channel, _note, _prevValue, _scheduleTime) => { },
1081
- delayModLFO: (_channel, note, _prevValue, scheduleTime) => this.setDelayModLFO(note, scheduleTime),
1082
- freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
1083
- delayVibLFO: (_channel, _note, _prevValue, _scheduleTime) => { },
1084
- freqVibLFO: (_channel, _note, _prevValue, _scheduleTime) => { },
1119
+ chorusEffectsSend: (_channel, _note, _scheduleTime) => { },
1120
+ reverbEffectsSend: (_channel, _note, _scheduleTime) => { },
1121
+ delayModLFO: (_channel, note, scheduleTime) => {
1122
+ if (0 < channel.state.modulationDepth) {
1123
+ this.setDelayModLFO(note, scheduleTime);
1124
+ }
1125
+ },
1126
+ freqModLFO: (_channel, note, scheduleTime) => {
1127
+ if (0 < channel.state.modulationDepth) {
1128
+ this.setFreqModLFO(note, scheduleTime);
1129
+ }
1130
+ },
1131
+ delayVibLFO: (_channel, _note, _scheduleTime) => { },
1132
+ freqVibLFO: (_channel, _note, _scheduleTime) => { },
1085
1133
  };
1086
1134
  }
1087
1135
  getControllerState(channel, noteNumber, velocity) {
@@ -1104,7 +1152,7 @@ class MidyGM1 {
1104
1152
  continue;
1105
1153
  note.voiceParams[key] = value;
1106
1154
  if (key in this.voiceParamsHandlers) {
1107
- this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1155
+ this.voiceParamsHandlers[key](channel, note, scheduleTime);
1108
1156
  }
1109
1157
  else {
1110
1158
  if (volumeEnvelopeKeySet.has(key))
@@ -1275,8 +1323,8 @@ class MidyGM1 {
1275
1323
  }
1276
1324
  handlePitchBendRangeRPN(channelNumber, scheduleTime) {
1277
1325
  const channel = this.channels[channelNumber];
1278
- this.limitData(channel, 0, 127, 0, 99);
1279
- const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1326
+ this.limitData(channel, 0, 127, 0, 127);
1327
+ const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
1280
1328
  this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
1281
1329
  }
1282
1330
  setPitchBendRange(channelNumber, value, scheduleTime) {
@@ -1284,7 +1332,7 @@ class MidyGM1 {
1284
1332
  scheduleTime ??= this.audioContext.currentTime;
1285
1333
  const state = channel.state;
1286
1334
  const prev = state.pitchWheelSensitivity;
1287
- const next = value / 128;
1335
+ const next = value / 12800;
1288
1336
  state.pitchWheelSensitivity = next;
1289
1337
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
1290
1338
  this.updateChannelDetune(channel, scheduleTime);
@@ -1293,14 +1341,15 @@ class MidyGM1 {
1293
1341
  handleFineTuningRPN(channelNumber, scheduleTime) {
1294
1342
  const channel = this.channels[channelNumber];
1295
1343
  this.limitData(channel, 0, 127, 0, 127);
1296
- const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
1344
+ const value = channel.dataMSB * 128 + channel.dataLSB;
1345
+ const fineTuning = (value - 8192) / 8192 * 100;
1297
1346
  this.setFineTuning(channelNumber, fineTuning, scheduleTime);
1298
1347
  }
1299
1348
  setFineTuning(channelNumber, value, scheduleTime) {
1300
1349
  const channel = this.channels[channelNumber];
1301
1350
  scheduleTime ??= this.audioContext.currentTime;
1302
1351
  const prev = channel.fineTuning;
1303
- const next = (value - 8192) / 8.192; // cent
1352
+ const next = value;
1304
1353
  channel.fineTuning = next;
1305
1354
  channel.detune += next - prev;
1306
1355
  this.updateChannelDetune(channel, scheduleTime);
@@ -1308,14 +1357,14 @@ class MidyGM1 {
1308
1357
  handleCoarseTuningRPN(channelNumber, scheduleTime) {
1309
1358
  const channel = this.channels[channelNumber];
1310
1359
  this.limitDataMSB(channel, 0, 127);
1311
- const coarseTuning = channel.dataMSB;
1360
+ const coarseTuning = (channel.dataMSB - 64) * 100;
1312
1361
  this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
1313
1362
  }
1314
1363
  setCoarseTuning(channelNumber, value, scheduleTime) {
1315
1364
  const channel = this.channels[channelNumber];
1316
1365
  scheduleTime ??= this.audioContext.currentTime;
1317
1366
  const prev = channel.coarseTuning;
1318
- const next = (value - 64) * 100; // cent
1367
+ const next = value;
1319
1368
  channel.coarseTuning = next;
1320
1369
  channel.detune += next - prev;
1321
1370
  this.updateChannelDetune(channel, scheduleTime);
@@ -1324,7 +1373,7 @@ class MidyGM1 {
1324
1373
  scheduleTime ??= this.audioContext.currentTime;
1325
1374
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1326
1375
  }
1327
- resetAllStates(channelNumber) {
1376
+ resetChannelStates(channelNumber) {
1328
1377
  const scheduleTime = this.audioContext.currentTime;
1329
1378
  const channel = this.channels[channelNumber];
1330
1379
  const state = channel.state;
@@ -1399,10 +1448,8 @@ class MidyGM1 {
1399
1448
  for (let i = 0; i < this.channels.length; i++) {
1400
1449
  this.allSoundOff(i, 0, scheduleTime);
1401
1450
  const channel = this.channels[i];
1402
- channel.bank = 0;
1403
1451
  channel.isDrum = false;
1404
1452
  }
1405
- this.channels[9].bank = 128;
1406
1453
  this.channels[9].isDrum = true;
1407
1454
  }
1408
1455
  handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
@@ -1423,16 +1470,11 @@ class MidyGM1 {
1423
1470
  const volume = (data[5] * 128 + data[4]) / 16383;
1424
1471
  this.setMasterVolume(volume, scheduleTime);
1425
1472
  }
1426
- setMasterVolume(volume, scheduleTime) {
1473
+ setMasterVolume(value, scheduleTime) {
1427
1474
  scheduleTime ??= this.audioContext.currentTime;
1428
- if (volume < 0 && 1 < volume) {
1429
- console.error("Master Volume is out of range");
1430
- }
1431
- else {
1432
- this.masterVolume.gain
1433
- .cancelScheduledValues(scheduleTime)
1434
- .setValueAtTime(volume * volume, scheduleTime);
1435
- }
1475
+ this.masterVolume.gain
1476
+ .cancelScheduledValues(scheduleTime)
1477
+ .setValueAtTime(value * value, scheduleTime);
1436
1478
  }
1437
1479
  handleSysEx(data, scheduleTime) {
1438
1480
  switch (data[0]) {
@@ -1470,15 +1512,15 @@ Object.defineProperty(MidyGM1, "channelSettings", {
1470
1512
  configurable: true,
1471
1513
  writable: true,
1472
1514
  value: {
1515
+ scheduleIndex: 0,
1473
1516
  detune: 0,
1474
1517
  programNumber: 0,
1475
- bank: 0,
1476
1518
  dataMSB: 0,
1477
1519
  dataLSB: 0,
1478
1520
  rpnMSB: 127,
1479
1521
  rpnLSB: 127,
1480
1522
  modulationDepthRange: 50, // cent
1481
- fineTuning: 0, // cb
1482
- coarseTuning: 0, // cb
1523
+ fineTuning: 0, // cent
1524
+ coarseTuning: 0, // cent
1483
1525
  }
1484
1526
  });