@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.
@@ -11,12 +11,6 @@ class MidyGM1 {
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,
@@ -147,10 +141,10 @@ class MidyGM1 {
147
141
  const response = await fetch(midiUrl);
148
142
  const arrayBuffer = await response.arrayBuffer();
149
143
  const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
144
+ this.ticksPerBeat = midi.header.ticksPerBeat;
150
145
  const midiData = this.extractMidiData(midi);
151
146
  this.instruments = midiData.instruments;
152
147
  this.timeline = midiData.timeline;
153
- this.ticksPerBeat = midi.header.ticksPerBeat;
154
148
  this.totalTime = this.calcTotalTime();
155
149
  }
156
150
  setChannelAudioNodes(audioContext) {
@@ -229,18 +223,20 @@ class MidyGM1 {
229
223
  async scheduleTimelineEvents(t, offset, queueIndex) {
230
224
  while (queueIndex < this.timeline.length) {
231
225
  const event = this.timeline[queueIndex];
232
- const time = this.ticksToSecond(event.ticks, this.secondsPerBeat);
233
- if (time > t + this.lookAhead)
226
+ if (event.startTime > t + this.lookAhead)
234
227
  break;
235
228
  switch (event.type) {
236
229
  case "controller":
237
230
  this.handleControlChange(event.channel, event.controllerType, event.value);
238
231
  break;
239
232
  case "noteOn":
240
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, time + this.startDelay - offset);
241
- break;
233
+ if (event.velocity !== 0) {
234
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
235
+ break;
236
+ }
237
+ /* falls through */
242
238
  case "noteOff": {
243
- const notePromise = this.scheduleNoteRelease(event.channel, event.noteNumber, event.velocity, time + this.startDelay - offset);
239
+ const notePromise = this.scheduleNoteRelease(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
244
240
  if (notePromise) {
245
241
  this.notePromises.push(notePromise);
246
242
  }
@@ -249,9 +245,6 @@ class MidyGM1 {
249
245
  case "programChange":
250
246
  this.handleProgramChange(event.channel, event.programNumber);
251
247
  break;
252
- case "setTempo":
253
- this.secondsPerBeat = event.microsecondsPerBeat / 1000000;
254
- break;
255
248
  case "sysEx":
256
249
  this.handleSysEx(event.data);
257
250
  }
@@ -260,9 +253,8 @@ class MidyGM1 {
260
253
  return queueIndex;
261
254
  }
262
255
  getQueueIndex(second) {
263
- const ticks = this.secondToTicks(second, this.secondsPerBeat);
264
256
  for (let i = 0; i < this.timeline.length; i++) {
265
- if (ticks <= this.timeline[i].ticks) {
257
+ if (second <= this.timeline[i].startTime) {
266
258
  return i;
267
259
  }
268
260
  }
@@ -370,18 +362,28 @@ class MidyGM1 {
370
362
  timeline.push(event);
371
363
  });
372
364
  });
365
+ const priority = {
366
+ setTempo: 0,
367
+ controller: 1,
368
+ };
373
369
  timeline.sort((a, b) => {
374
- if (a.ticks !== b.ticks) {
370
+ if (a.ticks !== b.ticks)
375
371
  return a.ticks - b.ticks;
376
- }
377
- if (a.type !== "controller" && b.type === "controller") {
378
- return -1;
379
- }
380
- if (a.type === "controller" && b.type !== "controller") {
381
- return 1;
382
- }
383
- return 0;
372
+ return (priority[a.type] || 2) - (priority[b.type] || 2);
384
373
  });
374
+ let prevTempoTime = 0;
375
+ let prevTempoTicks = 0;
376
+ let secondsPerBeat = 0.5;
377
+ for (let i = 0; i < timeline.length; i++) {
378
+ const event = timeline[i];
379
+ const timeFromPrevTempo = this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
380
+ event.startTime = prevTempoTime + timeFromPrevTempo;
381
+ if (event.type === "setTempo") {
382
+ prevTempoTime += this.ticksToSecond(event.ticks - prevTempoTicks, secondsPerBeat);
383
+ secondsPerBeat = event.microsecondsPerBeat / 1000000;
384
+ prevTempoTicks = event.ticks;
385
+ }
386
+ }
385
387
  return { instruments, timeline };
386
388
  }
387
389
  stopNotes() {
@@ -433,32 +435,12 @@ class MidyGM1 {
433
435
  }
434
436
  }
435
437
  calcTotalTime() {
436
- const endOfTracks = [];
437
- let prevTicks = 0;
438
438
  let totalTime = 0;
439
- let secondsPerBeat = 0.5;
440
439
  for (let i = 0; i < this.timeline.length; i++) {
441
440
  const event = this.timeline[i];
442
- switch (event.type) {
443
- case "setTempo": {
444
- const durationTicks = event.ticks - prevTicks;
445
- totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
446
- secondsPerBeat = event.microsecondsPerBeat / 1000000;
447
- prevTicks = event.ticks;
448
- break;
449
- }
450
- case "endOfTrack":
451
- endOfTracks.push(event);
452
- }
441
+ if (totalTime < event.startTime)
442
+ totalTime = event.startTime;
453
443
  }
454
- let maxTicks = 0;
455
- for (let i = 0; i < endOfTracks.length; i++) {
456
- const event = endOfTracks[i];
457
- if (maxTicks < event.ticks)
458
- maxTicks = event.ticks;
459
- }
460
- const durationTicks = maxTicks - prevTicks;
461
- totalTime += this.ticksToSecond(durationTicks, secondsPerBeat);
462
444
  return totalTime;
463
445
  }
464
446
  currentTime() {
@@ -486,43 +468,8 @@ class MidyGM1 {
486
468
  const lfo = new OscillatorNode(audioContext, {
487
469
  frequency: 5,
488
470
  });
489
- const lfoGain = new GainNode(audioContext);
490
- lfo.connect(lfoGain);
491
471
  return {
492
472
  lfo,
493
- lfoGain,
494
- };
495
- }
496
- createReverbEffect(audioContext, options = {}) {
497
- const { decay = 0.8, preDecay = 0, } = options;
498
- const sampleRate = audioContext.sampleRate;
499
- const length = sampleRate * decay;
500
- const impulse = new AudioBuffer({
501
- numberOfChannels: 2,
502
- length,
503
- sampleRate,
504
- });
505
- const preDecayLength = Math.min(sampleRate * preDecay, length);
506
- for (let channel = 0; channel < impulse.numberOfChannels; channel++) {
507
- const channelData = impulse.getChannelData(channel);
508
- for (let i = 0; i < preDecayLength; i++) {
509
- channelData[i] = Math.random() * 2 - 1;
510
- }
511
- for (let i = preDecayLength; i < length; i++) {
512
- const attenuation = Math.exp(-(i - preDecayLength) / sampleRate / decay);
513
- channelData[i] = (Math.random() * 2 - 1) * attenuation;
514
- }
515
- }
516
- const convolverNode = new ConvolverNode(audioContext, {
517
- buffer: impulse,
518
- });
519
- const dryGain = new GainNode(audioContext);
520
- const wetGain = new GainNode(audioContext);
521
- convolverNode.connect(wetGain);
522
- return {
523
- convolverNode,
524
- dryGain,
525
- wetGain,
526
473
  };
527
474
  }
528
475
  connectNoteEffects(channel, gainNode) {
@@ -559,12 +506,6 @@ class MidyGM1 {
559
506
  .exponentialRampToValueAtTime(attackVolume, volAttack)
560
507
  .setValueAtTime(attackVolume, volHold)
561
508
  .linearRampToValueAtTime(sustainVolume, volDecay);
562
- if (channel.modulation > 0) {
563
- const lfoGain = channel.modulationEffect.lfoGain;
564
- lfoGain.connect(bufferSource.detune);
565
- lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
566
- lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
567
- }
568
509
  // filter envelope
569
510
  const maxFreq = this.audioContext.sampleRate / 2;
570
511
  const baseFreq = this.centToHz(noteInfo.initialFilterFc);
@@ -588,10 +529,23 @@ class MidyGM1 {
588
529
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
589
530
  .setValueAtTime(adjustedPeekFreq, modHold)
590
531
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
532
+ let lfoGain;
533
+ if (channel.modulation > 0) {
534
+ const vibratoDelay = startTime + channel.vibratoDelay;
535
+ const vibratoAttack = vibratoDelay + 0.1;
536
+ lfoGain = new GainNode(this.audioContext, {
537
+ gain: 0,
538
+ });
539
+ lfoGain.gain
540
+ .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
541
+ .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
542
+ channel.modulationEffect.lfo.connect(lfoGain);
543
+ lfoGain.connect(bufferSource.detune);
544
+ }
591
545
  bufferSource.connect(filterNode);
592
546
  filterNode.connect(gainNode);
593
547
  bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
594
- return { bufferSource, gainNode, filterNode };
548
+ return { bufferSource, gainNode, filterNode, lfoGain };
595
549
  }
596
550
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
597
551
  const channel = this.channels[channelNumber];
@@ -604,16 +558,17 @@ class MidyGM1 {
604
558
  const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
605
559
  if (!noteInfo)
606
560
  return;
607
- const { bufferSource, gainNode, filterNode } = await this
561
+ const { bufferSource, gainNode, filterNode, lfoGain } = await this
608
562
  .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
609
563
  this.connectNoteEffects(channel, gainNode);
610
564
  const scheduledNotes = channel.scheduledNotes;
611
565
  const scheduledNote = {
612
- gainNode,
613
- filterNode,
614
566
  bufferSource,
615
- noteNumber,
567
+ filterNode,
568
+ gainNode,
569
+ lfoGain,
616
570
  noteInfo,
571
+ noteNumber,
617
572
  startTime,
618
573
  };
619
574
  if (scheduledNotes.has(noteNumber)) {
@@ -640,7 +595,7 @@ class MidyGM1 {
640
595
  continue;
641
596
  if (targetNote.ending)
642
597
  continue;
643
- const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
598
+ const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
644
599
  const velocityRate = (velocity + 127) / 127;
645
600
  const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
646
601
  gainNode.gain.cancelScheduledValues(stopTime);
@@ -662,6 +617,8 @@ class MidyGM1 {
662
617
  bufferSource.disconnect(0);
663
618
  filterNode.disconnect(0);
664
619
  gainNode.disconnect(0);
620
+ if (lfoGain)
621
+ lfoGain.disconnect(0);
665
622
  resolve();
666
623
  };
667
624
  bufferSource.stop(volEndTime);
@@ -672,28 +629,21 @@ class MidyGM1 {
672
629
  const now = this.audioContext.currentTime;
673
630
  return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
674
631
  }
675
- releaseSustainPedal(channelNumber) {
676
- const now = this.audioContext.currentTime;
632
+ releaseSustainPedal(channelNumber, halfVelocity) {
633
+ const velocity = halfVelocity * 2;
677
634
  const channel = this.channels[channelNumber];
635
+ const promises = [];
678
636
  channel.sustainPedal = false;
679
637
  channel.scheduledNotes.forEach((scheduledNotes) => {
680
638
  scheduledNotes.forEach((scheduledNote) => {
681
639
  if (scheduledNote) {
682
- const { bufferSource, gainNode, filterNode, noteInfo } = scheduledNote;
683
- const volEndTime = now + noteInfo.volRelease;
684
- gainNode.gain.cancelScheduledValues(now);
685
- gainNode.gain.linearRampToValueAtTime(0, volEndTime);
686
- const maxFreq = this.audioContext.sampleRate / 2;
687
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
688
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
689
- const modEndTime = now + noteInfo.modRelease;
690
- filterNode.frequency
691
- .cancelScheduledValues(stopTime)
692
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
693
- bufferSource.stop(volEndTime);
640
+ const { noteNumber } = scheduledNote;
641
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
642
+ promises.push(promise);
694
643
  }
695
644
  });
696
645
  });
646
+ return promises;
697
647
  }
698
648
  handleMIDIMessage(statusByte, data1, data2) {
699
649
  const channelNumber = statusByte & 0x0F;
@@ -775,13 +725,9 @@ class MidyGM1 {
775
725
  }
776
726
  }
777
727
  setModulation(channelNumber, modulation) {
778
- const now = this.audioContext.currentTime;
779
728
  const channel = this.channels[channelNumber];
780
- channel.modulation = (modulation * 100 / 127) *
781
- channel.modulationDepthRange;
782
- const lfoGain = channel.modulationEffect.lfoGain;
783
- lfoGain.gain.cancelScheduledValues(now);
784
- lfoGain.gain.setValueAtTime(channel.modulation, now);
729
+ channel.modulation = (modulation / 127) *
730
+ (channel.modulationDepthRange * 100);
785
731
  }
786
732
  setVolume(channelNumber, volume) {
787
733
  const channel = this.channels[channelNumber];
@@ -810,7 +756,7 @@ class MidyGM1 {
810
756
  const isOn = value >= 64;
811
757
  this.channels[channelNumber].sustainPedal = isOn;
812
758
  if (!isOn) {
813
- this.releaseSustainPedal(channelNumber);
759
+ this.releaseSustainPedal(channelNumber, value);
814
760
  }
815
761
  }
816
762
  setRPNMSB(channelNumber, value) {
@@ -951,7 +897,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
951
897
  configurable: true,
952
898
  writable: true,
953
899
  value: {
954
- volume: 1,
900
+ volume: 100 / 127,
955
901
  pan: 0,
956
902
  vibratoRate: 5,
957
903
  vibratoDepth: 0.5,
@@ -963,7 +909,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
963
909
  pitchBend: 0,
964
910
  fineTuning: 0,
965
911
  coarseTuning: 0,
966
- modulationDepthRange: 2,
912
+ modulationDepthRange: 0.5,
967
913
  }
968
914
  });
969
915
  Object.defineProperty(MidyGM1, "effectSettings", {
@@ -34,7 +34,6 @@ export class MidyGM2 {
34
34
  };
35
35
  constructor(audioContext: any);
36
36
  ticksPerBeat: number;
37
- secondsPerBeat: number;
38
37
  totalTime: number;
39
38
  reverbFactor: number;
40
39
  masterFineTuning: number;
@@ -65,7 +64,6 @@ export class MidyGM2 {
65
64
  pannerNode: any;
66
65
  modulationEffect: {
67
66
  lfo: any;
68
- lfoGain: any;
69
67
  };
70
68
  reverbEffect: {
71
69
  convolverNode: any;
@@ -117,7 +115,6 @@ export class MidyGM2 {
117
115
  pannerNode: any;
118
116
  modulationEffect: {
119
117
  lfo: any;
120
- lfoGain: any;
121
118
  };
122
119
  reverbEffect: {
123
120
  convolverNode: any;
@@ -138,7 +135,6 @@ export class MidyGM2 {
138
135
  pannerNode: any;
139
136
  modulationEffect: {
140
137
  lfo: any;
141
- lfoGain: any;
142
138
  };
143
139
  reverbEffect: {
144
140
  convolverNode: any;
@@ -205,7 +201,6 @@ export class MidyGM2 {
205
201
  getActiveChannelNotes(scheduledNotes: any): any;
206
202
  createModulationEffect(audioContext: any): {
207
203
  lfo: any;
208
- lfoGain: any;
209
204
  };
210
205
  createReverbEffect(audioContext: any, options?: {}): {
211
206
  convolverNode: any;
@@ -225,14 +220,15 @@ export class MidyGM2 {
225
220
  bufferSource: any;
226
221
  gainNode: any;
227
222
  filterNode: any;
223
+ lfoGain: any;
228
224
  }>;
229
225
  calcBank(channel: any, channelNumber: any): any;
230
226
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
231
227
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
232
228
  scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
233
229
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
234
- releaseSustainPedal(channelNumber: any): void;
235
- releaseSostenuto(channelNumber: any): void;
230
+ releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
231
+ releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
236
232
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | any[] | Promise<any>;
237
233
  handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
238
234
  handleProgramChange(channelNumber: any, program: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AAMA;IAyBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF,+BAMC;IAjED,qBAAmB;IACnB,uBAAqB;IACrB,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;IAsChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;;;;;MAuBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAWC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EA6CC;IAED,mCAQC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA4FC;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;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED;;;;OAiFC;IAED,gDAQC;IAED,kGA+CC;IAED,0EAGC;IAED,sIA2CC;IAED,0FAGC;IAED,8CAuBC;IAED,2CAYC;IAED,wFAqBC;IAED,sFAeC;IAED,4DAIC;IAED,+DAEC;IAED,8DAGC;IAED,mFAuDC;IAED,+CAEC;IAED,yDAQC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAQC;IAED,uDAIC;IAED,gDAEC;IAED,gDAEC;IAED,+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,sCAIC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AAMA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF,+BAMC;IAhED,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;IAsChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;;;;MAuBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAWC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EA4CC;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,4CASC;IAED,gDAKC;IAED;;MAOC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED;;;;;OAsFC;IAED,gDAQC;IAED,kGAgDC;IAED,0EAGC;IAED,sIA6CC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAqBC;IAED,sFAeC;IAED,4DAIC;IAED,+DAEC;IAED,8DAGC;IAED,mFAuDC;IAED,+CAEC;IAED,yDAIC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAUC;IAED,uDAGC;IAED,gDAEC;IAED,gDAEC;IAED,+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,sCAIC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}