diff --git a/index.html b/index.html index 5fdfca46..9a0b7b2f 100644 --- a/index.html +++ b/index.html @@ -207,6 +207,9 @@ + + + diff --git a/libs/thirdparty/codec-parser.min.js b/libs/thirdparty/codec-parser.min.js new file mode 100644 index 00000000..fb4ac2e9 --- /dev/null +++ b/libs/thirdparty/codec-parser.min.js @@ -0,0 +1 @@ +var CodecParser=function(e){"use strict";const t=Symbol,s=(()=>{const e="left",t="center",s="right";return["","front ","side ","rear "].map((i=>[[e,s],[e,s,t],[e,t,s],[t,e,s],[t]].flatMap((e=>e.map((e=>i+e)).join(", ")))))})(),i="LFE",r="monophonic (mono)",n="stereo",a="surround",o=(e,...t)=>`${[r,n,`linear ${a}`,"quadraphonic",`5.0 ${a}`,`5.1 ${a}`,`6.1 ${a}`,`7.1 ${a}`][e-1]} (${t.join(", ")})`,h=[r,o(2,s[0][0]),o(3,s[0][2]),o(4,s[1][0],s[3][0]),o(5,s[1][2],s[3][0]),o(6,s[1][2],s[3][0],i),o(7,s[1][2],s[2][0],s[3][4],i),o(8,s[1][2],s[2][0],s[3][0],i)],c=48e3,l=44100,u=32e3,d=24e3,p=22050,_=16e3,m=8e3,g="absoluteGranulePosition",f="bandwidth",b="bitDepth",C="bitrate",y=C+"Maximum",S=C+"Minimum",w=C+"Nominal",P="buffer",k=P+"Fullness",v="codec",F=v+"Frames",x="coupledStreamCount",H="crc",I=H+"16",M=H+"32",U="data",O="description",N="duration",$="emphasis",A="hasOpusPadding",R="header",B="isContinuedPacket",L="isCopyrighted",D="isFirstPage",T="isHome",E="isLastPage",G="isOriginal",z="isPrivate",q="isVbr",V="layer",j="length",W="mode",J=W+"Extension",K="mpeg",Q=K+"Version",X="numberAACFrames",Y="outputGain",Z="preSkip",ee="profile",te=t(),se="protection",ie="rawData",re="segments",ne="subarray",ae="version",oe="vorbis",he=oe+"Comments",ce=oe+"Setup",le="block",ue=le+"ingStrategy",de=t(),pe=le+"Size",_e=le+"size0",me=le+"size1",ge=t(),fe="channel",be=fe+"MappingFamily",Ce=fe+"MappingTable",ye=fe+"Mode",Se=t(),we=fe+"s",Pe="copyright",ke=Pe+"Id",ve=Pe+"IdStart",Fe="frame",xe=Fe+"Count",He=Fe+"Length",Ie="Number",Me=Fe+Ie,Ue=Fe+"Padding",Oe=Fe+"Size",Ne="Rate",$e="inputSample"+Ne,Ae="page",Re=Ae+"Checksum",Be=t(),Le=Ae+"SegmentTable",De=Ae+"Sequence"+Ie,Te="sample",Ee=Te+Ie,Ge=Te+Ne,ze=t(),qe=Te+"s",Ve="stream",je=Ve+"Count",We=Ve+"Info",Je=Ve+"Serial"+Ie,Ke=Ve+"StructureVersion",Qe="total",Xe=Qe+"BytesOut",Ye=Qe+"Duration",Ze=Qe+"Samples",et=t(),tt=t(),st=t(),it=t(),rt=t(),nt=t(),at=t(),ot=t(),ht=t(),ct=t(),lt=t(),ut=t(),dt=t(),pt=t(),_t=t(),mt=t(),gt=t(),ft=t(),bt=Uint8Array,Ct=DataView,yt="reserved",St="bad",wt="free",Pt="none",kt="16bit CRC",vt=(e,t,s)=>{for(let i=0;i0;e--)r=s(r);e[i]=r}return e},Ft=vt(new bt(256),(e=>e),(e=>128&e?7^e<<1:e<<1)),xt=[vt(new Uint16Array(256),(e=>e<<8),(e=>e<<1^(32768&e?32773:0)))],Ht=[vt(new Uint32Array(256),(e=>e),(e=>e>>>1^3988292384*(1&e)))];for(let e=0;e<15;e++){xt.push(new Uint16Array(256)),Ht.push(new Uint32Array(256));for(let t=0;t<=255;t++)xt[e+1][t]=xt[0][xt[e][t]>>>8]^xt[e][t]<<8,Ht[e+1][t]=Ht[e][t]>>>8^Ht[0][255&Ht[e][t]]}const It=e=>{const t=e[j],s=t-16;let i=0,r=0;for(;r<=s;)i=Ht[15][255&(e[r++]^i)]^Ht[14][255&(e[r++]^i>>>8)]^Ht[13][255&(e[r++]^i>>>16)]^Ht[12][e[r++]^i>>>24]^Ht[11][e[r++]]^Ht[10][e[r++]]^Ht[9][e[r++]]^Ht[8][e[r++]]^Ht[7][e[r++]]^Ht[6][e[r++]]^Ht[5][e[r++]]^Ht[4][e[r++]]^Ht[3][e[r++]]^Ht[2][e[r++]]^Ht[1][e[r++]]^Ht[0][e[r++]];for(;r!==t;)i=Ht[0][255&(i^e[r++])]^i>>>8;return~i},Mt=(...e)=>{const t=new bt(e.reduce(((e,t)=>e+t[j]),0));return e.reduce(((e,s)=>(t.set(s,e),e+s[j])),0),t},Ut=e=>String.fromCharCode(...e),Ot=[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15],Nt=e=>Ot[15&e]<<4|Ot[e>>4];class $t{constructor(e){this._data=e,this._pos=8*e[j]}set position(e){this._pos=e}get position(){return this._pos}read(e){const t=Math.floor(this._pos/8),s=this._pos%8;this._pos-=e;return(Nt(this._data[t-1])<<8)+Nt(this._data[t])>>7-s&255}}class At{constructor(e,t){this._onCodecHeader=e,this._onCodecUpdate=t,this[_t]()}[mt](){this._isEnabled=!0}[_t](){this._headerCache=new Map,this._codecUpdateData=new WeakMap,this._codecHeaderSent=!1,this._codecShouldUpdate=!1,this._bitrate=null,this._isEnabled=!1}[pt](e,t){if(this._onCodecUpdate){this._bitrate!==e&&(this._bitrate=e,this._codecShouldUpdate=!0);const s=this._codecUpdateData.get(this._headerCache.get(this._currentHeader));this._codecShouldUpdate&&s&&this._onCodecUpdate({bitrate:e,...s},t),this._codecShouldUpdate=!1}}[ht](e){const t=this._headerCache.get(e);return t&&this._updateCurrentHeader(e),t}[ct](e,t,s){this._isEnabled&&(this._codecHeaderSent||(this._onCodecHeader({...t}),this._codecHeaderSent=!0),this._updateCurrentHeader(e),this._headerCache.set(e,t),this._codecUpdateData.set(t,s))}_updateCurrentHeader(e){this._onCodecUpdate&&e!==this._currentHeader&&(this._codecShouldUpdate=!0,this._currentHeader=e)}}const Rt=new WeakMap,Bt=new WeakMap;class Lt{constructor(e,t){this._codecParser=e,this._headerCache=t}*[at](){let e;for(;;){if(e=yield*this.Frame[lt](this._codecParser,this._headerCache,0),e)return e;this._codecParser[tt](1)}}*[ot](e){let t=yield*this[at]();const s=Bt.get(t)[j];if(e||this._codecParser._flushing||(yield*this.Header[ht](this._codecParser,this._headerCache,s)))return this._headerCache[mt](),this._codecParser[tt](s),this._codecParser[it](t),t;this._codecParser[rt](`Missing ${Fe} at ${s} bytes from current position.`,`Dropping current ${Fe} and trying again.`),this._headerCache[_t](),this._codecParser[tt](1)}}class Dt{constructor(e,t){Bt.set(this,{[R]:e}),this[U]=t}}class Tt extends Dt{static*[lt](e,t,s,i,r){const n=yield*e[ht](s,i,r);if(n){const e=Rt.get(n)[He],i=Rt.get(n)[qe];return new t(n,(yield*s[et](e,r))[ne](0,e),i)}return null}constructor(e,t,s){super(e,t),this[R]=e,this[qe]=s,this[N]=s/e[Ge]*1e3,this[Me]=null,this[Xe]=null,this[Ze]=null,this[Ye]=null,Bt.get(this)[j]=t[j]}}const Et="unsynchronizationFlag",Gt="extendedHeaderFlag",zt="experimentalFlag",qt="footerPresent";class Vt{static*getID3v2Header(e,t,s){const i={};let r=yield*e[et](3,s);if(73!==r[0]||68!==r[1]||51!==r[2])return null;if(r=yield*e[et](10,s),i[ae]=`id3v2.${r[3]}.${r[4]}`,15&r[5])return null;if(i[Et]=!!(128&r[5]),i[Gt]=!!(64&r[5]),i[zt]=!!(32&r[5]),i[qt]=!!(16&r[5]),128&r[6]||128&r[7]||128&r[8]||128&r[9])return null;const n=r[6]<<21|r[7]<<14|r[8]<<7|r[9];return i[j]=10+n,new Vt(i)}constructor(e){this[ae]=e[ae],this[Et]=e[Et],this[Gt]=e[Gt],this[zt]=e[zt],this[qt]=e[qt],this[j]=e[j]}}class jt{constructor(e){Rt.set(this,e),this[b]=e[b],this[C]=null,this[we]=e[we],this[ye]=e[ye],this[Ge]=e[Ge]}}const Wt={0:[wt,wt,wt,wt,wt],16:[32,32,32,32,8],240:[St,St,St,St,St]},Jt=(e,t,s)=>8*((e+s)%t+t)*(1<<(e+s)/t)-8*t*(t/8|0);for(let e=2;e<15;e++)Wt[e<<4]=[32*e,Jt(e,4,0),Jt(e,4,-1),Jt(e,8,4),Jt(e,8,0)];const Kt="bands ",Qt=" to 31",Xt={0:Kt+4+Qt,16:Kt+8+Qt,32:Kt+12+Qt,48:Kt+16+Qt},Yt="bitrateIndex",Zt="v2",es="v1",ts="Intensity stereo ",ss=", MS stereo ",is="on",rs="off",ns={0:ts+rs+ss+rs,16:ts+is+ss+rs,32:ts+rs+ss+is,48:ts+is+ss+is},as={0:{[O]:yt},2:{[O]:"Layer III",[Ue]:1,[J]:ns,[es]:{[Yt]:2,[qe]:1152},[Zt]:{[Yt]:4,[qe]:576}},4:{[O]:"Layer II",[Ue]:1,[J]:Xt,[qe]:1152,[es]:{[Yt]:1},[Zt]:{[Yt]:4}},6:{[O]:"Layer I",[Ue]:4,[J]:Xt,[qe]:384,[es]:{[Yt]:0},[Zt]:{[Yt]:3}}},os="MPEG Version ",hs="ISO/IEC ",cs={0:{[O]:`${os}2.5 (later extension of MPEG 2)`,[V]:Zt,[Ge]:{0:11025,4:12e3,8:m,12:yt}},8:{[O]:yt},16:{[O]:`${os}2 (${hs}13818-3)`,[V]:Zt,[Ge]:{0:p,4:d,8:_,12:yt}},24:{[O]:`${os}1 (${hs}11172-3)`,[V]:es,[Ge]:{0:l,4:c,8:u,12:yt}},length:j},ls={0:kt,1:Pt},us={0:Pt,1:"50/15 ms",2:yt,3:"CCIT J.17"},ds={0:{[we]:2,[O]:n},64:{[we]:2,[O]:"joint "+n},128:{[we]:2,[O]:"dual channel"},192:{[we]:1,[O]:r}};class ps extends jt{static*[ht](e,t,s){const i={},r=yield*Vt.getID3v2Header(e,t,s);r&&(yield*e[et](r[j],s),e[tt](r[j]));const n=yield*e[et](4,s),a=Ut(n[ne](0,4)),o=t[ht](a);if(o)return new ps(o);if(255!==n[0]||n[1]<224)return null;const h=cs[24&n[1]];if(h[O]===yt)return null;const c=6&n[1];if(as[c][O]===yt)return null;const l={...as[c],...as[c][h[V]]};if(i[Q]=h[O],i[V]=l[O],i[qe]=l[qe],i[se]=ls[1&n[1]],i[j]=4,i[C]=Wt[240&n[2]][l[Yt]],i[C]===St)return null;if(i[Ge]=h[Ge][12&n[2]],i[Ge]===yt)return null;if(i[Ue]=2&n[2]&&l[Ue],i[z]=!!(1&n[2]),i[He]=Math.floor(125*i[C]*i[qe]/i[Ge]+i[Ue]),!i[He])return null;const u=192&n[3];if(i[ye]=ds[u][O],i[we]=ds[u][we],i[J]=l[J][48&n[3]],i[L]=!!(8&n[3]),i[G]=!!(4&n[3]),i[$]=us[3&n[3]],i[$]===yt)return null;i[b]=16;{const{length:e,frameLength:s,samples:r,...n}=i;t[ct](a,i,n)}return new ps(i)}constructor(e){super(e),this[C]=e[C],this[$]=e[$],this[Ue]=e[Ue],this[L]=e[L],this[G]=e[G],this[z]=e[z],this[V]=e[V],this[J]=e[J],this[Q]=e[Q],this[se]=e[se]}}class _s extends Tt{static*[lt](e,t,s){return yield*super[lt](ps,_s,e,t,s)}constructor(e,t,s){super(e,t,s)}}class ms extends Lt{constructor(e,t,s){super(e,t),this.Frame=_s,this.Header=ps,s(this[v])}get[v](){return K}*[ut](){return yield*this[ot]()}}const gs={0:"MPEG-4",8:"MPEG-2"},fs={0:"valid",2:St,4:St,6:St},bs={0:kt,1:Pt},Cs={0:"AAC Main",64:"AAC LC (Low Complexity)",128:"AAC SSR (Scalable Sample Rate)",192:"AAC LTP (Long Term Prediction)"},ys={0:96e3,4:88200,8:64e3,12:c,16:l,20:u,24:d,28:p,32:_,36:12e3,40:11025,44:m,48:7350,52:yt,56:yt,60:"frequency is written explicitly"},Ss={0:{[we]:0,[O]:"Defined in AOT Specific Config"},64:{[we]:1,[O]:r},128:{[we]:2,[O]:o(2,s[0][0])},192:{[we]:3,[O]:o(3,s[1][3])},256:{[we]:4,[O]:o(4,s[1][3],s[3][4])},320:{[we]:5,[O]:o(5,s[1][3],s[3][0])},384:{[we]:6,[O]:o(6,s[1][3],s[3][0],i)},448:{[we]:8,[O]:o(8,s[1][3],s[2][0],s[3][0],i)}};class ws extends jt{static*[ht](e,t,s){const i={},r=yield*e[et](7,s),n=Ut([r[0],r[1],r[2],252&r[3]|3&r[6]]),a=t[ht](n);if(a)Object.assign(i,a);else{if(255!==r[0]||r[1]<240)return null;if(i[Q]=gs[8&r[1]],i[V]=fs[6&r[1]],i[V]===St)return null;const e=1&r[1];i[se]=bs[e],i[j]=e?7:9,i[te]=192&r[2],i[ze]=60&r[2];const s=2&r[2];if(i[ee]=Cs[i[te]],i[Ge]=ys[i[ze]],i[Ge]===yt)return null;i[z]=!!s,i[Se]=448&(r[2]<<8|r[3]),i[ye]=Ss[i[Se]][O],i[we]=Ss[i[Se]][we],i[G]=!!(32&r[3]),i[T]=!!(8&r[3]),i[ke]=!!(8&r[3]),i[ve]=!!(4&r[3]),i[b]=16,i[qe]=1024,i[X]=3&r[6];{const{length:e,channelModeBits:s,profileBits:r,sampleRateBits:a,frameLength:o,samples:h,numberAACFrames:c,...l}=i;t[ct](n,i,l)}}if(i[He]=8191&(r[3]<<11|r[4]<<3|r[5]>>5),!i[He])return null;const o=2047&(r[5]<<6|r[6]>>2);return i[k]=2047===o?"VBR":o,new ws(i)}constructor(e){super(e),this[ke]=e[ke],this[ve]=e[ve],this[k]=e[k],this[T]=e[T],this[G]=e[G],this[z]=e[z],this[V]=e[V],this[j]=e[j],this[Q]=e[Q],this[X]=e[X],this[ee]=e[ee],this[se]=e[se]}get audioSpecificConfig(){const e=Rt.get(this),t=e[te]+64<<5|e[ze]<<5|e[Se]>>3,s=new bt(2);return new Ct(s[P]).setUint16(0,t,!1),s}}class Ps extends Tt{static*[lt](e,t,s){return yield*super[lt](ws,Ps,e,t,s)}constructor(e,t,s){super(e,t,s)}}class ks extends Lt{constructor(e,t,s){super(e,t),this.Frame=Ps,this.Header=ws,s(this[v])}get[v](){return"aac"}*[ut](){return yield*this[ot]()}}class vs extends Tt{static _getFrameFooterCrc16(e){return(e[e[j]-2]<<8)+e[e[j]-1]}static[ft](e){const t=vs._getFrameFooterCrc16(e),s=(e=>{const t=e[j],s=t-16;let i=0,r=0;for(;r<=s;)i^=e[r++]<<8|e[r++],i=xt[15][i>>8]^xt[14][255&i]^xt[13][e[r++]]^xt[12][e[r++]]^xt[11][e[r++]]^xt[10][e[r++]]^xt[9][e[r++]]^xt[8][e[r++]]^xt[7][e[r++]]^xt[6][e[r++]]^xt[5][e[r++]]^xt[4][e[r++]]^xt[3][e[r++]]^xt[2][e[r++]]^xt[1][e[r++]]^xt[0][e[r++]];for(;r!==t;)i=(255&i)<<8^xt[0][i>>8^e[r++]];return i})(e[ne](0,-2));return t===s}constructor(e,t,s){t[We]=s,t[I]=vs._getFrameFooterCrc16(e),super(t,e,Rt.get(t)[qe])}}const Fs="get from STREAMINFO metadata block",xs={0:"Fixed",1:"Variable"},Hs={0:yt,16:192};for(let e=2;e<16;e++)Hs[e<<4]=e<6?576*2**(e-2):2**e;const Is={0:Fs,1:88200,2:176400,3:192e3,4:m,5:_,6:p,7:d,8:u,9:l,10:c,11:96e3,15:St},Ms={0:{[we]:1,[O]:r},16:{[we]:2,[O]:o(2,s[0][0])},32:{[we]:3,[O]:o(3,s[0][1])},48:{[we]:4,[O]:o(4,s[1][0],s[3][0])},64:{[we]:5,[O]:o(5,s[1][1],s[3][0])},80:{[we]:6,[O]:o(6,s[1][1],i,s[3][0])},96:{[we]:7,[O]:o(7,s[1][1],i,s[3][4],s[2][0])},112:{[we]:8,[O]:o(8,s[1][1],i,s[3][0],s[2][0])},128:{[we]:2,[O]:`${n} (left, diff)`},144:{[we]:2,[O]:`${n} (diff, right)`},160:{[we]:2,[O]:`${n} (avg, diff)`},176:yt,192:yt,208:yt,224:yt,240:yt},Us={0:Fs,2:8,4:12,6:yt,8:16,10:20,12:24,14:yt};class Os extends jt{static _decodeUTF8Int(e){if(e[0]>254)return null;if(e[0]<128)return{value:e[0],length:1};let t=1;for(let s=64;s&e[0];s>>=1)t++;let s=t-1,i=0,r=0;for(;s>0;r+=6,s--){if(128!=(192&e[s]))return null;i|=(63&e[s])<>t)<{let t=0;const s=e[j];for(let i=0;i!==s;i++)t=Ft[t^e[i]];return t})(i[ne](0,r[j]-1)))return null;if(!a){const{blockingStrategyBits:e,frameNumber:s,sampleNumber:i,samples:a,sampleRateBits:o,blockSizeBits:h,crc:c,length:l,...u}=r;t[ct](n,r,u)}return new Os(r)}constructor(e){super(e),this[I]=null,this[ue]=e[ue],this[pe]=e[pe],this[Me]=e[Me],this[Ee]=e[Ee],this[We]=null}}class Ns extends Lt{constructor(e,t,s){super(e,t),this.Frame=vs,this.Header=Os,s(this[v])}get[v](){return"flac"}*_getNextFrameSyncOffset(e){const t=yield*this._codecParser[et](2,0),s=t[j]-2;for(;e{const t=Os[gt](e,this._headerCache);if(t)return new vs(e,t,this._streamInfo);this._codecParser[rt]("Failed to parse Ogg FLAC frame","Skipping invalid FLAC frame")})).filter((e=>!!e))),e}}class $s{static*[ht](e,t,s){const i={};let r=yield*e[et](28,s);if(79!==r[0]||103!==r[1]||103!==r[2]||83!==r[3])return null;i[Ke]=r[4];if(248&r[5])return null;i[E]=!!(4&r[5]),i[D]=!!(2&r[5]),i[B]=!!(1&r[5]);const n=new Ct(bt.from(r[ne](0,28))[P]);i[g]=((e,t)=>{try{return e.getBigInt64(t,!0)}catch{const s=128&e.getUint8(t+7)?-1:1;let i=e.getUint32(t,!0),r=e.getUint32(t+4,!0);return-1===s&&(i=1+~i,r=1+~r),r>1048575&&console.warn("This platform does not support BigInt"),s*(i+r*2**32)}})(n,6),i[Je]=n.getInt32(14,!0),i[De]=n.getInt32(18,!0),i[Re]=n.getInt32(22,!0);const a=r[26];i[j]=a+27,r=yield*e[et](i[j],s),i[He]=0,i[Le]=[],i[Be]=bt.from(r[ne](27,i[j]));for(let e=0,t=0;e{const t=Ws[gt](this._identificationHeader,e,this._headerCache);if(t){null===this._preSkipRemaining&&(this._preSkipRemaining=t[Z]);let s=t[Oe]*t[xe]/1e3*t[Ge];return this._preSkipRemaining>0&&(this._preSkipRemaining-=s,s=this._preSkipRemaining<0?-this._preSkipRemaining:0),new Rs(e,t,s)}this._codecParser[nt]("Failed to parse Ogg Opus Header","Not a valid Ogg Opus file")}))),e}}class Ks extends Tt{constructor(e,t,s){super(t,e,s)}}const Qs={};for(let e=0;e<8;e++)Qs[e+6]=2**(6+e);class Xs extends jt{static[gt](e,t,s,i){if(e[j]<30)throw new Error("Out of data while inside an Ogg Page");const r=Ut(e[ne](0,30)),n=t[ht](r);if(n)return new Xs(n);const a={[j]:30};if("vorbis"!==r.substr(0,7))return null;a[U]=bt.from(e[ne](0,30));const o=new Ct(a[U][P]);if(a[ae]=o.getUint32(7,!0),0!==a[ae])return null;if(a[we]=e[11],a[ye]=h[a[we]-1]||"application defined",a[Ge]=o.getUint32(12,!0),a[y]=o.getInt32(16,!0),a[w]=o.getInt32(20,!0),a[S]=o.getInt32(24,!0),a[me]=Qs[(240&e[28])>>4],a[_e]=Qs[15&e[28]],a[_e]>a[me])return null;if(1!==e[29])return null;a[b]=32,a[ce]=i,a[he]=s;{const{length:e,data:s,version:i,vorbisSetup:n,vorbisComments:o,...h}=a;t[ct](r,a,h)}return new Xs(a)}constructor(e){super(e),this[y]=e[y],this[S]=e[S],this[w]=e[w],this[_e]=e[_e],this[me]=e[me],this[U]=e[U],this[he]=e[he],this[ce]=e[ce]}}class Ys extends Lt{constructor(e,t,s){super(e,t),this.Frame=Ks,s(this[v]),this._identificationHeader=null,this._setupComplete=!1,this._prevBlockSize=null}get[v](){return oe}[dt](e){e[F]=[];for(const t of Bt.get(e)[re])if(1===t[0])this._headerCache[mt](),this._identificationHeader=e[U],this._setupComplete=!1;else if(3===t[0])this._vorbisComments=t;else if(5===t[0])this._vorbisSetup=t,this._mode=this._parseSetupHeader(t),this._setupComplete=!0;else if(this._setupComplete){const s=Xs[gt](this._identificationHeader,this._headerCache,this._vorbisComments,this._vorbisSetup);s?e[F].push(new Ks(t,s,this._getSamples(t,s))):this._codecParser[logError]("Failed to parse Ogg Vorbis Header","Not a valid Ogg Vorbis file")}return e}_getSamples(e,t){const s=this._mode.blockFlags[e[0]>>1&this._mode.mask]?t[me]:t[_e],i=null===this._prevBlockSize?0:(this._prevBlockSize+s)/4;return this._prevBlockSize=s,i}_parseSetupHeader(e){const t=new $t(e),s={count:0,blockFlags:[]};for(;1&~t.read(1););let i;for(;s.count<64&&t.position>0;){Nt(t.read(8));let e=0;for(;0===t.read(8)&&e++<3;);if(4!==e){1+((126&Nt(i))>>1)!==s.count&&this._codecParser[rt]("vorbis derived mode count did not match actual mode count");break}i=t.read(7),s.blockFlags.unshift(1&i),t.position+=6,s.count++}return s.mask=(1<1&&e[De]>1&&this._codecParser[rt]("Unexpected gap in Ogg Page Sequence Number.",`Expected: ${this._pageSequenceNumber+1}, Got: ${e[De]}`),this._pageSequenceNumber=e[De]}_parsePage(e){null===this._isSupported&&(this._pageSequenceNumber=e[De],this._isSupported=this._checkCodecSupport(e)),this._checkPageSequenceNumber(e);const t=Bt.get(e),s=Rt.get(t[R]);let i=0;if(t[re]=s[Le].map((t=>e[U][ne](i,i+=t))),this._continuedPacket[j]&&(t[re][0]=Mt(this._continuedPacket,t[re][0]),this._continuedPacket=new bt),255===s[Be][s[Be][j]-1]&&(this._continuedPacket=Mt(this._continuedPacket,t[re].pop())),null!==this._previousAbsoluteGranulePosition&&(e[qe]=Number(e[g]-this._previousAbsoluteGranulePosition)),this._previousAbsoluteGranulePosition=e[g],this._isSupported){const t=this._parser[dt](e);return this._codecParser[it](t),t}return e}}class ei extends Lt{constructor(e,t,s){super(e,t),this._onCodec=s,this.Frame=As,this.Header=$s,this._streams=new Map,this._currentSerialNumber=null}get[v](){const e=this._streams.get(this._currentSerialNumber);return e?e.codec:""}*[ut](){const e=yield*this[ot](!0);this._currentSerialNumber=e[Je];let t=this._streams.get(this._currentSerialNumber);return t||(t=new Zs(this._codecParser,this._headerCache,this._onCodec),this._streams.set(this._currentSerialNumber,t)),e[E]&&this._streams.delete(this._currentSerialNumber),t._parsePage(e)}}const ti=()=>{};const si=g,ii=f,ri=b,ni=C,ai=y,oi=S,hi=w,ci=P,li=k,ui=v,di=F,pi=x,_i=H,mi=I,gi=M,fi=U,bi=O,Ci=N,yi=$,Si=A,wi=R,Pi=B,ki=L,vi=D,Fi=T,xi=E,Hi=G,Ii=z,Mi=q,Ui=V,Oi=j,Ni=W,$i=J,Ai=K,Ri=Q,Bi=X,Li=Y,Di=Z,Ti=ee,Ei=se,Gi=ie,zi=re,qi=ne,Vi=ae,ji=oe,Wi=he,Ji=ce,Ki=ue,Qi=pe,Xi=_e,Yi=me,Zi=be,er=Ce,tr=ye,sr=we,ir=ke,rr=ve,nr=Fe,ar=xe,or=He,hr=Me,cr=Ue,lr=Oe,ur=$e,dr=Re,pr=Le,_r=De,mr=Ee,gr=Ge,fr=qe,br=je,Cr=We,yr=Je,Sr=Ke,wr=Xe,Pr=Ye,kr=Ze;return e.CodecParser=class{constructor(e,{onCodec:t,onCodecHeader:s,onCodecUpdate:i,enableLogging:r=!1,enableFrameCRC32:n=!0}={}){this._inputMimeType=e,this._onCodec=t||ti,this._onCodecHeader=s||ti,this._onCodecUpdate=i,this._enableLogging=r,this._crc32=n?It:ti,this[_t]()}get[v](){return this._parser?this._parser[v]:""}[_t](){this._headerCache=new At(this._onCodecHeader,this._onCodecUpdate),this._generator=this._getGenerator(),this._generator.next()}*flush(){this._flushing=!0;for(let e=this._generator.next();e.value;e=this._generator.next())yield e.value;this._flushing=!1,this[_t]()}*parseChunk(e){for(let t=this._generator.next(e);t.value;t=this._generator.next())yield t.value}parseAll(e){return[...this.parseChunk(e),...this.flush()]}*_getGenerator(){if(this._inputMimeType.match(/aac/))this._parser=new ks(this,this._headerCache,this._onCodec);else if(this._inputMimeType.match(/mpeg/))this._parser=new ms(this,this._headerCache,this._onCodec);else if(this._inputMimeType.match(/flac/))this._parser=new Ns(this,this._headerCache,this._onCodec);else{if(!this._inputMimeType.match(/ogg/))throw new Error(`Unsupported Codec ${mimeType}`);this._parser=new ei(this,this._headerCache,this._onCodec)}for(this._frameNumber=0,this._currentReadPosition=0,this._totalBytesIn=0,this._totalBytesOut=0,this._totalSamples=0,this._sampleRate=void 0,this._rawData=new Uint8Array(0);;){const e=yield*this._parser[ut]();e&&(yield e)}}*[et](e=0,t=0){let s;for(;this._rawData[j]<=e+t;){if(s=yield,this._flushing)return this._rawData[ne](t);s&&(this._totalBytesIn+=s[j],this._rawData=Mt(this._rawData,s))}return this._rawData[ne](t)}[tt](e){this._currentReadPosition+=e,this._rawData=this._rawData[ne](e)}[st](e){this._sampleRate=e[R][Ge],e[R][C]=e[N]>0?8*Math.round(e[U][j]/e[N]):0,e[Me]=this._frameNumber++,e[Xe]=this._totalBytesOut,e[Ze]=this._totalSamples,e[Ye]=this._totalSamples/this._sampleRate*1e3,e[M]=this._crc32(e[U]),this._headerCache[pt](e[R][C],e[Ye]),this._totalBytesOut+=e[U][j],this._totalSamples+=e[qe]}[it](e){if(e[F]){if(e[E]){let t=e[qe];e[F].forEach((e=>{const s=e[qe];t0?t:0,e[N]=e[qe]/e[R][Ge]*1e3),t-=s,this[st](e)}))}else e[qe]=0,e[F].forEach((t=>{e[qe]+=t[qe],this[st](t)}));e[N]=e[qe]/this._sampleRate*1e3||0,e[Ze]=this._totalSamples,e[Ye]=this._totalSamples/this._sampleRate*1e3||0,e[Xe]=this._totalBytesOut}else this[st](e)}_log(e,t){if(this._enableLogging){const s=[`${v}: ${this[v]}`,`inputMimeType: ${this._inputMimeType}`,`readPosition: ${this._currentReadPosition}`,`totalBytesIn: ${this._totalBytesIn}`,`${Xe}: ${this._totalBytesOut}`],i=Math.max(...s.map((e=>e[j])));t.push(`--stats--${"-".repeat(i-9)}`,...s,"-".repeat(i)),e("codec-parser",t.reduce(((e,t)=>e+"\n "+t),""))}}[rt](...e){this._log(console.warn,e)}[nt](...e){this._log(console.error,e)}},e.absoluteGranulePosition=si,e.bandwidth=ii,e.bitDepth=ri,e.bitrate=ni,e.bitrateMaximum=ai,e.bitrateMinimum=oi,e.bitrateNominal=hi,e.blockSize=Qi,e.blockingStrategy=Ki,e.blocksize0=Xi,e.blocksize1=Yi,e.buffer=ci,e.bufferFullness=li,e.channelMappingFamily=Zi,e.channelMappingTable=er,e.channelMode=tr,e.channels=sr,e.codec=ui,e.codecFrames=di,e.copyrightId=ir,e.copyrightIdStart=rr,e.coupledStreamCount=pi,e.crc=_i,e.crc16=mi,e.crc32=gi,e.data=fi,e.description=bi,e.duration=Ci,e.emphasis=yi,e.frame=nr,e.frameCount=ar,e.frameLength=or,e.frameNumber=hr,e.framePadding=cr,e.frameSize=lr,e.hasOpusPadding=Si,e.header=wi,e.inputSampleRate=ur,e.isContinuedPacket=Pi,e.isCopyrighted=ki,e.isFirstPage=vi,e.isHome=Fi,e.isLastPage=xi,e.isOriginal=Hi,e.isPrivate=Ii,e.isVbr=Mi,e.layer=Ui,e.length=Oi,e.mode=Ni,e.modeExtension=$i,e.mpeg=Ai,e.mpegVersion=Ri,e.numberAACFrames=Bi,e.outputGain=Li,e.pageChecksum=dr,e.pageSegmentTable=pr,e.pageSequenceNumber=_r,e.preSkip=Di,e.profile=Ti,e.protection=Ei,e.rawData=Gi,e.sampleNumber=mr,e.sampleRate=gr,e.samples=fr,e.segments=zi,e.streamCount=br,e.streamInfo=Cr,e.streamSerialNumber=yr,e.streamStructureVersion=Sr,e.subarray=qi,e.totalBytesOut=wr,e.totalDuration=Pr,e.totalSamples=kr,e.version=Vi,e.vorbis=ji,e.vorbisComments=Wi,e.vorbisSetup=Ji,e}({}); diff --git a/libs/thirdparty/ogg-opus-decoder.min.js b/libs/thirdparty/ogg-opus-decoder.min.js new file mode 100644 index 00000000..9525201c Binary files /dev/null and b/libs/thirdparty/ogg-opus-decoder.min.js differ diff --git a/libs/thirdparty/ogg-vorbis-decoder.min.js b/libs/thirdparty/ogg-vorbis-decoder.min.js new file mode 100644 index 00000000..0f030ae9 --- /dev/null +++ b/libs/thirdparty/ogg-vorbis-decoder.min.js @@ -0,0 +1,195 @@ +/* Copyright 2021-2023 Ethan Halsall. This file is part of wasm-audio-decoders. https://github.com/eshaz/wasm-audio-decoders */ +var t,s;t=this,s=function(t,s){const i=(t,s=4294967295,i=79764919)=>{const e=new Int32Array(256);let r,n,h,o=s;for(r=0;r<256;r++){for(h=r<<24,n=8;n>0;--n)h=2147483648&h?h<<1^i:h<<1;e[r]=h}for(r=0;r>24^t[r])];return o},e=(t,s=i)=>{const e=t=>new Uint8Array(t.length/2).map(((s,i)=>parseInt(t.substring(2*i,2*(i+1)),16))),r=t=>e(t)[0],n=new Map;[,8364,,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,,381,,,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,,382,376].forEach(((t,s)=>n.set(t,s)));const h=new Uint8Array(t.length);let o,a,l,c=!1,u=0,U=42,d=t.length>13&&"dynEncode"===t.substring(0,9),f=0;d&&(f=11,a=r(t.substring(9,f)),a<=1&&(f+=2,U=r(t.substring(11,f))),1===a&&(f+=8,l=(t=>new DataView(e(t).buffer).getInt32(0,!0))(t.substring(13,f))));const p=256-U;for(let i=f;i255){const t=n.get(o);t&&(o=t+127)}c&&(c=!1,o-=64),h[u++]=o0?o+p:o-U}else c=!0;const M=h.subarray(0,u);if(d&&1===a){const t=s(M);if(t!==l){const s="Decode failed crc32 validation";throw console.error("`simple-yenc`\n",s+"\n","Expected: "+l+"; Got: "+t+"\n","Visit https://github.com/eshaz/simple-yenc for more information"),Error(s)}}return M};function r(){const t=Uint8Array,s=Float32Array;r.t||Object.defineProperties(r,{t:{value:new WeakMap},u:{value(t,s){r.t.set(t,Promise.resolve(s))}},U:{value(t,s){let i=r.t.get(t);return i||(s?i=WebAssembly.compile(e(s)):(s=t.M,i=r.Y(s).then((t=>WebAssembly.compile(t)))),r.t.set(t,i)),i}},T:{value(t,i){let e=new s(i),r=0,n=0;for(;r({O:t,channelData:s,samplesDecoded:i,sampleRate:e,bitDepth:r})},v:{value(t,s,i,e,n,h){let o,a,l=[];for(o=0;o{const e=String.raw`dynEncode0114db91da9bu‡*t“““t“““““t““““$#“U¤¤“U¤¤3yƒ†„‰zzss|yu„svu‡yÚ&ˆ“4<054<,5T44^T44<(6U~J(44< ~A544U~6J0444ˆ†545 444J0444‰J,4U“4ˆU“‡…Ò“7U45“4U4Z“4U4U^/6545T4T44BUˆ~64CU~O4U54U~5 U5T4B4Z!4U~5U5U5T4U~6U4ZTU5U5T44~4O4U2ZTU5T44Z!4B6T44Uˆ~64B6U~O44Uˆ~4O4U~54U~5 44~C4~54U~5 44~5454Uˆ4B6Ub!444~UO4U~5 “U5“4U4ZTUŠ#44U$4†64<4~B6^“4<444~Uˆ~B4U~54Uˆ544~544~Uˆ5 µ“Uä#UJUè#5TT4U0ZTTUX5U5T4T4Uà#~4OU4U $~Cˆ4~54U~5 T44$6U\!TTT4UaT4<6T4<64<Z!44~4N4<U~5 4U”Z!4U±_TUŠ#44U•Uˆ6UÔ~B$544$6U\!4Uˆ6U¤#~B44Uä#~B$~64<6_TU‰#444U”~B~6~54<Y!44<_!T4Y!4<64~444~AN44<U~6J4U5 44J4U”[!U#44UŠO4U~54U~5 U54 “7U6844J44J 4UJ4UJ04VK(44<J44<J$4U´~54U~5 4U¤~5!TTT4U$5"U“5TTTTTTT4U$"4VK,U54<(6U~64<$6_!4< 64~6A54A544U~6#J(U’54A4U‡[!44J(44#~A4Uˆ6U“‡UŠU…[!44†64~64_!4<64~54<6T4<4]TU5 T4Y!44~44~AN4U~54U~54U5 44J(44J UÄA!U5U”#UôJU"UÔJUœ#UÔ"JU˜#U´"JT4U´ZTU5T4UôZTU5T4UDZTU5T4U$[T44~UO4U~5 UÔUô4U~U´$.U5T4UP[T4U~4~UO4U~5 U˜#<Uœ#<4U~U2$.UÄUN 44 ~UO4U~5 44!~UO4U~5 4U~4~UO4U~5 44J44J(U5 44U¤~J@44Uä~J<44UD~J844U~J44U$54U$5U‘54U$54U1^4U1^†!4U•~54U~5U”54U~6U4U^/65T4T4U$54U~4BUˆ~4O4U54U~5 UU'464U'_/54UˆU”~5T4T4U~4BUˆ~UO4U54U~5 U54Uä~4U¤~4U~U'$!44~5U5T44\T44U<~$6U\!4U#aT4U~4Uˆ~4O4U~5 U5U5U5TTT4U$"4YTU5 4Uˆ4~C5U5 U5U5444$4~64~\TUŽ5 4U~4Uˆ~5T4Y!44O4U~54U~54U5 4CYTU‹5 4Uä~4U¤~4U~4$6TU54U\!44Bæ4Bä~[!4U~4UD~4U~4Uˆ~4$6TUŒ54U\!44B†4B„~[!44U<~4U4~$5 4U"U˜#$544"†Y!454U^!44<J44<(J454U~84­U”N!#%'+/37?GOWgw‡—·×÷Uä;U”9$%& !"#`;r.U(r,e).then((t=>WebAssembly.instantiate(t,{}))).then((({exports:e})=>{const r=new Map(Object.entries(e)),n=r.get("puff"),h=r.get("memory").buffer,o=new t(h),a=new DataView(h);let l=r.get("__heap_base");const c=s.length,u=l;l+=4,a.setInt32(u,c,!0);const U=l;l+=c,o.set(s,U);const d=l;l+=4,a.setInt32(d,o.byteLength-l,!0),n(l,d,U,u),i(o.slice(l,l+a.getInt32(d,!0)))}))}))}}}),Object.defineProperty(this,"M",{enumerable:!0,get:()=>this.B}),this.F=(t,s,i)=>{let e=[],r=0;for(;r{const e=this.B.V(s.BYTES_PER_ELEMENT*t);return i&&this.S.add(e),{H:e,$:t,C:new s(this.B.I,e,t)}},this.free=()=>{this.S.forEach((t=>{this.B.free(t)})),this.S.clear()},this.J=t=>{const s=[],i=new Uint8Array(this.B.I);for(let e=i[t];0!==e;e=i[++t])s.push(e);return String.fromCharCode.apply(null,s)},this.P=(t,s,i,e,r,n)=>{t.push({message:s,frameLength:i,frameNumber:e,inputBytes:r,outputSamples:n})},this.instantiate=(t,s)=>(s&&r.u(t,s),this.B=new t(r).instantiate(),this.S=new Set,this.B.ready.then((()=>this)))}class n extends((()=>globalThis.Worker||s)()){constructor(t,s,i,e){r.t||new r;let n=r.t.get(i);if(!n){let t,s="text/javascript",h=`'use strict';(${""+((t,s,i)=>{let e,r,n=new Promise((t=>{r=t}));self.onmessage=({data:{id:h,command:o,data:a}})=>{let l,c=n,u={id:h};"init"===o?(Object.defineProperties(t,{D:{value:s},N:{value:i},module:{value:a.module},Z:{value:!0}}),e=new t(a.options),r()):"free"===o?e.free():"ready"===o?c=c.then((()=>e.ready)):"reset"===o?c=c.then((()=>e.reset())):(Object.assign(u,e[o](Array.isArray(a)?a.map((t=>new Uint8Array(t))):new Uint8Array(a))),l=u.channelData?u.channelData.map((t=>t.buffer)):[]),c.then((()=>self.postMessage(u,l)))}})})(${i}, ${r}, ${e})`;try{t=void 0!==process.versions.node}catch{}n=t?`data:${s};base64,${Buffer.from(h).toString("base64")}`:URL.createObjectURL(new Blob([h],{type:s})),r.t.set(i,n)}super(n,{name:s}),this.K=Number.MIN_SAFE_INTEGER,this.R=new Map,this.onmessage=({data:t})=>{const{id:s,...i}=t;this.R.get(s)(i),this.R.delete(s)},new e(r).U().then((s=>{this.G("init",{module:s,options:t})}))}async G(t,s){return new Promise((i=>{this.postMessage({command:t,id:this.K,data:s}),this.R.set(this.K++,i)}))}get ready(){return this.G("ready")}async free(){await this.G("free").finally((()=>{this.terminate()}))}async reset(){await this.G("reset")}}const h=(t,s)=>{Object.defineProperty(t,"name",{value:s})},o=Symbol,a=(()=>{const t="left",s="center",i="right";return["","front ","side ","rear "].map((e=>[[t,i],[t,i,s],[t,s,i],[s,t,i],[s]].flatMap((t=>t.map((t=>e+t)).join(", ")))))})(),l="LFE",c="monophonic (mono)",u="stereo",U="surround",d=(t,...s)=>`${[c,u,"linear "+U,"quadraphonic","5.0 "+U,"5.1 "+U,"6.1 "+U,"7.1 "+U][t-1]} (${s.join(", ")})`,f=[c,d(2,a[0][0]),d(3,a[0][2]),d(4,a[1][0],a[3][0]),d(5,a[1][2],a[3][0]),d(6,a[1][2],a[3][0],l),d(7,a[1][2],a[2][0],a[3][4],l),d(8,a[1][2],a[2][0],a[3][0],l)],p=48e3,M=44100,y=32e3,m=24e3,w=22050,g=16e3,Y=8e3,T="absoluteGranulePosition",b="bandwidth",A="bitDepth",O="bitrate",v=O+"Maximum",B=O+"Minimum",F=O+"Nominal",_="buffer",V=_+"Fullness",k="codec",S=k+"Frames",q="coupledStreamCount",H="crc",$=H+"16",C=H+"32",I="data",j="description",J="duration",x="emphasis",P="hasOpusPadding",D="header",E="isContinuedPacket",N="isCopyrighted",Z="isFirstPage",z="isHome",K="isLastPage",Q="isOriginal",R="isPrivate",G="isVbr",X="layer",L="length",W="mode",tt=W+"Extension",st="mpeg",it=st+"Version",et="numberAACFrames",rt="outputGain",nt="preSkip",ht="profile",ot=o(),at="protection",lt="segments",ct="subarray",ut="version",Ut="vorbis",dt=Ut+"Comments",ft=Ut+"Setup",pt="block",Mt=pt+"ingStrategy",yt=o(),mt=pt+"Size",wt=pt+"size0",gt=pt+"size1",Yt=o(),Tt="channel",bt=Tt+"MappingFamily",At=Tt+"MappingTable",Ot=Tt+"Mode",vt=o(),Bt=Tt+"s",Ft="copyright",_t=Ft+"Id",Vt=Ft+"IdStart",kt="frame",St=kt+"Count",qt=kt+"Length",Ht="Number",$t=kt+Ht,Ct=kt+"Padding",It=kt+"Size",jt="Rate",Jt="inputSample"+jt,xt="page",Pt=xt+"Checksum",Dt=o(),Et=xt+"SegmentTable",Nt=xt+"Sequence"+Ht,Zt="sample",zt=Zt+Ht,Kt=Zt+jt,Qt=o(),Rt=Zt+"s",Gt="stream",Xt=Gt+"Count",Lt=Gt+"Info",Wt=Gt+"Serial"+Ht,ts=Gt+"StructureVersion",ss="total",is=ss+"BytesOut",es=ss+"Duration",rs=ss+"Samples",ns=o(),hs=o(),os=o(),as=o(),ls=o(),cs=o(),us=o(),Us=o(),ds=o(),fs=o(),ps=o(),Ms=o(),ys=o(),ms=o(),ws=o(),gs=o(),Ys=o(),Ts=o(),bs=Uint8Array,As=DataView,Os="reserved",vs="bad",Bs="free",Fs="none",_s="16bit CRC",Vs=(t,s,i)=>{for(let e=0;e0;t--)r=i(r);t[e]=r}return t},ks=Vs(new bs(256),(t=>t),(t=>128&t?7^t<<1:t<<1)),Ss=[Vs(new Uint16Array(256),(t=>t<<8),(t=>t<<1^(32768&t?32773:0)))],qs=[Vs(new Uint32Array(256),(t=>t),(t=>t>>>1^3988292384*(1&t)))];for(let Ye=0;Ye<15;Ye++){Ss.push(new Uint16Array(256)),qs.push(new Uint32Array(256));for(let t=0;t<=255;t++)Ss[Ye+1][t]=Ss[0][Ss[Ye][t]>>>8]^Ss[Ye][t]<<8,qs[Ye+1][t]=qs[Ye][t]>>>8^qs[0][255&qs[Ye][t]]}const Hs=t=>{const s=t[L],i=s-16;let e=0,r=0;for(;r<=i;)e=qs[15][255&(t[r++]^e)]^qs[14][255&(t[r++]^e>>>8)]^qs[13][255&(t[r++]^e>>>16)]^qs[12][t[r++]^e>>>24]^qs[11][t[r++]]^qs[10][t[r++]]^qs[9][t[r++]]^qs[8][t[r++]]^qs[7][t[r++]]^qs[6][t[r++]]^qs[5][t[r++]]^qs[4][t[r++]]^qs[3][t[r++]]^qs[2][t[r++]]^qs[1][t[r++]]^qs[0][t[r++]];for(;r!==s;)e=qs[0][255&(e^t[r++])]^e>>>8;return~e},$s=(...t)=>{const s=new bs(t.reduce(((t,s)=>t+s[L]),0));return t.reduce(((t,i)=>(s.set(i,t),t+i[L])),0),s},Cs=t=>String.fromCharCode(...t),Is=[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15],js=t=>Is[15&t]<<4|Is[t>>4];class Js{constructor(t){this.X=t,this.L=8*t[L]}set position(t){this.L=t}get position(){return this.L}read(t){const s=Math.floor(this.L/8),i=this.L%8;return this.L-=t,(js(this.X[s-1])<<8)+js(this.X[s])>>7-i&255}}class xs{constructor(t,s){this.W=t,this.tt=s,this[ws]()}[gs](){this.st=!0}[ws](){this.it=new Map,this.et=new WeakMap,this.rt=!1,this.nt=!1,this.ht=null,this.st=!1}[ms](t,s){if(this.tt){this.ht!==t&&(this.ht=t,this.nt=!0);const i=this.et.get(this.it.get(this.ot));this.nt&&i&&this.tt({lt:t,...i},s),this.nt=!1}}[ds](t){const s=this.it.get(t);return s&&this.ct(t),s}[fs](t,s,i){this.st&&(this.rt||(this.W({...s}),this.rt=!0),this.ct(t),this.it.set(t,s),this.et.set(s,i))}ct(t){this.tt&&t!==this.ot&&(this.nt=!0,this.ot=t)}}const Ps=new WeakMap,Ds=new WeakMap;class Es{constructor(t,s){this.ut=t,this.it=s}*[us](){let t;for(;;){if(t=yield*this.Ut[ps](this.ut,this.it,0),t)return t;this.ut[hs](1)}}*[Us](t){let s=yield*this[us]();const i=Ds.get(s)[L];if(t||this.ut.dt||(yield*this.ft[ds](this.ut,this.it,i)))return this.it[gs](),this.ut[hs](i),this.ut[as](s),s;this.ut[ls](`Missing ${kt} at ${i} bytes from current position.`,`Dropping current ${kt} and trying again.`),this.it[ws](),this.ut[hs](1)}}class Ns{constructor(t,s){Ds.set(this,{[D]:t}),this[I]=s}}class Zs extends Ns{static*[ps](t,s,i,e,r){const n=yield*t[ds](i,e,r);if(n){const t=Ps.get(n)[qt],e=Ps.get(n)[Rt];return new s(n,(yield*i[ns](t,r))[ct](0,t),e)}return null}constructor(t,s,i){super(t,s),this[D]=t,this[Rt]=i,this[J]=i/t[Kt]*1e3,this[$t]=null,this[is]=null,this[rs]=null,this[es]=null,Ds.get(this)[L]=s[L]}}const zs="unsynchronizationFlag",Ks="extendedHeaderFlag",Qs="experimentalFlag",Rs="footerPresent";class Gs{static*Mt(t,s,i){const e={};let r=yield*t[ns](3,i);if(73!==r[0]||68!==r[1]||51!==r[2])return null;if(r=yield*t[ns](10,i),e[ut]=`id3v2.${r[3]}.${r[4]}`,15&r[5])return null;if(e[zs]=!!(128&r[5]),e[Ks]=!!(64&r[5]),e[Qs]=!!(32&r[5]),e[Rs]=!!(16&r[5]),128&r[6]||128&r[7]||128&r[8]||128&r[9])return null;const n=r[6]<<21|r[7]<<14|r[8]<<7|r[9];return e[L]=10+n,new Gs(e)}constructor(t){this[ut]=t[ut],this[zs]=t[zs],this[Ks]=t[Ks],this[Qs]=t[Qs],this[Rs]=t[Rs],this[L]=t[L]}}class Xs{constructor(t){Ps.set(this,t),this[A]=t[A],this[O]=null,this[Bt]=t[Bt],this[Ot]=t[Ot],this[Kt]=t[Kt]}}const Ls={0:[Bs,Bs,Bs,Bs,Bs],16:[32,32,32,32,8],240:[vs,vs,vs,vs,vs]},Ws=(t,s,i)=>8*((t+i)%s+s)*(1<<(t+i)/s)-8*s*(s/8|0);for(let Ye=2;Ye<15;Ye++)Ls[Ye<<4]=[32*Ye,Ws(Ye,4,0),Ws(Ye,4,-1),Ws(Ye,8,4),Ws(Ye,8,0)];const ti="bands ",si=" to 31",ii={0:ti+4+si,16:ti+8+si,32:ti+12+si,48:ti+16+si},ei="bitrateIndex",ri="v2",ni="v1",hi="Intensity stereo ",oi=", MS stereo ",ai="on",li="off",ci={0:hi+li+oi+li,16:hi+ai+oi+li,32:hi+li+oi+ai,48:hi+ai+oi+ai},ui={0:{[j]:Os},2:{[j]:"Layer III",[Ct]:1,[tt]:ci,[ni]:{[ei]:2,[Rt]:1152},[ri]:{[ei]:4,[Rt]:576}},4:{[j]:"Layer II",[Ct]:1,[tt]:ii,[Rt]:1152,[ni]:{[ei]:1},[ri]:{[ei]:4}},6:{[j]:"Layer I",[Ct]:4,[tt]:ii,[Rt]:384,[ni]:{[ei]:0},[ri]:{[ei]:3}}},Ui="MPEG Version ",di="ISO/IEC ",fi={0:{[j]:Ui+"2.5 (later extension of MPEG 2)",[X]:ri,[Kt]:{0:11025,4:12e3,8:Y,12:Os}},8:{[j]:Os},16:{[j]:`${Ui}2 (${di}13818-3)`,[X]:ri,[Kt]:{0:w,4:m,8:g,12:Os}},24:{[j]:`${Ui}1 (${di}11172-3)`,[X]:ni,[Kt]:{0:M,4:p,8:y,12:Os}},length:L},pi={0:_s,1:Fs},Mi={0:Fs,1:"50/15 ms",2:Os,3:"CCIT J.17"},yi={0:{[Bt]:2,[j]:u},64:{[Bt]:2,[j]:"joint "+u},128:{[Bt]:2,[j]:"dual channel"},192:{[Bt]:1,[j]:c}};class mi extends Xs{static*[ds](t,s,i){const e={},r=yield*Gs.Mt(t,s,i);r&&(yield*t[ns](r[L],i),t[hs](r[L]));const n=yield*t[ns](4,i),h=Cs(n[ct](0,4)),o=s[ds](h);if(o)return new mi(o);if(255!==n[0]||n[1]<224)return null;const a=fi[24&n[1]];if(a[j]===Os)return null;const l=6&n[1];if(ui[l][j]===Os)return null;const c={...ui[l],...ui[l][a[X]]};if(e[it]=a[j],e[X]=c[j],e[Rt]=c[Rt],e[at]=pi[1&n[1]],e[L]=4,e[O]=Ls[240&n[2]][c[ei]],e[O]===vs)return null;if(e[Kt]=a[Kt][12&n[2]],e[Kt]===Os)return null;if(e[Ct]=2&n[2]&&c[Ct],e[R]=!!(1&n[2]),e[qt]=Math.floor(125*e[O]*e[Rt]/e[Kt]+e[Ct]),!e[qt])return null;const u=192&n[3];if(e[Ot]=yi[u][j],e[Bt]=yi[u][Bt],e[tt]=c[tt][48&n[3]],e[N]=!!(8&n[3]),e[Q]=!!(4&n[3]),e[x]=Mi[3&n[3]],e[x]===Os)return null;e[A]=16;{const{length:t,frameLength:i,yt:r,...n}=e;s[fs](h,e,n)}return new mi(e)}constructor(t){super(t),this[O]=t[O],this[x]=t[x],this[Ct]=t[Ct],this[N]=t[N],this[Q]=t[Q],this[R]=t[R],this[X]=t[X],this[tt]=t[tt],this[it]=t[it],this[at]=t[at]}}class wi extends Zs{static*[ps](t,s,i){return yield*super[ps](mi,wi,t,s,i)}constructor(t,s,i){super(t,s,i)}}class gi extends Es{constructor(t,s,i){super(t,s),this.Ut=wi,this.ft=mi,i(this[k])}get[k](){return st}*[Ms](){return yield*this[Us]()}}const Yi={0:"MPEG-4",8:"MPEG-2"},Ti={0:"valid",2:vs,4:vs,6:vs},bi={0:_s,1:Fs},Ai={0:"AAC Main",64:"AAC LC (Low Complexity)",128:"AAC SSR (Scalable Sample Rate)",192:"AAC LTP (Long Term Prediction)"},Oi={0:96e3,4:88200,8:64e3,12:p,16:M,20:y,24:m,28:w,32:g,36:12e3,40:11025,44:Y,48:7350,52:Os,56:Os,60:"frequency is written explicitly"},vi={0:{[Bt]:0,[j]:"Defined in AOT Specific Config"},64:{[Bt]:1,[j]:c},128:{[Bt]:2,[j]:d(2,a[0][0])},192:{[Bt]:3,[j]:d(3,a[1][3])},256:{[Bt]:4,[j]:d(4,a[1][3],a[3][4])},320:{[Bt]:5,[j]:d(5,a[1][3],a[3][0])},384:{[Bt]:6,[j]:d(6,a[1][3],a[3][0],l)},448:{[Bt]:8,[j]:d(8,a[1][3],a[2][0],a[3][0],l)}};class Bi extends Xs{static*[ds](t,s,i){const e={},r=yield*t[ns](7,i),n=Cs([r[0],r[1],r[2],252&r[3]|3&r[6]]),h=s[ds](n);if(h)Object.assign(e,h);else{if(255!==r[0]||r[1]<240)return null;if(e[it]=Yi[8&r[1]],e[X]=Ti[6&r[1]],e[X]===vs)return null;const t=1&r[1];e[at]=bi[t],e[L]=t?7:9,e[ot]=192&r[2],e[Qt]=60&r[2];const i=2&r[2];if(e[ht]=Ai[e[ot]],e[Kt]=Oi[e[Qt]],e[Kt]===Os)return null;e[R]=!!i,e[vt]=448&(r[2]<<8|r[3]),e[Ot]=vi[e[vt]][j],e[Bt]=vi[e[vt]][Bt],e[Q]=!!(32&r[3]),e[z]=!!(8&r[3]),e[_t]=!!(8&r[3]),e[Vt]=!!(4&r[3]),e[A]=16,e[Rt]=1024,e[et]=3&r[6];{const{length:t,wt:i,gt:r,Yt:h,frameLength:o,yt:a,Tt:l,...c}=e;s[fs](n,e,c)}}if(e[qt]=8191&(r[3]<<11|r[4]<<3|r[5]>>5),!e[qt])return null;const o=2047&(r[5]<<6|r[6]>>2);return e[V]=2047===o?"VBR":o,new Bi(e)}constructor(t){super(t),this[_t]=t[_t],this[Vt]=t[Vt],this[V]=t[V],this[z]=t[z],this[Q]=t[Q],this[R]=t[R],this[X]=t[X],this[L]=t[L],this[it]=t[it],this[et]=t[et],this[ht]=t[ht],this[at]=t[at]}get bt(){const t=Ps.get(this),s=t[ot]+64<<5|t[Qt]<<5|t[vt]>>3,i=new bs(2);return new As(i[_]).setUint16(0,s,!1),i}}class Fi extends Zs{static*[ps](t,s,i){return yield*super[ps](Bi,Fi,t,s,i)}constructor(t,s,i){super(t,s,i)}}class _i extends Es{constructor(t,s,i){super(t,s),this.Ut=Fi,this.ft=Bi,i(this[k])}get[k](){return"aac"}*[Ms](){return yield*this[Us]()}}class Vi extends Zs{static At(t){return(t[t[L]-2]<<8)+t[t[L]-1]}static[Ts](t){const s=Vi.At(t),i=(t=>{const s=t[L],i=s-16;let e=0,r=0;for(;r<=i;)e^=t[r++]<<8|t[r++],e=Ss[15][e>>8]^Ss[14][255&e]^Ss[13][t[r++]]^Ss[12][t[r++]]^Ss[11][t[r++]]^Ss[10][t[r++]]^Ss[9][t[r++]]^Ss[8][t[r++]]^Ss[7][t[r++]]^Ss[6][t[r++]]^Ss[5][t[r++]]^Ss[4][t[r++]]^Ss[3][t[r++]]^Ss[2][t[r++]]^Ss[1][t[r++]]^Ss[0][t[r++]];for(;r!==s;)e=(255&e)<<8^Ss[0][e>>8^t[r++]];return e})(t[ct](0,-2));return s===i}constructor(t,s,i){s[Lt]=i,s[$]=Vi.At(t),super(s,t,Ps.get(s)[Rt])}}const ki="get from STREAMINFO metadata block",Si={0:"Fixed",1:"Variable"},qi={0:Os,16:192};for(let Ye=2;Ye<16;Ye++)qi[Ye<<4]=Ye<6?576*2**(Ye-2):2**Ye;const Hi={0:ki,1:88200,2:176400,3:192e3,4:Y,5:g,6:w,7:m,8:y,9:M,10:p,11:96e3,15:vs},$i={0:{[Bt]:1,[j]:c},16:{[Bt]:2,[j]:d(2,a[0][0])},32:{[Bt]:3,[j]:d(3,a[0][1])},48:{[Bt]:4,[j]:d(4,a[1][0],a[3][0])},64:{[Bt]:5,[j]:d(5,a[1][1],a[3][0])},80:{[Bt]:6,[j]:d(6,a[1][1],l,a[3][0])},96:{[Bt]:7,[j]:d(7,a[1][1],l,a[3][4],a[2][0])},112:{[Bt]:8,[j]:d(8,a[1][1],l,a[3][0],a[2][0])},128:{[Bt]:2,[j]:u+" (left, diff)"},144:{[Bt]:2,[j]:u+" (diff, right)"},160:{[Bt]:2,[j]:u+" (avg, diff)"},176:Os,192:Os,208:Os,224:Os,240:Os},Ci={0:ki,2:8,4:12,6:Os,8:16,10:20,12:24,14:Os};class Ii extends Xs{static Ot(t){if(t[0]>254)return null;if(t[0]<128)return{value:t[0],length:1};let s=1;for(let n=64;n&t[0];n>>=1)s++;let i=s-1,e=0,r=0;for(;i>0;r+=6,i--){if(128!=(192&t[i]))return null;e|=(63&t[i])<>s)<{let s=0;const i=t[L];for(let e=0;e!==i;e++)s=ks[s^t[e]];return s})(e[ct](0,r[L]-1)))return null;if(!h){const{vt:t,frameNumber:i,Bt:e,yt:h,Yt:o,Ft:a,_t:l,length:c,...u}=r;s[fs](n,r,u)}return new Ii(r)}constructor(t){super(t),this[$]=null,this[Mt]=t[Mt],this[mt]=t[mt],this[$t]=t[$t],this[zt]=t[zt],this[Lt]=null}}class ji extends Es{constructor(t,s,i){super(t,s),this.Ut=Vi,this.ft=Ii,i(this[k])}get[k](){return"flac"}*Vt(t){const s=yield*this.ut[ns](2,0),i=s[L]-2;for(;t{const s=Ii[Ys](t,this.it);if(s)return new Vi(t,s,this.kt);this.ut[ls]("Failed to parse Ogg FLAC frame","Skipping invalid FLAC frame")})).filter((t=>!!t))),t}}class Ji{static*[ds](t,s,i){const e={};let r=yield*t[ns](28,i);if(79!==r[0]||103!==r[1]||103!==r[2]||83!==r[3])return null;if(e[ts]=r[4],248&r[5])return null;e[K]=!!(4&r[5]),e[Z]=!!(2&r[5]),e[E]=!!(1&r[5]);const n=new As(bs.from(r[ct](0,28))[_]);e[T]=(t=>{try{return t.getBigInt64(6,!0)}catch{const s=128&t.getUint8(13)?-1:1;let i=t.getUint32(6,!0),e=t.getUint32(10,!0);return-1===s&&(i=1+~i,e=1+~e),e>1048575&&console.warn("This platform does not support BigInt"),s*(i+e*2**32)}})(n),e[Wt]=n.getInt32(14,!0),e[Nt]=n.getInt32(18,!0),e[Pt]=n.getInt32(22,!0);const h=r[26];e[L]=h+27,r=yield*t[ns](e[L],i),e[qt]=0,e[Et]=[],e[Dt]=bs.from(r[ct](27,e[L]));for(let o=0,a=0;o{const s=Li[Ys](this.Ht,t,this.it);if(s){null===this.$t&&(this.$t=s[nt]);let i=s[It]*s[St]/1e3*s[Kt];return this.$t>0&&(this.$t-=i,i=this.$t<0?-this.$t:0),new Pi(t,s,i)}this.ut[cs]("Failed to parse Ogg Opus Header","Not a valid Ogg Opus file")}))),t}}class te extends Zs{constructor(t,s,i){super(s,t,i)}}const se={};for(let Ye=0;Ye<8;Ye++)se[Ye+6]=2**(6+Ye);class ie extends Xs{static[Ys](t,s,i,e){if(t[L]<30)throw Error("Out of data while inside an Ogg Page");const r=Cs(t[ct](0,30)),n=s[ds](r);if(n)return new ie(n);const h={[L]:30};if("vorbis"!==r.substr(0,7))return null;h[I]=bs.from(t[ct](0,30));const o=new As(h[I][_]);if(h[ut]=o.getUint32(7,!0),0!==h[ut])return null;if(h[Bt]=t[11],h[Ot]=f[h[Bt]-1]||"application defined",h[Kt]=o.getUint32(12,!0),h[v]=o.getInt32(16,!0),h[F]=o.getInt32(20,!0),h[B]=o.getInt32(24,!0),h[gt]=se[(240&t[28])>>4],h[wt]=se[15&t[28]],h[wt]>h[gt])return null;if(1!==t[29])return null;h[A]=32,h[ft]=e,h[dt]=i;{const{length:t,data:i,version:e,Ct:n,It:o,...a}=h;s[fs](r,h,a)}return new ie(h)}constructor(t){super(t),this[v]=t[v],this[B]=t[B],this[F]=t[F],this[wt]=t[wt],this[gt]=t[gt],this[I]=t[I],this[dt]=t[dt],this[ft]=t[ft]}}class ee extends Es{constructor(t,s,i){super(t,s),this.Ut=te,i(this[k]),this.Ht=null,this.jt=!1,this.Jt=null}get[k](){return Ut}[ys](t){t[S]=[];for(const s of Ds.get(t)[lt])if(1===s[0])this.it[gs](),this.Ht=t[I],this.jt=!1;else if(3===s[0])this.xt=s;else if(5===s[0])this.Pt=s,this.Dt=this.Et(s),this.jt=!0;else if(this.jt){const i=ie[Ys](this.Ht,this.it,this.xt,this.Pt);i?t[S].push(new te(s,i,this.Nt(s,i))):this.ut[logError]("Failed to parse Ogg Vorbis Header","Not a valid Ogg Vorbis file")}return t}Nt(t,s){const i=this.Dt.Zt[t[0]>>1&this.Dt.mask]?s[gt]:s[wt],e=null===this.Jt?0:(this.Jt+i)/4;return this.Jt=i,e}Et(t){const s=new Js(t),i={count:0,Zt:[]};for(;1&~s.read(1););let e;for(;i.count<64&&s.position>0;){js(s.read(8));let t=0;for(;0===s.read(8)&&t++<3;);if(4!==t){1+((126&js(e))>>1)!==i.count&&this.ut[ls]("vorbis derived mode count did not match actual mode count");break}e=s.read(7),i.Zt.unshift(1&e),s.position+=6,i.count++}return i.mask=(1<1&&t[Nt]>1&&this.ut[ls]("Unexpected gap in Ogg Page Sequence Number.",`Expected: ${this.ss+1}, Got: ${t[Nt]}`),this.ss=t[Nt]}es(t){null===this.Rt&&(this.ss=t[Nt],this.Rt=this.Wt(t)),this.ts(t);const s=Ds.get(t),i=Ps.get(s[D]);let e=0;if(s[lt]=i[Et].map((s=>t[I][ct](e,e+=s))),this.Kt[L]&&(s[lt][0]=$s(this.Kt,s[lt][0]),this.Kt=new bs),255===i[Dt][i[Dt][L]-1]&&(this.Kt=$s(this.Kt,s[lt].pop())),null!==this.Gt&&(t[Rt]=Number(t[T]-this.Gt)),this.Gt=t[T],this.Rt){const s=this.Lt[ys](t);return this.ut[as](s),s}return t}}class ne extends Es{constructor(t,s,i){super(t,s),this.zt=i,this.Ut=xi,this.ft=Ji,this.rs=new Map,this.ns=null}get[k](){const t=this.rs.get(this.ns);return t?t.hs:""}*[Ms](){const t=yield*this[Us](!0);this.ns=t[Wt];let s=this.rs.get(this.ns);return s||(s=new re(this.ut,this.it,this.zt),this.rs.set(this.ns,s)),t[K]&&this.rs.delete(this.ns),s.es(t)}}const he=()=>{};class oe{constructor(t,{os:s,ls:i,cs:e,us:r=!1,Us:n=!0}={}){this.ds=t,this.zt=s||he,this.W=i||he,this.tt=e,this.fs=r,this.ps=n?Hs:he,this[ws]()}get[k](){return this.Lt?this.Lt[k]:""}[ws](){this.it=new xs(this.W,this.tt),this.Ms=this.ys(),this.Ms.next()}*flush(){this.dt=!0;for(let t=this.Ms.next();t.value;t=this.Ms.next())yield t.value;this.dt=!1,this[ws]()}*ws(t){for(let s=this.Ms.next(t);s.value;s=this.Ms.next())yield s.value}parseAll(t){return[...this.ws(t),...this.flush()]}*ys(){if(this.ds.match(/aac/))this.Lt=new _i(this,this.it,this.zt);else if(this.ds.match(/mpeg/))this.Lt=new gi(this,this.it,this.zt);else if(this.ds.match(/flac/))this.Lt=new ji(this,this.it,this.zt);else{if(!this.ds.match(/ogg/))throw Error("Unsupported Codec "+mimeType);this.Lt=new ne(this,this.it,this.zt)}for(this.gs=0,this.Ys=0,this.Ts=0,this.bs=0,this.As=0,this.Os=void 0,this.vs=new Uint8Array(0);;){const t=yield*this.Lt[Ms]();t&&(yield t)}}*[ns](t=0,s=0){let i;for(;this.vs[L]<=t+s;){if(i=yield,this.dt)return this.vs[ct](s);i&&(this.Ts+=i[L],this.vs=$s(this.vs,i))}return this.vs[ct](s)}[hs](t){this.Ys+=t,this.vs=this.vs[ct](t)}[os](t){this.Os=t[D][Kt],t[D][O]=t[J]>0?8*Math.round(t[I][L]/t[J]):0,t[$t]=this.gs++,t[is]=this.bs,t[rs]=this.As,t[es]=this.As/this.Os*1e3,t[C]=this.ps(t[I]),this.it[ms](t[D][O],t[es]),this.bs+=t[I][L],this.As+=t[Rt]}[as](t){if(t[S]){if(t[K]){let s=t[Rt];t[S].forEach((t=>{const i=t[Rt];s0?s:0,t[J]=t[Rt]/t[D][Kt]*1e3),s-=i,this[os](t)}))}else t[Rt]=0,t[S].forEach((s=>{t[Rt]+=s[Rt],this[os](s)}));t[J]=t[Rt]/this.Os*1e3||0,t[rs]=this.As,t[es]=this.As/this.Os*1e3||0,t[is]=this.bs}else this[os](t)}Bs(t,s){if(this.fs){const i=[`${k}: ${this[k]}`,"inputMimeType: "+this.ds,"readPosition: "+this.Ys,"totalBytesIn: "+this.Ts,`${is}: ${this.bs}`],e=Math.max(...i.map((t=>t[L])));s.push("--stats--"+"-".repeat(e-9),...i,"-".repeat(e)),t("codec-parser",s.reduce(((t,s)=>t+"\n "+s),""))}}[ls](...t){this.Bs(console.warn,t)}[cs](...t){this.Bs(console.error,t)}}const ae=S,le=I,ce=D,ue=K,Ue=dt,de=ft,fe=rs;function pe(t){function s(){}for(var i=new Uint8Array(123),e=25;e>=0;--e)i[48+e]=52+e,i[65+e]=e,i[97+e]=26+e;var r,n;i[43]=62,i[47]=63,pe.M||Object.defineProperty(pe,"M",{get:()=>String.raw`dynEncode01a33fc84f8dïoØå§ä³¨s¢þéõ?§šæ—ƒQ¬·ñì>_•å”è£Mí'…ËÏÊí çåzmòeúaݐ§¢é,šî= 9z…ó¡= $.JõT¬K„'¨®ç ƒä“Ó·ßé‘àäà(u¾ŽÃ÷¯ÃìÍH¶3KQtíŒ;Çø=}ÉóX=}Çã»°£)½SÉVy{oEqûUHhéì8÷7ó[Ÿ”¼ˆiÖ¨A2º»ƒÈ(ä+Ú»‰Ðé*7šç}ìÏց@ü€¿À¿B;?=}A?>ýÿûüÿ~{xÎ#†ë5X.]J +=Mi=M°UH +˜5šâ’ª±ÀéOmrn¿¯2Ljª•˜{• ÝájÔֆ ±Û©bŸÕé)j{èüül8üàJr/¡™¨‘‡²Ýþ^UÜàÈO6܅jÙ5èâ×að1º€HH=M2Û6íÊóI̧[TÜïÚÉ)„ÁW|âßHeF,l Ì¥ 'ž0Äb8ÏK“ß è!Œe‘€J +vÑz–Œ& WrnV "5Êñ ýkwP”4Ãp¨‘¸RAx[p¼Ïóƒ;š7>ÃÑþ¨VU?°Rˆ›q9~ò‰Q”ڃw‡I=}­žœ• f›òÔ·rñ¾ª¸gðx7QE5-±®M߀ãl–?£Á•ãÃE óØ{ËÒÂÌ@µ –z5fñ‘}.;¾ê%Mi‡‰_³ZU@å.ɂñ‰î¾Þ€m„Ät†×¢. ,ä²BB;朤‘äûûZ_¡V)Œ‡ŒªAUµ¾BfÈéŸ Ò&§¦$¢Š»ôåÀk£z‹(Ÿ_zܟ^r>†ï ¾bˆ>ÁŽð/ªÄDTçQŽlÄIÒ°Q}:8_þŽ!@ßvñ!œOö +\”\ebVpȜ±Ö~\¥NÐ'eŸBœb·#ÈN¥ŸY ßà#W´0a]¼!þ½\nרT¿¿‡¶çÉS­îäx7óÎ6ñI-ììùpfåh¸Çú,cµÔeiÂeic ÓÂçˆ#ê²up¹aÛÝb0¼þ7–Ÿ™Ü”>F3ÞÔTþñfkï±bxö”€„öfٍ†ˆ±…6H„”ΟÊß6 šÒ°ÂyV¶Á?»‰‚@?{¨Q¥+=}¥±ÿԓÌá9%û!>Âì&¸Õ@Ó„Uùq÷?ñqt˜¿“çIU+÷g‚y+5sä +¼¬åí• GÖ®‡S1Kƒ;…B\©™f‹hX˜d)ºÐ¿²Uw ûûX1ƒ5°Ò1„LÁ”-Œ;j= piN:}îºB¯ÅÉwXtô§¶¸3Œy¶¯oJáàLž¸jÎn®În -ЏýûÄàsT ½ÎòÓÄes ´{OsèÚÎþ´ƒÇ“å9SekF†‰TV³úŽK-”š÷WE¾OÔ +µR½‹ø*ٞvVèJ°§†¼Å¤ªàCŠœsr²^Õvü3ããÔ¯/´ Poü|$ RÞ§KuÉ#þ‰[ªß{¦ÑVú>ý>Ôws~~š9ž=Mz¶-ږ•hiò>ñ²1ždüÿ!ü>ý>í¾¿ñ赙À ž/«~yæ¨Êwór„b0©ì!ˆ ~°»Ïžøj§Ý#ß88š÷AlÛ¦3¹ÇS8“oK:xÜ×'dAkÉÜ<É;–)3ÀbÐ{í«Û5?þ +ÈNøRÙᾙ¹/+ß>ۏ¾6åô}mzQ|p÷Mž¼ðjÑí©;2½&Ì#g×g|/[k*Zõ8ZvãXƒçàF–ª¯a*÷«ª+V,\¨rÏâ'É:G-àëˆW£óÿ‡ˆîŸ‚8rpÿ]k€=Mîéû|]´$Î"’îá“VìDŽ_d%~m¢>aj%U6¨^8"žb ]¥®}l†ì­ïèÈfõ lÔ֋çÝÜIÃqÜ;ñ‘°[‚ÙåTàÆä>í•÷™õ{¯oí½3*iú•D¿gÔæÁ@sÙ Ñèځ=MÉkáÑüçuêÕÙðw¨ À ³Fô~{ï_µ9Ѭ5,}í5×0ÿ>_6l*{îò)¯GjE7(aôrÃbôÚj˜Ç½ T°ÜÃÄgk;ß_߇Â֍ûÄRZðùÒ¸§=MX*gê[ÉXÚ©^uN„~u~® +:Õ_NøW‰ëP\^WpÀttØ÷2ÿ2wTØï2þ2v6y6;V˜ÌۆI÷?ªê„̶^&>&í£F]VBfŒ¨Tße|þîõ¹ù±úÁ$˜]ö wá†yƒÏV.îbÁۂŠÆ.Þð¡ØÛ†ïtnZ$þ:—KÌòñӘð?«Ÿ»…±l“â¶ +SӀÿZ·ƒ©'7 +s%»4äcL+øÈ{ +^ÑKCU¨n9ðÑ~g³/rӆV$ +[cÉ6 &›(¿ô‚qðx ú’Æ6ÞYSÇO×Èò{ ’-L;áèÚÆUhk(öyžÄ„è7´Þ1CY˜¤Dèé׃40,ä$Em9†¦S£"¨ü“ÇkU€ô¹ŠË UgÎ{>%[2\ösiUîçÓ.œÀ¨ãôÂÏûÇ~që¤ +˜$¼À&*ãhæM#Ïî8å#ûûU@·I‡§a†Âú[Hc°cŠ¥öém¶«è䪪ˆ#÷‡ˆñg;ß¾óºš/9í? g~ïB9¨'Süß.›),‡gi±Æèzåëf¹,Èâ^k¿|Ð= ”ÿÉUvˆñÚÍÖـ"&•|ª[Å¢lŽy‚D¸ÖÏñI´QCÑWõȉà°zÈ2_Ǹß(ËÒ¹pi}çkU̇-PV$MÍ)}éžD×q¹{¤aä†.|í+/ýºIë¸××I¥^bü½ö?Ñ/ưuCH¥g±•‚|¹MÞ#L²„ææûºÿ 'œîA-÷B¿7<ޙԭ 9ì&Ô¢fÓ1EãÈ/ cE%5ÙO‡£.uK~¨¸;Ëíô°ÿu7uRö¦×³…®= °~£3€0Èö‡ˆpI ìx‚ûŸ)þÏXËyey„o I%Ùý°ðà8­Kð!9¡ƒ‰C£'Ý Y0¶±Ú}îùÛÆze"ߦ[ÌM…ˆ÷qAš?¬;QW–et„ bW¬ýÛ)aW–et„+*˜¾­j¥h8Î +ÉëÎ +~øqù|ô*= DÑW©â +q'$iÇùçÆ®sç4|¶¢USæK|cÅÀ(46?µŠƒ#ËM0÷rYNAP÷r¦á«Ò×{²¶ÁØäpg´e—Ý|¸ìH=MïmÿOn@óå+(÷Ð MI3“ìñÈó倝Óz*^,áÿJ²Úz* [Òíxê|Žfp˜„ûÂûûBŒf4þs*þs*ö [ñ‘bG²ÐnjÄ%Àßñ«Zù\è¬è ‘ K-ÚÈ®”Û“×ΏVyT­´1˜bØüJÅZÕüJÅÚ=}›<ó´nul/6|@{æÔî½àb|é ª³½wTŒAª³uêÀ.¸šNvΘ‘æSUL-÷h›rH7ÙV +VÕ-pL¿ã.PÒ¿êÃÿqÎÁE­k…ÜŒ¦ÙÇéÙ²—l¥(t³p–OYúHp’cÖ¸ó„æÞy5½¶ÎÕ9Ȍ³˜³²ô³2§'6Š­´É‘»_9Ɏ^¿Ï«;ÙËa­&rb—f±_ZØÀª%#ŽÎh?̑;zÀVš‰j¿Ž2lúꋆGÂ^hì +ÊÀþߌðT†Gximü1Oå®&¥ïߗµe‰cË/9/ÍN͟ê¤MÎü´M +ƒ•]Fökj®òJ¤C©K•'îO§•GiÆb6ô¡ØfÒéþ‚h°¶˜À= µfŠaG€Hߵэ¬Ãà ÖO¥*–Ýh·s-#ýtúø*³D¡S6(t,ÀF0(´¡†¿nƒ,Ùádý˜Y@²íۄâÀnhé9ÿ{~@øJèT\†VåԞOó¢üdr턦ür… +¨& +ÝJB¸ŸV%tr­QžóӞO—Våâ SBi¾ÿڐçܘÄ6OLi.p·Å(›Õ“xD¨ +D¨¼¬ ؍ª¼L9¯¨ÌšÓn¤° BªbêtÓ«óÚ;[K°èî¦AÀ—Ο™0ý'ð§|ÌÒÌ÷´rq½ó ̎µazûû8œm´Ê/ŸœV:È&®¬ØÌZ& Uk£‡r¿Ãî9>ÊR‡rѤçśz"ÈBw&!Aúÿ’‘üd˜–&ù¤º¤ûë©ÿ?þlâSwÉ£kl¾2ž9²bÏáz«–­®k€(Ȫ-Ðgýuäƪa­ß÷Ðî5A3*®ÖÖ»­·G‚V@øb1r# éof:&z:ryG~qb˜f“ÜVš\Q°%¿¦cœåþ„¤6á0n"՘a•Õ:¬®{ ™$îN³ºüÜ~=}Ą6¬ÁÛÏÓ¥Y¬ŒÆ€m‚šÝÜÀ,ØAà64æ0kÐÇižL#=MMôk%9ÿ[þ|Ä[Á<0¯ ý¥þÖváÐ\yí¬-zÌOï1ä@ ¤†ÙÙ®»…ÏÈïõ¯•j¢×ì”=}G›þÈÌûû¢r5·5¥„ƒ×ZíõÅä֔þ&žRX*ƒü°Q:ýBÖ.ùžkô){©Ô™B­>´MLZØî’Rf," ŸÊ⟢¢p¦Ò"ìÖ!¡o4•ºÕ††z0ŸŠÎÆm0'$¦g,ˆžòߖö k†bßì°qºU‰L?R©@c‹4¯Öçî쐁 U ú˜Û‹(ZEO DXzÄC=}°×D$ãÞ)é]S²œÄz¿¾ß‚ÝEL·kÍMVañ„%FJ +4ôñ=Mچ™fÒ¶‡AÉÂíÁ|…Ô¤joÁÏ3ÜV5íÇ-:5rKÜ[uò¦\ŽÀÜ]3ނµÂ«âQÙúх åô=}*´TLß8Åî7>Â'±„:LCdh úÕe÷͏M73ëFrԉmÝР“†ÿÀ’ÓM|"6d‡@O…¹‘cs‚Z¡g%n’B³ý£UMŒKÅÖ0Ý8ÒõÏu·ø¶g͜¡ñš¾€ÚÓN Š0þR)h= †þ‘P{nøQ€I3Åß314™’ÐT‘= Ìy”Åy¾–l܅= ÚÃiUCg´WœQ(ŠÁ$^])Ù⑥þ 2­Û·>å˜ms&b¼úªèôQ'Yè¨FüyIr1ýÎmaî0' ÇÈýªÿ’#}^W_W}2QjAÂ*ϐšO¾“rûÿ +Dñöëß_\Z= Â)¡# ú„⟡]ôö¯öx1ÓȒ6γ5_x}']Ö+—ü\c°”O×ì9¯g_³Šd‘óai"©[9ûЀC¹^\:R¢ìŵ‡ýrt‡q׉Çܹ–‰'ÆY[7lÅ4ÓtÒ¡SÛà -W~ üx¯d…ãG@fýÉOsó–Í@O¹ g.æ ,ŒHåÑ?_w‰Š•XÜi~ZS(E¶²ü×¹RJ=M¹Å_=Mø˜ÑL^ålZcþ‹ëÙõðÛà„¸‚õ/ð±Üzé/™whW:ÍÐFÔ #ÉG´ÂÈWŸ²ÿÌM+ínríî#ÌDoñ•„L}ª.–ñ¸ŠçpƒÑ}Ê YW’õ]jãN¬k¸Áç÷F€Lã äªÇÔ)üõ¤ùæÈð} +æ® õ9~%žñõª÷èþæutµØî «ï‚h w3ZyVÖfÛÓÖ/fOP= =M»Ô4ßýa3‘øœ¿„‡$±äšFY\ò[¬)¢Rø¼‘fلO ü71w¿ŠæZlý֋° +ÓÚ? .)fsnW®Æù–«Iò‡Í?Uñš¥A1ƒ¢7+_꼉ßÝÙâÕЦ]s¿îs¿ðS9šÓšŽú ÞWLs_Iu™=M¨6ÛèÝë§bCgт {F¼©¦¶ÌKDZüMv•)Fº»ý»è eI ÏÚëÆ*$&Ù轔Öe´XSX×øØê½ñ4 ÍàG+ôgÒ  ÈVæT[å^~×?|#K·ÇlLÎØi;ùV'j÷œ0ðÀók—=MîAxyvšJbÞà0ŒVHÈA€¤M¸;O¸‘/(SÔ«¦üÆ3؍kU´Jf³)óg»1W¾‚lÄë§1ŠTõù݇’†d%=MaPu™í(pD\0Þ0;7àe“(ÙД´¬µ o6˪@L$Žë¸Ç×cL›Jß¼Òó,]þ&5µÒ‚&ñ´…tÉvâPŠx XŽÔ šÔ8ç‹ÖHëKÊ0]f ê8-rüºªå±­¼(óåНµÒ3äóOôIø—Áþìj# = —bF%4»XîÛð«!@$¹@Bl‘ý’U@O‘EDç/ }òªT3Á$È%nJµ’Æž„[gÊóÀ;=M5»Ã:_ÿq¿BßÀ¥1æ-e ×D¦»6*„Û¿×_ +ÔQ’¦sá0ޏêw—Š]ÂÕÌ6Y£>3CëxâF4!y'Èw†±Y¾ŽžñÕõ0ww‹*·ÕX•3ógg5^/’S–Z.´‚ËÆK†9éÓm"Ž}bŽ}âV|‰þPê¬t¤]pbŠ=M»tJ= ^¼-ÄÞ˜=}žWȼ"¾ix=}žØW.¢µ}d=}žÜôwŸ·}S‰!/þ·KÂãŽys}ؚ±Wwþ¿0—˜&)” +±}Èóðí×=M© ~šÜPAŠ9Z†”ƒ*Sό؜ñÑMN·c¶Hï–ëŠB¹]h_þq^&Z=}* „’! ÓœÿÉÊìíƒv‰™“×®>‹BŸŸ²Å2!´4œp_ÊݛÕ!©diŽtÓ7Ȳ¶–®â.;Ù1Ðëb¡º÷ñF"inˆ/­­¯YNÓ(ÐÿÄ<š–›(ÓC–9~õ-­"‹§7LQ‡<1Œ¢‡­ŸAäHÕ«´Dî‹ù–‘PŠ€= ñc{?ýW:Í;Ԃ]cán¿˜¼üÍJŸ£)™Glú!¯dئ:PNÂg½ÍŽ^–JÑjÅ^¿6f(ÄA?anD(°Hsn +$R§Ärkx÷ò­ÅáwۍÈo]?!$"A""÷ð lÄ–ä¾qLô;±óðŒýó;^ð=M,±ð(&²ÀMªªÍü"Kb(6tÒevö&h„ÌD}ÔeŒ¼<}TÑØõnzÞª 7º}óì.$¦‰„1Vå Ç=}5Mþü²!ìm¹™¼6ˆj¦š€›ƒDƒ©èÒ·e § ‚"ŠGœÑÜ%ɶÒÏYa[Jðe#¥ïÔ/;}¿:óöª +¨ÓGÄ< 3mN¢¥¶«Ÿ°XقK—Þ°2ÞŹð¤d|9c¹õ.“J3¼«+™|°*Oôž‘ æï1²„/½©°„ˆ¬·³'´öÛ~ö|õJÛOi¹Ê·û† Ø<¦í>;°HÜVßÒ= &XÀîŽ15 ¦ðöƒÊ0ÊÀùl†=}¯~ñkÄÁ’41qø?ùÕºÏO,K,N¥‘EPÕ§v4‰Íøþú•³¥Êˆ‹k+*5­çr#1DÛ<窣…´¯|{Ù1¬‰ÇÆIje¥Yàkadž°–ÓÊ 3$AßzŽHÇ·Åö­Ë6·Uœl þq5!¢×“EGm*°¥þ6¶ÔùWøJéWÅN$p õ}ÙÕ/}°•,+:Ù +µ'ãŸã²_‰6U˜Á|á÷*c»\ÆU,g*ðÜÆl1ud™ 8¯à“-y÷I ŒÌxÉü.ù†e=Mɔ=Mk«?<çNd2o:Sò°ÑXtÅÝjVZTÃïÎòê;èXúQÉ6ØØ91Ï=M1èpî®Å(è(P I³p¾[Ÿy)$ì$ö®…K÷„¥{®!nP‘Úvä6œÐ’ÈVþCr¶>èqvÉ2ÇtDd@«=M¤c¶ç¨·íi«5A‰›,¸Ôfª/ْX¬‚š×ƒò*2® å¸oô +{ë.»Üy½Ct EâaúÅç¶Gtœd‡nòÔQº5Ñ66/Ë1=}wk*—l$¡J_!JÄ©m,v›À„p£re /7P$Ç.6TAE%8Ō7RÓ]Vú¨µpžPßGOëüâëüÂGO2GO2HO2GO2=Mëe›HI@Kgg,p^›OSü5ÏÆyˤ˜&.‰+&Adëg=MÿšŸ‘O[¦çŒÀ‘mƒØa% É­V­8=MûŽPNfýú’‰0uÝ'|!·‚ÉñjŒÖañ÷eWÂÏÍø4M;ȅ0 À'ú4¨àª¦~F>Dïo6¼.r]“gO:¦]§½„”Xhg†õ(Ýۂ†š‰@´¨¯ß£éD˜üÄF§ƒÃ»y©õŽ@€= ´LM‰ô‚¯[y‡B3솆ÿºw•K)¢w¹ÆFY=MäՉñsÏGBGˆ'÷@Ki=Md¥”Ck¤­3v»Õ=M÷N®fºŠÍ;Çà“³8MG>†Œ(’pÅ ²fÌ ŒÇ$‡€î2Ô.ÿ8ˆ…doÿ£©æ.oŒÏóSÛpX¡ ێzÉw%šUµ¹ÍîԭہMÁ†5fg +ûîBÚþdŸâõ´ñŠB­,ײc‘ÒŽ Pþq×wŠÀ²©Ã= BOµ%DىϪðõ™üQqÀ5 +ÀRÃgèˈ‰øÅ7IÑÜ" eŸø;âqæÞ±g&:ã'sÆk®¨ÏíLàôoªH'÷:'E÷df5*îx= £ñlí®F$«wmٓõîUT¬—»™åÕHFÔ]ò&÷*$Þ)Ê<{\‘ΘÍ?Çâ›V¹"œpžAn·{‘²Îbu¾âaÍ«ªáéĊ9z_KW´ûS§%í ©BÆÝ®¿µt&ƒdùë³ïú7ô£ NÎ'ð µÅ©ïz0´™î¦êUtâKΣ¤=MdTrzU8vM[§óѳ±I„[‚ý¢wIÃÑyn·QÔÝUæµöېðÀ¶{}IC°¶"ë®ó ß›ƒ^vèZ\y}׳BE6’°Èʊ’вJQ$-9Çê~>t‰m w¿²ˆŠ¦¨®®(|nÈÛp».„ÛTÀ^æ©É6•:ݽ獲=}÷ ÕjÛ¼Œ!ÙñI¯Ž.y_ŒüØ5ku–á"P/qžÕ¡>FˆÕëfâ=Mg4‘_’q|= lrö™RCD‘žö™zê]ÚæšKN4_©vÚè}ò͜p[üÊãᮍÓʃ.ÆiñC˜ÿêÀž}2&[»ïmá­rÍ=}ĺ2x>ÄÂéèÖ{ÝÎæ—íǶáN!|Äώd[?9èH^ù—®µ;Z\º¬yI‡ì²p>œÚ¾POŽ?@†º“ý\zþ}53)¡ÐÁÍç6 O#¤ˆáÊcËc£ûÀ¨Û¥Û2¤y£ûVBر«£Š›_ÅcpF£–¤ƒŸËc=M¬ƒ¢g£(þf!yÛãO“í#·ƒm£ºÞñ#Ñ£ï:£áÛ©¢næâ¿ã#/àÁyÏW²C ‡'՟X؈%¦)Ü p¹€ü˜CQõãÓÛ}~¯$OîLàHJ¸cø:«7í­döº¥ºXJæ9Ô÷ÜÀ›ëé0þ“õÕÒÈkîщȺ–õK,þ‰H‡í‘3:?77nóŽKÔæÈÊÖ÷*¯Õw¼=Moøllÿ8#N5£ØÔQ]Vóú1(=}•®ëdɌüŽ×üNihó.ˆF¹Uø†bg…³n ûæyª(…âïþ¨5꯯;Þ_ï «Ö­&tÊBƒ¨Å|’ø ö×P = 4o™Q…”&@²ªÞ= Ê—;·iª*[€/³‰<“{O +ӊ(Ӄ &;Õ8 „8üÂÓoC“ý¢€¸ë€ÓQž;ÖËÖÒߨŽÄ)¯Ï«fqF¹ÀÆ1È>›'ô&9(T•j²õÚÙ¡%¸&ù(”F" ”–0†cÿ2S s +{“¦O¦>©Y'®ë=McsԁØÞ©ú;ò}›°1¯‡û{VH!t;¬OÃ?½‡Ü¶q £Î eÃpHXzÿÞ¿žýxÓê¬ P‡\šM'È¿…ë9þÿ„}‚#Î'́ƒ}”÷»é¥¾åzt僵´„ÈAú.C„\}!iÔjÛAڌ¶¾;’ðOŸ ÞÉ(àp’ø;åòXCy¹JùŒÛ51J.Nòvô.=M@ƒo”g1f”ë+³­1 +kË;´«ô7À® Eˆ»¹éôäxT/½1¸xâ{Û¹ûÝysäzü°>;‰{e«j'S†ßMqÊØ>ÔÏzöp‰ýP³5=MX= :¸Š[ð1·u1ºkĒÌï¬v+FÎoŒÍ0Ûçs•2Eî§tävòÐÒ×8Õ/ ž3ùŽ»¤.\i8“{Ô#¤)eÎç³^Hšòtéæ»e½¶g፻¶^À¤g4x޳©3èô;¸¹-Ks—Á%ÌÂ>Ð= /³Åas8,}F9ÔRšrsh¿Ø³ú1jé ]Oš+E¶™ÛM94”%k’˜H4v|ß‚Í±ÎØÑLN ¤0ãÀØjj®0 i+òwÝeD 缸¶ëûº?k1Zp½r¡ ¢ÍV=}OHV+?®r7šâÔúTÃ\´æ~ÓY^‘É\9<„¹ a§ñ$‘î;òîâ3öuJ?­"ã¡Ãoœ3ÿeKJŠ}T^f[ÞÖQõø|R÷©ìȌĎ랰Y8¦}òþr5IPò„8/0x¼v‰SP—|IëÞ(æ.ɇÕéH#2廗[_¯ÈHÇ·y”-1“c’©ÈQËàH‹(DÛËvšìR Œ„£Ú"/u¡D$uDT˜Â%Þûµ«HÝ>f·!Ô0ŠŸL‡3¼m!_“À÷4{Çýàrá©-¯ú”Êðÿ(jˆè€‹²ÅÎÃÌĖUÒ±T§#i(9í97­öºÛïH¥v•%!ßOWª·Ä~ãÀ½<€A i޶…/%ÝØ²1PŒÂé Ú/¨Ueiñ$ÛÚOw±·Ü9C±0o»>ôí±ã¥PuÔµ·Ü=}C±0o»ãУ¶åËgí…éð£"}3£Éx{F"S^¼±b^Ù v„wsõ×ý½cë=}P£i,Ðx' S¶¿£aDŠçH‚Oó>.ü,LœD…º‚Lý.œ6Іm…™¹ÿS~©'È3>§,à:FAú4{vìÆùHèâ~ëAú$çpq¬…%=}AúÐûV©¼rf9Ø y’Ñ0\î«ï= ©Hܓ‰±(äâ°6^€ÏOнڀ/}KŒZXÊ]w˜Òbþ/U}Ôn$q2èÚ3¡ˆdüzbP®µPÔ0‰Õ˞í6¡LK¢oЃE¬= Îèt¬ZóÉk+= ¬˜ý À²‚­qAå¬'þä²'þì[¶'2ã[6'㛯'Ӗ›v{Uj˜²^¸¹îŒ,¹<ªJm=MÖ¸ʎ5,ró±Â.Ó=}txHë2ÿë'±: ò>A6Ü3ÚȖùaÈÐ=M%Œmٌ$UÀ°²åœ4“ä ÝÞÇ8èðSæ\ö>qâ¯}ÑxO…üx͒¼É¦;çÒgÿ;»D.úâoÆì:Ñq|Ѝm}'¤@nÍ +Ác[)•Ôph¦ýI”Á­×Åà+(w9¥¨ìGkÁ,Í òש%t’°gX C°C€ ©}½g3¿´‹„Û±F£îÔòêÖú™¼ ÔlÝÂÓª±†FOkéûT•b–l›SOÀ"4²äqǽbê¥,ß*i\ô;S€‘FÀÿž$ªAÉÞF¸l”UKŸÑIè$u¾Oå]É×ʴ̊Œ´Tö>[çƒû ³Ü™¦dÍO =}¿fW,u&Gâþø¦ì>N=}ù»F‰…½.Ö]MÌÔ¡‘c·»vTø02ƃM1†«Mø°=MöÙr /*‚R +ÙÔ ä +\.ô¦Î0ƾ¸êÅé#îmQ‹k;ÞbUä€Jrr ý¬È˜ ¯Mo†,ܲìºBë®N­™äè $*{$ô²ùn]/!§¶­!ª ¢OÖ³©oIz¤4Áeîô[&ãvÃÉÎ{HÍë©Ò\Îú4¥¥ß£»‹œ±WðNÕÔs{BµN+Z‚,ôToI5yhÐ}naO,dèDqU È0q¶œÍRÝÒ4LÜ2¾á{å¬Ï]<º† +}QÅÿ«^pY½ŸŒ.ÓÖv•UÓ\)Œ›ƒ³ I’®Öй q̬vÔ­ "8w¾ñ +I°ÅîHõøì˜úÝwܤßi‹%aƒ0B*;Ó´±ï5Û|ƒK´ú¥²IéëÕ!¾úÆURD՟J7žFEz\û.*zlîAԓ¼…£·”Vä)•«zp!V[á»DBÿx³Ãœ‚Â%:WŽÄËÙ¦ˆ6ó{³Ûyµ2ã ~Ó= !°'c¥‹þóí1ÀëÚ2ÓG„«¦š[ÖÞm3Má0j»)†ÃæÃæMª¼Û]ŠÂ +“1œ½W¬ŸµAEçRè̵·×GÃ;ƒK£ø‹àÇê=}pöÄá²kéâ@§oÎDEÂÈ«™F¡U …v¨bò^ÄÏ×í*Y¿KÝÛގã=}ÆÊ<MâH¡·Ç˜ØßÃ.JpÀ)ñ*,Ë)ШŸÏݘk0-<Òq™ ÿd‘¤7¸¥ƒù¥…W'B.2WKà§Ìëz_و +Žäñ!J^žÇÀf Õ‹Ÿ£…ìµÜ¡ÓÅë Äî€öÉò¥Òimu³Ô(ÌB邒€"‘—,ßà"]!í»1ƒ§=MèCf/WrëF,O(ÄCC1Å*U$J\ŠìV]•AÁ†ò¬ÑÜ'‡Héô2Z>,©ï‰¶Hö¥VڀCJAuĞÿ“ÔKG”;Ë5Þ¿*ðÚûB{½ rèJK+”ˆÿ zþ âÏÔx½†ãõ'»ÞL+0¶Á¯²¸cšG‰:ù=  + +´æVüK¾ C0‚ÎSý‘4Å0= ší⠒-~Šˆ¥ mÒRU…ì8c¹sž\E²¼ïû n¦¸]͈60ÐÃù/Æìâ$Å}}#üİ^;¥ŠøKBÍùe…æ!™Ö ˆ lB9Zþ O>£ñop!= Ö)T]ŽÒ"•>(Š¡üâ>?–¾ !zöù†þá›Ta–\‡8 LI¾ºM‹Ä¼ÈÍ%Iãù끞Âz†^Úb{½æ@å@ j¯®[ÖrŸ’Åñú%®d‚m‹FvãüÓÇ},/3ÓÎr¾k:†>Dx²tÁ7_ÔfŸ²ÖJ¿=}òޑÕ|AõEÒ*ù"O& }¥Hˆ0çôWùØ!… +Çs®zã4‘æœr„ ŽPÅgû›lâɝøÑ@ þ~ʰ¶¨ú’퍾Y YÓ·¿~a…²ÆÛіS,‘|Œ6ól\ŸÌ—PË_Vy†Îù ±J¥½„ræÎ•ùìT®G[ù‰BnWôËieºNúûi¨°×@S¤••‘ˆaýP?ÒdçùN:"°”þÅÿŸ[òœp™dÄPÞ02Ùâ¾Þ†אŸŸw +ðƒAŒ, (–<´£é8oŽå¥’ ++»«4D‡¬Ø¥C¹À»ÍÒ°9KÓp*ÖB´¦£¶ÑDfœ’Eƒt8ÔÎÍY˜0ÍEW¢?Ÿ >ø¸ãè#Ûº?b҈®íq¯~Èëî·õ;œ‡î¼P£x~v/Ç!eÌ·5“Šî4_7ÕÊòÄÕËuPÔmN{ô~Ò:!%ªõ±Fc3ÑHüµç°ÅF)wf8—('¬Dc=}ƄH'"må/Á­£7p|© Ò  =}TÍ}œ¨6†jÐáèÔÝyh¸È¬Ôz®1ô2âô«+ùÏ$Ý 0±Á™ +­j›Œ–P–¶™\GB¬žÖ¾šOÂÖù¨½¨¸$%EX,=}ÉÓ(GAӓ=}8Ž%ÅÓmÎ= •Û@2=MÖõ“Ézr¹ý»Z+ïuûGï;ï&#ÇüüH]X•ååd¨r¯Œ*É՞ñéx&*¯ÕX¸ç7NO k™CaàOB„zrSEs®8·Œþ{:4G= —æô‚«Ò„éå©#EÜu]õßºSÑÍ£ðÙs¨G6ú‘!óñ‰gÔ" ÎLiý¦Qð +¤º,DÃ'óö¼Gâ{ü>Á’¬¹(¯â2©ë£ЄèïDI+Àgï k7iï8¥ؔ“í3ÿð(—èsnĨÀ†Ï•ꪦßHAu-/ë¦ÜX'mÞ=MÎçnKk fõMÓ<–ùŒ +͍ÆEÒ̚®fÌóV\È!”ÓñQq¹^4SyáèÏ-=}<¹×3ýy À’ ·ðmå6·¡­MI¾<Vrº2F}Õ²jAŠ¿«Êðå„w°F+i×?_˜}AÒð(ûï~wÖìzW|Ô]ÇA¥ˆ³l<@gúæ©GŸNà­ß·v›´Ósx)™<›ýޙƒóAЗ|d4hÐ\k +'Î6­óbö¶Eaó-”œdcóöçšë)<7*ãkcëÌ·ãk·ۄ¤ £·q¬²þŒ?Á2ÿ²m †>L š”ú=MxoèÌìŒÎº?€LŽ=M¬V·Â÷¢ñ÷¢0<èäÇå“Áü—ðxîG5,ñi¤Á×׿±\rˆx\.)¿À¾c{žùÀYKž$öGÍmÓa°ÿ)sJÒCÓØw‡øè²1yÞ±µü·æ«wê /¤"+œÅL‰œýÓª*¤.ÄxȂkâw%¦6…eºå¿æÃ.|„sƒ!-07ê¿#ð¸s*IY±'UZ¬@®óõ¦L·Î¢S&k@>øþ8g{O qèª ³ÄE&õ#«CIþóö‘HL)Cd±äbãûÁ úb¾àÇóQòOõ »¿Ç‘|c>/ã—˜H–û7».\ZiéÊÌÔÐ1syìæjƒÓ¼–Ô¤•dŸ—A·Ä;ïpÜÀãזìW7˜€·[~yäÔ=}é3Õ0¡ÀX¾Óqä@>Ä{°÷¯ÕøÃ=}Cã×ÿH»B»ÕB=MåÛ¬o=}ó'Cä®ö +'=}=}¦M…ÞÖ0Ts*he–óÁÃtÛVXÑÔSB+¹f}=}9”íÍþgҚ&ucõ›m6~ + ÀH÷ý:Q¼BÍ)³’#Ì'žDDšI,ˆ¨pDIºUŠ=Mѯ ±w'A¾ì¤ì4K+÷ò¤ˆ¤Û·&æ{«§ô‰:|U®õ"ûÿ~S+Yɋ¶±Z•˜|á,NÐoGIöçJ€-°º úIÉ8·Ù­®¡¹'_,È +Z±LORŠü Vpêh«RÜ IÀÑ¥o騋y㵤†æº ±dÒ@T¨àNۋ¶fòn×ÕçYӄ} Ö¶Ûø° ]_¤€ ¶ˆn ¥š†* °Þú¦=M/ ^N©òߌEŒ®Ý†ZÕs´ +1Wޔ.«:àÛ¶UMÖ'ø*TM̱ö딤Ïv]zœ‡t'I„5=}¶þ‰^cˆì^Q‡Ô—ÃÏöv;ïÂ-rŸ¯" ;»º»1è«[&@»R=M]-„*àç½µ×µä7כ@ ‹¼=}'ÀáŸæÄÃú‹\Näe-^ö•ìý¨8§pöè H ð£º&ËÆdt#Àȁñ²²w5kÊ_^RÓþ%c}ç:—DŠ6ƒN¹ø-+Nuº·‰cºY\kÔp]Ž´"É»K[¬‰=M 0Õÿ‘ÖÖ ×½¦)Dδ{Ôºõ´šÍm7º©l¸<ÞÎdú*Y@ Ž+ÔG;Aº5*L?¬/Ðý˜3ŽšUïUÇkh¬è£Q1 Ó~§ÏççŽ,i¹Åõxs£îþÀPû÷ÀoʑD6¬Ý= È¼DýMË34²C Š^‡¸¾£±¹hÁéKñ€˜ózŽ–¤/StÓ¹weP—ͱÆBů0²Í7ñ1ôfãËßúp[^pô’Ü8Ì2÷It0²‡>Â.Ì46bà†­/”YÀ–=}ŒáånӒ‘ÜûôQɹcœ$dJ´d*+©ytç¾ìVY1´©eo?s;’þÓ|i}kÀëöH‚§‡ZõFÜN(µŸâ¾Ì/C‡B,\¡g®E= z¦G ʁ\ÙZN¡ñ{:w”WQ½%zÎôÆýˆ:XNü®l2¾þï- €¼âë*6_E.²Ö>†¯§Ñ²«lÕ bo”ÿŠ›T¼=Mô¡¥5»Dðs‰ìz”7÷,Ç?]¸vŒÿ±v[Òð­Òù)h?/Xði³Ù¦î7Áš%Ë&ü\×/–Ò6@³xÈ7=}<ÂOR'ŸrJ!3ueh¼#H+[¶Ð!ã¢Ö¼È³ˆ¶»½±_Þ½‰÷Œywñ&C§&Aþ¦bÞe?Ä´jãÍÿ•µÁÇfø¶«ÜèEÀŒöñ(ÀâèèéÝw¼ ¦%(Ó?{Žò¥˜kŠÄ/©yxÓÂ;Š·•æ„74‰Ö5ٍ®jëËÊҍ>#g¬ûkMJOŸËHIÉuTä‚(tZr»“_‚§U‰¥_wßڂGNE‚m e(|¨Š“,=MYz»¿Ù™I*‡èf/k]i jÿ&rpP;~ß]û4Èv¹HCa,Jy1”Y+LÙT((CÀV‰ÙS‰ý1Ð8ÞnÊÉÆ<ÌòÎ'¹&ìT…̸BÖarÄÿîˆÝǰ¼9“°¨2¬ø—îÛ½³q;Ô5rë\“ù?€ÆÍâ²x©çè v‡÷BA©GE)ãà×›AõüP‡ÌÂe÷jIÔ鈁4pèUv=} þ¾‘¬œ;/ùç4ìôdzóÃÐ&³urÐÙòˆònUmh+ÍlÝEí¶m„¥åó!ë¹gK€áQ¹Š…t­jR…ª÷ÃÊ2Œíd¿bm’ËrÝÎíY­_»o-o½;úÞÔ +DSfí,Lú«&ÂûöW’'€K¨Òâ­IÌ_B†zQh^9šF˵éW:=}ïì×-²ÏðÑær€–c€üù¬Ñ/ԗÃDEù´“i¼QP4%Úíö”ð]¡åŠa–E×òg$€ éÇbÇìUbÔ¯Â×£¦Ã£c™lê¥õ3)yÉØïnÿš—Ùïnÿš—Ùïnÿš—ÙïnB°M]ù£)DÀˆñc±[^ŸÍÜ2ÃÌ8 +v†±l8‹22)vß îóÝÏ?xÎÄwêÚËéÂx÷ù…"¨xÆÑëÞ-ϒ-ü^Çgæ,r‰x"Վ2ípÔ±­Y}QË&¶g + oñÛ²[K(ý ™â8¬—Z¢‡Ò¸Î&\@¿®Å„úÊ Ü‚ª9ƞ÷.8‹VâöémÏ8pO{ÜT1Oâ‘p¬Ïj=M9Â=  *7ɲÐÖñ튕ý} µŠŒº-‰VžSlTüuŠ";M·y +jƹÿn±1=Mꁄ& OŒÛ½‡íf· •⇍T09*ÂD9qôq¾—ž/‡ŽatŠ6x…‚¥”u»™=M²–$€“r&áÛ߁¿fœ †¸{¤[Q ¾ß|±*‰q'¢<Üݐ¥ |‘ {ƒ06ô¡ëM±e0Á«Ì,£œéj}û›*Ksåʤ¶THiý°}xã-¹Ê]ﱅ;+là1gR¿ÎܙŸ‰€g”²*ïY£ÝiÍh+hém{OÿˆJ^:SUú4óˆÉe~ìo‡ÎèDö]ûÁœ÷xDZ¼}4è9±ޜǙ‰= ²î\+ÈzR +Y²šV¡Ÿ¯îv= l}aûiÍ>t(= iWƒæÊÝJØFfM· úCát¶ ;¦¨u<„éQHó–wƒ]=}¯uÚôõZ‡ ƒ£(2òD[]öØ.™‡ti¸Ë9âDÀ®t=}"átnPèwŸ{òý<øòb= >dÎóª&°ƒ·% šY;Úf·‚P]ƒ^ ²Ë®„,a+±½«ß» Š)deë'‡ýe+µ•¯’0ä~I”« .Ø 6ô ݸP"ñj5–Vó‘†#¹0T󐯔.Îïêpêö3ªMúDxÉÂєæZ£Ï Î«¨Z%„н-@Òf“ï§õÚç”fË÷wçHÏmð…3~³*w:ÍÀ-jñš§WÙñ' űûM0ä¹½;wƒ\©j=M„7Gh ~ýŸ94ï…[Bk¾j4¶¿FR7Š[ƒK͇»üddNÜ'1î°¿™ð¿¡ìmÇð¢x¡j/øù9n/Ož‚kXAš,Ì.íÌÀ-7-† efzñeTòΨ–öŒ%,î$’˜ÂdHîÊT!Kÿo¾ʑƒÒ±0ûÏ!_Vô_s”3{o¡_ûX“1Ɯ@[֘:ÖbÈÎSf* +ŒŽªÕ‚tiÛYÍ©n€Ø2Ð諾R °ÈîüzÂN<ìZÐÏŒÁ™W¢´™GÌ&ø¼Z ‡TjZ’U°¨HvÙÕO3ØrŸh¥éÙTc“9—= ¤H›XIIטú-v.qéšJo+dw +Œ«}?QCöHçI†¡Éz·Þsϝ8ˆë„‘– ºÝ¡È;9‡èt'ö¬„ˆ¥]Ž\']ùÑJ#xÎìØЌá‚ãœnYsÑY&ŸðÒ’ÆŽ"±4PvÔM¾=}éÝ)ÊLt¼Õ;ìÔ<Üw‘“¸}a˜½ý.éÐ= ê¼®Átêöu“º×=Mt¾mÿ™$ÑOå(¾"óqµ}“-ìNG”Š=MŒwH©U™5^o­ ¬Ô뺕 ‹©¾CÔŸý=}ú¾¯*±À=Mz´’K։m^eX +ם®Ôøàԑð»Ôf,=  OÀ#I\&ƒâw¥¦=}°ä2êæTö14AÆsL‚ˇÁ09O“YØÝŒ«ëÞÌ$¶äx£ /(‡[€?£†ÜÙ+.ÇRìSŸ×¼¡Ñös™„÷;TŒû" ©I‹r%@rÏmTj6Ü+=}.Ql1˜q¬L@3×h¸´¬~â²vQpj›²‘óùcõz®-ف»‚dªQTws²Ú½ŸsRsÂê©cŒçÃiO¯%RÓ¯®ä«¯(*ݯTFftLÓ¿ÖZé—;ëß ¿¬YË­.^èÆÖƽ·¦h»Gï.(–æ,¿Q‡h¢ ¸ï²Mø² ‹?Î+j—4¬Ë8# BµS÷ÅÑÒí'f ç›ófcô°j=M§¹ëzy½Ë×»7C¤H S™H©9Ýý{°x….= ¹€…Xs~LhHÏп ô•î.ɿѠÛ_§1?Œ¿½ò1®ô¿nºwPŠ‹>otØ(FÏjŒn¿¬ZÛ¿Á{âŠû”9æ™<†Œ‚1Ј¿Ê"í‚NÛyx'w÷zûÙûØ×…{È š*fòãò•øõÌ 3ŸœRTÒ$ OUÿ G "Þúôž@üM}À¸þ9I/Kõ™= õŒ¡Å| +ö$UèÊ<“ùÊOéÑ;T¶—ž®(Z¹®À7>'úž±¬‡šåžíÄÒìêÿsNP›õæïpÁÇaaÌ1ÂÖäoVAÈ1íºF',1p[›Ñ\;XP QZð5Yú¯ï7!íOyr´"ȺNÅÂ-ºa·€žÏ½qѶþ xAœ±‘eaôòØWÂÎoYY êSÞ ­‰Ô®OI= °.¢Ü(àÌ@p¡x|íòyø£vR² +î÷,Œ¥ùú= ;НY?X¢U5È:R°ÙTۜÙo +ºÞÒOÞkU‰Áüúv{·-pb1 o 2ŽÒVjRTaZ<&Ÿ¤ö‹ˆÖ”ُp¾yŠxêùžˆ¾~zóo/úÓVüxˆ}‚oÇçé¤@þópÀíKòîç´Ðý4¢ Ù‡a^àð2ë‰v^s²õ‘ìsB}a—_£Ü¢ñ=}mÄ×Uìñ4ÉF ëÂη8—f(tF¨Kèm- æþòIR¶JupˆÖ‹€|Wbfÿ…Aq'V÷äÆ¥_ uÀâÕ9ªô“1óØ+G^»íN÷ôè[=}5ÍÛ0më:›TüíÐþÒJ¶Òú3v„ÇM-è}bzóôJ×Eàðn~ë9ÞÙv*KPÖ Þ\°ìà0vE…àg’’ÈF6žLŠ9&õ ;U_2uS jlcÝ8K݀ %Y×ÞèêÍñö|JV‡A=}CvºV̄—è=}ŸÕ|BîÿHØ>QöBXÚ ðu]^Ñ€BùÐë0ŸÔ‰œ—çþã= œ=MƒÒl„½U›Ð(Ò ò@ùGÑÎâö“=}RGV„Þ= ÿëYº6ŒAõ•€ñ²ú33•.uÕMk—ï°¨ytd?‚™íÆ\™*¡ÅffRpƒ›= Üq  $ò(Д_몾-ÿE*ȝ»õ#Þh) '9èi{f§ZaXeÒPô¡T°_®žzÓ–ßu¤‘>%€+á@³6€¼Æq$æ=Múßâs°Šêl]Á+ü™ºIəç ûÒߖ°PæÂ~=}4EÒA±*\@žæê£ú±ãdj$Ëbâ« Ò;ƅ#¨ÁR½/ÔÔæ3†Ý·»â¯ÚªmÔpە¢À1¼œÆ˜oë¯{ß­¬tF4Ünµ)ü„N4¥²aÌSœ-ØÏZÎ4ÁÑCqé' )∯J–«•ðã'š»ç2¿®N †‘¡Û7¾2²A¶So…_žœÚa}:i¹0ÉåRhÇ@'ù.ÊæOÀ5¾“ß­õÆJE^hח¢×°ÚZñêF€¯=}$¤Ê EFžÏ ؾªV +¦!™“Ž#Qü±;†Ôª€˜‰ÅÃÑ =}ÆY8ÞåXy¬*ۆiÀ +/“ä<ÎÙhU› p°âÀŠÄCÁßx–”}kÀ¨~€ÔFbªI?—cHʉe 5ºÂ„qïßmš)F= ²_– óÜfº©š cy²úR›Äë„2ÀŒ¡;¹#0þ¥ƒQÂ+ïøÃ"²ç¾*ÃWr°e.€·/Ȱd¼%ÖÚæÃZ«h'xDKϰh;LÓò¬fü= —¯jl?—Ú0c›ƒ«»‰èÃTÇ©çÅÓÞµèéô‹Œµ¨&ôëP5QFYÕ½°œt{}ЩøÁԋÇ©hÔkÀµ.,Tëq+h@UûÁ¿* ¤Sâ·*Ғ†ƒsè(FÖø›WåÝÖÿùDðH­é¦†ô†¿2ŠN…4ÔÁ=}uïA#±6¶“ÌÙ¤ +šÊ€ §gyÇÃKúÅeÊÉÅÌÏ´ü>¶D6@+3š,K=MQi!=Mç{M´Rii¸=M·Áôƕ)½•Ò=M—I$ö²°Î×óryÃÍç&ön׳6Ø "]ásÖ.©ÎŽHÐi˜xSå®8@¹¹–WS¬z…åЕ٠ԋ#¹ÐòÔÊò®VjýÌ ÐOíXûÿR(&Áêé1ðàܗÏÛ¯Ðܙ1„[â<&àjòÚ11x¿²…Ûq1”nÜwÛßÚw‡žÜ·êÜ­vÛ B‰óÛ‡ kˆ;"&|À1‹ª¿&A¿ÐƒKd•é­ž1†—êÌaÒuž¿"ë-ÛíîÛmÛ¥¡ˆKQƒ“œha5FO-fK)FðÐê6‡üÀjq1ÌbÜíǍ©Ì_ÇW4ÆÕ[,ÆôõÆÔ¢fÒôìYëÕÇúȜ5²5Åì(GýõÒ 7× ‚¢¢ë P½€\4xj~\ùä(ïTC :ÅÞDëtœU<$o|ýt8˜z2Œy1ž®OhÕ +2ñm/ˆR¯Þˆvî¡m‡.8ÀÙ̈z±Ì†oÖ8qΑ͒=  R_HêóÞW¸}'˜³ÉX׺=}ý¬ìžÉ«®2pµ½©«¸¹„%†ÑW"SÿÛI^ñË÷¶‘lš¬vž¿º¥ +A¹]m´‰›‚³]qÇo2ȗ”è;PͺÍÀ¨ ”GZa[!{¢Æ«Á£f£#Ã#pY~Ÿ>¾X~Y~Y~Y8C€^ZtVf Ž.æÇJ%·ªrt4N—žÀ½Îãú%BvS¢Þ]5 8]: Hà"üËM;>uÛ'Œqá ýÌQë*œø,ðï6í,Ök3±Z>u—¼Î½9XF¸°ñˆFÞ¬ºöÝæ£@@²ÄÎBë}m¼êéz5 ~i¯Õ@kDP&(GÜôéF¾=M‹–Xéƒ F’½>ÀCl"÷6Úð\.l^·œú#–=M«˜Pèðt©[jéƒÙŸ–|,Ê!C½aªձfô_>â$i† +3•œß)ØÑ$ژÍÑQa,>‘ãl°{[‹ÄM»AC̆Ü1°¾b‘¶ñ¨!hðŸ'dÂ÷Ô# À®*UF2(¾pœÔõÌ'¦™ƒ9å{à܋RögnüÈ °Mƒ J½oýדRü®‘P݃y&¢+¤º¾fVÌáÂÚ*§ü–¿³X(X<à×EÀtw¯r0K]Ф´äÒ+[žCMd²ñž¦4žÚ»‚ÀæRÆ¿íúEëĩޭ†cß´î6ÔýA-~ÚÕ3A¨r<¿·V‡†ìäÄ ‘8ÕC¯Zñ÷ÅeQ½Ec|£Š= p{ƒ{­¢vÀó¬%y6g:˜†Ór³Ÿ90%|¿š‘ò¤0֛NÙ2¨%ƒ¤Cu§£cYzY""‘yY~Y ~Y~YÚVMBÛÊÇ;ÂÇ, S×ÒnÓÈô9Zvˆ\œý$=}ýè-yÍVnü(P¦xë\Fñå<©Îz˜ér^²¶•\âmU¦L]ß«P-dˑ½mugÏÀdƒ#4ûÒ½08n»r½ê@€ ñBë9€Ç=M ñ„Cn÷׺ðŠ9¢¯)Ñ@h3'|ÿ‚Ô:\–û¤A5.[¢!Ô¢éæú$?ýWˆ=MV’5Ñ&ø—¼=McèPH ÒF˜­é˜hêzxÏ~U¹1ÿ \$ÝPð»9‰€NPo€qc_À{â~h0\[ù[º–Zž4xÝkžæÏÚße‡ðj;N%æaʔ_=}üöqµø1ªbÒS~È9š= eigNÿÀ^ØÚŒù^pwFÛ[ÑFʂ†Km z‚Šu¦Ù[àgŸjÆ‚ ï•DÊûQ- ‰Ö…ªß +[›®mnÓØ¡Èø‘R» ›=}[º ]—9ú‚”h)o¢#‹2ÿõžg-‘ò_L›/ñ}º™*HyŒ¾È)‚Ô¢¦˜žâtÜ¢.o bl{“*h"4?¢2ì¶°ÁgÂømÈï~ej­ö=}oSî×ÊÛo%<á9WÛýDp€âً YžÎÞ%Á}–I¦²ÒÝ5u,Þ*?ÜV~¨nÐQïþŠézw˜àÛDŒ,…ÅÒ=MŠv)²ZɞÕ_ÂmF <Øß#ò&¦<àëHad¯¸e´Xóm8f›P»q֜«oÆ :1…]÷×6Èó7+Hb¿h…¹hFÐíCPÊ4‡AÇu5´˜GSÿ7->˰¦T¯=}hÕ¬·reýžÊl¿õGô:l†Cá¨OøËG÷T+ÚmhOóÐ#Ìp½‡˜ÑÊ= ¨| ÎL½û(—3=Mi°N˜+ý1MfïàȆ½»ÙT4Ð6>ÞX¼=Mò­ù„ï.Fq§¨\ƒ^y(ò9QËI[UH@¾~\tžP¾pÍ,a‡ñÿ/ªZÓud6¶ÖßÜud.a= _›•m®ž—ñVÚ{ªŽøá络×e:؂œ†jBî!/“j@â(ɤÓÅ#Ž¥K¯g ÃR¥&˵g£p-¨–•ìÓqŒÄû¯„[Ëù墽m>õËÜ© fë>¹iœbWK°ŠtŒ7´A´då0ëe=MżÕ5$‘ôÛï1EzšË¹êZâ=}ayÔ¯½½&z.»iÇhÕ ðÏô†;Î0t(ÛÕ= …üØÔAØÂ†lrï˜)§9É5Ïìi<çiÝʄ×ŬtvǕF5ÔÒ¼n‚l ‡JÓö­Ž]GuRÄ8Ò*õtÕî†åZןÒG´„7q*$Œç‚íÊgÞ&ŽÆÖ³ßoƒúãɦjU‰ìÉà&VØá®0Jü°mIŽ[Ýám (±Òꇉ±<‘\Â3îgHÿù|‡Tð8ê˜tßٌŠVKÙÂBPœwXÑÃË^#X®®ÛŸa#A·÷ÄjRºõþg¯ü´•àè_úï>±oÐi‡èÔ¥’µÙ™Ñ„4µñÌÈÑR7MHûßÎH2þ¼àø«:4€E¿R›y2̊hé®Íø÷dn¸/÷d¬^íù×÷…[]5gZ8•KåÏNåYT­€òø·ú&E=}f¾N˜D=}ðÁØ;ë=MSþöfh:Ø;ÞwuHI9\®=MÜ"J9è)»‰w„Ôa1:ʎ+òûàò]z†[TAQR<€c„E©Éð=MCpL©ïý×ÈC´ìÆáî½\V´pÉW=}ûwF,þ=}{™C,£àÄ”å?¬<= ék‰å‡ýÅQç-[R³ÔT+Š:èßÈ /2;>øqò·ÆÝà †ÞԖïțÉìó:ó_çe~®µ¿¡í‹Çù17n HþÈȉM7ap÷Ýl›;ÈfX2¯€HÕ9B8”ŸN/Î GTY,‡OAÏÊ:?¨²‡oNÃpød=}ï¨Þ^Ìórù“ b% ò·? JÇlèìr×ÈèÈÉΔ}Ig”)5.â8[>K«®øtE÷è2aÍ9oøÔR­F^ÍM¢Ÿ‚ç0ôS=}D¨=M9öÖ-½ð=M­r¢¢0½@±€»—2¤#X¦­¥Ë;ºwYÎMñ~Y€N€}YPCY~Y¢É^üf79)ámÖ°ÖÙ»ý@Ùß=Mk¼Tî\úgõb)ê.¼lÝjÕ&j¬n5ª9íâ϶ÛÑ +ËŒ ø‚WN†³j˜ü1¿Â±ÝÕdžۼ”7=}nÖEGJÀr0™AٍjÕЕ¿‚ƒÌ{vŒMJò™BApT¦Ôv©‡®ÇÇ[ CNc;$œ&¦ޅce$*¦Br§Ñ€±¯!¸›˜Æóá)sÅ<óÔ.“RCÓ½„äØÄ¶Äê„D’„lÄz¿Do­Ä‚©„‹„xÈĊàDgîÄ~º„ŒâÄgõÉô•JúM‰÷í¸ù…9óÙúó­zõiÚù™XôqøY—ö/ËlêÍöÊÍÒ Í ¹ÌùÌLyÌòYͶšÍl1̈́/“U•9•D¡(Ն= õ>•BÐ#¼Õ¯FÓ?~i7U~YŽqY~Y~Y +M¡z“›‘ž„AKö»×Òµýº¦Ð+*]Êä¼öDVŒ”= ±p-%Šé<•F…èó45ËðܗQ5žMFs9©Ëôöw]Èý{¹À+ñèžEÜ ó6¤Ë™³WÛmؽPÁ*ÅïåøJEôÖs]| ‚ßWÄýY§˜1-Påbyj’'ÙäLYFÄÞt8€Ëqá÷…#Áå 2CäjóR¶KtÌ7š/ŠBæÊqFºô-n z¸zæjRÞ"¶Ð= (å€äx’jàžá-³ˆ'MàéPACòô2 +Kfwƒ¨Í¡µH(¥béJe¾÷„Эš±Z)’ôk² Qœ+±ýLò‹/8Ÿ/™þ‰Òqþ‹RK(ÀTǂóÊÞ ôaFMÙfû?ÂK_°Y™wŸçOT°V¸Aý“Ë$Uùn}û¾Ö,PZ{ñ~È^šTàZú™q៝! ha|š"a–" ¢= ÂC†D]Ÿ©ý£H¢×N)®ÙòmdÙaŒàڑš?ÚcÚÎcÆÛD¥ýÛ= ¥/Ûï‡QۆHxÛjÆ£ÜKºÜ)8ÓÜ YîÜãj ܪ‡&Ü ÿ7ÜboHÜ©Ø[Üä8nÜ ‚Üà˜Ü[§d¦#»ë·¨S}Y¾–‚|OP~Y~Y~Y8GO ÁBZÖ°(ftA® (^oҝúÊ Ô¢š¶>ÃgQ¹ÍU¶=}o‰;´þWY²ÂÜր‚ û#Ë.Œ,ay¼ïüF+Tƒ½ùYb@àë½AÖ/b¨,>QƑrþ‹R*^ Š^¦7L/a9ADÑÿIʌÚl02ôp0B ’Bד¤ˆ ¢?7t<‰R‡Ÿ42IºçŒ?ÏÀqöɆµQsI[´ µáåiRϋ#>°ýññúÝz‡ì²\ŸKÔ+-àeŒ^»=MÞ4ëÎIõìXÑ.a¸ˆ6ú ¬ €ôì¢QNÊbžÐgÐvìMÜ × !'OÉ r߄”›¿þ(W,ØsÑþ2Œ‹ð{Œ\¸øYޓ>x_X~*¦ªD¿§)¸j”~—ÌÛ@ߪ: +º”:„¸;º*¡~Ûc²2õ«:ñŸ£ÒB4ܘ¾×§¢åê¢b‹· «0ˆò4”¥Ñڂƒóxõž/Q^¿[Úm06´Ÿ“ôµéqK5éh 7ø%VK­¥§xCÄK$EýÓ:täa]Á­o¯ˆ 'î¬ÉsMÕ&¢'¨™ü†¢’4f 2p‰1{>„/q¼úk‚—ãXCºI¦ sۍª­gGéÓqø'…“œ«7Îé„𯗑hæjÉ´"Òîä±½Ùù vB|k2nh"Z®Cºÿ§ŽH›Ð_¿”Ke °ÝAzf_^´n7ý™ãÞyèeø² ¹S³ÈòꓘۗFÊDÃß&V:ŠDtZ+È%ƒ¤S­££CY~™nY|±Â|YÔ~Y~Y^kàü†y®P¼ ›“_ÒòNžu£Ý%©•€u¯*Ó…ÓIaÀñ’Æ”´/…ÿ  ¥÷iÕK¿–¸ÊÓLŜÍ6õâ(Ùdי˜A$~<ÿBÛ>Hé+ÿÚ2d– Ä x.›–¬z +ÏF|ܵŠùE€S­NR›S!À^uy¶—Æö¹ êo¹Ù+Yu\Þ5sºnڐˆ’j“uäi/ào”‹Æÿ/8*ò³â‡·^Fñ–ƒ–üAÛrh<º Ñ= g°jÎ02Ê!Y§h°5«8éõþ9F‡Œõ´„õªLµóEi:ó°¹ý ”—†¯‚ˆpØÄ޲+ÔÿB-²²3”3cª2‹ÎoÿpÊoúmã( +'nßêø¨˜µÒÜҔ-bµR”4&b—þ/‡ÄR—AdÂ žÆH…òϝ<bcZR/t¡é+ŒÑÞ¡ÃošÊ¶E"T֖ڞΡSK†ÒÈæ¢–]ÙPÎÛ_êºd¨àÁ̎Ƙg#|b°ZØ;ÔE¶Þðt> 1 P¨nòý…Ih—yÿYSäÐe9N8oÁ„#šÍêy?…ac¼¢ÑŸ¯èY0DßTh6·ÌÒ]væ DÒÍF Ì|Ác8ãџ®… ӆ^åSþ´i,GaèÚ×5Žôïÿ¾&aåÏà—…û=}¹r5Ô,WÀì…z.©œJS꽺ë +&Ó&å:ïÇýUS;æSúšßG^•Ì|ükIŠè?²Ò ŠKõýq:ÄuÑÊO[%e8ø'®N´ªysÕU1úŽÜ ycE9éŚØVˆ®èPK +vN±åvÞ¼ÜD>£¯¥¤£™?~_Ä~ûQÐ~V~qØ~Yþ¢*¤o­½#€$@Ó«žCä[â;@6*ƒ»Ù'ªò)Û»…)ĕÛúº2‰«¹Í1gfËA82ep×^š/äM¯‰q¥?ÞÒkîäÄ}¥'ë Å/{¥´_Uç“óºè^»ô7^1艍Û;ÛA:†´9>c“P¶Ü‚ß´:ìo5†i߂juÀžT³à#„¤ÁG“fÄ/²Âii´Ž2ƒ¿·P×à1}}¶_½µ‚/6+^?užvëÁàfr=œu_’3éʉÏP9ŠSÁ> ºK>´(y..a'(xIb_åˆDN¯?Ò&R¯LU«]†'íÒàðm(I•¹dZ< *¾Ò‘u"—Á¨î’Ög¦ºâV¢æÓ'm.jo۔'‘8ò<ě¯ˆ +,qý*ºß4ì*oÎåG6ݎ°îªaÈEƒÐq¤’ëSr9ò œ4ªù¼Н,krB¡'6 +õâÆWž®r!DtúOðaêã1¶Â‡[sË*E/ßìL•~FãgÒ¨AÑöçõ4híO8ú…Jµ18´Ìói‹[­µ„ͯ~EíX^͘Vx³Ôó†{ _áKV_è@k>½qxã;É*eØ\_ø®°ÒЉVn÷>û´ÀÁ̋S]úª„Ê]/Ow‰Èà1HXBÔä ×@*¤p–¯,ÔØÇóo+çÆ©½Æ˜m»>© ×d҂'Jðü¼m[€ /˜Ú|©Ñ;†]c¥ôî®WÉýUën5O4ù{ YåT~-RòPÔOXɗ]°F­[Äîñ5ÐZ±{Ä» ê7[à‰âôý†#->¦Y®¿ìXm÷ÊŒÞôΏo®¸ +ɱđF®É€Ñ_‘mVéúá‚fQ0N‘Ïqêø}&äLÁK”/Êè^= ±™åä>ºb= ^dªÂŸú›uŠ…n4(!›|TyÚ!; †‡‡’¯Ø›ñ[{²ïØ"“üƒÊ7"Åڟºù"´Ú“Ú¥Â!DŠÚ¢ žÂ_B¡úvÍŸfO™Âú’›VauBlÚ¥³Ï°ãçÉ3\($]·g.Ã!?® ùÓU +åëﮢlóõ5æ\ϯ„gÓ%²*–ØÑ?ªÆû¹¶d[ 4/1-ç·vÅE{[Þ=}Ît‹/ÄÐÇ)|Þ¶¿îa÷¿*&…+5/Š ß赬«ëaçż¡,‹-Êqâ,H…l¯šòI=}·U¡H3çÍl¢õlÆ0I" «,JVqâÀ¼”¯/ Ök·r ãɲÈU«²kaÙM$|쉓ÓË*½LÿÁ©ŠuXà²83ß ’oÊÃ×Â|¨«)º“ÄòÅ­üaZ+kş™¸–Ä];fªí;¾þå’dõU†íœ[¯~'Õw29–ÑL?ªjÎ3ðLƒelíGv¡¢¸ŠÕM[ÏppO=MÁBN„ ñ°=Mº=MÔrNh–õÀĨLJTKAôU°oÌ#Ž0ë:ڕ— ¶ý8ð[€ÉM]}óu1øîÞâA³zyät.ñýÝݼ•]®t‘ÑIb Ú΁šSYb*˜ÂüÞS28BžûÛ[B žô›2D£+&¤c nYz]:Ö!oY8Ô~Y>€pYþvt…ú)¹ Wë®,AÙêUW,=Ms y´°‹3FæF±dj ÕwˆQÑ,f¼ÿ:þé'‰ŽÔ'Ážñ‹DEòù$áXƘ|€OrÞ¶¦®°³1ÕCõcµöæ1¸½• 7Ä}½æ¿o/«ÍÕÅ!W´ /k×ÖÉVk»d Øi›<<ezôÉÿú>S€w7”(IWÁí'}7òÉ ÒÓF’ëÖå=}p[ñaÜÄÆo áñF¹oäðö®¯2XClc¥P¿®ÚûŽS¨<9ÏkW‡PȜªÐF _Ìÿ’p4ºçªœÓT@²2¹›³!n*ñqaï6›UEÒÊòœtg2ÖÜ!P˜‘â"–ÆÔBZ= Ÿëñ©èTR·– +ètŽå·ô‰tp1I]Õ½.t/Àpš#»G¢jS ¯Zqg¿.Ïd&”}³.„vûQiÔò¹êœ›ÛÀ.F‰¿b††Œ}àAFî…T_¼òQ¥€­¶KLðCê¥ ‚Çó´3+žb,ÿ@?ë0Ý´ª@Ç ÈɁÆ,Qî5{à,²ùl[‰È¶<BÈFŠ««ãc(á¥]Òº“„)#±ù×¾O©æ¡ý‹²Ív»ÿ3é>m×µÚI”ˆ½î?u´·JKÝ@{pŒ/"?q¸Š¯oîÕ«Plé´2ÖSq‡ñôÉÖNÕì~;l<ª<·ÞTÜâ®fDüRpEj@ύOVLnmI¢U9 +F"Ð>°I܇H‰sà*'œÜ/Þ>fÜ@ÿ!j‡…sѺŠL\»ÀnˆqŠÿ¹¨‰ôÑr†Œ+ß]o†mVßŕŒ;rĜü˜šz–üýã¢7¥…^· + ¤ >®šX£èóÄ(ßÍgþÄA2­Y΅ú ´=}èwÒËe¢ï´¾! ûÔ°mñ0ËéÔQ2-”YÐdÍF' +àíûàJ§<]'ú틍mèÌ¡ØHWýþ톘õɼ8ÔÕQ/ÑPn+Àúi†wàÌfkQ?¤Œˆ?ù1ho519ö—Q?ÿš­å®Í›yR%°®ÍËâüdXÒ¸·œ­üý͇|ęî('A¸µŸæ+ø#+ވН¸4_ïg„VŅ"+;‹Ò‰õ<»@Ä¿:+TâЉjºÔ½¬èµžîûàò%ž¬¬¦‚ì{ŒÅˆ97ҀG;͈máÌîAëôûl›5ÇFUX/ºÆ5ôi­ÜÞ凳m<Óç14¿öX?h’‡›Üλ:Qƒê¨Ñ†¸«YÌk^HÃè, K5N¸í½©Ì?R•ôÈËlUôÔÓìh7¢÷o ót[í(•O­jr͙[¡;?¥ˆ­#»ý~Y^àXP pP~Y~Y~†ì•¾ª0Þ{º3/¦’骆QÜû•~deñ)zu5_6¸>@7_„qÂÊB^˂èêdèO.Q¾÷= Å™òºŽU^Lnpb>Ef€—ÿˆ  üڊá[\=}qf4-Á ™s\2*{–à/â‹Ät¶²D'y¹™J,:ÒU:r†_ƒxÈÒaÒ… —ÔM52@WŒˆuÂ^š›Û zj4"á9‰•Fw•6nŽ +jãâø¹œükr:ê!íi®sÝ#Q¦×šªËßÛcŽè¤IÀ­ó—ô#%Çp¶GæPÃFä9š®U3ü%‹Z&7Ÿ¸‡=M‰³$íÄZ²ôx±Ôõ+p¾å?ܺ…>³~6)¼üÛï= ©›fû¦™_;"ò;ôÚf›¹‰ÿ]«Šíê{}ßeÓÔåp„´ÁÏ1ÃÀ+’ñ'MâÆ—nÃS]/cD‰¥ª§$¸æ¨Ï·Ë^úƒm°¤šº$nLçí¸¶,Ù³ü…¸|€êr4>ç?¸Ì¢å‡ ÿ,±,8Ç ÆÔÿ9{Øï…¥,þ^Çñÿ4û“ñe6ûÉ <xUY:¼Cmÿ»Ñ6DïTÕ8Ñn·"Æ6Ï$eˆ¿­æÎ͇LS×O3!#%Õæ ©(~Ê·áß+ŒÂ­ÚöNOÌOÅwõìxì$I5[í¢͊YMÇßNõ‡,½†Ê8… Øõ·t[ôöª0µ-i™d½è + /C”;éŸ^½â·ìE6à:q2•ݪ=}‹»Á@7€I1yì[ójZð +ˆ @~²×È֛;nƒÿ& 0°½\¯A<©}&êV°=MÓ» gƒc°&ª¯jÙ7‡ë6,xð½ZÕŽÚŒv€‡žË¶Šß6¾ðÂÔì"fG“Ž;¡ºJT@rè® +.’НL¹Â©ûW?ÔLhœ¹Âòýga”ŽPhfûÙþu{ûœùS|ycI=MÊ>ø%ðEÙPi{¯>V@p™ Ù>Ñ{o F U̶Òm ôˆ2µZå7‘˜F ÂHóË-bþ7tfEÏ?™B#%¦.¤ƒ|YžL~Ùâ6}Y~Y~YXÔBjÓ7DjÏæ€ø)7^¯Úñ½×Ÿ•?Ÿ^92aXכjUÃ@ˆýnÐâwT‚{•Ênh¤9<†ÐxŠüû¼]~•xÌ9–òÞ¡"nôŽ®êr‚¿öÛÉ ¯Í ÷t4Fa†HI1¼@¿\©Û0‡?;ae”eÙê™f1)º¿¦’É¡Ašïjv’î2 p6Ñ\ŠÉr‹ŸAWßÇ“¬¹ßò–¿‡4AÄVßÖ9‰ퟒ–܈Ì"p–fÆra:¥°‚®_”øC~R$Š5©MG²or¸÷דhãdÝ$MµKxÍóQÖ#c +F¦jJ)§ž$WN¦Zɪ¥ªñ¨Õ°¯= Â[›ÿ‡ŠÚÆèá1[Æ!2Wð½44uÜÅ¢¹-ÒÀ<ùØõ_!‡9é=}X-ß|Èõ^nSkÚ¶Å8 +é©Ñ-OÿÀÜ_È!‡Tk–4œÚÅ@Âé­áÒ_™)ӊ¦Dƒ:elÂ(m"¬ž6¬Œu¬ V¬J–¬‚ºº­:ºG]sPaÓã'áp(ِ(=M€(× (º*®Îi¬˜J¬’p¶iÒ¸¡üÎïÅTê û{훝5ó‰Is˜$ӎ¼–nm$Zµ¡DUˆ®qšír‚7-¢ÍâHó1HøÝô¡kPOËK‡ÏDOÒJGMM/KPß8MI FŽG}öœJb5K5 W•¢çˆNŒmö8.7ÄÎ7uŽ7ZË®ÍșÎú2˕;„££å.‹Z~ylY~nY~Y~Yr]v0#b~$i2ޝèªu= ®}B,Ö.«â-Eb¬¢ÐÉè³NF-@Tg›êµTÅH»BñWæ·oÑ¡qÆñ·³ --Hg\E”ø0“ùêzóKL[xõ‡ÊIýߌÅu™·P-ܦh”D…xë”~Û´ 5ëÓðgBÈEp¶bϵr:-ÆÎh/XE|é4ƒ{ÄI’´è±'–t¢[+ݓ@¶¬Ò(}N…b>EŽÀ†akû¯ÉÑa·Ô-mÂgsroIÒp˜oºâ;¢"ٔ!Öl¡¯–¶¢"¶Ô5œÉM\¾€-ijذµ;7,p–G ä­öÐXœÜ@ü9d·Æw ÔÀUªÿÑvÒ2œábŒõsßÐ͸Öþí;KHð>U ò–Èöa-¼öp¿N´XðM]Iå †)„= Tüw×Áþ=}&Pp$YdòVñ ñêÖWV €x7µš×B˜Ù*;ô ¥r£¶óƒÁ°Ø *>ÎÆïrôÉq»VOÌÞá›A³ßðÕBñÔJ=}rJ*¿Öo†<þe‚ù>\T€ß~á×‘ْԐ°á™¨@ӒÚÂìZª3\f£#fÐz»u…~™9 i|±V~Y~~ +~y6pR]›"¼"’(W™ò™ûšha‹pF˜‰±=Mút?¶[¢v;bOèQ”jþqs6¢Ün¢Vò.Á·=M¡î(ô„ivGÒÓû:q.ـÛýú +7é@dUû= ýJáç P­¶×ÝɝHð5EA 3흘ÕÕ¼6M¼vÍp¿K3Q–%ý¼‘ªÄWxX򫔻 ¦D×qyi舿À@FgvËú:ŠÕ ×t= Ê5M‰þ¾N[ê½x®ø9(Ó%(ycûy(‚7õö¾’lçyAšÇ¶Ë2‹i»¹U@®_Ç'%ȑàPŒpëIT'¿ŠÝrœénj7³ËRKì¶Ýs0¤¯ß6;hÅ9T1P„³ÊÛj¸2jߊ°Â›o!”2óe,Å)µìÉ8×ÕkÇ>¿lÆB¥­äj*s–¤à£ןt.Õ£1£¢+®Ø:kdØóŒàÙZš?ÙэÙB~Y ~Y~Y~À®)±…}ŽdŸë¼%‹¡ŸX¼q ÜÆüÎÖ-5at+"ÁnwMkß=}ãqç‚?oj—ÌËB)‰7‘q‡ö”Ïà*9\k«o +m›(+‹ÑÅ" µ9§Ðˆ…˜ˆ_Kþdî”øÿ_)¸¡Ì\^ÁLa°oÕ'…tÐ×ø2m’~,xѷ׃’,= ÿ‘}jÇ^¦ßág”Üê“òÜm(†ßÞµXTJT.à&= eÜwRƒ|îoÒ= /R ø1Ÿ\1vL>/?>X¸2²œ¡´*òá‹U +{¯!¤èÒ¯!pۖZÿ3rRrÂ4]–¼d†\Z˜Þ.9ŸÇ=Mm±¢îzâIŸ +þ.£Ê¾¨ÜÒg,ÝYy,Ó\75©ÔÏ=}h\zÑd+Áì/ªìV G¯1¶üÝö±L蕷Qôp±èAÂùå˜%ÕQš÷+NíoØ%ˆwTÙsãoü6î„ýïotÉCô**Ƙ üCrr@©×ÿׇi¾Æ¿h0ŒÔÙ +CÌr•p³ÖêaȍžP3 pÞ´¾±þ{ÕÍy1#ÉosäÊGÝÖï|(²aQg÷[Ô=}ªz±ŕd®Æ ¿… »‹*¡ "¡â°ƒ_ ö…%«»µe&€D×!¯†FøÛèh´¿=}Â(up 5÷»?¿nEì”_3c©(ç‰ÈÉtàÜ(+NL+ ITÔÒ@‹È<ÿ 8è_ÙÞ]3â?êMáÕ®{SO+æB-ܵóeY¬ï= ñ4 ¨&MÛH=}–½šwõTAF¹° ¯ ŖÖÆ¿þûÝI0°r}ü¼wd›¨·¢Œi×^*Ηtò³ß£é¤££·Y~‰òzU~Y>~Y„ ~فžï'@àōfmþ \ÓaÂP´ »˜LâΩ?Ú# ½¦¼!³ç©mÓ9ò)“óÿv*•ë»ý*)V= ‚«qž0C‡ëMú0…œËQ<2ɕi»ÏI2HæÛ9«˜¯ÝàA#t¶Ëò;#_Ÿço2ï “l1>/V·/2ñƒK ‡ÉðX=}rqp7<‡ +qS”Ïo‘qi Œ¿Yxq6jÿOaoæäÓBUD®“ìüã2qåÒEœ·>º¸W'ðUî|¼9ÿX?¡áÎk3ûleÖN˛3È ÂD÷ +*¼2.eî2J»<àZqŸ$“ˆîµa†Lz/µ_„…Zïÿ›±d‡š‡² ãæzªy ‡ätʅuá5“(usR»"Æ}”Þñ¢cքª² ‡gò™ŸEòƒÒ/Y ‰&zGÙ+4Åilx…¯³J<§®Ç3ûÏlî썺ët Éh1œu<í– A¯ +ïÚò9öür:(\¿Îºm–cõA—h­ŸYôƒqèaÝË5Sý,Qíï}Koëúdßó¨$ã8L4MÏÝOivçØmòNÃ÷ê‰M©™œ½¯C,8ÉZ^𮀒}¯ÀK&6?±­†ÔxtH 1ÁRO˜ûaéB¼+‘Ùcß}#J¯¼?k+Þé¿KÕeŠ@ÔZèï³k; _Ì9©IÏþéTN¼eÀ‰+g; O¨ð\¹§âTÇOx5ÔÎA0T _$Øî‹’fÔôþµõ´bjn=MÞwb{öàw$}±«¹ÞUvv4úž?뢎g*^q¿‰%[®ý¶ÿ¼„-’éگŀ“[ƒñ¢Gâû„lýæòÑO”eØw.= Š_¼ù‰pCÒἈ.4,Ò—/Â6RœYBË,£ﮇ;öÃÚè&Sì²t.uª'³E¾å‘=}óyé(…Ð/šðß3®ê§¸á´ܸlA•뛸h6LtÇ^疋¯þ­{™Ø9z¶†k/†Šdq¸g–¥)6µ¼Š5+’4Ç×î5S8ïx/·‘JӄÍPXL/7¢fhÈ"´)¢è‘y⚟æô’âÜþ'¡Ò}~ש0î +DÝB1 +sZݦ"\ï:4·6hEê|[¯l„(±Z±‰Åø9îaÁr:‰VÍÙBò­Û×E³*åyBËELQ+xiτG¼ùÏð~ ]÷;÷Ðf” Õ mu~ÅnZ¶U/Ô6&áÜçÒn‚äANæ‡úÁ¨Â¨…ÉËS“H¥M„-ïBF+šÚèÉÎTHµîÇ7üÿKoQm_÷ödHä(j÷3ÕöbÍ!z÷œ‘Mi|t=MIi¡ó‘oôê*Ý(Œ½¿ÿJä#_ÉŠÎØŒÞKì#4¹}= ýkÿxåxð¾zT}w_w G±i.;ÆO.þaњYŽUëö2"þ˜ ÜJêB&áVꋜLṄ©/ÿ#/SÄ•°,| +E1´rÝ/[!l+ªQi+/ÚižPÔ&†ðau'üöaù<ՒKÌè9 ±ÙfŸ4¼yD‰'ÑÓJg%àè¹»VE4Èé¢ý“1È ¤ùgrTe0î$Ø´O ÕW âÙkXDÖs9B<ÅêTÀ-ª=Mr= $™_2ÿ'¦ÄîÀ»Îkj=Mêñݕn,œ?k"Š'ö-Öý>?tsdIn(w¾Ñïc8 +ÿ춆íL¾˜êsXŠ™ëY_ +UªnÚón62àA›”õSºµ.= »Ì0ø@ÚzrÖrp*aLáן—DFs:,ª ܈*eËâ=MœΰmY&+D⾪›vžwBO[£Ezª3O¼ë Ѩ•Ûß+C8äOú±iNÆ;–Ú&œâÛ'“HÄ»Ì'Es½-•˜óO¦fë¿i÷\×*6 Ð?ޤ´wÒ1ãîá5nÐ4Ï= Ál;CËéÏ0I×~Fœ!Î=}kS»9R/dûÐ98àTgß+è³¶j4˜Ïs, +…¢”ïÁr‡§‘òc{×41´, 3ë!µ²ÉÔ|á$‰C¯qbq! É&™ +u:hClüϳm‰Ù}:U ­k„‰U¿w€Bn' +\gI‰D= Ê2Ryœ_;;rÞä¤ÕبÿÑCàK3ήsÍl#Åÿ‚·Ñ^;¤*.,Ìd¬ÕÕHëY:Eb·B›Õ½Z:TpE¿X¬ˆŸÒKãGY%lúΓ°N‡†]홻÷Ôç¸ÆbùÔIO=}(ŠNÜ´ó&›M½5=MüÖG¹FòXÏËQfŽÀL +}-mW¾«¼u#ŽJ©ßí½,ázëã¶îúؔZ<'„p}/è®JÒXs¿}-ˆ)ù¾Z¾Ž¨OQ +ŠPiÃbV&§~ñ¹ñ’çýI.¢]]3!yh%oµ1Ɗx–*„#’åW›YPX‡‚Y0rnY–M|Y~Y~ib†æapڟ‰ j¡z¿a¶•‹ª¸Üâû÷/þa·oµ +}ìñm+FÁËñ JÛ~ÙìC˜ã%䯬A +îóÜø5ªêÇ µ:çú5œ5›R6…d̉¼ë4AÄ(y ‘¿5 .;×v GŒ4IP¯üëz:ÄÊæ)mlU—À5Q[Ï^ uÉ.lB·žlFB_m¡ìwWÏâ™.‚õb‹±›hVQw-¡‰ˆ†‚¯; éXƒÚ>‰k4!ý±T‚±:Fã±2P•+b5Š%@ßE¸•oûEcƒ¨=}ø¸K0úC!WÅ@¥èiôç{æ´ä6ŲzËDê,ç¦ÈÉÎ]Õij8Œ7ô‰+ÕA m+ôä H­ö÷óQóaSÍYúø|NDíì5ÍU†x{ã0}Â=MûM”[†rºø +o¶-Ðw¬Ñùj$©ýºØ3útƒú_©­×t|N,€Úðe= ØÌv…ÉÐßy…H(¹™hý×Ðs žÈ‡÷XÌîs ’ÛÀOšŽÓöï*B!ݟxH&‡â]ïQ.Q0ÑE¸]g®Ž”…]Á–W˜+ìvæ-á^2ŒÖåùBgó /©˜œ[f#.’°{ˆ=Mcé¦‰RØi âÙØDÄΉ¾TE´ +÷08 'ye²½vÈ/ 3ÔҞ/”{=Meðے%æ= ¬¶z;•A̜ÉI?Ï×Èa¼žÑ<´H i|ÖíÊ=}–”+ÜVo”­ØŠ‰3Ü6ü³™t%’Á¹XwÉîõAû lz­îQ-ûTž)¸N˜P;>hG|y/à +pÛIÉûV _°>±|ëm)Äٟ½WdJÐhÂ<ˆ nLÙþ·èë" jÞÞAú~´aeÁ„Þ™+¼~öør|¨±'*I¦A ±@‰ëƒìê1mÞ5º†kŽÔñïáƒ'6'¶º&@³½eJÖ²âÜVn<• +¢„€îº±Û]Ób(™ØQGàhne¸6Õú79]Äf0r©ožv–´b‡1%)Þ) ‘[ð–Ô7ª°­Áï.šjòÁÁ™çòi6wòÉ2“…wÁÒw"ô»ƒh)!Òg6 t>p v¢R²šž3„f„ÉÂ%ڛUŽLÒ¤YaP”È8Âú‘žô‘*ç<Â.矬[ŒrðÆ"i¹†TÓ#z¥;R¾ãý£m¥ÜÔ3ܽ$·J¨…Ûà«TÖäÿ÷±å×Ì+l6& áÁïIÿÓdTdÛ1®d›«=}UÄËê)CÛÍ·ˆ D ë)·½-= ó»¤%Óõ"'ôº»i;dÓù Ûja)–}Ò¿fµYêӗÔ%p´_‹-c!îǰ’ÅœjÆ5r4矾hoÚôß0ÅCÈ-|"ꖉ× ñt-i¯Bj“Ä)M»QP2ȘìÏʏ,N0ˆ›}ÛõÁhhÉ1UÛP1ŠR*” ýhVTÍA‹8¬^BcËn§͵ÛXÅ4A^,û;Êaß4tn6“^6g¾VÇÙ9ÈôÊ5/ ;ÉV\×uìqLñÁËu4,gWÑIAeãX.X p‹ ¢œ5x^\Atð˜OÁëV*üào?~.„ˆùŒ—…?vn™œ 'ÿ†+Âl ÿý{òd˜!¾—öøsr6h¦× ¢£R0© Ò½ƒ= “Ã@Úªœ•Jî©ÊÕ玐Ä;°ôÙg&ˆf7À‰=}hӌ¸fûq³µ7¡ÎÇûëŒP3ãè볎ÑÇ»4ÄÇ{|ÄÇ֎Ò$‰b§¢ Á#/ñޜJY™‘N~YÙ~Y4@}Ù¹XŸé’í洙äÅ=Mñ³”n©´¾q-[žõ'ºö^[;B¾.üÞûoX,o>g7ÞÒI’ÔÖ¿µ±:3“],'“ó¬XÊî»]üe?®µ±= 4“oÌhøí­pG½ÉˆBÌõù4…E7ŠHû€Õ-99tS\/¤f/<Íhù¼*UʆVþ1V/?%Ї÷ ññŽ ©IˆŸ@Ð +}ÜÎà­Y:ôE(%n‚ÌÛðd^W¥è1¥–ª¨%CÇ=}DÇîÈèaúõ=M]5Õ87pôõ„&5-%--¼ÍmZ÷ô™¸¼vø7k÷ôËêè•&-CCi4Dé~õpŠ’wß>I‰ýpŒØÞ‚Nb¡½I9t“ê®°= bvs=})/W)Y©|°Z€ý±7yU[\9â* âønZþXÛUWßNbîj—õݝÙvô!;±n·Àzڎû äj²÷Ô8wD,Aüæ—;GÊYìr@¹= uvlÃ÷ãƦ¶ª¼Kàd#&$p +¯"cØK+4D°/ç©»ÜÙykƒXé'öéáØutåkg%]É͗Ø7,Nï;1>û—kç)oT }wՖKJ<’¡ÚFû —Ö1ž KЧõ¹‰ùV³ y‚¹ß.Ðï{ä~eÎRFù]ùUDF8mbþüÞr- +Z8‚Qü\ÎUôÖ ü¾]YÙ]¢’ᄀÙ©ˆ}ۗd)úSvN/@o‰7fwB +m Þ:r~¬= Yvt‘Ôª:¾Ý²ó"?&bzÝëB““,¦d‹Gý¤Ê¦=MÞ.[lÉŽªÚŠJ^Ü8Ënp™ZyÝàoЄ²‚°•s_*Žß‡âˆd“wfæ&cdŸ¥¬¯ŽÌóŽâ¤¢õ$*ª§Y¹›ð‘Hw.aE:†= /ŸˆN +4šUØzŸÁÂêywê +2¦¶—û’æ}juJáͼ ‡‰švb9Âüò↢˜¶ÿ… +|!â(œ|˰óÕ¿\£(~©û#c/£†u¤Çü¤a¬3²õQû3¨°%kžªŒÛÝk™8ä¿ê&—_Â>Í'Ñ´Eüˆ³Þ)[–+ßå—{Áô+N’æÛ€=M7¯–†Ó.ª)ÜàÙo~x_jfžÓï®ð'dl«ÆMç;vÌe=}‚ÅB¶„ƒ™«ÊÆ?„OÎmLµô1×-Uã7ðEÏý¸ŸtËâ2uì¸F=MßéEõ¯ *»\âjûÚÖiB<Ô½h«a,z¹ +4Û Žhl‚Êq€¢†o½J„$ÛuþyíµW«É³4!n§¸)Ê[ßÌds°¬WJ¶o5Ã2álißìÕMÈ|tܬbÊìrìõ5ÈÜì’5Ÿë”O - ˜ö™ìÄ/ëÔ -¶žöçI»ÇqfÑ"ÁW»³ª£C¹»-!Ö~™V€lY~Y~Y~Y^ÝµËøìï$v§PµkÿCf¶$¨¢-?lǕÈZë}=}Å\],÷BÏu&kSVÅv,Ï0ÐTä7{ùó +à,= ÈÙp3û\ø‘È,ԒÈÇ3LRl·ÑÉÆ×9œ#o0ÜÕm—\Éö•VïUÇ­zï(ŒÈ¸M×öëÛû„s(q·U÷ۜøDÕ8g1m=}uÍèþK½EuDmҞ÷©õ,˜û0¢0á×áWs Û÷†å0ž× €u›^阇ݎ¿@¢m‹÷=MDÖ+qv¨Ý@‰Ú¹ x\ß1Zf7GÑ"þÑðî[¿Å™ŒÞnˆ…ù:Ϻýb|·~†îÙ²ÒBêN=}Á–]?¾’”Ž=M2Ïjß­à—K¢*ò2wáB| 9X“üÖ{ +€ßBxÞ ¹”<¡ +û_#±õãX¥¤w±ëÐƒ˜'#= N¦i¿®KÓÀÂÄZõ(ï˜åm7¬œVkM$Ä8Š'~™b»…_¬ÜµÎE=My+ª\Æ=}ú.|ÄE|‹+žîÆÍÂ+ÛÎe›½ ó¿£{«#Ãû~Y^<ƒwY~Y~Y~YÿÔÐ#’è¦÷Õ¥Oµƒúb£o£†Úª[Ù㕺¤/»sÒ¶OΦÚà+u½U±(-–Duñ ¼Ýa(A&—pÁGhÃM© táë04ä'h䕐°µ’3jÂ%í³Q³Ø%ÄUy«”K+œFæ÷½Å„¡³Þ' —Ìç?ç9‰±¶­(\õÆï6ñ2±v ;že_´‰hjÓØ¶éÛ=}Îå„'”xÆ_Œ³ä¿¸}…«ò™çËÐ垚´yi¶9Ãæè¤= -ãJY¥v*¬>Ò*c6.¥¬ç,ȵ֖¢ÐƵh¸œÿ‡l£4–ÖèYµ<ýGf¶´ˆRë}×µ2.ëO÷ÈýØ3kvö…qç,TNÇmÂ9þƒº,o,Y‡94&ÕºNlw Ò[ïX¨Õ +k§ÒmOÌ6ß)åJ¯­Šîˇٸ]ÚùÛþÄ¡·¨0e“Æ­’Ë_žE3ÄTíÛZíæ÷ùø<ùló8˜øY b +øÙßøü™úý=M0$(Ø=}l½í0Øa1tÛ]çŸÁ0mú×õ_tÛ¶ö +ãé@EÈ»Ý ½Œ‹HVE1UõŠ•¤@}Ú‘= ¢¢†½ÿqÃ:Ô{ ¾G=MsN2dXl©(ù¾—à…Iä52©ñß64üð™8Ô +>¢VÉ$áÉXÊ>ü՟Clw¾É–>Ï/ÔÒVhwõ¹fð.Ñ0Ï=MšW«ïSë^…. úÏé_YS{Çñ_KðhÉ">î@]1Uì\s x¾q|?† Y\;dqŠۋóUMfy±Ô©Ü×?„S@æ5F±ÞÏ ‹“ßQîQÏÑXî\/ØÿÙ\‚pȍû:¯.-¬AôB];b—‡Á4_ÁȔkù|Æî2º ß1ï”;\mFˆ$¢Ø2ÂRàYêš ByÊ +B®%òš½áŒ~œÀq—\VqJ|´BzÚ ý= šµNµxÖ÷… tí·5ÎÔ¡Â&…£Ãç£çt~Y| ~Y~Y~Y€“n³väCmÓ$lV¨‘³kïcbÐӞ&c\»Å"u-ÒÏõF]+R.— ÒÕ+k7<ÅÖy,ïâÏՔ)k3ZÅÀFí LÇmˆ:ëøü…PÇTZ8g“¡µJëgÌRçzîµüBî?ÐtÝTïo¿ÕVnk‡ÃVx:Ìv ~ <%®¹4ìõI–<ÎÂÆW;­&vÍÿÍK3QM%PËoÀdžÎç>Eó(%[êD–¨(Tò·¹¡óûºþ 8ÀhøYwõüMoCÕ+ín¼ÍØzKáC•ŒUm¡ŠÍf!NwëG´È>iY5½ðŽ ÇWØÙw Ó÷†Œ¯0×EÁs»!üƉ0¤b×áÊs”U‹?ËQ6Z9qÓæŠÞ@&]ñx|\办@%ŠêÝa-Ü@d~%©ôM»Ç5¯õXÓëv~ÈÛ&›®°¡Ù!oƒ—ð&ø¯©èÕ45=}l•E7l-ÝÉ$5M&lTäɐJ=}ÇüU:ì˜VÉz‘>[BïAéZ«öüptK_h©¹|ß.Ä@Б²XËáf“Í.Öϵ@ZKšÓ%"Y{µf‰!Í>‹U8ẐcIDU¬*{×@ †QS4|÷ÞöƒAð5²Ù’EÛ¿ð‰ÓÐ*&úmÄú* +]fŸÙ±åâ*k¶¿9ÒKX|„l´*Az¿…_;—vÄèß:lŒ…¹ŒötˆÆ:an=}qÑÔ)] vÿ9ÐZfHsÌ:yZ¹a\“zˆúå2¯<àm¸”³M'%ê*sFþz+À‰t!]êSeF”ñ2ÕJßu€™›™}†oÂ2ܬ=}—lþkŠèèB¨0=}á¢ÝÏ¼‡öUr há@Z›ç܍֕MòŸ +áÆaè©Wò¸Öúã(u¤=M¨ø¦=MY¯;uփyk#V¥‡ø¤F¨ß¸“‹ùã/z¤ü"ª7,ºõÊ+7ÒçÒºTæ§ÆGéŽ3…= æu0±œÞ·iM3^åA­LÎǁN36âf¢¯9¦[¸ô³#V~YŽrYy^ª||Y~Y~ّûBê¯-óË(d ©æ.¾L CLäÆnƒô¦;d~L©‘õ&*þ¯½¿Ô{|Æ&äB¯èÓl6cGò6æœïÅ:ØÌþjGþ6HalœáÉBê<Ç;5HìŒÀÉx1>w= ¹…JÉØa'Æü‡ð 4Ï&hR¹ÚSû~óÎ.}AèE¹:êû/@ô!éÙ0Ý}vÙ,©}ۆpT,U}Iiñ>ôj±Xœ¡{ÉnÊ>}„Àÿ*ÔLÀ¹ˆ{ÿ¸*f0åd2{s‚»*V¿?¸±8ZÜGᅓ™Efqž± ‚Ý“~„ê:Øìý,÷süü:-XX”:€ÿ*\Ÿ>ƒuAn~xÑP]¿Þ‰5†-n“*њ‚\D"lã¯2FìàE™KócFÊÁÎ=Mÿ=M‰”€“ë}hƂã2-àr• aFˆ¹2 ªàA–Ë~ƚ®2'àáè“ܾz +ôÍÂ@€J÷´B¸ŸsžŒ6f™\yxJ¢ÃB—†™o• [Šl ÂcŠ~<ó€ðB2ò oòáú¢œ70¸SÛüãgo¤Ð•ªOÎÀ“JýãHi¤f}ª¿±¥!š­kàÏCœG#¤–¥¹O­{VЃuD#ꊥ­›—ÏgB¥Ÿ®»öÄL +éd_%aȨ0M«UÚ³Ð{Š«©¶Óˇ2´O;Ä _ç“ += äl†%.:§À‘¬= · Í›½EîAÈ6×pÇÜ7ú5PGçlF5PÇfÐlhq5G ÈðéíÉï4¿<Ҝ_í5 >GŸäì^Ö5zȆÁì‰_8ϞÄü¸ìTÅQ­hwo-S­bGEè- иZ¯híHs ·è)ÌáòóO7•~8 ª7‘Ê8¡:7Z8´ò%!N—žI R×,=M70 +íçîßlo/Î wÎ +ÇNRÏ wŒ 猎 +×Âç?ÏB· ÿü W€} ›!†r”CÒ§¢)t‘D˜Hô•6T•:´Ü%vÍ)VÖ$¶ÆLvÓPªAqV_ñ]ñ–UqoMqT]qJIñEñtñæ²é2 ò9qyÏyQù¹I‘)¿Ý@–)h¤}±Ã©EX~Y~þ~Ya€ÇX~Y~YžÂ/*-:¥a¸ÃŸF£š©{›Ôã¢B¤m*ç,ƒÛ¾€ÔÅaF jj1œ|¿–܁ƒ»i4Y-ê8i1~P¿PÝÜ—Wސßkô"7†—çê¡<1(®¿ù§yޙg€ºWލ{i”’UF‡jƒê1 ÒÀâÛu¢w¬‰¼³yV×-Š rFçABÌàôÕµˆ“Gp’lÿw6H/ÊöòMýA›°à„þáZ–|ŽßVž¥æß¸êµò•_?„|ׂ†9•Q˜üTnv“Cʅ¬r9A©úàb ᕇŒÜ™öš>Š”Âòjóež«å:‹en[åVÇeH÷å”/eå/årÅe(5å)­åb=MeœÝå…iåN9em™äbQå½Ê׍Æï»Ç¿»Ä_ÁÊg?͇<ÐwýËѯÎo{ÏÏ~ÌÒâ͏ßÀgº&Ÿœ‰{îD~DqúĂÄñ«ˆûˆë=MïHÈõˆöÍ©HþÙÈÁˆ!äÔ(Õm.-m{íY]m|éí>¹mBYm[qí…AmÕô՚öy°±…4m•í5Ìm'íkžítÊ-_÷yaö‘ ùÅ¢÷•¡ö½(՝(ډçÔYj×qhÖÁÈÙáIÓ¡IØeÙŇÓʼnØe¸Õ%ºÖa8Õ7ÚQøÔ±w×9zÖéØÙÝWÓmWØ5ÕáÖ±ª/, ׋ÇÂßÁϽ×Ag<??‹¥Æõ†ŸíÆSq+Z|{Y€Kajc‹w+m;që|û€kjû Ã‹šw;šu+œq»Ÿl››p ›~Û·OÚ åš ë›ß/>¢¯9¦»¸ã³'V~YŽrY~_@~Y~YYqVò6Ò=}åÏ¢1Šk5{tÒ\†=Mꋯ1uü¿fÛ±‡΃ëúgôZ'¼j)­1ø¿â=Mۅw‡ ‹KswtWHFþjƒ11ÀvÝ šo8bªœ¢Ó22¯½Š˜É  9ZBŠ+Vk”L†íjIX14¾À°ZÝՒŸ|†Û!cÔ¡+F}¬ê/Ö1nŠÀÒÞíÿŸ_ˆû rôšDƇöj}º1M¿B‚ÞÉ"‘"ܽŸAF¾ æ¼„f¼öŽ”F½–»,…»V†¾Ô¶¾5¼äõ<ŠºéÃ&ôÑ.TÜ^´ÊZtÚBVC4N+Ôa[”G74J?ÔUEœA»ì»P¾޼’½½<=}½þ».þ^é'ñédQÁŽï +ß7´34Kô'ôWt?ÔEÔ-T]T9TQT"$ T‚n/¦Î0€/pŽ0¸>/|þ0tÞ/^0 ž/Uª0Ïê/ j0²Ê0 +/8Š0¤:/½ú0wÚ/Z/Æ0¤2àÕ…àØ‘ßډ_Ս_ØE"Óñ"•Ù0â/êb/¬"0ŸähFƒË4ÄOÛ¶W0Çå踖E.?Tg,ÛE”ßD‹AçŸðÉ)I·„u+Ԍç'/áV«4ð[¿…ÑtÅR[9·ÒÇÙ·¹”-,$ÈgvõEüTE_«üì×QÅù¶4- Xhy]Åç¦ôH4;€çß=MÉuY·þ.XhŽ™ÅðÊtW.;xðÿ‹Ä%š¹|žo,µÞ©+côKzÀÑm/·Pê.ÄhŸ”žç U«ï¸ÂI.ÃVçgÜÅqß4$ëXëGþÅÕϖ(ë^íGÿƵP¶Ôڛ]Ú,â>h~P…qJ{_þoŽ’âËíÀ¹L2-3jèƒ'{íï߯ý@¶œÑ+«:gnhøt]Ëö'[Ðñ´‘,­¤R–ó?Ç)á´(A,ÒrhM:E„´œP{šñW¢Ã"µªâ.yèG’v²ô :›—þïŸÎÕ¨Œr,C  ¿_â‚å— +K¡Ç­vB˜žö‹âš³¨k$¨ó×ñ}Y~Y~Y~Y~Y~ º’ÉŠ×îb=Mž!ªu©y=}©xj Ž(sfŒ >*ye +þ(vŒ +>éwn ét´ .é’9J9ÙX9 GAYþߐ Ðå^2¶|?ç6;÷JRpNR@6õn=}ýÎ;iá†W™¯JZ¾Û&Àk~c< !¹ðù@Ù"ÞÅJˆu¼‰T>ÛRáØBQØbéUCáS3_(CÍê–˜8z†±ÎD"ÐLzvˆÉÏFNÏN ÏzÐ~ ‘­Qúì¶üüªýèöûx”xßrþf˜ÜIYQX1 +ONÞOJw‘Ñ=MZU!šÆrt{Ÿ=}‹ü>Œ÷>^Ҟ=}¶Zw$YU>ür>^0ùvþG@9ôzþ_¦ÀpM6 j@àöº^*^=M^n9ÿоZ*ñ\JžH²wžáŠýÂÐ ֌ +Q ØFâZÀ ٝ‚ÿ&X<½ƒãæ€Öê) Ú*BÆ_Z4¡7iü¶Ÿ9Æ¿R°‘Óq /ÚÆ “®ïwGñalA-q4„¡v–ž4Øf:;Ùv¿WfT +>ÀV‰›˜ÿŠ<Šr¼Ö‚‚CZxµHpŊV­fhÜ:!;\o¯9ÇüSåî_õÆl_ø1<_iÿy]± zi˜aúӎ0QðYðQî>p‰HäAî9P®°YÜâfZØlNlPîáèð•ÖݐR‡´ç}Ž?ÇuÌRÇ != Ôܱ›!kóŽíaæ!Ç2!º|2 &ìNäeA2+j>‚ñ[±rKLáæö÷…Œ>j•.i\ÈúÌKæ—ýõ„_’0º×R_C/™à4ќÑ\å¹À—·áô„-³M;&gy舿lWt \åM¾Qh¸õ‹Gž1§ýŒ1¿Å5 ]ľ› 1Ù?¼ßÄ= !]ºê«JFQqA4ùò€(ŸDLÑoñ¶ñþ$,ÿÅUi]Õ6[ëPüé/ãòoBA‰ÐlWz˜Ç›cÒ¶=M®Ù—¤^·±º±ùô>né7i/9Z•çÉüô3à³ð_Ãóö˽÷PÖ]÷ÃÝ¡ ßýyÄ¿ÉiF”@ Âý0eèS¡¤7aâ¬= ›A$weZó‚šr4…ÚWT$-‚؅RnpÛvj¶ÚXŒÁ+= :«ß‰ù«›_¶qn¿#†:0jž*†· +ßA»ŒgR¯Ní= P{SÂò>9'¢ÛA$ +Š™ŠÛ"eÚ?ùèÈQ|éõÓmï- –ànüìŠj¤‡ñÒä?NåkZp@돣Õ]= Û¼ÔßAç5ݚú´†þksž¬7ç=MrqÎF5^&A6ژa4Zâ·}OV49IqsÆrÐqQ£]t͋\aÄWfƒñ×Ul£ÝwÛAMre 0é”ð Ê@T +¸À3= (QJH5‹ƒi¼wp!6©ø"ñâaŸí= ÑÎOKB4¨÷ÕVÀîàûœæ¹í£>œMath.atan(t),a:t=>Math.cos(t),d:t=>Math.exp(t),e:t=>Math.log(t),f:(t,s)=>Math.pow(t,s),c:t=>Math.sin(t),g:(t,s,i)=>r.copyWithin(t,s,s+i),h(t){r.length,(()=>{throw"OOM"})()}}};this.u=s=>{t.u(pe,s)},this.U=()=>t.U(pe),this.instantiate=()=>(this.U().then((t=>WebAssembly.instantiate(t,d))).then((t=>{const i=t.exports;var e;h=i.k,o=i.l,a=i.m,l=i.n,c=i.o,u=i.p,U=i.q,e=(n=i.i).buffer,r=new Uint8Array(e),(t=>{t.j()})(i),s()})),this.ready=new Promise((t=>{s=t})).then((()=>{this.I=n.buffer,this.V=o,this.free=U,this.Fs=h,this._s=a,this.Vs=l,this.ks=c,this.Ss=u})),this)}function Me(){return this.qs=()=>(new this.Hs).instantiate(this.$s,this.Cs).then((t=>{this.Is=t,this.js=this.Is._(this.Js,Uint8Array),this.xs=!0,this.Ps=this.Is._(1,Uint32Array),this.Ds=this.Is._(1,Uint32Array),this.Es=this.Is._(1,Uint32Array),this.Os=this.Is._(1,Uint32Array),this.Ns=this.Is._(1,Uint32Array),this.Zs=this.Is._(256,Uint32Array),this.zs=this.Is._(1,Int32Array),this.gs=0,this.Ks=0,this.Qs=0,this.Rs=this.Is.M.Fs(this.js.H,this.Ps.H,this.Ds.H,this.Es.H,this.Os.H,this.Ns.H,this.Zs.H,this.zs.H,256)})),Object.defineProperty(this,"ready",{enumerable:!0,get:()=>this.Gs}),this.reset=()=>(this.free(),this.qs()),this.free=()=>{this.Is.M.Ss(this.Rs),this.Is.free()},this.sendSetupHeader=t=>{this.js.C.set(t),this.Ps.C[0]=t.length,this.Is.M._s(this.Rs,this.xs),this.xs=!1},this.initDsp=()=>{this.Is.M.Vs(this.Rs)},this.decodePackets=t=>{let s=[],i=0,e=[];for(let r=0;r{if("vorbis"!==t)throw Error("@wasm-audio-decoders/ogg-vorbis does not support this codec "+t)},new r,this.qs(),this[ye](Me)}qs(){this.Ls=!0,this.Ws=0,this.ut=new oe("audio/ogg",{os:this.zt,Us:!1})}[ye](t){if(this.Rs){const t=this.Rs;t.ready.then((()=>t.free()))}this.Rs=new t,this.Gs=this.Rs.ready}get ready(){return this.Gs}async reset(){return this.qs(),this.Rs.reset()}free(){this.Rs.free()}async ti(t){const s=[];for(let r=0;rt[le])))}const i=await this.Rs.decodePackets(s);this.Ws+=i.samplesDecoded;const e=t[t.length-1];if(e&&e[ue]){const t=this.Ws-e[fe];if(t>0){for(let s=0;s setTimeout(res, time)); + } + class AudioEffect { + constructor(ac) { } + /** + * 连接至其他效果器 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + connect(target, output, input) { + this.output.connect(target.input, output, input); + } + + /** + * 与其他效果器取消连接 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + disconnect(target, output, input) { + if (!target) { + if (!isNil(output)) { + this.output.disconnect(output); + } else { + this.output.disconnect(); + } + } else { + if (!isNil(output)) { + if (!isNil(input)) { + this.output.disconnect(target.input, output, input); + } else { + this.output.disconnect(target.input, output); + } + } else { + this.output.disconnect(target.input); + } + } + } + } + + class StereoEffect extends AudioEffect { + constructor(ac) { + super(ac); + const panner = ac.createPanner(); + this.input = panner; + this.output = panner; + } + + /** + * 设置音频朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 Number + * @param y 朝向y坐标 Number + * @param z 朝向z坐标 Number + */ + setOrientation(x, y, z) { + this.output.orientationX.value = x; + this.output.orientationY.value = y; + this.output.orientationZ.value = z; + } + /** + * 设置音频位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 Number + * @param y 位置y坐标 Number + * @param z 位置z坐标 Number + */ + setPosition(x, y, z) { + this.output.positionX.value = x; + this.output.positionY.value = y; + this.output.positionZ.value = z; + } + end() { } + + start() { } + } + class VolumeEffect extends AudioEffect { + constructor(ac) { + super(ac); + const gain = ac.createGain(); + this.input = gain; + this.output = gain; + } + + /** + * 设置音量大小 + * @param volume 音量大小 Number + */ + setVolume(volume) { + this.output.gain.value = volume; + } + + /** + * 获取音量大小 Number + */ + getVolume() { + return this.output.gain.value; + } + + end() { } + + start() { } + } + class ChannelVolumeEffect extends AudioEffect { + /** 所有的音量控制节点 */ + + constructor(ac) { + super(ac); + /** 所有的音量控制节点 */ + this.gain = []; + const splitter = ac.createChannelSplitter(); + const merger = ac.createChannelMerger(); + this.output = merger; + this.input = splitter; + for (let i = 0; i < 6; i++) { + const gain = ac.createGain(); + splitter.connect(gain, i); + gain.connect(merger, 0, i); + this.gain.push(gain); + } + } + + /** + * 设置某个声道的音量大小 + * @param channel 要设置的声道,可填0-5 Number + * @param volume 这个声道的音量大小 Number + */ + setVolume(channel, volume) { + if (!this.gain[channel]) return; + this.gain[channel].gain.value = volume; + } + + /** + * 获取某个声道的音量大小,可填0-5 + * @param channel 要获取的声道 Number + */ + getVolume(channel) { + if (!this.gain[channel]) return 0; + return this.gain[channel].gain.value; + } + + end() { } + + start() { } + } + class DelayEffect extends AudioEffect { + constructor(ac) { + super(ac); + + const delay = ac.createDelay(); + this.input = delay; + this.output = delay; + } + + /** + * 设置延迟时长 + * @param delay 延迟时长,单位秒 Number + */ + setDelay(delay) { + this.output.delayTime.value = delay; + } + + /** + * 获取延迟时长 + */ + getDelay() { + return this.output.delayTime.value; + } + + end() { } + + start() { } + } + class EchoEffect extends AudioEffect { + constructor(ac) { + super(ac); + /** 当前增益 */ + this.gain = 0.5; + /** 是否正在播放 */ + this.playing = false; + const delay = ac.createDelay(); + const gain = ac.createGain(); + gain.gain.value = 0.5; + delay.delayTime.value = 0.05; + delay.connect(gain); + gain.connect(delay); + /** 延迟节点 */ + this.delay = delay; + /** 反馈增益节点 */ + this.gainNode = gain; + + this.input = gain; + this.output = gain; + } + + /** + * 设置回声反馈增益大小 + * @param gain 增益大小,范围 0-1,大于等于1的视为0.5,小于0的视为0 Number + */ + setFeedbackGain(gain) { + const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain; + this.gain = resolved; + if (this.playing) this.gainNode.gain.value = resolved; + } + + /** + * 设置回声间隔时长 + * @param delay 回声时长,范围 0.01-Infinity,小于0.01的视为0.01 Number + */ + setEchoDelay(delay) { + const resolved = delay < 0.01 ? 0.01 : delay; + this.delay.delayTime.value = resolved; + } + + /** + * 获取反馈节点增益 + */ + getFeedbackGain() { + return this.gain; + } + + /** + * 获取回声间隔时长 + */ + getEchoDelay() { + return this.delay.delayTime.value; + } + + end() { + this.playing = false; + const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10; + sleep(this.delay.delayTime.value * echoTime).then(() => { + if (!this.playing) this.gainNode.gain.value = 0; + }); + } + + start() { + this.playing = true; + this.gainNode.gain.value = this.gain; + } + } + + class StreamLoader { + constructor(url) { + /** 传输目标 Set*/ + this.target = new Set(); + this.loading = false; + } + + /** + * 将加载流传递给字节流读取对象 + * @param reader 字节流读取对象 IStreamReader + */ + pipe(reader) { + if (this.loading) { + console.warn( + "Cannot pipe new StreamReader object when stream is loading." + ); + return; + } + this.target.add(reader); + reader.piped(this); + return this; + } + + async start() { + if (this.loading) return; + this.loading = true; + const response = await window.fetch(this.url); + const stream = response.body; + if (!stream) { + console.error("Cannot get reader when fetching '" + this.url + "'."); + return; + } + // 获取读取器 + this.stream = stream; + const reader = response.body?.getReader(); + const targets = [...this.target]; + + await Promise.all(targets.map((v) => v.start(stream, this, response))); + if (reader && reader.read) { + // 开始流传输 + while (true) { + const { value, done } = await reader.read(); + await Promise.all( + targets.map((v) => v.pump(value, done, response)) + ); + if (done) break; + } + } else { + // 如果不支持流传输 + const buffer = await response.arrayBuffer(); + const data = new Uint8Array(buffer); + await Promise.all(targets.map((v) => v.pump(data, true, response))); + } + + this.loading = false; + targets.forEach((v) => v.end(true)); + + // + } + + cancel(reason) { + if (!this.stream) return; + this.stream.cancel(reason); + this.loading = false; + this.target.forEach((v) => v.end(false, reason)); + } + } + const fileSignatures = [ + [AudioType.Mp3, [0x49, 0x44, 0x33]], + [AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]], + [AudioType.Wav, [0x52, 0x49, 0x46, 0x46]], + [AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]], + [AudioType.Aac, [0xff, 0xf1]], + [AudioType.Aac, [0xff, 0xf9]], + ]; + const oggHeaders = [ + [AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]], + ]; + + function checkAudioType(data) { + let audioType = ""; + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + for (const [type, value] of fileSignatures) { + if (value.every((v, i) => toCheck[i] === v)) { + audioType = type; + break; + } + } + if (audioType === AudioType.Ogg) { + // 如果是ogg的话,进一步判断是不是opus + for (const [key, value] of oggHeaders) { + const has = toCheck.some((_, i) => { + return value.every((v, ii) => toCheck[i + ii] === v); + }); + if (has) { + audioType = key; + break; + } + } + } + + return audioType; + } + class AudioDecoder { + /** + * 注册一个解码器 + * @param type 要注册的解码器允许解码的类型 + * @param decoder 解码器对象 + */ + static registerDecoder(type, decoder) { + if (!this.decoderMap) this.decoderMap = new Map(); + if (this.decoderMap.has(type)) { + console.warn( + "Audio stream decoder for audio type '" + + type + + "' has already existed." + ); + return; + } + + this.decoderMap.set(type, decoder); + } + + /** + * 解码音频数据 + * @param data 音频文件数据 + * @param player AudioPlayer实例 + */ + static async decodeAudioData(data, player) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + const type = checkAudioType(data); + if (type === "") { + console.error( + "Unknown audio type. Header: '" + + [...toCheck] + .map((v) => v.toString().padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return null; + } + if (isAudioSupport(type)) { + if (data.buffer instanceof ArrayBuffer) { + return player.ac.decodeAudioData(data.buffer); + } else { + return null; + } + } else { + const Decoder = this.decoderMap.get(type); + if (!Decoder) { + return null; + } else { + const decoder = new Decoder(); + await decoder.create(); + const decodedData = await decoder.decode(data); + if (!decodedData) return null; + const buffer = player.ac.createBuffer( + decodedData.channelData.length, + decodedData.channelData[0].length, + decodedData.sampleRate + ); + decodedData.channelData.forEach((v, i) => { + buffer.copyToChannel(v, i); + }); + decoder.destroy(); + return buffer; + } + } + } + } + + class VorbisDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggVorbisDecoderWebWorker(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 解码整个文件 + * @param data 文件数据 + */ + async decodeAll(data) { + return this.decoder?.decodeFile(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return this.decoder?.flush(); + } + } + + class OpusDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggOpusDecoderWebWorker(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 解码整个文件 + * @param data 文件数据 + */ + async decodeAll(data) { + return this.decoder?.decodeFile(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return await this.decoder?.flush(); + } + } + const mimeTypeMap = { + [AudioType.Aac]: "audio/aac", + [AudioType.Flac]: "audio/flac", + [AudioType.Mp3]: "audio/mpeg", + [AudioType.Ogg]: "application/ogg", + [AudioType.Opus]: "application/ogg", + [AudioType.Wav]: "application/ogg", + }; + + function isOggPage(data) { + return !isNil(data.isFirstPage); + } + class AudioStreamSource { + constructor(context) { + this.output = context.createBufferSource(); + /** 是否已经完全加载完毕 */ + this.loaded = false; + /** 是否正在播放 */ + this.playing = false; + /** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */ + this.buffered = 0; + /** 已经缓冲的采样点数量 */ + this.bufferedSamples = 0; + /** 歌曲时长,加载完毕之前保持为 0 */ + this.duration = 0; + /** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */ + this.bufferPlayDuration = 1; + /** 音频的采样率,未成功解析出之前保持为 0 */ + this.sampleRate = 0; + //是否循环播放 + this.loop = false; + /** 上一次播放是从何时开始的 */ + this.lastStartWhen = 0; + /** 开始播放时刻 */ + this.lastStartTime = 0; + /** 上一次播放的缓存长度 */ + this.lastBufferSamples = 0; + + /** 是否已经获取到头文件 */ + this.headerRecieved = false; + /** 音频类型 */ + this.audioType = ""; + /** 每多长时间组成一个缓存 Float32Array */ + this.bufferChunkSize = 10; + /** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */ + this.audioData = []; + + this.errored = false; + this.ac = context; + } + /** 当前已经播放了多长时间 */ + get currentTime() { + return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; + } + /** + * 设置每个缓存数据的大小,默认为10秒钟一个缓存数据 + * @param size 每个缓存数据的时长,单位秒 + */ + setChunkSize(size) { + if (this.controller?.loading || this.loaded) return; + this.bufferChunkSize = size; + } + + piped(controller) { + this.controller = controller; + } + + async pump(data, done) { + if (!data || this.errored) return; + if (!this.headerRecieved) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + this.audioType = checkAudioType(data); + if (!this.audioType) { + console.error( + "Unknown audio type. Header: '" + + [...toCheck] + .map((v) => v.toString(16).padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return; + } + // 创建解码器 + const Decoder = AudioDecoder.decoderMap.get(this.audioType); + if (!Decoder) { + this.errored = true; + console.error( + "Cannot decode stream source type of '" + + this.audioType + + "', since there is no registered decoder for that type." + ); + return Promise.reject( + `Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.` + ); + } + this.decoder = new Decoder(); + // 创建数据解析器 + const mime = mimeTypeMap[this.audioType]; + const parser = new CodecParser(mime); + this.parser = parser; + await this.decoder.create(); + this.headerRecieved = true; + } + + const decoder = this.decoder; + const parser = this.parser; + if (!decoder || !parser) { + this.errored = true; + return Promise.reject( + "No parser or decoder attached in this AudioStreamSource" + ); + } + + await this.decodeData(data, decoder, parser); + if (done) await this.decodeFlushData(decoder, parser); + this.checkBufferedPlay(); + } + + /** + * 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告 + */ + checkSampleRate(info) { + for (const one of info) { + const frame = isOggPage(one) ? one.codecFrames[0] : one; + if (frame) { + const rate = frame.header.sampleRate; + if (this.sampleRate === 0) { + this.sampleRate = rate; + break; + } else { + if (rate !== this.sampleRate) { + console.warn("Sample rate in stream audio must be constant."); + } + } + } + } + } + + /** + * 解析音频数据 + */ + async decodeData(data, decoder, parser) { + // 解析音频数据 + const audioData = await decoder.decode(data); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.parseChunk(data)]; + + // 检查采样率 + this.checkSampleRate(audioInfo); + // 追加音频数据 + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 解码剩余数据 + */ + async decodeFlushData(decoder, parser) { + const audioData = await decoder.flush(); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.flush()]; + + this.checkSampleRate(audioInfo); + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 追加音频数据 + */ + appendDecodedData(data, info) { + const channels = data.channelData.length; + if (channels === 0) return; + if (this.audioData.length !== channels) { + this.audioData = []; + for (let i = 0; i < channels; i++) { + this.audioData.push([]); + } + } + // 计算出应该放在哪 + const chunk = this.sampleRate * this.bufferChunkSize; + const sampled = this.bufferedSamples; + const pushIndex = Math.floor(sampled / chunk); + const bufferIndex = sampled % chunk; + const dataLength = data.channelData[0].length; + let buffered = 0; + let nowIndex = pushIndex; + let toBuffer = bufferIndex; + while (buffered < dataLength) { + const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk; + + for (let i = 0; i < channels; i++) { + const audioData = this.audioData[i]; + if (!audioData[nowIndex]) { + audioData.push(new Float32Array(chunk)); + } + const toPush = data.channelData[i].slice(buffered, buffered + rest); + + audioData[nowIndex].set(toPush, toBuffer); + } + buffered += rest; + nowIndex++; + toBuffer = 0; + } + + this.buffered += + info.reduce((prev, curr) => prev + curr.duration, 0) / 1000; + this.bufferedSamples += info.reduce( + (prev, curr) => prev + curr.samples, + 0 + ); + } + + /** + * 检查已缓冲内容,并在未开始播放时播放 + */ + checkBufferedPlay() { + if (this.playing || this.sampleRate === 0) return; + const played = this.lastBufferSamples / this.sampleRate; + const dt = this.buffered - played; + if (this.loaded) { + this.playAudio(played); + return; + } + if (dt < this.bufferPlayDuration) return; + + this.lastBufferSamples = this.bufferedSamples; + // 需要播放 + this.mergeBuffers(); + if (!this.buffer) return; + if (this.playing) this.output.stop(); + this.createSourceNode(this.buffer); + this.output.loop = false; + this.output.start(0, played); + this.lastStartTime = this.ac.currentTime; + this.playing = true; + this.output.addEventListener("ended", () => { + this.playing = false; + this.checkBufferedPlay(); + }); + } + + mergeBuffers() { + const buffer = this.ac.createBuffer( + this.audioData.length, + this.bufferedSamples, + this.sampleRate + ); + const chunk = this.sampleRate * this.bufferChunkSize; + const bufferedChunks = Math.floor(this.bufferedSamples / chunk); + const restLength = this.bufferedSamples % chunk; + for (let i = 0; i < this.audioData.length; i++) { + const audio = this.audioData[i]; + const data = new Float32Array(this.bufferedSamples); + for (let j = 0; j < bufferedChunks; j++) { + data.set(audio[j], chunk * j); + } + if (restLength !== 0) { + data.set( + audio[bufferedChunks].slice(0, restLength), + chunk * bufferedChunks + ); + } + + buffer.copyToChannel(data, i, 0); + } + this.buffer = buffer; + } + + async start() { + delete this.buffer; + this.headerRecieved = false; + this.audioType = ""; + this.errored = false; + this.buffered = 0; + this.sampleRate = 0; + this.bufferedSamples = 0; + this.duration = 0; + this.loaded = false; + if (this.playing) this.output.stop(); + this.playing = false; + this.lastStartTime = this.ac.currentTime; + } + + end(done, reason) { + if (done && this.buffer) { + this.loaded = true; + delete this.controller; + this.mergeBuffers(); + + this.duration = this.buffered; + this.audioData = []; + this.decoder?.destroy(); + delete this.decoder; + delete this.parser; + } else { + console.warn( + "Unexpected end when loading stream audio, reason: '" + + (reason ?? "") + + "'" + ); + } + } + + playAudio(when) { + if (!this.buffer) return; + this.lastStartTime = this.ac.currentTime; + if (this.playing) this.output.stop(); + if (this.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.playing = true; + + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + if (this.loop && !this.output.loop) this.play(0); + }); + } + /** + * 开始播放这个音频源 + */ + play(when) { + if (this.playing || this.errored) return; + if (this.loaded && this.buffer) { + this.playing = true; + this.playAudio(when); + } else { + this.controller?.start(); + } + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + if (this.playing) this.output.stop(); + this.playing = false; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + /** + * 停止播放这个音频源 + * @returns 音频暂停的时刻 number + */ + stop() { + if (this.playing) this.output.stop(); + this.playing = false; + return this.ac.currentTime - this.lastStartTime; + } + /** + * 连接到音频路由图上,每次调用播放的时候都会执行一次 + * @param target 连接至的目标 IAudioInput + */ + connect(target) { + this.target = target; + } + /** + * 设置是否循环播放 + * @param loop 是否循环 boolean) + */ + setLoop(loop) { + this.loop = loop; + } + } + class AudioElementSource { + constructor(context) { + const audio = new Audio(); + audio.preload = "none"; + this.output = context.createMediaElementSource(audio); + this.audio = audio; + this.ac = context; + audio.addEventListener("play", () => { + this.playing = true; + if (this.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + }); + audio.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + }); + } + get duration() { + return this.audio.duration; + } + get currentTime() { + return this.audio.currentTime; + } + /** + * 设置音频源的路径 + * @param url 音频路径 + */ + setSource(url) { + this.audio.src = url; + } + + play(when = 0) { + if (this.playing) return; + this.audio.currentTime = when; + this.audio.play(); + } + + stop() { + this.audio.pause(); + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + return this.audio.currentTime; + } + + connect(target) { + this.output.connect(target.input); + } + + setLoop(loop) { + this.audio.loop = loop; + } + } + class AudioBufferSource { + constructor(context) { + this.output = context.createBufferSource(); + /** 是否循环 */ + this.loop = false; + /** 上一次播放是从何时开始的 */ + this.lastStartWhen = 0; + /** 播放开始时刻 */ + this.lastStartTime = 0; + this.duration = 0; + this.ac = context; + } + get currentTime() { + return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; + } + + /** + * 设置音频源数据 + * @param buffer 音频源,可以是未解析的 ArrayBuffer,也可以是已解析的 AudioBuffer + */ + async setBuffer(buffer) { + if (buffer instanceof ArrayBuffer) { + this.buffer = await this.ac.decodeAudioData(buffer); + } else { + this.buffer = buffer; + } + this.duration = this.buffer.duration; + } + + play(when) { + if (this.playing || !this.buffer) return; + this.playing = true; + this.lastStartTime = this.ac.currentTime; + if (this.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + if (this.loop && !this.output.loop) this.play(0); + }); + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + + stop() { + this.output.stop(); + return this.ac.currentTime - this.lastStartTime; + } + + connect(target) { + this.target = target; + } + + setLoop(loop) { + this.loop = loop; + } + } + class AudioPlayer { + constructor() { + /** 音频播放上下文 */ + this.ac = new AudioContext(); + /** 音量节点 */ + this.gain = this.ac.createGain(); + this.gain.connect(this.ac.destination); + this.audioRoutes = new Map(); + } + /** + * 解码音频数据 + * @param data 音频数据 + */ + decodeAudioData(data) { + return AudioDecoder.decodeAudioData(data, this); + } + /** + * 设置音量 + * @param volume 音量 + */ + setVolume(volume) { + this.gain.gain.value = volume; + } + + /** + * 获取音量 + */ + getVolume() { + return this.gain.gain.value; + } + + /** + * 创建一个音频源 + * @param Source 音频源类 + */ + createSource(Source) { + return new Source(this.ac); + } + + /** + * 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况 + */ + createStreamSource() { + return new AudioStreamSource(this.ac); + } + + /** + * 创建一个通过 audio 元素播放的音频源 + */ + createElementSource() { + return new AudioElementSource(this.ac); + } + + /** + * 创建一个通过 AudioBuffer 播放的音频源 + */ + createBufferSource() { + return new AudioBufferSource(this.ac); + } + + /** + * 获取音频目的地 + */ + getDestination() { + return this.gain; + } + + /** + * 创建一个音频效果器 + * @param Effect 效果器类 + */ + createEffect(Effect) { + return new Effect(this.ac); + } + + /** + * 创建一个修改音量的效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * |----------| + * ``` + */ + createVolumeEffect() { + return new VolumeEffect(this.ac); + } + + /** + * 创建一个立体声效果器 + * ```txt + * |------------| + * Input ----> | PannerNode | ----> Output + * |------------| + * ``` + */ + createStereoEffect() { + return new StereoEffect(this.ac); + } + + /** + * 创建一个修改单个声道音量的效果器 + * ```txt + * |----------| + * -> | GainNode | \ + * |--------------| / |----------| -> |------------| + * Input ----> | SplitterNode | ...... | MergerNode | ----> Output + * |--------------| \ |----------| -> |------------| + * -> | GainNode | / + * |----------| + * ``` + */ + createChannelVolumeEffect() { + return new ChannelVolumeEffect(this.ac); + } + + /** + * 创建一个延迟效果器 + * |-----------| + * Input ----> | DelayNode | ----> Output + * |-----------| + */ + createDelay() { + return new DelayEffect(this.ac); + } + + /** + * 创建一个回声效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * ^ |----------| | + * | | + * | |------------| ↓ + * |-- | Delay Node | <-- + * |------------| + * ``` + */ + createEchoEffect() { + return new EchoEffect(this.ac); + } + + /** + * 创建一个音频播放路由 + * @param source 音频源 + */ + createRoute(source) { + return new AudioRoute(source, this); + } + + /** + * 添加一个音频播放路由,可以直接被播放 + * @param id 这个音频播放路由的名称 + * @param route 音频播放路由对象 + */ + addRoute(id, route) { + if (!this.audioRoutes) this.audioRoutes = new Map(); + if (this.audioRoutes.has(id)) { + console.warn( + "Audio route with id of '" + + id + + "' has already existed. New route will override old route." + ); + } + this.audioRoutes.set(id, route); + } + + /** + * 根据名称获取音频播放路由对象 + * @param id 音频播放路由的名称 + */ + getRoute(id) { + return this.audioRoutes.get(id); + } + /** + * 移除一个音频播放路由 + * @param id 要移除的播放路由的名称 + */ + removeRoute(id) { + this.audioRoutes.delete(id); + } + /** + * 播放音频 + * @param id 音频名称 + * @param when 从音频的哪个位置开始播放,单位秒 + */ + play(id, when) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot play audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + + route.play(when); + } + + /** + * 暂停音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + pause(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot pause audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + return route.pause(); + } + + /** + * 停止音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + stop(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot stop audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + return route.stop(); + } + + /** + * 继续音频播放 + * @param id 音频名称 + */ + resume(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot pause audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + route.resume(); + } + + /** + * 设置听者位置,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 + * @param y 位置y坐标 + * @param z 位置z坐标 + */ + setListenerPosition(x, y, z) { + const listener = this.ac.listener; + listener.positionX.value = x; + listener.positionY.value = y; + listener.positionZ.value = z; + } + + /** + * 设置听者朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 + * @param y 朝向y坐标 + * @param z 朝向z坐标 + */ + setListenerOrientation(x, y, z) { + const listener = this.ac.listener; + listener.forwardX.value = x; + listener.forwardY.value = y; + listener.forwardZ.value = z; + } + + /** + * 设置听者头顶朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 头顶朝向x坐标 + * @param y 头顶朝向y坐标 + * @param z 头顶朝向z坐标 + */ + setListenerUp(x, y, z) { + const listener = this.ac.listener; + listener.upX.value = x; + listener.upY.value = y; + listener.upZ.value = z; + } + } + class AudioRoute { + constructor(source, player) { + source.route = this; + this.output = source.output; + + /** 效果器路由图 */ + this.effectRoute = []; + + /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ + this.endTime = 0; + /** 暂停时播放了多长时间 */ + this.pauseCurrentTime = 0; + /** 当前播放状态 */ + this.player = player; + this.status = AudioStatus.Stoped; + + this.shouldStop = false; + /** + * 每次暂停或停止时自增,用于判断当前正在处理的情况。 + * 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作 + */ + this.stopIdentifier = 0; + /** 暂停时刻 */ + this.pauseTime = 0; + this.source = source; + this.source.player = player; + } + /** 音频时长,单位秒 */ + get duration() { + return this.source.duration; + } + /** 当前播放了多长时间,单位秒 */ + get currentTime() { + if (this.status === AudioStatus.Paused) { + return this.pauseCurrentTime; + } else { + return this.source.currentTime; + } + } + set currentTime(time) { + this.source.stop(); + this.source.play(time); + } + /** + * 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。 + * @param time 暂停或停止时,经过多长时间之后才会结束音频的播放 + */ + setEndTime(time) { + this.endTime = time; + } + + /** + * 当音频播放时执行的函数,可以用于音频淡入效果 + * @param fn 音频开始播放时执行的函数 + */ + onStart(fn) { + this.audioStartHook = fn; + } + + /** + * 当音频暂停或停止时执行的函数,可以用于音频淡出效果 + * @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。 + * 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象 + */ + onEnd(fn) { + this.audioEndHook = fn; + } + + /** + * 开始播放这个音频 + * @param when 从音频的什么时候开始播放,单位秒 + */ + async play(when = 0) { + if (this.status === AudioStatus.Playing) return; + this.link(); + await this.player.ac.resume(); + if (this.effectRoute.length > 0) { + const first = this.effectRoute[0]; + this.source.connect(first); + const last = this.effectRoute.at(-1); + last.connect({ input: this.player.getDestination() }); + } else { + this.source.connect({ input: this.player.getDestination() }); + } + this.source.play(when); + this.status = AudioStatus.Playing; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + if (this.status !== AudioStatus.Playing) { + this.status = AudioStatus.Playing; + } + } + + /** + * 暂停音频播放 + */ + async pause() { + if (this.status !== AudioStatus.Playing) return; + this.status = AudioStatus.Pausing; + this.stopIdentifier++; + const identifier = this.stopIdentifier; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + if ( + this.status !== AudioStatus.Pausing || + this.stopIdentifier !== identifier + ) { + return; + } + this.pauseCurrentTime = this.source.currentTime; + const time = this.source.stop(); + this.pauseTime = time; + if (this.shouldStop) { + this.status = AudioStatus.Stoped; + this.endAllEffect(); + + this.shouldStop = false; + } else { + this.status = AudioStatus.Paused; + this.endAllEffect(); + } + this.endAllEffect(); + } + + /** + * 继续音频播放 + */ + resume() { + if (this.status === AudioStatus.Playing) return; + if ( + this.status === AudioStatus.Pausing || + this.status === AudioStatus.Stoping + ) { + this.audioStartHook?.(this); + + return; + } + if (this.status === AudioStatus.Paused) { + this.play(this.pauseTime); + } else { + this.play(0); + } + this.status = AudioStatus.Playing; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + } + + /** + * 停止音频播放 + */ + async stop() { + if (this.status !== AudioStatus.Playing) { + if (this.status === AudioStatus.Pausing) { + this.shouldStop = true; + } + return; + } + this.status = AudioStatus.Stoping; + this.stopIdentifier++; + const identifier = this.stopIdentifier; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + if ( + this.status !== AudioStatus.Stoping || + this.stopIdentifier !== identifier + ) { + return; + } + this.source.stop(); + this.status = AudioStatus.Stoped; + this.pauseTime = 0; + this.endAllEffect(); + } + + /** + * 添加效果器 + * @param effect 要添加的效果,可以是数组,表示一次添加多个 + * @param index 从哪个位置开始添加,如果大于数组长度,那么加到末尾,如果小于0,那么将会从后面往前数。默认添加到末尾 + */ + addEffect(effect, index) { + if (isNil(index)) { + if (effect instanceof Array) { + this.effectRoute.push(...effect); + } else { + this.effectRoute.push(effect); + } + } else { + if (effect instanceof Array) { + this.effectRoute.splice(index, 0, ...effect); + } else { + this.effectRoute.splice(index, 0, effect); + } + } + this.setOutput(); + if (this.source.playing) this.link(); + } + + /** + * 移除一个效果器 + * @param effect 要移除的效果 + */ + removeEffect(effect) { + const index = this.effectRoute.indexOf(effect); + if (index === -1) return; + this.effectRoute.splice(index, 1); + effect.disconnect(); + this.setOutput(); + if (this.source.playing) this.link(); + } + + setOutput() { + const effect = this.effectRoute.at(-1); + if (!effect) this.output = this.source.output; + else this.output = effect.output; + } + + /** + * 连接音频路由图 + */ + link() { + this.effectRoute.forEach((v) => v.disconnect()); + this.effectRoute.forEach((v, i) => { + const next = this.effectRoute[i + 1]; + if (next) { + v.connect(next); + } + }); + } + + startAllEffect() { + this.effectRoute.forEach((v) => v.start()); + } + + endAllEffect() { + this.effectRoute.forEach((v) => v.end()); + } + } + + const audioPlayer = new AudioPlayer(); + + class BgmController { + constructor(player) { + this.mainGain = player.createVolumeEffect(); + this.player = player; + /** bgm音频名称的前缀 */ + this.prefix = "bgms."; + /** 每个 bgm 的音量控制器 */ + this.gain = new Map(); + + /** 正在播放的 bgm */ + this.playingBgm = ""; + /** 是否正在播放 */ + this.playing = false; + + /** 是否已经启用 */ + this.enabled = true; + /** 是否屏蔽所有的音乐切换 */ + this.blocking = false; + /** 渐变时长 */ + this.transitionTime = 2000; + } + + /** + * 设置音频渐变时长 + * @param time 渐变时长 + */ + setTransitionTime(time) { + this.transitionTime = time; + for (const [, value] of this.gain) { + value.transition.time(time); + } + } + + /** + * 屏蔽音乐切换 + */ + blockChange() { + this.blocking = true; + } + + /** + * 取消屏蔽音乐切换 + */ + unblockChange() { + this.blocking = false; + } + + /** + * 设置总音量大小 + * @param volume 音量大小 + */ + setVolume(volume) { + this.mainGain.setVolume(volume); + this._volume = volume; + } + /** + * 获取总音量大小 + */ + getVolume() { + return this.mainGain.getVolume(); + } + /** + * 设置是否启用 + * @param enabled 是否启用 + */ + setEnabled(enabled) { + if (enabled) this.resume(); + else this.stop(); + this.enabled = enabled; + } + + /** + * 设置 bgm 音频名称的前缀 + */ + setPrefix(prefix) { + this.prefix = prefix; + } + + getId(name) { + return `${this.prefix}${name}`; + } + + /** + * 根据 bgm 名称获取其 AudioRoute 实例 + * @param id 音频名称 + */ + get(id) { + return this.player.getRoute(this.getId(id)); + } + + /** + * 添加一个 bgm + * @param id 要添加的 bgm 的名称 + * @param url 指定 bgm 的加载地址 + */ + addBgm(id, url = `project/bgms/${id}`) { + const type = guessTypeByExt(id); + if (!type) { + console.warn( + "Unknown audio extension name: '" + + id.split(".").slice(0, -1).join(".") + + "'" + ); + return; + } + const gain = this.player.createVolumeEffect(); + if (isAudioSupport(type)) { + const source = audioPlayer.createElementSource(); + source.setSource(url); + source.setLoop(true); + const route = new AudioRoute(source, audioPlayer); + route.addEffect([gain, this.mainGain]); + audioPlayer.addRoute(this.getId(id), route); + this.setTransition(id, route, gain); + } else { + const source = audioPlayer.createStreamSource(); + const stream = new StreamLoader(url); + stream.pipe(source); + source.setLoop(true); + const route = new AudioRoute(source, audioPlayer); + route.addEffect([gain, this.mainGain]); + audioPlayer.addRoute(this.getId(id), route); + this.setTransition(id, route, gain); + } + } + + /** + * 移除一个 bgm + * @param id 要移除的 bgm 的名称 + */ + removeBgm(id) { + this.player.removeRoute(this.getId(id)); + const gain = this.gain.get(id); + gain?.transition.ticker.destroy(); + this.gain.delete(id); + } + + setTransition(id, route, gain) { + const transition = new Transition(); + transition + .time(this.transitionTime) + .mode(linear()) + .transition("volume", 0); + + const tick = () => { + gain.setVolume(transition.value.volume); + }; + + /** + * @param expect 在结束时应该是正在播放还是停止 + */ + const setTick = async (expect) => { + transition.ticker.remove(tick); + transition.ticker.add(tick); + const identifier = route.stopIdentifier; + await sleep(this.transitionTime + 500); + if (route.status === expect && identifier === route.stopIdentifier) { + transition.ticker.remove(tick); + if (route.status === AudioStatus.Playing) { + gain.setVolume(1); + } else { + gain.setVolume(0); + } + } + }; + + route.onStart(async () => { + transition.transition("volume", 1); + setTick(AudioStatus.Playing); + }); + route.onEnd(() => { + transition.transition("volume", 0); + setTick(AudioStatus.Paused); + }); + route.setEndTime(this.transitionTime); + + this.gain.set(id, { effect: gain, transition }); + } + + /** + * 播放一个 bgm + * @param id 要播放的 bgm 名称 + */ + play(id, when) { + if (this.blocking) return; + if (id !== this.playingBgm && this.playingBgm) { + this.player.pause(this.getId(this.playingBgm)); + } + this.playingBgm = id; + if (!this.enabled) return; + this.player.play(this.getId(id), when); + this.playing = true; + } + + /** + * 继续当前的 bgm + */ + resume() { + if (this.blocking || !this.enabled || this.playing) return; + if (this.playingBgm) { + this.player.resume(this.getId(this.playingBgm)); + } + this.playing = true; + } + + /** + * 暂停当前的 bgm + */ + pause() { + if (this.blocking || !this.enabled) return; + if (this.playingBgm) { + this.player.pause(this.getId(this.playingBgm)); + } + this.playing = false; + } + + /** + * 停止当前的 bgm + */ + stop() { + if (this.blocking || !this.enabled) return; + if (this.playingBgm) { + this.player.stop(this.getId(this.playingBgm)); + } + this.playing = false; + } + } + const bgmController = new BgmController(audioPlayer); + + class SoundPlayer { + constructor(player) { + /** 每个音效的唯一标识符 */ + this.num = 0; + this.enabled = true; + this.gain = player.createVolumeEffect(); + /** 每个音效的数据 */ + this.buffer = new Map(); + /** 所有正在播放的音乐 */ + this.playing = new Set(); + this.player = player; + } + /** + * 设置是否启用音效 + * @param enabled 是否启用音效 + */ + setEnabled(enabled) { + if (!enabled) this.stopAllSounds(); + this.enabled = enabled; + } + + /** + * 设置音量大小 + * @param volume 音量大小 + */ + setVolume(volume) { + this.gain.setVolume(volume); + } + /** + * 获取音量大小 + */ + getVolume() { + return this.gain.getVolume(); + } + /** + * 添加一个音效 + * @param id 音效名称 + * @param data 音效的Uint8Array数据 + */ + async add(id, data) { + const buffer = await this.player.decodeAudioData(data); + if (!buffer) { + console.warn( + "Cannot decode sound '" + + id + + "', since audio file may not supported by 2.b." + ); + return; + } + this.buffer.set(id, buffer); + } + + /** + * 播放一个音效 + * @param id 音效名称 + * @param position 音频位置,[0, 0, 0]表示正中心,x轴指向水平向右,y轴指向水平向上,z轴指向竖直向上 + * @param orientation 音频朝向,[0, 1, 0]表示朝向前方 + */ + play(id, position = [0, 0, 0], orientation = [1, 0, 0]) { + if (!this.enabled || !id) return -1; + const buffer = this.buffer.get(id); + if (!buffer) { + console.warn( + "Cannot play sound '" + + id + + "', since there is no added data named it." + ); + return -1; + } + const soundNum = this.num++; + + const source = this.player.createBufferSource(); + source.setBuffer(buffer); + const route = this.player.createRoute(source); + const stereo = this.player.createStereoEffect(); + stereo.setPosition(position[0], position[1], position[2]); + stereo.setOrientation(orientation[0], orientation[1], orientation[2]); + route.addEffect([stereo, this.gain]); + this.player.addRoute(`sounds.${soundNum}`, route); + route.play(); + source.output.addEventListener("ended", () => { + this.playing.delete(soundNum); + }); + this.playing.add(soundNum); + return soundNum; + } + + /** + * 停止一个音效 + * @param num 音效的唯一 id + */ + stop(num) { + const id = `sounds.${num}`; + const route = this.player.getRoute(id); + if (route) { + route.stop(); + this.player.removeRoute(id); + this.playing.delete(num); + } + } + + /** + * 停止播放所有音效 + */ + stopAllSounds() { + this.playing.forEach((v) => { + const id = `sounds.${v}`; + const route = this.player.getRoute(id); + if (route) { + route.stop(); + this.player.removeRoute(id); + } + }); + this.playing.clear(); + } + } + const soundPlayer = new SoundPlayer(audioPlayer); + + function loadAllBgm() { + const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + for (const bgm of data.main.bgms) { + bgmController.addBgm(bgm); + } + } + loadAllBgm(); + AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder); + AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder); + + core.plugin.audioSystem = { + AudioType, + AudioDecoder, + AudioStatus, + checkAudioType, + isAudioSupport, + audioPlayer, + soundPlayer, + bgmController, + guessTypeByExt, + BgmController, + SoundPlayer, + EchoEffect, + DelayEffect, + ChannelVolumeEffect, + VolumeEffect, + StereoEffect, + AudioEffect, + AudioPlayer, + AudioRoute, + AudioStreamSource, + AudioElementSource, + AudioBufferSource, + loadAllBgm, + StreamLoader, + }; + //bgm相关复写 + control.prototype.playBgm = (bgm, when) => { + bgm = core.getMappedName(bgm); + if (main.mode != "play" || !core.material.bgms[bgm]) return; + // 如果不允许播放 + if (!core.musicStatus.bgmStatus) { + try { + core.musicStatus.playingBgm = bgm; + core.musicStatus.lastBgm = bgm; + core.material.bgms[bgm].pause(); + } catch (e) { + console.error(e); + } + return; + } + core.setMusicBtn(); + + try { + bgmController.play(bgm, when); + } catch (e) { + console.log("无法播放BGM " + bgm); + console.error(e); + core.musicStatus.playingBgm = null; + } + + }; + control.prototype.pauseBgm = () => { + bgmController.pause(); + core.setMusicBtn(); + }; + + control.prototype.resumeBgm = function () { + bgmController.resume(); + core.setMusicBtn(); + }; + control.prototype.checkBgm = function () { + core.playBgm(bgmController.playingBgm || main.startBgm); + }; + control.prototype.triggerBgm = function () { + core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; + if (bgmController.playing) bgmController.pause(); + else bgmController.resume(); + core.setMusicBtn(); + core.setLocalStorage("bgmStatus", core.musicStatus.bgmStatus); + }; + //sound相关复写 + control.prototype.playSound = function ( + sound, + _pitch, + callback, + position, + orientation + ) { + if (main.mode != "play" || !core.musicStatus.soundStatus) return callback?.(); + const name = core.getMappedName(sound); + const num = soundPlayer.play(name, position, orientation); + const route = audioPlayer.getRoute(`sounds.${num}`); + if (!route) { + callback?.(); + return -1; + } else { + sleep(route.duration * 1000).then(() => callback?.()); + return num; + } + }; + control.prototype.stopSound = function (id) { + if (isNil(id)) { + soundPlayer.stopAllSounds(); + } else { + soundPlayer.stop(id); + } + }; + control.prototype.getPlayingSounds = function () { + return [...soundPlayer.playing]; + }; + //sound加载复写 + loader.prototype._loadOneSound_decodeData = function (name, data) { + if (data instanceof Blob) { + var blobReader = new zip.BlobReader(data); + blobReader.init(function () { + blobReader.readUint8Array(0, blobReader.size, function (uint8) { + //core.loader._loadOneSound_decodeData(name, uint8.buffer); + soundPlayer.add(name, uint8); + }); + }); + return; + } + if (data instanceof ArrayBuffer) { + const uint8 = new Uint8Array(data); + soundPlayer.add(name, uint8); + } + }; + //音量控制复写 + soundPlayer.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + bgmController.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + actions.prototype._clickSwitchs_sounds_userVolume = function (delta) { + var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); + if (value == 0 && delta < 0) return; + core.musicStatus.userVolume = core.clamp( + Math.pow(value + delta, 2) / 100, + 0, + 1 + ); + //audioContext 音效 不受designVolume 影响 + if (core.musicStatus.gainNode != null) + core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; + soundPlayer.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + bgmController.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + core.setLocalStorage("userVolume", core.musicStatus.userVolume); + core.playSound("确定"); + core.ui._drawSwitchs_sounds(); + }; + } } \ No newline at end of file diff --git a/project/sounds/floor.mp3 b/project/sounds/floor.mp3 index 96dc4fa3..2b24efbc 100644 Binary files a/project/sounds/floor.mp3 and b/project/sounds/floor.mp3 differ