@marmooo/midy 0.0.2 → 0.0.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.
package/esm/midy-GM2.js CHANGED
@@ -8,12 +8,6 @@ export class MidyGM2 {
8
8
  writable: true,
9
9
  value: 120
10
10
  });
11
- Object.defineProperty(this, "secondsPerBeat", {
12
- enumerable: true,
13
- configurable: true,
14
- writable: true,
15
- value: 0.5
16
- });
17
11
  Object.defineProperty(this, "totalTime", {
18
12
  enumerable: true,
19
13
  configurable: true,
@@ -174,10 +168,10 @@ export class MidyGM2 {
174
168
  const response = await fetch(midiUrl);
175
169
  const arrayBuffer = await response.arrayBuffer();
176
170
  const midi = parseMidi(new Uint8Array(arrayBuffer));
171
+ this.ticksPerBeat = midi.header.ticksPerBeat;
177
172
  const midiData = this.extractMidiData(midi);
178
173
  this.instruments = midiData.instruments;
179
174
  this.timeline = midiData.timeline;
180
- this.ticksPerBeat = midi.header.ticksPerBeat;
181
175
  this.totalTime = this.calcTotalTime();
182
176
  }
183
177
  setChannelAudioNodes(audioContext) {
@@ -263,18 +257,20 @@ export class MidyGM2 {
263
257
  async scheduleTimelineEvents(t, offset, queueIndex) {
264
258
  while (queueIndex < this.timeline.length) {
265
259
  const event = this.timeline[queueIndex];
266
- const time = this.ticksToSecond(event.ticks, this.secondsPerBeat);
267
- if (time > t + this.lookAhead)
260
+ if (event.startTime > t + this.lookAhead)
268
261
  break;
269
262
  switch (event.type) {
270
263
  case "controller":
271
264
  this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
272
265
  break;
273
266
  case "noteOn":
274
- await this.scheduleNoteOn(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, time + this.startDelay - offset);
275
- break;
267
+ if (event.velocity !== 0) {
268
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
269
+ break;
270
+ }
271
+ /* falls through */
276
272
  case "noteOff": {
277
- const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, time + this.startDelay - offset);
273
+ const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
278
274
  if (notePromise) {
279
275
  this.notePromises.push(notePromise);
280
276
  }
@@ -283,9 +279,6 @@ export class MidyGM2 {
283
279
  case "programChange":
284
280
  this.handleProgramChange(event.channel, event.programNumber);
285
281
  break;
286
- case "setTempo":
287
- this.secondsPerBeat = event.microsecondsPerBeat / 1000000;
288
- break;
289
282
  case "sysEx":
290
283
  this.handleSysEx(event.data);
291
284
  }
@@ -294,9 +287,8 @@ export class MidyGM2 {
294
287
  return queueIndex;
295
288
  }
296
289
  getQueueIndex(second) {
297
- const ticks = this.secondToTicks(second, this.secondsPerBeat);
298
290
  for (let i = 0; i < this.timeline.length; i++) {
299
- if (ticks <= this.timeline[i].ticks) {
291
+ if (second <= this.timeline[i].startTime) {
300
292
  return i;
301
293
  }
302
294
  }
@@ -438,18 +430,28 @@ export class MidyGM2 {
438
430
  timeline.push(event);
439
431
  });
440
432
  });
433
+ const priority = {
434
+ setTempo: 0,
435
+ controller: 1,
436
+ };
441
437
  timeline.sort((a, b) => {
442
- if (a.ticks !== b.ticks) {
438
+ if (a.ticks !== b.ticks)
443
439
  return a.ticks - b.ticks;
444
- }
445
- if (a.type !== "controller" && b.type === "controller") {
446
- return -1;
447
- }
448
- if (a.type === "controller" && b.type !== "controller") {
449
- return 1;
450
- }
451
- return 0;
440
+ return (priority[a.type] || 2) - (priority[b.type] || 2);
452
441
  });
442
+ let prevTempoTime = 0;
443
+ let prevTempoTicks = 0;
444
+ let secondsPerBeat = 0.5;
445
+ for (let i = 0; i < timeline.length; i++) {
446
+ const event = timeline[i];
447
+ const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
448
+ event.startTime = prevTempoTime + timeFromPrevTempo;
449
+ if (event.type === "setTempo") {
450
+ prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
451
+ secondsPerBeat = event.microsecondsPerBeat / 1000000;
452
+ prevTempoTicks = event.ticks;
453
+ }
454
+ }
453
455
  return { instruments, timeline };
454
456
  }
455
457
  stopNotes() {
@@ -501,32 +503,12 @@ export class MidyGM2 {
501
503
  }
502
504
  }
503
505
  calcTotalTime() {
504
- const endOfTracks = [];
505
- let prevTicks = 0;
506
506
  let totalTime = 0;
507
- let secondsPerBeat = 0.5;
508
507
  for (let i = 0; i < this.timeline.length; i++) {
509
508
  const event = this.timeline[i];
510
- switch (event.type) {
511
- case "setTempo": {
512
- const durationTicks = event.ticks - prevTicks;
513
- totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
514
- secondsPerBeat = event.microsecondsPerBeat / 1000000;
515
- prevTicks = event.ticks;
516
- break;
517
- }
518
- case "endOfTrack":
519
- endOfTracks.push(event);
520
- }
521
- }
522
- let maxTicks = 0;
523
- for (let i = 0; i < endOfTracks.length; i++) {
524
- const event = endOfTracks[i];
525
- if (maxTicks < event.ticks)
526
- maxTicks = event.ticks;
509
+ if (totalTime < event.startTime)
510
+ totalTime = event.startTime;
527
511
  }
528
- const durationTicks = maxTicks - prevTicks;
529
- totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
530
512
  return totalTime;
531
513
  }
532
514
  currentTime() {
@@ -554,11 +536,8 @@ export class MidyGM2 {
554
536
  const lfo = new OscillatorNode(audioContext, {
555
537
  frequency: 5,
556
538
  });
557
- const lfoGain = new GainNode(audioContext);
558
- lfo.connect(lfoGain);
559
539
  return {
560
540
  lfo,
561
- lfoGain,
562
541
  };
563
542
  }
564
543
  createReverbEffect(audioContext, options = {}) {
@@ -690,12 +669,6 @@ export class MidyGM2 {
690
669
  .exponentialRampToValueAtTime(attackVolume, volAttack)
691
670
  .setValueAtTime(attackVolume, volHold)
692
671
  .linearRampToValueAtTime(sustainVolume, volDecay);
693
- if (channel.modulation > 0) {
694
- const lfoGain = channel.modulationEffect.lfoGain;
695
- lfoGain.connect(bufferSource.detune);
696
- lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
697
- lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
698
- }
699
672
  // filter envelope
700
673
  const softPedalFactor = 1 -
701
674
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
@@ -721,6 +694,19 @@ export class MidyGM2 {
721
694
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
722
695
  .setValueAtTime(adjustedPeekFreq, modHold)
723
696
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
697
+ let lfoGain;
698
+ if (channel.modulation > 0) {
699
+ const vibratoDelay = startTime + channel.vibratoDelay;
700
+ const vibratoAttack = vibratoDelay + 0.1;
701
+ lfoGain = new GainNode(this.audioContext, {
702
+ gain: 0,
703
+ });
704
+ lfoGain.gain
705
+ .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
706
+ .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
707
+ channel.modulationEffect.lfo.connect(lfoGain);
708
+ lfoGain.connect(bufferSource.detune);
709
+ }
724
710
  bufferSource.connect(filterNode);
725
711
  filterNode.connect(gainNode);
726
712
  if (this.mono && channel.currentBufferSource) {
@@ -728,7 +714,7 @@ export class MidyGM2 {
728
714
  channel.currentBufferSource = bufferSource;
729
715
  }
730
716
  bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
731
- return { bufferSource, gainNode, filterNode };
717
+ return { bufferSource, gainNode, filterNode, lfoGain };
732
718
  }
733
719
  calcBank(channel, channelNumber) {
734
720
  if (channel.bankMSB === 121) {
@@ -750,7 +736,7 @@ export class MidyGM2 {
750
736
  const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
751
737
  if (!noteInfo)
752
738
  return;
753
- const { bufferSource, gainNode, filterNode } = await this
739
+ const { bufferSource, gainNode, filterNode, lfoGain } = await this
754
740
  .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
755
741
  this.connectNoteEffects(channel, gainNode);
756
742
  if (channel.sostenutoPedal) {
@@ -764,11 +750,12 @@ export class MidyGM2 {
764
750
  }
765
751
  const scheduledNotes = channel.scheduledNotes;
766
752
  const scheduledNote = {
767
- gainNode,
768
- filterNode,
769
753
  bufferSource,
770
- noteNumber,
754
+ filterNode,
755
+ gainNode,
756
+ lfoGain,
771
757
  noteInfo,
758
+ noteNumber,
772
759
  startTime,
773
760
  };
774
761
  if (scheduledNotes.has(noteNumber)) {
@@ -797,7 +784,7 @@ export class MidyGM2 {
797
784
  continue;
798
785
  if (targetNote.ending)
799
786
  continue;
800
- const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
787
+ const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
801
788
  const velocityRate = (velocity + 127) / 127;
802
789
  const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
803
790
  gainNode.gain.cancelScheduledValues(stopTime);
@@ -819,6 +806,8 @@ export class MidyGM2 {
819
806
  bufferSource.disconnect(0);
820
807
  filterNode.disconnect(0);
821
808
  gainNode.disconnect(0);
809
+ if (lfoGain)
810
+ lfoGain.disconnect(0);
822
811
  resolve();
823
812
  };
824
813
  bufferSource.stop(volEndTime);
@@ -829,41 +818,34 @@ export class MidyGM2 {
829
818
  const now = this.audioContext.currentTime;
830
819
  return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
831
820
  }
832
- releaseSustainPedal(channelNumber) {
833
- const now = this.audioContext.currentTime;
821
+ releaseSustainPedal(channelNumber, halfVelocity) {
822
+ const velocity = halfVelocity * 2;
834
823
  const channel = this.channels[channelNumber];
824
+ const promises = [];
835
825
  channel.sustainPedal = false;
836
826
  channel.scheduledNotes.forEach((scheduledNotes) => {
837
827
  scheduledNotes.forEach((scheduledNote) => {
838
828
  if (scheduledNote) {
839
- const { bufferSource, gainNode, filterNode, noteInfo } = scheduledNote;
840
- const volEndTime = now + noteInfo.volRelease;
841
- gainNode.gain.cancelScheduledValues(now);
842
- gainNode.gain.linearRampToValueAtTime(0, volEndTime);
843
- const maxFreq = this.audioContext.sampleRate / 2;
844
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
845
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
846
- const modEndTime = now + noteInfo.modRelease;
847
- filterNode.frequency
848
- .cancelScheduledValues(stopTime)
849
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
850
- bufferSource.stop(volEndTime);
829
+ const { noteNumber } = scheduledNote;
830
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
831
+ promises.push(promise);
851
832
  }
852
833
  });
853
834
  });
835
+ return promises;
854
836
  }
855
- releaseSostenuto(channelNumber) {
856
- const now = this.audioContext.currentTime;
837
+ releaseSostenutoPedal(channelNumber, halfVelocity) {
838
+ const velocity = halfVelocity * 2;
857
839
  const channel = this.channels[channelNumber];
840
+ const promises = [];
858
841
  channel.sostenutoPedal = false;
859
842
  channel.sostenutoNotes.forEach((activeNote) => {
860
- const { gainNode, bufferSource, noteInfo } = activeNote;
861
- const fadeTime = noteInfo.volRelease;
862
- gainNode.gain.cancelScheduledValues(now);
863
- gainNode.gain.linearRampToValueAtTime(0, now + fadeTime);
864
- bufferSource.stop(now + fadeTime);
843
+ const { noteNumber } = activeNote;
844
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
845
+ promises.push(promise);
865
846
  });
866
847
  channel.sostenutoNotes.clear();
848
+ return promises;
867
849
  }
868
850
  handleMIDIMessage(statusByte, data1, data2) {
869
851
  const channelNumber = omni ? 0 : statusByte & 0x0F;
@@ -973,13 +955,9 @@ export class MidyGM2 {
973
955
  this.channels[channelNumber].bankMSB = msb;
974
956
  }
975
957
  setModulation(channelNumber, modulation) {
976
- const now = this.audioContext.currentTime;
977
958
  const channel = this.channels[channelNumber];
978
- channel.modulation = (modulation * 100 / 127) *
979
- channel.modulationDepthRange;
980
- const lfoGain = channel.modulationEffect.lfoGain;
981
- lfoGain.gain.cancelScheduledValues(now);
982
- lfoGain.gain.setValueAtTime(channel.modulation, now);
959
+ channel.modulation = (modulation / 127) *
960
+ (channel.modulationDepthRange * 100);
983
961
  }
984
962
  setPortamentoTime(channelNumber, portamentoTime) {
985
963
  this.channels[channelNumber].portamentoTime = portamentoTime / 127;
@@ -1014,7 +992,7 @@ export class MidyGM2 {
1014
992
  const isOn = value >= 64;
1015
993
  this.channels[channelNumber].sustainPedal = isOn;
1016
994
  if (!isOn) {
1017
- this.releaseSustainPedal(channelNumber);
995
+ this.releaseSustainPedal(channelNumber, value);
1018
996
  }
1019
997
  }
1020
998
  setPortamento(channelNumber, value) {
@@ -1043,11 +1021,13 @@ export class MidyGM2 {
1043
1021
  const activeNotes = this.getActiveNotes(channel);
1044
1022
  channel.sostenutoNotes = new Map(activeNotes);
1045
1023
  }
1024
+ else {
1025
+ this.releaseSostenutoPedal(channelNumber, value);
1026
+ }
1046
1027
  }
1047
1028
  setSoftPedal(channelNumber, softPedal) {
1048
1029
  const channel = this.channels[channelNumber];
1049
1030
  channel.softPedal = softPedal / 127;
1050
- this.updateChannelGain(channel);
1051
1031
  }
1052
1032
  setRPNMSB(channelNumber, value) {
1053
1033
  this.channels[channelNumber].rpnMSB = value;
@@ -1273,7 +1253,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1273
1253
  writable: true,
1274
1254
  value: {
1275
1255
  currentBufferSource: null,
1276
- volume: 1,
1256
+ volume: 100 / 127,
1277
1257
  pan: 0,
1278
1258
  portamentoTime: 0,
1279
1259
  reverb: 0,
@@ -1290,7 +1270,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1290
1270
  pitchBend: 0,
1291
1271
  fineTuning: 0,
1292
1272
  coarseTuning: 0,
1293
- modulationDepthRange: 2,
1273
+ modulationDepthRange: 0.5,
1294
1274
  }
1295
1275
  });
1296
1276
  Object.defineProperty(MidyGM2, "effectSettings", {
@@ -22,7 +22,6 @@ export class MidyGMLite {
22
22
  };
23
23
  constructor(audioContext: any);
24
24
  ticksPerBeat: number;
25
- secondsPerBeat: number;
26
25
  totalTime: number;
27
26
  noteCheckInterval: number;
28
27
  lookAhead: number;
@@ -48,7 +47,6 @@ export class MidyGMLite {
48
47
  pannerNode: any;
49
48
  modulationEffect: {
50
49
  lfo: any;
51
- lfoGain: any;
52
50
  };
53
51
  expression: number;
54
52
  modulation: number;
@@ -77,7 +75,6 @@ export class MidyGMLite {
77
75
  pannerNode: any;
78
76
  modulationEffect: {
79
77
  lfo: any;
80
- lfoGain: any;
81
78
  };
82
79
  };
83
80
  createChannels(audioContext: any): {
@@ -87,7 +84,6 @@ export class MidyGMLite {
87
84
  pannerNode: any;
88
85
  modulationEffect: {
89
86
  lfo: any;
90
- lfoGain: any;
91
87
  };
92
88
  expression: number;
93
89
  modulation: number;
@@ -131,12 +127,6 @@ export class MidyGMLite {
131
127
  getActiveChannelNotes(scheduledNotes: any): any;
132
128
  createModulationEffect(audioContext: any): {
133
129
  lfo: any;
134
- lfoGain: any;
135
- };
136
- createReverbEffect(audioContext: any, options?: {}): {
137
- convolverNode: any;
138
- dryGain: any;
139
- wetGain: any;
140
130
  };
141
131
  connectNoteEffects(channel: any, gainNode: any): void;
142
132
  cbToRatio(cb: any): number;
@@ -145,12 +135,13 @@ export class MidyGMLite {
145
135
  bufferSource: any;
146
136
  gainNode: any;
147
137
  filterNode: any;
138
+ lfoGain: any;
148
139
  }>;
149
140
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
150
141
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
151
142
  scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
152
143
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
153
- releaseSustainPedal(channelNumber: any): void;
144
+ releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
154
145
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | any[] | Promise<any>;
155
146
  handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
156
147
  handleProgramChange(channelNumber: any, program: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAMA;IAoBE;;;;;;;;;;;;MAYE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAhDD,qBAAmB;IACnB,uBAAqB;IACrB,kBAAc;IACd,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;IA0BhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;MAgBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;QAWC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EA6CC;IAED,mCAQC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA0DC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBA2BC;IAED,sBAGC;IAED,4CASC;IAED,gDAKC;IAED;;;MAUC;IAED;;;;MAoCC;IAED,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED;;;;OAuEC;IAED,kGAsCC;IAED,0EAGC;IAED,sIA0CC;IAED,0FAGC;IAED,8CAuBC;IAED,wFAqBC;IAED,sFAeC;IAED,4DAGC;IAED,+DAEC;IAED,8DAGC;IAED,mFA+BC;IAED,yDAQC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,sCAKC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAcC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,sCAIC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAMA;IAmBE;;;;;;;;;;;;MAYE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA/CD,qBAAmB;IACnB,kBAAc;IACd,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;IA0BhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;MAgBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;QAWC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EA4CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAyEC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4CASC;IAED,gDAKC;IAED;;MAOC;IAED,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED;;;;;OA4EC;IAED,kGAuCC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAqBC;IAED,sFAeC;IAED,4DAGC;IAED,+DAEC;IAED,8DAGC;IAED,mFA+BC;IAED,yDAIC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,sCAKC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAcC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,sCAIC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}