@marmooo/midy 0.0.5 → 0.0.7

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.
@@ -183,17 +183,16 @@ export class MidyGMLite {
183
183
  this.totalTime = this.calcTotalTime();
184
184
  }
185
185
  setChannelAudioNodes(audioContext) {
186
- const gainNode = new GainNode(audioContext, {
187
- gain: MidyGMLite.channelSettings.volume,
188
- });
189
- const pannerNode = new StereoPannerNode(audioContext, {
190
- pan: MidyGMLite.channelSettings.pan,
191
- });
192
- pannerNode.connect(gainNode);
193
- gainNode.connect(this.masterGain);
186
+ const { gainLeft, gainRight } = this.panToGain(MidyGMLite.channelSettings.pan);
187
+ const gainL = new GainNode(audioContext, { gain: gainLeft });
188
+ const gainR = new GainNode(audioContext, { gain: gainRight });
189
+ const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
190
+ gainL.connect(merger, 0, 0);
191
+ gainR.connect(merger, 0, 1);
192
+ merger.connect(this.masterGain);
194
193
  return {
195
- gainNode,
196
- pannerNode,
194
+ gainL,
195
+ gainR,
197
196
  };
198
197
  }
199
198
  createChannels(audioContext) {
@@ -278,7 +277,7 @@ export class MidyGMLite {
278
277
  this.handleProgramChange(event.channel, event.programNumber);
279
278
  break;
280
279
  case "pitchBend":
281
- this.handlePitchBend(event.channel, event.value);
280
+ this.setPitchBend(event.channel, event.value);
282
281
  break;
283
282
  case "sysEx":
284
283
  this.handleSysEx(event.data);
@@ -358,7 +357,6 @@ export class MidyGMLite {
358
357
  const tmpChannels = new Array(16);
359
358
  for (let i = 0; i < tmpChannels.length; i++) {
360
359
  tmpChannels[i] = {
361
- durationTicks: new Map(),
362
360
  programNumber: -1,
363
361
  bank: this.channels[i].bank,
364
362
  };
@@ -375,16 +373,6 @@ export class MidyGMLite {
375
373
  instruments.add(`${channel.bank}:0`);
376
374
  channel.programNumber = 0;
377
375
  }
378
- channel.durationTicks.set(event.noteNumber, {
379
- ticks: event.ticks,
380
- noteOn: event,
381
- });
382
- break;
383
- }
384
- case "noteOff": {
385
- const { ticks, noteOn } = tmpChannels[event.channel].durationTicks
386
- .get(event.noteNumber);
387
- noteOn.durationTicks = event.ticks - ticks;
388
376
  break;
389
377
  }
390
378
  case "programChange": {
@@ -398,8 +386,8 @@ export class MidyGMLite {
398
386
  });
399
387
  });
400
388
  const priority = {
401
- setTempo: 0,
402
- controller: 1,
389
+ controller: 0,
390
+ sysEx: 1,
403
391
  };
404
392
  timeline.sort((a, b) => {
405
393
  if (a.ticks !== b.ticks)
@@ -504,7 +492,8 @@ export class MidyGMLite {
504
492
  return noteList[0];
505
493
  }
506
494
  connectNoteEffects(channel, gainNode) {
507
- gainNode.connect(channel.pannerNode);
495
+ gainNode.connect(channel.gainL);
496
+ gainNode.connect(channel.gainR);
508
497
  }
509
498
  cbToRatio(cb) {
510
499
  return Math.pow(10, cb / 200);
@@ -521,9 +510,7 @@ export class MidyGMLite {
521
510
  }
522
511
  setVolumeEnvelope(channel, note) {
523
512
  const { instrumentKey, startTime, velocity } = note;
524
- note.gainNode = new GainNode(this.audioContext, {
525
- gain: 0,
526
- });
513
+ note.gainNode = new GainNode(this.audioContext, { gain: 0 });
527
514
  let volume = (velocity / 127) * channel.volume * channel.expression;
528
515
  if (volume === 0)
529
516
  volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
@@ -713,21 +700,15 @@ export class MidyGMLite {
713
700
  }
714
701
  handlePitchBendMessage(channelNumber, lsb, msb) {
715
702
  const pitchBend = msb * 128 + lsb;
716
- this.handlePitchBend(channelNumber, pitchBend);
703
+ this.setPitchBend(channelNumber, pitchBend);
717
704
  }
718
- handlePitchBend(channelNumber, pitchBend) {
719
- const now = this.audioContext.currentTime;
705
+ setPitchBend(channelNumber, pitchBend) {
720
706
  const channel = this.channels[channelNumber];
707
+ const prevPitchBend = channel.pitchBend;
721
708
  channel.pitchBend = (pitchBend - 8192) / 8192;
722
- const semitoneOffset = this.calcSemitoneOffset(channel);
723
- const activeNotes = this.getActiveNotes(channel, now);
724
- activeNotes.forEach((activeNote) => {
725
- const { bufferSource, instrumentKey, noteNumber } = activeNote;
726
- const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
727
- bufferSource.playbackRate
728
- .cancelScheduledValues(now)
729
- .setValueAtTime(playbackRate * pressure, now);
730
- });
709
+ const detuneChange = (channel.pitchBend - prevPitchBend) *
710
+ channel.pitchBendRange * 100;
711
+ this.updateDetune(channel, detuneChange);
731
712
  }
732
713
  handleControlChange(channelNumber, controller, value) {
733
714
  switch (controller) {
@@ -746,9 +727,9 @@ export class MidyGMLite {
746
727
  case 64:
747
728
  return this.setSustainPedal(channelNumber, value);
748
729
  case 100:
749
- return this.setRPNMSB(channelNumber, value);
750
- case 101:
751
730
  return this.setRPNLSB(channelNumber, value);
731
+ case 101:
732
+ return this.setRPNMSB(channelNumber, value);
752
733
  case 120:
753
734
  return this.allSoundOff(channelNumber);
754
735
  case 121:
@@ -780,12 +761,17 @@ export class MidyGMLite {
780
761
  channel.volume = volume / 127;
781
762
  this.updateChannelGain(channel);
782
763
  }
764
+ panToGain(pan) {
765
+ const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
766
+ return {
767
+ gainLeft: Math.cos(theta),
768
+ gainRight: Math.sin(theta),
769
+ };
770
+ }
783
771
  setPan(channelNumber, pan) {
784
- const now = this.audioContext.currentTime;
785
772
  const channel = this.channels[channelNumber];
786
- channel.pan = pan / 127 * 2 - 1; // -1 (left) - +1 (right)
787
- channel.pannerNode.pan.cancelScheduledValues(now);
788
- channel.pannerNode.pan.setValueAtTime(channel.pan, now);
773
+ channel.pan = pan;
774
+ this.updateChannelGain(channel);
789
775
  }
790
776
  setExpression(channelNumber, expression) {
791
777
  const channel = this.channels[channelNumber];
@@ -795,8 +781,13 @@ export class MidyGMLite {
795
781
  updateChannelGain(channel) {
796
782
  const now = this.audioContext.currentTime;
797
783
  const volume = channel.volume * channel.expression;
798
- channel.gainNode.gain.cancelScheduledValues(now);
799
- channel.gainNode.gain.setValueAtTime(volume, now);
784
+ const { gainLeft, gainRight } = this.panToGain(channel.pan);
785
+ channel.gainL.gain
786
+ .cancelScheduledValues(now)
787
+ .setValueAtTime(volume * gainLeft, now);
788
+ channel.gainR.gain
789
+ .cancelScheduledValues(now)
790
+ .setValueAtTime(volume * gainRight, now);
800
791
  }
801
792
  setSustainPedal(channelNumber, value) {
802
793
  const isOn = value >= 64;
@@ -805,6 +796,17 @@ export class MidyGMLite {
805
796
  this.releaseSustainPedal(channelNumber, value);
806
797
  }
807
798
  }
799
+ handleRPN(channelNumber) {
800
+ const channel = this.channels[channelNumber];
801
+ const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
802
+ switch (rpn) {
803
+ case 0:
804
+ this.handlePitchBendRangeMessage(channelNumber);
805
+ break;
806
+ default:
807
+ console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
808
+ }
809
+ }
808
810
  setRPNMSB(channelNumber, value) {
809
811
  this.channels[channelNumber].rpnMSB = value;
810
812
  }
@@ -813,16 +815,33 @@ export class MidyGMLite {
813
815
  }
814
816
  setDataEntry(channelNumber, value, isMSB) {
815
817
  const channel = this.channels[channelNumber];
816
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
817
818
  isMSB ? channel.dataMSB = value : channel.dataLSB = value;
818
- const { dataMSB, dataLSB } = channel;
819
- switch (rpn) {
820
- case 0:
821
- channel.pitchBendRange = dataMSB + dataLSB / 100;
822
- break;
823
- default:
824
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
825
- }
819
+ this.handleRPN(channelNumber);
820
+ }
821
+ updateDetune(channel, detuneChange) {
822
+ const now = this.audioContext.currentTime;
823
+ const activeNotes = this.getActiveNotes(channel, now);
824
+ activeNotes.forEach((activeNote) => {
825
+ const { bufferSource } = activeNote;
826
+ const detune = bufferSource.detune.value + detuneChange;
827
+ bufferSource.detune
828
+ .cancelScheduledValues(now)
829
+ .setValueAtTime(detune, now);
830
+ });
831
+ }
832
+ handlePitchBendRangeMessage(channelNumber) {
833
+ const channel = this.channels[channelNumber];
834
+ this.limitData(channel, 0, 127, 0, 99);
835
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
836
+ this.setPitchBendRange(channelNumber, pitchBendRange);
837
+ }
838
+ setPitchBendRange(channelNumber, pitchBendRange) {
839
+ const channel = this.channels[channelNumber];
840
+ const prevPitchBendRange = channel.pitchBendRange;
841
+ channel.pitchBendRange = pitchBendRange;
842
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
843
+ channel.pitchBend * 100;
844
+ this.updateDetune(channel, detuneChange);
826
845
  }
827
846
  allSoundOff(channelNumber) {
828
847
  const now = this.audioContext.currentTime;
@@ -899,9 +918,9 @@ export class MidyGMLite {
899
918
  }
900
919
  handleMasterVolumeSysEx(data) {
901
920
  const volume = (data[5] * 128 + data[4]) / 16383;
902
- this.handleMasterVolume(volume);
921
+ this.setMasterVolume(volume);
903
922
  }
904
- handleMasterVolume(volume) {
923
+ setMasterVolume(volume) {
905
924
  if (volume < 0 && 1 < volume) {
906
925
  console.error("Master Volume is out of range");
907
926
  }
@@ -942,7 +961,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
942
961
  writable: true,
943
962
  value: {
944
963
  volume: 100 / 127,
945
- pan: 0,
964
+ pan: 64,
946
965
  bank: 0,
947
966
  dataMSB: 0,
948
967
  dataLSB: 0,
package/esm/midy.d.ts CHANGED
@@ -84,8 +84,8 @@ export class Midy {
84
84
  lfoFilterDepth: number;
85
85
  lfoAmplitudeDepth: number;
86
86
  };
87
- gainNode: any;
88
- pannerNode: any;
87
+ gainL: any;
88
+ gainR: any;
89
89
  reverbEffect: {
90
90
  convolverNode: any;
91
91
  dryGain: any;
@@ -131,8 +131,8 @@ export class Midy {
131
131
  loadSoundFont(soundFontUrl: any): Promise<void>;
132
132
  loadMIDI(midiUrl: any): Promise<void>;
133
133
  setChannelAudioNodes(audioContext: any): {
134
- gainNode: any;
135
- pannerNode: any;
134
+ gainL: any;
135
+ gainR: any;
136
136
  reverbEffect: {
137
137
  convolverNode: any;
138
138
  dryGain: any;
@@ -164,8 +164,8 @@ export class Midy {
164
164
  lfoFilterDepth: number;
165
165
  lfoAmplitudeDepth: number;
166
166
  };
167
- gainNode: any;
168
- pannerNode: any;
167
+ gainL: any;
168
+ gainR: any;
169
169
  reverbEffect: {
170
170
  convolverNode: any;
171
171
  dryGain: any;
@@ -261,12 +261,16 @@ export class Midy {
261
261
  handleProgramChange(channelNumber: any, program: any): void;
262
262
  handleChannelPressure(channelNumber: any, pressure: any): void;
263
263
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
264
- handlePitchBend(channelNumber: any, pitchBend: any): void;
264
+ setPitchBend(channelNumber: any, pitchBend: any): void;
265
265
  handleControlChange(channelNumber: any, controller: any, value: any): any;
266
266
  setBankMSB(channelNumber: any, msb: any): void;
267
267
  setModulation(channelNumber: any, modulation: any): void;
268
268
  setPortamentoTime(channelNumber: any, portamentoTime: any): void;
269
269
  setVolume(channelNumber: any, volume: any): void;
270
+ panToGain(pan: any): {
271
+ gainLeft: number;
272
+ gainRight: number;
273
+ };
270
274
  setPan(channelNumber: any, pan: any): void;
271
275
  setExpression(channelNumber: any, expression: any): void;
272
276
  setBankLSB(channelNumber: any, lsb: any): void;
@@ -280,11 +284,21 @@ export class Midy {
280
284
  setVibratoRate(channelNumber: any, vibratoRate: any): void;
281
285
  setVibratoDepth(channelNumber: any, vibratoDepth: any): void;
282
286
  setVibratoDelay(channelNumber: any, vibratoDelay: any): void;
287
+ limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
288
+ limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
289
+ handleRPN(channelNumber: any, value: any): void;
283
290
  incrementRPNValue(channelNumber: any): void;
284
291
  decrementRPNValue(channelNumber: any): void;
285
292
  setRPNMSB(channelNumber: any, value: any): void;
286
293
  setRPNLSB(channelNumber: any, value: any): void;
287
294
  setDataEntry(channelNumber: any, value: any, isMSB: any): void;
295
+ updateDetune(channel: any, detuneChange: any): void;
296
+ handlePitchBendRangeMessage(channelNumber: any): void;
297
+ setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
298
+ handleFineTuningMessage(channelNumber: any): void;
299
+ setFineTuning(channelNumber: any, fineTuning: any): void;
300
+ handleCoarseTuningMessage(channelNumber: any): void;
301
+ setCoarseTuning(channelNumber: any, coarseTuning: any): void;
288
302
  allSoundOff(channelNumber: any): any[];
289
303
  resetAllControllers(channelNumber: any): void;
290
304
  allNotesOff(channelNumber: any): any[];
@@ -297,11 +311,11 @@ export class Midy {
297
311
  GM2SystemOn(): void;
298
312
  handleUniversalRealTimeExclusiveMessage(data: any): void;
299
313
  handleMasterVolumeSysEx(data: any): void;
300
- handleMasterVolume(volume: any): void;
314
+ setMasterVolume(volume: any): void;
301
315
  handleMasterFineTuningSysEx(data: any): void;
302
- handleMasterFineTuning(fineTuning: any): void;
316
+ setMasterFineTuning(fineTuning: any): void;
303
317
  handleMasterCoarseTuningSysEx(data: any): void;
304
- handleMasterCoarseTuning(coarseTuning: any): void;
318
+ setMasterCoarseTuning(coarseTuning: any): void;
305
319
  handleExclusiveMessage(data: any): void;
306
320
  handleSysEx(data: any): void;
307
321
  scheduleTask(callback: any, startTime: any): Promise<any>;
package/esm/midy.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,cAAa;IACb,cAAa;IACb,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAoBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA2GC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAmBC;IAED,iDAiCC;IAED,0DAmBC;IAED,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAwDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,0DAiBC;IAED,0EAkEC;IAED,+CAEC;IAED,yDAiBC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAWC;IAED,uDAGC;IAED,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,4CAkBC;IAED,4CAkBC;IAED,gDAEC;IAED,gDAEC;IAGD,+DAuBC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,sCAQC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAx/CD;IASE,gFAKC;IAbD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,cAAa;IACb,cAAa;IACb,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAqBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgGC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MAqCC;IAED,sDA6BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAwDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;IAED,0EAkEC;IAED,+CAEC;IAED,yDAiBC;IAED,iEAEC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,sCAUC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAWC;IAED,uDAGC;IAED,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,kFAeC;IAED,2DAMC;IAGD,gDAqBC;IAED,4CAEC;IAED,4CAEC;IAED,gDAEC;IAED,gDAEC;IAED,+DAIC;IAED,oDAUC;IAED,sDAKC;IAED,iEAOC;IAED,kDAKC;IAED,yDAGC;IAED,oDAKC;IAED,6DAGC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA1hDD;IASE,gFAKC;IAbD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}