@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.
@@ -186,17 +186,16 @@ class MidyGMLite {
186
186
  this.totalTime = this.calcTotalTime();
187
187
  }
188
188
  setChannelAudioNodes(audioContext) {
189
- const gainNode = new GainNode(audioContext, {
190
- gain: MidyGMLite.channelSettings.volume,
191
- });
192
- const pannerNode = new StereoPannerNode(audioContext, {
193
- pan: MidyGMLite.channelSettings.pan,
194
- });
195
- pannerNode.connect(gainNode);
196
- gainNode.connect(this.masterGain);
189
+ const { gainLeft, gainRight } = this.panToGain(MidyGMLite.channelSettings.pan);
190
+ const gainL = new GainNode(audioContext, { gain: gainLeft });
191
+ const gainR = new GainNode(audioContext, { gain: gainRight });
192
+ const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
193
+ gainL.connect(merger, 0, 0);
194
+ gainR.connect(merger, 0, 1);
195
+ merger.connect(this.masterGain);
197
196
  return {
198
- gainNode,
199
- pannerNode,
197
+ gainL,
198
+ gainR,
200
199
  };
201
200
  }
202
201
  createChannels(audioContext) {
@@ -281,7 +280,7 @@ class MidyGMLite {
281
280
  this.handleProgramChange(event.channel, event.programNumber);
282
281
  break;
283
282
  case "pitchBend":
284
- this.handlePitchBend(event.channel, event.value);
283
+ this.setPitchBend(event.channel, event.value);
285
284
  break;
286
285
  case "sysEx":
287
286
  this.handleSysEx(event.data);
@@ -361,7 +360,6 @@ class MidyGMLite {
361
360
  const tmpChannels = new Array(16);
362
361
  for (let i = 0; i < tmpChannels.length; i++) {
363
362
  tmpChannels[i] = {
364
- durationTicks: new Map(),
365
363
  programNumber: -1,
366
364
  bank: this.channels[i].bank,
367
365
  };
@@ -378,16 +376,6 @@ class MidyGMLite {
378
376
  instruments.add(`${channel.bank}:0`);
379
377
  channel.programNumber = 0;
380
378
  }
381
- channel.durationTicks.set(event.noteNumber, {
382
- ticks: event.ticks,
383
- noteOn: event,
384
- });
385
- break;
386
- }
387
- case "noteOff": {
388
- const { ticks, noteOn } = tmpChannels[event.channel].durationTicks
389
- .get(event.noteNumber);
390
- noteOn.durationTicks = event.ticks - ticks;
391
379
  break;
392
380
  }
393
381
  case "programChange": {
@@ -401,8 +389,8 @@ class MidyGMLite {
401
389
  });
402
390
  });
403
391
  const priority = {
404
- setTempo: 0,
405
- controller: 1,
392
+ controller: 0,
393
+ sysEx: 1,
406
394
  };
407
395
  timeline.sort((a, b) => {
408
396
  if (a.ticks !== b.ticks)
@@ -507,7 +495,8 @@ class MidyGMLite {
507
495
  return noteList[0];
508
496
  }
509
497
  connectNoteEffects(channel, gainNode) {
510
- gainNode.connect(channel.pannerNode);
498
+ gainNode.connect(channel.gainL);
499
+ gainNode.connect(channel.gainR);
511
500
  }
512
501
  cbToRatio(cb) {
513
502
  return Math.pow(10, cb / 200);
@@ -524,9 +513,7 @@ class MidyGMLite {
524
513
  }
525
514
  setVolumeEnvelope(channel, note) {
526
515
  const { instrumentKey, startTime, velocity } = note;
527
- note.gainNode = new GainNode(this.audioContext, {
528
- gain: 0,
529
- });
516
+ note.gainNode = new GainNode(this.audioContext, { gain: 0 });
530
517
  let volume = (velocity / 127) * channel.volume * channel.expression;
531
518
  if (volume === 0)
532
519
  volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
@@ -716,21 +703,15 @@ class MidyGMLite {
716
703
  }
717
704
  handlePitchBendMessage(channelNumber, lsb, msb) {
718
705
  const pitchBend = msb * 128 + lsb;
719
- this.handlePitchBend(channelNumber, pitchBend);
706
+ this.setPitchBend(channelNumber, pitchBend);
720
707
  }
721
- handlePitchBend(channelNumber, pitchBend) {
722
- const now = this.audioContext.currentTime;
708
+ setPitchBend(channelNumber, pitchBend) {
723
709
  const channel = this.channels[channelNumber];
710
+ const prevPitchBend = channel.pitchBend;
724
711
  channel.pitchBend = (pitchBend - 8192) / 8192;
725
- const semitoneOffset = this.calcSemitoneOffset(channel);
726
- const activeNotes = this.getActiveNotes(channel, now);
727
- activeNotes.forEach((activeNote) => {
728
- const { bufferSource, instrumentKey, noteNumber } = activeNote;
729
- const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
730
- bufferSource.playbackRate
731
- .cancelScheduledValues(now)
732
- .setValueAtTime(playbackRate * pressure, now);
733
- });
712
+ const detuneChange = (channel.pitchBend - prevPitchBend) *
713
+ channel.pitchBendRange * 100;
714
+ this.updateDetune(channel, detuneChange);
734
715
  }
735
716
  handleControlChange(channelNumber, controller, value) {
736
717
  switch (controller) {
@@ -749,9 +730,9 @@ class MidyGMLite {
749
730
  case 64:
750
731
  return this.setSustainPedal(channelNumber, value);
751
732
  case 100:
752
- return this.setRPNMSB(channelNumber, value);
753
- case 101:
754
733
  return this.setRPNLSB(channelNumber, value);
734
+ case 101:
735
+ return this.setRPNMSB(channelNumber, value);
755
736
  case 120:
756
737
  return this.allSoundOff(channelNumber);
757
738
  case 121:
@@ -783,12 +764,17 @@ class MidyGMLite {
783
764
  channel.volume = volume / 127;
784
765
  this.updateChannelGain(channel);
785
766
  }
767
+ panToGain(pan) {
768
+ const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
769
+ return {
770
+ gainLeft: Math.cos(theta),
771
+ gainRight: Math.sin(theta),
772
+ };
773
+ }
786
774
  setPan(channelNumber, pan) {
787
- const now = this.audioContext.currentTime;
788
775
  const channel = this.channels[channelNumber];
789
- channel.pan = pan / 127 * 2 - 1; // -1 (left) - +1 (right)
790
- channel.pannerNode.pan.cancelScheduledValues(now);
791
- channel.pannerNode.pan.setValueAtTime(channel.pan, now);
776
+ channel.pan = pan;
777
+ this.updateChannelGain(channel);
792
778
  }
793
779
  setExpression(channelNumber, expression) {
794
780
  const channel = this.channels[channelNumber];
@@ -798,8 +784,13 @@ class MidyGMLite {
798
784
  updateChannelGain(channel) {
799
785
  const now = this.audioContext.currentTime;
800
786
  const volume = channel.volume * channel.expression;
801
- channel.gainNode.gain.cancelScheduledValues(now);
802
- channel.gainNode.gain.setValueAtTime(volume, now);
787
+ const { gainLeft, gainRight } = this.panToGain(channel.pan);
788
+ channel.gainL.gain
789
+ .cancelScheduledValues(now)
790
+ .setValueAtTime(volume * gainLeft, now);
791
+ channel.gainR.gain
792
+ .cancelScheduledValues(now)
793
+ .setValueAtTime(volume * gainRight, now);
803
794
  }
804
795
  setSustainPedal(channelNumber, value) {
805
796
  const isOn = value >= 64;
@@ -808,6 +799,17 @@ class MidyGMLite {
808
799
  this.releaseSustainPedal(channelNumber, value);
809
800
  }
810
801
  }
802
+ handleRPN(channelNumber) {
803
+ const channel = this.channels[channelNumber];
804
+ const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
805
+ switch (rpn) {
806
+ case 0:
807
+ this.handlePitchBendRangeMessage(channelNumber);
808
+ break;
809
+ default:
810
+ console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
811
+ }
812
+ }
811
813
  setRPNMSB(channelNumber, value) {
812
814
  this.channels[channelNumber].rpnMSB = value;
813
815
  }
@@ -816,16 +818,33 @@ class MidyGMLite {
816
818
  }
817
819
  setDataEntry(channelNumber, value, isMSB) {
818
820
  const channel = this.channels[channelNumber];
819
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
820
821
  isMSB ? channel.dataMSB = value : channel.dataLSB = value;
821
- const { dataMSB, dataLSB } = channel;
822
- switch (rpn) {
823
- case 0:
824
- channel.pitchBendRange = dataMSB + dataLSB / 100;
825
- break;
826
- default:
827
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
828
- }
822
+ this.handleRPN(channelNumber);
823
+ }
824
+ updateDetune(channel, detuneChange) {
825
+ const now = this.audioContext.currentTime;
826
+ const activeNotes = this.getActiveNotes(channel, now);
827
+ activeNotes.forEach((activeNote) => {
828
+ const { bufferSource } = activeNote;
829
+ const detune = bufferSource.detune.value + detuneChange;
830
+ bufferSource.detune
831
+ .cancelScheduledValues(now)
832
+ .setValueAtTime(detune, now);
833
+ });
834
+ }
835
+ handlePitchBendRangeMessage(channelNumber) {
836
+ const channel = this.channels[channelNumber];
837
+ this.limitData(channel, 0, 127, 0, 99);
838
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
839
+ this.setPitchBendRange(channelNumber, pitchBendRange);
840
+ }
841
+ setPitchBendRange(channelNumber, pitchBendRange) {
842
+ const channel = this.channels[channelNumber];
843
+ const prevPitchBendRange = channel.pitchBendRange;
844
+ channel.pitchBendRange = pitchBendRange;
845
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
846
+ channel.pitchBend * 100;
847
+ this.updateDetune(channel, detuneChange);
829
848
  }
830
849
  allSoundOff(channelNumber) {
831
850
  const now = this.audioContext.currentTime;
@@ -902,9 +921,9 @@ class MidyGMLite {
902
921
  }
903
922
  handleMasterVolumeSysEx(data) {
904
923
  const volume = (data[5] * 128 + data[4]) / 16383;
905
- this.handleMasterVolume(volume);
924
+ this.setMasterVolume(volume);
906
925
  }
907
- handleMasterVolume(volume) {
926
+ setMasterVolume(volume) {
908
927
  if (volume < 0 && 1 < volume) {
909
928
  console.error("Master Volume is out of range");
910
929
  }
@@ -946,7 +965,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
946
965
  writable: true,
947
966
  value: {
948
967
  volume: 100 / 127,
949
- pan: 0,
968
+ pan: 64,
950
969
  bank: 0,
951
970
  dataMSB: 0,
952
971
  dataLSB: 0,
package/script/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>;
@@ -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"}