@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 MidyGMLite {
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 MidyGMLite {
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 MidyGMLite {
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 MidyGMLite {
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 MidyGMLite {
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 MidyGMLite {
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 MidyGMLite {
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 MidyGMLite {
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) {
@@ -546,7 +493,7 @@ class MidyGMLite {
546
493
  });
547
494
  let volume = (velocity / 127) * channel.volume * channel.expression;
548
495
  if (volume === 0)
549
- volume = 1e-6; // exponentialRampToValueAtTime() requirea a non-zero value
496
+ volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
550
497
  const attackVolume = this.cbToRatio(-noteInfo.initialAttenuation) * volume;
551
498
  const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
552
499
  const volDelay = startTime + noteInfo.volDelay;
@@ -558,12 +505,6 @@ class MidyGMLite {
558
505
  .exponentialRampToValueAtTime(attackVolume, volAttack)
559
506
  .setValueAtTime(attackVolume, volHold)
560
507
  .linearRampToValueAtTime(sustainVolume, volDecay);
561
- if (channel.modulation > 0) {
562
- const lfoGain = channel.modulationEffect.lfoGain;
563
- lfoGain.connect(bufferSource.detune);
564
- lfoGain.gain.cancelScheduledValues(startTime + channel.vibratoDelay);
565
- lfoGain.gain.setValueAtTime(channel.modulation, startTime + channel.vibratoDelay);
566
- }
567
508
  // filter envelope
568
509
  const maxFreq = this.audioContext.sampleRate / 2;
569
510
  const baseFreq = this.centToHz(noteInfo.initialFilterFc);
@@ -587,10 +528,23 @@ class MidyGMLite {
587
528
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
588
529
  .setValueAtTime(adjustedPeekFreq, modHold)
589
530
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
531
+ let lfoGain;
532
+ if (channel.modulation > 0) {
533
+ const vibratoDelay = startTime + channel.vibratoDelay;
534
+ const vibratoAttack = vibratoDelay + 0.1;
535
+ lfoGain = new GainNode(this.audioContext, {
536
+ gain: 0,
537
+ });
538
+ lfoGain.gain
539
+ .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
540
+ .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
541
+ channel.modulationEffect.lfo.connect(lfoGain);
542
+ lfoGain.connect(bufferSource.detune);
543
+ }
590
544
  bufferSource.connect(filterNode);
591
545
  filterNode.connect(gainNode);
592
546
  bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
593
- return { bufferSource, gainNode, filterNode };
547
+ return { bufferSource, gainNode, filterNode, lfoGain };
594
548
  }
595
549
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
596
550
  const channel = this.channels[channelNumber];
@@ -603,16 +557,17 @@ class MidyGMLite {
603
557
  const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
604
558
  if (!noteInfo)
605
559
  return;
606
- const { bufferSource, gainNode, filterNode } = await this
560
+ const { bufferSource, gainNode, filterNode, lfoGain } = await this
607
561
  .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
608
562
  this.connectNoteEffects(channel, gainNode);
609
563
  const scheduledNotes = channel.scheduledNotes;
610
564
  const scheduledNote = {
611
- gainNode,
612
- filterNode,
613
565
  bufferSource,
614
- noteNumber,
566
+ filterNode,
567
+ gainNode,
568
+ lfoGain,
615
569
  noteInfo,
570
+ noteNumber,
616
571
  startTime,
617
572
  };
618
573
  if (scheduledNotes.has(noteNumber)) {
@@ -639,7 +594,7 @@ class MidyGMLite {
639
594
  continue;
640
595
  if (targetNote.ending)
641
596
  continue;
642
- const { bufferSource, filterNode, gainNode, noteInfo } = targetNote;
597
+ const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
643
598
  const velocityRate = (velocity + 127) / 127;
644
599
  const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
645
600
  gainNode.gain.cancelScheduledValues(stopTime);
@@ -661,6 +616,8 @@ class MidyGMLite {
661
616
  bufferSource.disconnect(0);
662
617
  filterNode.disconnect(0);
663
618
  gainNode.disconnect(0);
619
+ if (lfoGain)
620
+ lfoGain.disconnect(0);
664
621
  resolve();
665
622
  };
666
623
  bufferSource.stop(volEndTime);
@@ -671,28 +628,21 @@ class MidyGMLite {
671
628
  const now = this.audioContext.currentTime;
672
629
  return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
673
630
  }
674
- releaseSustainPedal(channelNumber) {
675
- const now = this.audioContext.currentTime;
631
+ releaseSustainPedal(channelNumber, halfVelocity) {
632
+ const velocity = halfVelocity * 2;
676
633
  const channel = this.channels[channelNumber];
634
+ const promises = [];
677
635
  channel.sustainPedal = false;
678
636
  channel.scheduledNotes.forEach((scheduledNotes) => {
679
637
  scheduledNotes.forEach((scheduledNote) => {
680
638
  if (scheduledNote) {
681
- const { bufferSource, gainNode, filterNode, noteInfo } = scheduledNote;
682
- const volEndTime = now + noteInfo.volRelease;
683
- gainNode.gain.cancelScheduledValues(now);
684
- gainNode.gain.linearRampToValueAtTime(0, volEndTime);
685
- const maxFreq = this.audioContext.sampleRate / 2;
686
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
687
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
688
- const modEndTime = now + noteInfo.modRelease;
689
- filterNode.frequency
690
- .cancelScheduledValues(stopTime)
691
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
692
- bufferSource.stop(volEndTime);
639
+ const { noteNumber } = scheduledNote;
640
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
641
+ promises.push(promise);
693
642
  }
694
643
  });
695
644
  });
645
+ return promises;
696
646
  }
697
647
  handleMIDIMessage(statusByte, data1, data2) {
698
648
  const channelNumber = statusByte & 0x0F;
@@ -774,13 +724,9 @@ class MidyGMLite {
774
724
  }
775
725
  }
776
726
  setModulation(channelNumber, modulation) {
777
- const now = this.audioContext.currentTime;
778
727
  const channel = this.channels[channelNumber];
779
- channel.modulation = (modulation * 100 / 127) *
780
- channel.modulationDepthRange;
781
- const lfoGain = channel.modulationEffect.lfoGain;
782
- lfoGain.gain.cancelScheduledValues(now);
783
- lfoGain.gain.setValueAtTime(channel.modulation, now);
728
+ channel.modulation = (modulation / 127) *
729
+ (channel.modulationDepthRange * 100);
784
730
  }
785
731
  setVolume(channelNumber, volume) {
786
732
  const channel = this.channels[channelNumber];
@@ -809,7 +755,7 @@ class MidyGMLite {
809
755
  const isOn = value >= 64;
810
756
  this.channels[channelNumber].sustainPedal = isOn;
811
757
  if (!isOn) {
812
- this.releaseSustainPedal(channelNumber);
758
+ this.releaseSustainPedal(channelNumber, value);
813
759
  }
814
760
  }
815
761
  setRPNMSB(channelNumber, value) {
@@ -944,7 +890,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
944
890
  configurable: true,
945
891
  writable: true,
946
892
  value: {
947
- volume: 1,
893
+ volume: 100 / 127,
948
894
  pan: 0,
949
895
  vibratoRate: 5,
950
896
  vibratoDepth: 0.5,
@@ -954,7 +900,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
954
900
  dataLSB: 0,
955
901
  program: 0,
956
902
  pitchBend: 0,
957
- modulationDepthRange: 2,
903
+ modulationDepthRange: 0.5,
958
904
  }
959
905
  });
960
906
  Object.defineProperty(MidyGMLite, "effectSettings", {
package/script/midy.d.ts CHANGED
@@ -34,7 +34,6 @@ export class Midy {
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 Midy {
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 Midy {
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 Midy {
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 Midy {
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;
@@ -231,8 +226,8 @@ export class Midy {
231
226
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
232
227
  scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
233
228
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
234
- releaseSustainPedal(channelNumber: any): void;
235
- releaseSostenuto(channelNumber: any): void;
229
+ releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
230
+ releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
236
231
  handleMIDIMessage(statusByte: any, data1: any, data2: any): any;
237
232
  handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
238
233
  handleProgramChange(channelNumber: any, program: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.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,gEAqBC;IAED,sFAeC;IAED,4DAIC;IAED,+DAEC;IAED,8DAGC;IAED,0EAkEC;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,2DAMC;IAED,6DAMC;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,sCAIC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.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,gEAqBC;IAED,sFAeC;IAED,4DAIC;IAED,+DAEC;IAED,8DAGC;IAED,0EAkEC;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,2DAOC;IAED,6DAOC;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,sCAIC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}