@marmooo/midy 0.0.2 → 0.0.4

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/script/midy.js CHANGED
@@ -11,12 +11,6 @@ class Midy {
11
11
  writable: true,
12
12
  value: 120
13
13
  });
14
- Object.defineProperty(this, "secondsPerBeat", {
15
- enumerable: true,
16
- configurable: true,
17
- writable: true,
18
- value: 0.5
19
- });
20
14
  Object.defineProperty(this, "totalTime", {
21
15
  enumerable: true,
22
16
  configurable: true,
@@ -177,10 +171,10 @@ class Midy {
177
171
  const response = await fetch(midiUrl);
178
172
  const arrayBuffer = await response.arrayBuffer();
179
173
  const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
174
+ this.ticksPerBeat = midi.header.ticksPerBeat;
180
175
  const midiData = this.extractMidiData(midi);
181
176
  this.instruments = midiData.instruments;
182
177
  this.timeline = midiData.timeline;
183
- this.ticksPerBeat = midi.header.ticksPerBeat;
184
178
  this.totalTime = this.calcTotalTime();
185
179
  }
186
180
  setChannelAudioNodes(audioContext) {
@@ -215,6 +209,12 @@ class Midy {
215
209
  ...this.setChannelAudioNodes(audioContext),
216
210
  scheduledNotes: new Map(),
217
211
  sostenutoNotes: new Map(),
212
+ polyphonicKeyPressure: {
213
+ ...Midy.controllerDestinationSettings,
214
+ },
215
+ channelPressure: {
216
+ ...Midy.controllerDestinationSettings,
217
+ },
218
218
  };
219
219
  });
220
220
  return channels;
@@ -266,28 +266,36 @@ class Midy {
266
266
  async scheduleTimelineEvents(t, offset, queueIndex) {
267
267
  while (queueIndex < this.timeline.length) {
268
268
  const event = this.timeline[queueIndex];
269
- const time = this.ticksToSecond(event.ticks, this.secondsPerBeat);
270
- if (time > t + this.lookAhead)
269
+ if (event.startTime > t + this.lookAhead)
271
270
  break;
272
271
  switch (event.type) {
273
- case "controller":
274
- this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
275
- break;
276
272
  case "noteOn":
277
- await this.scheduleNoteOn(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, time + this.startDelay - offset);
278
- break;
273
+ if (event.velocity !== 0) {
274
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
275
+ break;
276
+ }
277
+ /* falls through */
279
278
  case "noteOff": {
280
- const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, time + this.startDelay - offset);
279
+ const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
281
280
  if (notePromise) {
282
281
  this.notePromises.push(notePromise);
283
282
  }
284
283
  break;
285
284
  }
285
+ case "noteAftertouch":
286
+ this.handlePolyphonicKeyPressure(event.channel, event.noteNumber, event.amount);
287
+ break;
288
+ case "controller":
289
+ this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
290
+ break;
286
291
  case "programChange":
287
292
  this.handleProgramChange(event.channel, event.programNumber);
288
293
  break;
289
- case "setTempo":
290
- this.secondsPerBeat = event.microsecondsPerBeat / 1000000;
294
+ case "channelAftertouch":
295
+ this.handleChannelPressure(event.channel, event.amount);
296
+ break;
297
+ case "pitchBend":
298
+ this.handlePitchBend(event.channel, event.value);
291
299
  break;
292
300
  case "sysEx":
293
301
  this.handleSysEx(event.data);
@@ -297,9 +305,8 @@ class Midy {
297
305
  return queueIndex;
298
306
  }
299
307
  getQueueIndex(second) {
300
- const ticks = this.secondToTicks(second, this.secondsPerBeat);
301
308
  for (let i = 0; i < this.timeline.length; i++) {
302
- if (ticks <= this.timeline[i].ticks) {
309
+ if (second <= this.timeline[i].startTime) {
303
310
  return i;
304
311
  }
305
312
  }
@@ -441,18 +448,28 @@ class Midy {
441
448
  timeline.push(event);
442
449
  });
443
450
  });
451
+ const priority = {
452
+ setTempo: 0,
453
+ controller: 1,
454
+ };
444
455
  timeline.sort((a, b) => {
445
- if (a.ticks !== b.ticks) {
456
+ if (a.ticks !== b.ticks)
446
457
  return a.ticks - b.ticks;
447
- }
448
- if (a.type !== "controller" && b.type === "controller") {
449
- return -1;
450
- }
451
- if (a.type === "controller" && b.type !== "controller") {
452
- return 1;
453
- }
454
- return 0;
458
+ return (priority[a.type] || 2) - (priority[b.type] || 2);
455
459
  });
460
+ let prevTempoTime = 0;
461
+ let prevTempoTicks = 0;
462
+ let secondsPerBeat = 0.5;
463
+ for (let i = 0; i < timeline.length; i++) {
464
+ const event = timeline[i];
465
+ const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
466
+ event.startTime = prevTempoTime + timeFromPrevTempo;
467
+ if (event.type === "setTempo") {
468
+ prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
469
+ secondsPerBeat = event.microsecondsPerBeat / 1000000;
470
+ prevTempoTicks = event.ticks;
471
+ }
472
+ }
456
473
  return { instruments, timeline };
457
474
  }
458
475
  stopNotes() {
@@ -504,32 +521,12 @@ class Midy {
504
521
  }
505
522
  }
506
523
  calcTotalTime() {
507
- const endOfTracks = [];
508
- let prevTicks = 0;
509
524
  let totalTime = 0;
510
- let secondsPerBeat = 0.5;
511
525
  for (let i = 0; i < this.timeline.length; i++) {
512
526
  const event = this.timeline[i];
513
- switch (event.type) {
514
- case "setTempo": {
515
- const durationTicks = event.ticks - prevTicks;
516
- totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
517
- secondsPerBeat = event.microsecondsPerBeat / 1000000;
518
- prevTicks = event.ticks;
519
- break;
520
- }
521
- case "endOfTrack":
522
- endOfTracks.push(event);
523
- }
527
+ if (totalTime < event.startTime)
528
+ totalTime = event.startTime;
524
529
  }
525
- let maxTicks = 0;
526
- for (let i = 0; i < endOfTracks.length; i++) {
527
- const event = endOfTracks[i];
528
- if (maxTicks < event.ticks)
529
- maxTicks = event.ticks;
530
- }
531
- const durationTicks = maxTicks - prevTicks;
532
- totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
533
530
  return totalTime;
534
531
  }
535
532
  currentTime() {
@@ -557,11 +554,8 @@ class Midy {
557
554
  const lfo = new OscillatorNode(audioContext, {
558
555
  frequency: 5,
559
556
  });
560
- const lfoGain = new GainNode(audioContext);
561
- lfo.connect(lfoGain);
562
557
  return {
563
558
  lfo,
564
- lfoGain,
565
559
  };
566
560
  }
567
561
  createReverbEffect(audioContext, options = {}) {
@@ -666,15 +660,19 @@ class Midy {
666
660
  centToHz(cent) {
667
661
  return 8.176 * Math.pow(2, cent / 1200);
668
662
  }
669
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
663
+ calcSemitoneOffset(channel) {
670
664
  const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
671
665
  const channelTuning = channel.coarseTuning + channel.fineTuning;
672
666
  const tuning = masterTuning + channelTuning;
673
- const semitoneOffset = channel.pitchBend * channel.pitchBendRange + tuning;
674
- const playbackRate = noteInfo.playbackRate(noteNumber) *
675
- Math.pow(2, semitoneOffset / 12);
667
+ return channel.pitchBend * channel.pitchBendRange + tuning;
668
+ }
669
+ calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
670
+ return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
671
+ }
672
+ async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
676
673
  const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
677
- bufferSource.playbackRate.value = playbackRate;
674
+ const semitoneOffset = this.calcSemitoneOffset(channel);
675
+ bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
678
676
  // volume envelope
679
677
  const gainNode = new GainNode(this.audioContext, {
680
678
  gain: 0,
@@ -693,12 +691,6 @@ class Midy {
693
691
  .exponentialRampToValueAtTime(attackVolume, volAttack)
694
692
  .setValueAtTime(attackVolume, volHold)
695
693
  .linearRampToValueAtTime(sustainVolume, volDecay);
696
- if (channel.modulation > 0) {
697
- const lfoGain = channel.modulationEffect.lfoGain;
698
- lfoGain.connect(bufferSource.detune);
699
- lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
700
- lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
701
- }
702
694
  // filter envelope
703
695
  const softPedalFactor = 1 -
704
696
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
@@ -724,6 +716,19 @@ class Midy {
724
716
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
725
717
  .setValueAtTime(adjustedPeekFreq, modHold)
726
718
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
719
+ let lfoGain;
720
+ if (channel.modulation > 0) {
721
+ const vibratoDelay = startTime + channel.vibratoDelay;
722
+ const vibratoAttack = vibratoDelay + 0.1;
723
+ lfoGain = new GainNode(this.audioContext, {
724
+ gain: 0,
725
+ });
726
+ lfoGain.gain
727
+ .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
728
+ .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
729
+ channel.modulationEffect.lfo.connect(lfoGain);
730
+ lfoGain.connect(bufferSource.detune);
731
+ }
727
732
  bufferSource.connect(filterNode);
728
733
  filterNode.connect(gainNode);
729
734
  if (this.mono && channel.currentBufferSource) {
@@ -731,7 +736,7 @@ class Midy {
731
736
  channel.currentBufferSource = bufferSource;
732
737
  }
733
738
  bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
734
- return { bufferSource, gainNode, filterNode };
739
+ return { bufferSource, gainNode, filterNode, lfoGain };
735
740
  }
736
741
  calcBank(channel, channelNumber) {
737
742
  if (channel.bankMSB === 121) {
@@ -753,7 +758,7 @@ class Midy {
753
758
  const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
754
759
  if (!noteInfo)
755
760
  return;
756
- const { bufferSource, gainNode, filterNode } = await this
761
+ const { bufferSource, gainNode, filterNode, lfoGain } = await this
757
762
  .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
758
763
  this.connectNoteEffects(channel, gainNode);
759
764
  if (channel.sostenutoPedal) {
@@ -767,11 +772,12 @@ class Midy {
767
772
  }
768
773
  const scheduledNotes = channel.scheduledNotes;
769
774
  const scheduledNote = {
770
- gainNode,
771
- filterNode,
772
775
  bufferSource,
773
- noteNumber,
776
+ filterNode,
777
+ gainNode,
778
+ lfoGain,
774
779
  noteInfo,
780
+ noteNumber,
775
781
  startTime,
776
782
  };
777
783
  if (scheduledNotes.has(noteNumber)) {
@@ -800,7 +806,7 @@ class Midy {
800
806
  continue;
801
807
  if (targetNote.ending)
802
808
  continue;
803
- const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
809
+ const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
804
810
  const velocityRate = (velocity + 127) / 127;
805
811
  const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
806
812
  gainNode.gain.cancelScheduledValues(stopTime);
@@ -822,6 +828,8 @@ class Midy {
822
828
  bufferSource.disconnect(0);
823
829
  filterNode.disconnect(0);
824
830
  gainNode.disconnect(0);
831
+ if (lfoGain)
832
+ lfoGain.disconnect(0);
825
833
  resolve();
826
834
  };
827
835
  bufferSource.stop(volEndTime);
@@ -832,41 +840,34 @@ class Midy {
832
840
  const now = this.audioContext.currentTime;
833
841
  return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
834
842
  }
835
- releaseSustainPedal(channelNumber) {
836
- const now = this.audioContext.currentTime;
843
+ releaseSustainPedal(channelNumber, halfVelocity) {
844
+ const velocity = halfVelocity * 2;
837
845
  const channel = this.channels[channelNumber];
846
+ const promises = [];
838
847
  channel.sustainPedal = false;
839
848
  channel.scheduledNotes.forEach((scheduledNotes) => {
840
849
  scheduledNotes.forEach((scheduledNote) => {
841
850
  if (scheduledNote) {
842
- const { bufferSource, gainNode, filterNode, noteInfo } = scheduledNote;
843
- const volEndTime = now + noteInfo.volRelease;
844
- gainNode.gain.cancelScheduledValues(now);
845
- gainNode.gain.linearRampToValueAtTime(0, volEndTime);
846
- const maxFreq = this.audioContext.sampleRate / 2;
847
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
848
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
849
- const modEndTime = now + noteInfo.modRelease;
850
- filterNode.frequency
851
- .cancelScheduledValues(stopTime)
852
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
853
- bufferSource.stop(volEndTime);
851
+ const { noteNumber } = scheduledNote;
852
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
853
+ promises.push(promise);
854
854
  }
855
855
  });
856
856
  });
857
+ return promises;
857
858
  }
858
- releaseSostenuto(channelNumber) {
859
- const now = this.audioContext.currentTime;
859
+ releaseSostenutoPedal(channelNumber, halfVelocity) {
860
+ const velocity = halfVelocity * 2;
860
861
  const channel = this.channels[channelNumber];
862
+ const promises = [];
861
863
  channel.sostenutoPedal = false;
862
864
  channel.sostenutoNotes.forEach((activeNote) => {
863
- const { gainNode, bufferSource, noteInfo } = activeNote;
864
- const fadeTime = noteInfo.volRelease;
865
- gainNode.gain.cancelScheduledValues(now);
866
- gainNode.gain.linearRampToValueAtTime(0, now + fadeTime);
867
- bufferSource.stop(now + fadeTime);
865
+ const { noteNumber } = activeNote;
866
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
867
+ promises.push(promise);
868
868
  });
869
869
  channel.sostenutoNotes.clear();
870
+ return promises;
870
871
  }
871
872
  handleMIDIMessage(statusByte, data1, data2) {
872
873
  const channelNumber = omni ? 0 : statusByte & 0x0F;
@@ -877,7 +878,7 @@ class Midy {
877
878
  case 0x90:
878
879
  return this.noteOn(channelNumber, data1, data2);
879
880
  case 0xA0:
880
- return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
881
+ return; // this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
881
882
  case 0xB0:
882
883
  return this.handleControlChange(channelNumber, data1, data2);
883
884
  case 0xC0:
@@ -885,7 +886,7 @@ class Midy {
885
886
  case 0xD0:
886
887
  return this.handleChannelPressure(channelNumber, data1);
887
888
  case 0xE0:
888
- return this.handlePitchBend(channelNumber, data1, data2);
889
+ return this.handlePitchBendMessage(channelNumber, data1, data2);
889
890
  default:
890
891
  console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
891
892
  }
@@ -893,17 +894,16 @@ class Midy {
893
894
  handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
894
895
  const now = this.audioContext.currentTime;
895
896
  const channel = this.channels[channelNumber];
896
- const scheduledNotes = channel.scheduledNotes.get(noteNumber);
897
- pressure /= 127;
898
- if (scheduledNotes) {
899
- scheduledNotes.forEach((scheduledNote) => {
900
- if (scheduledNote) {
901
- const { initialAttenuation } = scheduledNote.noteInfo;
902
- const gain = this.cbToRatio(-initialAttenuation) * pressure;
903
- scheduledNote.gainNode.gain.cancelScheduledValues(now);
904
- scheduledNote.gainNode.gain.setValueAtTime(gain, now);
905
- }
906
- });
897
+ pressure /= 64;
898
+ const activeNotes = this.getActiveNotes(channel);
899
+ if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
900
+ if (activeNotes.has(noteNumber)) {
901
+ const activeNote = activeNotes.get(noteNumber);
902
+ const gain = activeNote.gainNode.gain.value;
903
+ activeNote.gainNode.gain
904
+ .cancelScheduledValues(now)
905
+ .setValueAtTime(gain * pressure, now);
906
+ }
907
907
  }
908
908
  }
909
909
  handleProgramChange(channelNumber, program) {
@@ -912,11 +912,37 @@ class Midy {
912
912
  channel.program = program;
913
913
  }
914
914
  handleChannelPressure(channelNumber, pressure) {
915
- this.channels[channelNumber].channelPressure = pressure;
915
+ const now = this.audioContext.currentTime;
916
+ const channel = this.channels[channelNumber];
917
+ pressure /= 64;
918
+ channel.channelPressure = pressure;
919
+ const activeNotes = this.getActiveNotes(channel);
920
+ if (channel.channelPressure.amplitudeControl !== 1) {
921
+ activeNotes.forEach((activeNote) => {
922
+ const gain = activeNote.gainNode.gain.value;
923
+ activeNote.gainNode.gain
924
+ .cancelScheduledValues(now)
925
+ .setValueAtTime(gain * pressure, now);
926
+ });
927
+ }
928
+ }
929
+ handlePitchBendMessage(channelNumber, lsb, msb) {
930
+ const pitchBend = msb * 128 + lsb;
931
+ this.handlePitchBend(channelNumber, pitchBend);
916
932
  }
917
- handlePitchBend(channelNumber, lsb, msb) {
918
- const pitchBend = (msb * 128 + lsb - 8192) / 8192;
919
- this.channels[channelNumber].pitchBend = pitchBend;
933
+ handlePitchBend(channelNumber, pitchBend) {
934
+ const now = this.audioContext.currentTime;
935
+ const channel = this.channels[channelNumber];
936
+ channel.pitchBend = (pitchBend - 8192) / 8192;
937
+ const semitoneOffset = this.calcSemitoneOffset(channel);
938
+ const activeNotes = this.getActiveNotes(channel);
939
+ activeNotes.forEach((activeNote) => {
940
+ const { bufferSource, noteInfo, noteNumber } = activeNote;
941
+ const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
942
+ bufferSource.playbackRate
943
+ .cancelScheduledValues(now)
944
+ .setValueAtTime(playbackRate * pressure, now);
945
+ });
920
946
  }
921
947
  handleControlChange(channelNumber, controller, value) {
922
948
  switch (controller) {
@@ -987,13 +1013,9 @@ class Midy {
987
1013
  this.channels[channelNumber].bankMSB = msb;
988
1014
  }
989
1015
  setModulation(channelNumber, modulation) {
990
- const now = this.audioContext.currentTime;
991
1016
  const channel = this.channels[channelNumber];
992
- channel.modulation = (modulation * 100 / 127) *
993
- channel.modulationDepthRange;
994
- const lfoGain = channel.modulationEffect.lfoGain;
995
- lfoGain.gain.cancelScheduledValues(now);
996
- lfoGain.gain.setValueAtTime(channel.modulation, now);
1017
+ channel.modulation = (modulation / 127) *
1018
+ (channel.modulationDepthRange * 100);
997
1019
  }
998
1020
  setPortamentoTime(channelNumber, portamentoTime) {
999
1021
  this.channels[channelNumber].portamentoTime = portamentoTime / 127;
@@ -1028,7 +1050,7 @@ class Midy {
1028
1050
  const isOn = value >= 64;
1029
1051
  this.channels[channelNumber].sustainPedal = isOn;
1030
1052
  if (!isOn) {
1031
- this.releaseSustainPedal(channelNumber);
1053
+ this.releaseSustainPedal(channelNumber, value);
1032
1054
  }
1033
1055
  }
1034
1056
  setPortamento(channelNumber, value) {
@@ -1057,25 +1079,29 @@ class Midy {
1057
1079
  const activeNotes = this.getActiveNotes(channel);
1058
1080
  channel.sostenutoNotes = new Map(activeNotes);
1059
1081
  }
1082
+ else {
1083
+ this.releaseSostenutoPedal(channelNumber, value);
1084
+ }
1060
1085
  }
1061
1086
  setSoftPedal(channelNumber, softPedal) {
1062
1087
  const channel = this.channels[channelNumber];
1063
1088
  channel.softPedal = softPedal / 127;
1064
- this.updateChannelGain(channel);
1065
1089
  }
1066
1090
  setVibratoRate(channelNumber, vibratoRate) {
1067
1091
  const now = this.audioContext.currentTime;
1068
1092
  const channel = this.channels[channelNumber];
1069
1093
  channel.vibratoRate = vibratoRate / 127 * 4 + 3; // 3-7Hz
1070
- channel.modulationEffect.lfo.frequency.cancelScheduledValues(now);
1071
- channel.modulationEffect.lfo.frequency.setValueAtTime(depth, now);
1094
+ channel.modulationEffect.lfo.frequency
1095
+ .cancelScheduledValues(now)
1096
+ .setValueAtTime(channel.vibratoRate, now);
1072
1097
  }
1073
1098
  setVibratoDepth(channelNumber, vibratoDepth) {
1074
1099
  const now = this.audioContext.currentTime;
1075
1100
  const channel = this.channels[channelNumber];
1076
1101
  channel.vibratoDepth = vibratoDepth / 127;
1077
- channel.modulationEffect.lfoGain.gain.cancelScheduledValues(now);
1078
- channel.modulationEffect.lfoGain.gain.setValueAtTime(depth, now);
1102
+ channel.modulationEffect.lfoGain.gain
1103
+ .cancelScheduledValues(now)
1104
+ .setValueAtTime(channel.vibratoDepth, now);
1079
1105
  }
1080
1106
  setVibratoDelay(channelNumber, vibratoDelay) {
1081
1107
  // Access Virus: 0-10sec
@@ -1261,10 +1287,10 @@ class Midy {
1261
1287
  switch (data[3]) {
1262
1288
  // case 1:
1263
1289
  // // TODO
1264
- // return this.handleChannelPressure();
1290
+ // return this.setChannelPressure();
1265
1291
  // case 3:
1266
1292
  // // TODO
1267
- // return this.handleControlChange();
1293
+ // return this.setControlChange();
1268
1294
  default:
1269
1295
  console.warn(`Unsupported Exclusive Message ${data}`);
1270
1296
  }
@@ -1283,20 +1309,25 @@ class Midy {
1283
1309
  }
1284
1310
  }
1285
1311
  handleMasterVolumeSysEx(data) {
1286
- const volume = (data[5] * 128 + data[4] - 8192) / 8192;
1312
+ const volume = (data[5] * 128 + data[4]) / 16383;
1287
1313
  this.handleMasterVolume(volume);
1288
1314
  }
1289
1315
  handleMasterVolume(volume) {
1290
- const now = this.audioContext.currentTime;
1291
- this.masterGain.gain.cancelScheduledValues(now);
1292
- this.masterGain.gain.setValueAtTime(volume * volume, now);
1316
+ if (volume < 0 && 1 < volume) {
1317
+ console.error("Master Volume is out of range");
1318
+ }
1319
+ else {
1320
+ const now = this.audioContext.currentTime;
1321
+ this.masterGain.gain.cancelScheduledValues(now);
1322
+ this.masterGain.gain.setValueAtTime(volume * volume, now);
1323
+ }
1293
1324
  }
1294
1325
  handleMasterFineTuningSysEx(data) {
1295
1326
  const fineTuning = (data[5] * 128 + data[4] - 8192) / 8192;
1296
1327
  this.handleMasterFineTuning(fineTuning);
1297
1328
  }
1298
1329
  handleMasterFineTuning(fineTuning) {
1299
- if (fineTuning < 0 && 1 < fineTuning) {
1330
+ if (fineTuning < -1 && 1 < fineTuning) {
1300
1331
  console.error("Master Fine Tuning value is out of range");
1301
1332
  }
1302
1333
  else {
@@ -1347,7 +1378,7 @@ Object.defineProperty(Midy, "channelSettings", {
1347
1378
  writable: true,
1348
1379
  value: {
1349
1380
  currentBufferSource: null,
1350
- volume: 1,
1381
+ volume: 100 / 127,
1351
1382
  pan: 0,
1352
1383
  portamentoTime: 0,
1353
1384
  reverb: 0,
@@ -1364,7 +1395,7 @@ Object.defineProperty(Midy, "channelSettings", {
1364
1395
  pitchBend: 0,
1365
1396
  fineTuning: 0,
1366
1397
  coarseTuning: 0,
1367
- modulationDepthRange: 2,
1398
+ modulationDepthRange: 0.5,
1368
1399
  }
1369
1400
  });
1370
1401
  Object.defineProperty(Midy, "effectSettings", {
@@ -1384,3 +1415,16 @@ Object.defineProperty(Midy, "effectSettings", {
1384
1415
  pitchBendRange: 2,
1385
1416
  }
1386
1417
  });
1418
+ Object.defineProperty(Midy, "controllerDestinationSettings", {
1419
+ enumerable: true,
1420
+ configurable: true,
1421
+ writable: true,
1422
+ value: {
1423
+ pitchControl: 0,
1424
+ filterCutoffControl: 0,
1425
+ amplitudeControl: 1,
1426
+ lfoPitchDepth: 0,
1427
+ lfoFilterDepth: 0,
1428
+ lfoAmplitudeDepth: 0,
1429
+ }
1430
+ });