jacktrip-1.1/0000755000175000017500000000000012544046235013421 5ustar zmoelnigzmoelnigjacktrip-1.1/documentation/0000755000175000017500000000000012531357105016267 5ustar zmoelnigzmoelnigjacktrip-1.1/documentation/img/0000755000175000017500000000000012531357105017043 5ustar zmoelnigzmoelnigjacktrip-1.1/documentation/img/qjackctl.png0000644000175000017500000005260212531357105021352 0ustar zmoelnigzmoelnigPNG  IHDRzv IDATxw`M.زlccCOB HR)@ $a$޼BPƦaMdI.r%YV2cwvgggfwey`[=y{玴~z~,X`1f\|/MN,X`B-ےtKx<ɂ ,XH9vɚP,³` 2 g^za47$v,X`‘  -)Ƣ2ޅ?82 Ⲹ Z`c#  4'n͛:&s&4wyki2t^zc=pތj*HO7mzƣd> kk634ǬחD`֧#4֗h}5ٱ;y<z2zUNW`ڦ*J34N!OۇsN֎=g̟ٙUE^n&88<Ɩ]6T 爅# <3v%jZ􅍼n+.h0ȐNJ>iyI9o㤠Ęakzf]@~Nfw颦 U˟'U%|g洱lp-§ 3=6jg1kg?meӻ̞ zrMHTEصڙv">v"|q% )-G`ow?DՉקMkԕ޹liˮ}y=$3A6v26#=ł9LbdtwnO˫伓꫙z;viiQ=s(Ӽt,9~M3blr)%n'VP<=Esgܶy@!;uGU[J,Gee ǝΌ\+tЕf ҹ vSqzz%|uqnHKsjywmk0)/#;' ݂9u6yQmhk i? i.'u U1HhzvC}Hة|ʋ9iJK) xhΛYa\GNV:3+yMΝxF>FH"玚 r 7SPX}–gv}|fV3:t  2s^ U]0Dj*ضq3ݔ`w8uxe45dMLFإ盶$aWmo'!"si}x}2 dfe˪aS_0^/_MdE"ypEI;3egՇ.؅2Mdk9 H3BY1epY6m35%yo30_Kq`' HvǾRY3IzVnNagtuAIa>t?|ө-,7[)/g~Cq-, ċRRV 嘶q9N( u5L7ޞ~ Y_Mqq\sk;H~};S_fاa suavs駢ƙ8ٜ}|zYvs\cFۍ}fW3=1ޱSpz#DFF:L=v]6 FG{}Y !I ӎAF݆w<E s#r9wgRXԵsO#g~ Q\k>x2/l6jByo[_yްR]|8&͈ $Mx  ϥw9#}vuRݠoEMiW^gF$ vGzF&U҂( cv@/ u|zK#d; 8]. 0o}5%| ؾ;ک¦Z>z޹m y*v]{ye*sT;ȁn>zuL4ogxh3O9A<ԿN!|\q984?}1*KQSM}7%x;ϴN ;]W֣ȇͮv馸p:P=vf6ʶl: ~tcNΨ=orϿm 96頬̌,dh ؖP_t̟c׹#׳_ťH'%8"̭UTѵo3ɖEܲyCdf0r!@&,$$[B $G!$[@="$%)la#(%BI!Ҍ%:}+! Orc|WsL;I,1uhdBպ(跶~9q45p:deSv1,`Ӆ&!I"1 9WBiB@Ȫ ADu$ҋ|Ԅ'7qEFD" ,#IRTduΑD /b@KDFnۊgJE_],›X|8vsrɔ$$S-FW(&"Qn`TᅈF[G6 yoXROO-M;LoPMFŭlݡO3VZE!Y ,{&3;g IN!vq:E02r@Bix4i;F|S +QEYj]T|?J_%6E\8L}.wHMp;:Hiʩ$=Y%` }W=AB ' !Ce24 #G4QN UuxzÄ)8 P4B#AjQ]Swm_7d&c5)Mz[[!Hpը.agQD:aW N@BxGJA,ѩpYtHEuDڌOZ9ڲӉ mhdgE3o5Cʓd^E9Ϥ` YjzJaT/,`G;ޯH.MK&$oNG#NVopt!>0-U2ӶᰏxJ90"9ach/%RETzOL}pCoNoMu^#njԲE!>AIɁȋne9HF^uu5;wxR3Ilj poRHJ씲'3Bf-4B0 0:*ag%!a2RRsbd(朄t,b"/5 ?Fx"T3Fx ennZzC(SoTOɆQeyiBkwRvkw:5gpGFȒ (ؓv@kV?fÆ8NkxRWSNǎ(hTc/iӦק7>>Nww7ccc\.immaR:*RH۹_&I`%'} E`պK@is@eH;(߁ֶtxg2m3؅gw1[[g!7 ˺LgsL Ms()ӱMee_|OݩZ0xO k2} 49;)a5۷ӧSZZBv4fDy$V6G(2] tH61_E9/?/D+}ooEΚw/Gȁ9^]ތa{g#`fTWұB WrsySBذmo+/k0P[aVYkiZ@̝8>! kvezգ,4C+,CxjG^ZaxzomE4ՔJ?wn~٧shn׿Un7^tN*Sjڎyz a[*?PS_ٳg78lNgLF~fT<׿ z9ņR& C|>zޓoժU"ɶm"nؾ};cccG .;ʍ xͻ.˥t4OPV%;HĖо υ} gznIqQWm3z;ZOIp~y>lgl̅ockK5EE6Ғ06dlɦA=C̫e9H" G]'yQ3ϤmYbϩm`^^]G}${ݟCntt?saۙ7o>#?"o/L4l^!kXXI[~no&^|G$ovhkj{hmmʏ_'?{^[VT`* hnn&33ӧO:p7ٳ顿B"8x ܫظ/z$!i?@Qfj*ޠQՅt36-ryC{a12jQ06F]hnEi%IrکoGC!H#ؖ]s$M #2z.;':>F@## -UOߣbʛ9DdBi?P^~{8Gj WL?N^ Gz օ9,ZxbTO\BKHV&}*o"MNsxm_:/9e^۾]wnW@?g^}xe^NFsRn0ߨ, 9LvC: 7jCrk R赧SB0cFU AϘQW3fnڨ^CCìBo(CQUUtۨ N%K(((_7e0sR<|>p:!I'NWWn;B000= "!e?BZBn;/nsp'g5V`x}GؾuX|rzzKhqūt/6pocU@CD{G9͸^GlVek9y'⃺z仱-җ鲐M#R2FߏhSoѐlP3g՛3ima(۾w9sؼy}nM!((,dժ8%_ݎTa*/>e I PZZJ[[:att4|xpՅnQ~?i~0Cf[B^9 OF`\عg&{f"ծ)66--a 0$t(m)Q;RJ@P $y ;mE(. >pB򍎄U'm?8-+\B#=mT]YYI^^vI A^^v8@EE8~?CCC̟?ߏӏ$2ctyF8)A ׁv(&z+.tx:*nü:-SɈ|Z(x1: yc"*3ׅb 3nZE[G*8Ch`Ŭ08äW\W\{Ix⒰^pp(?<7%P-rIh:-[;v!)KHmݦ+C~ݲF2$}~Ƅ+ F9rv*CyI^0]btD׈ZǬ^)vf1,_wGϋhh7ʟh+U#n1fօ|[;b |z|i r Zv6 HD})F<<>|Lh;󶁀Zj:fF=Dq䂾nrq$Ӹ 抠S6&?>L d@^LtӾ5:2"ApiKoRt 1C<2#1-##x$=MaJxO\d!q̛+W<* L ~F=cz!DdtsJ~6XMZq%]ז#8/P9MAo~ 2gپU=LUBDy{9X<@|@CBꕎ"eB? di"P"&զj8TJC ak~PS_%Jz?6U#b!q̘1Љ[H8c>>opsxrѸ>i>7@]tTlhoy8RY@FrVrFݔTs"JT5k7;^ fUEeGB_Cp/lيHˌFQ^Z<#DpHDK,ZO?>4XsxJ}>8>9' (b(m|EV9fDGSBR|i^c(d{pKHg,ǃN|n奲хG"Pͩ>%t|(OQO&ERL{q([8:`^nBVL/v!" XdY: @tT%H>[e :ڊDv] IL=%? 4Yhy̖k_+s(pLZYq2١S?r(X:yQ 9Ev I2ف_m ͇=Orp9O#oocA=igP^YYiKȰn~QYMzX(оG;7oK%S^7*3~9ox?#.Y67n>30<8(|R9iE&k,}C2FB";db8whM,p!'`$wAw,wI 5)'L@Ko4jτH:UK3b"7 gQy$oXyQÌVY^"1yъbk.x1e`dG!zU|;!+t#s"zJ rx!Ú9]y '?LfyjC`-BtKގi` ٝz!qjw!QB ?IU]7ސi?yI^DksQC!ͱWDKT^`s qf Vc_rz[LFv ]e 4)#eݵ yA *ǫ"}왳1\#i xi;w >>ޛ9g%oU4CĴ~x\\*!D4" Rk& MUd8YCvR?a/X[M'XL fvD@(:|j GS^¾]TnxSEK5?茻G~K6txVAn)Dzv^DQ'؎GTq$N.zffTH.M,K L  Aގ8mYBW)VB6w !(YQC!pD"׋UtSoS] e&y'[hhS (񓠻ӮҌwE^2sp #5 ^| Gx~~ gBN9 50GH a+r!r09J ?rL"<'G;*GAR-0[tզvN>Qvmh=i$JTa-'ERݬ9z}?DFW>vR{3A@@S?qM^p" ' mȼ vW4T?ھaqGf-= 1|{ . k*YCЦ",c#4wT4.KcBJ#$'w,J"hHC7<C[~~!vwj9sxONM73cǞH&k1csf~.pt]JΛlm6lX餺^/u5 3r fv FoHI~AdXQ#+ɶ%I2]}(Qɋ76B:T Ցp>O7s}_\ض <>Z@C?Bl6>'|_G7f1H@qz:2n 7Sk݋?= _Kv{{{2{VgyV'8'-^93 !8%4̨f y ?McC-^| 3n/'Q{y}F`B62.w݊Q~d?G?~gKbdٳy6N3F k+L-.={LcYE/FxCa9r c+'vƛ7m3G<$d=yɴ! !uQp?7= /3_:\vxkسnbkV&?"$/k+3ig}T6nprco}_6nd|C ˯d~zϹo?(kq/0LCWҺ>8?OAx xo IgkP;^ 02N qJcwds .Oc󛃟;x?vvo\z;~Ęg,L[{'m흦v)w'`~(}1Ddg!*ɚ$LI8rh/R=1LKPX( ZG.Hvne\rC] av*v9 qUo&?"$/N:^j~Pܐ"ѐ f W},7d+ ϩڂw ܻ7}y|S1LwիWյ'8dl٫,_. ffyJ~kӺ+)ak93x7QƼs~|>> /+1|X$-{nwuwNFv]w(#HT"&w\jAD 2&cx0U'V%PvߣjȌː#` Z~_xG~>oݴu3PPPqBt9c]2ųYǟ,fgؚUɋk1;LNH6w3碄ۙ;wsey/bEOކcg:C7VCmS8\"UdC) Q=-D ;S3s,6ZhgM>|FU}|>?UU5!HOO-odmBVzQ咵D?vJDݤ먾&Q+9M'YF&r1T^]6hw"mh>LerR_`0Hveh!wHo{7a)%?bs84̬j; I=e4%lqNv~9!!'?ɝzwY7x{3N"ô/}zn:n[n?s=Ñt{6s}U`x;"lig#ҋhog֬F~_#ܳ~nu -|t.=& S;9Ņ2)X}֋r'x?f8m3'NM5R Nv;# ϼ;o I%2 1颯0篷"/{>+Ui7jg9N~: fQ[Ws#ضE'.W'm~&Qϊ]%vu+5xO+?{|B.,)ݝ|#~9p5W%|_gS&wW^W>*]k(]/[insȎ]wƕg@p1Yv] 6ACBlʑ;CY^ Qc8bTYy;6:wl}=--ގ/1:2Ě'fYb)kWn# Br\Čn3eOUӟtTʞjH@I0d{"GW͎U/)H\d1B2;̉,IGn=\uS qꯟk?±]Y+< dl/7.,KI])Iߖp7/B yK{RS+VNZ[Q~>lۻ3^2}7@CQ҅fDgn"#oO{kT&wp.w:= N(|+`8,Kf%}h~X|K"mXÛ ě3qtjk,'U@Fr]ҷ1IDATё"! Er|DηOQHe_.^gͺՌe*fK:wI D$1/#͒%Mymi$:vRB B (жD]%# 33ҭ{,#5ExIcg!qWi& =PVq v9Q:6y;ew,a vK٩T2rºhxrz iNƣ#DO,aDr"ʂѩȖ "P1<2Z-0іK$r%Fx5?gy96bk̛n39"Qz8 cj(*?JwLg>.F?LIOK崩3΄as EaAǼyMa۩a39m,XZXFBsxO\WaС݁‚ ,qh,X`Y`,³` ĝ[m6n|8t9f`6yj3qٱ]vfbƌy^JfzYhE1\RŎu))ɷ 9YJ^M/5>#^+9$?BcCP.f?2rFo!q˲gHn馔 s=1:OSi SrLGA'cocDr\x Nz} $/V mE$Rs? ͚2{^AYbt+NVl Ha9PfZƜ %yCrZt)t@7xcrM^tϧ4}2R kD*{m,o2:g9mW/“?]>e sr+B_37񛯕v&р_"3pn{w~Vzp@.7?EEҋ;?BpS*g:u?z-M~H `_)䦑eod<{-p՞<_JzvG=U|B~uOmuYd%dO!< ë?Xo3? ;3o|l[W2jڲYz?KN9(8RN#^jL6mByq,n]!nӟ .~aumK/$_~!5FGG9a\N8є_\Ko^';@Kg}om&Ǟ|Y/<];iƟ?#L'ڔ~oq xbǩ| x!?IMknט4yXZ}c$/$MxO|~ߍdcW}ٳ8X-tť\HcC-Q_[A}mEJl"T V! ~)oF[+l6qQԧ?K[۶p.aF5NϣnlgWv53Oy&ö8&B044Ģ?a;;7u%ꔈ^B d(;k!Q]O߉_t)_Wu^rc~{_K/ w K\xc鯘9kN8=/I]NyXvn/<'r*'r*/|Nz׻VU~C|Dib"|[fqL.} (~:6dF&~}o5F(ʧe+'->Y79rr^S^^믯׿^?4|+.Z?[rn{vlc{2,N]5&_\Dml{~CIVfnYy߱xIu}q~&F>}{=_?0l+//3zGx|T;q=۹]1Ͽggpz-r)q)Mw3? 5>v"U%%]QxtF~occef*{?N}*~{宼St),+ υl6Y_z)6=dt]NF8}O湧Ar3gTVV13 N9T~_#``_3V^EW><OZMя^e_wشO|3GOwQQy\av}a00ec/iO{l{YMȲaiDm Z|;X)Sm [Thi* K?tfޙ;̌>9=;w PZ%) vVZm>CZh;ucr7P^|ڽxү+!zgΜ-hM[up> q >O:+ƙCS(/o8MdfsI#oqޯkTA <{ڗ>~wB[4^x==ޝ={n3$% e9q\?2Uq4QӻJDDױFΠm[%:: ^( +}k4)..v)g^b2ot!RbT3gXh+ۿ͡1'zSV'O2,%%3%gͧm6潡N#=}Y!ex0N窂UY%&X"33>, n uu,q,3&3kWEIjFgqLh)srƳvj#27PMDEEqw}9$dY6#!;ucG l֣8y 20w8ڹWIozC#Z2%2w 3?؍&hh4zĊ &c2zW(OZ!.͜Gp+XFuzu$q{duio0wPDm!jQټQȔֻO⨨MGG7mp)Ck'~awY¹Wۈ~DԿ"x6o|kcUH5ʩXl0t  >;&M摹x'Hn3GX #i;?|Q#3 ;mqþ_Y &L%)y(22Ŕm'ptdd^Z 'ctb_ތ-0e@V/';z -d¸DnL DY_s9\1W91ocoz|4Q5,,9dN+,0yKF[ocРAn XǞ`4RW^{jcQS#2}pQl N~Oo\Ȭ۫a ~쭢89q45g*2#X]cAc|ZO[?,- wk}YMbg/(MbNZo NYs[;w5;1f|`o,tǾůz8wkploX7=O',"[mtyJZNCegScF+{'cw y=;wvl?Q|4Qu76@̸َ>9lZZZ0^mL^$!+`] L]fNjsB& IŚ=?iX^xї#K<))S[j>=iT&MjM_4+u(!\AobX}qKΫ]Fm]-9HH.m^:lͷ}[*fa- DG3k"bc.m^,`hl<&ޞHU Zʲ?šu>rGC`< _Nxcʕ}W^q}%/w-? P>NjX/Ka-0RlddJ4NǡV9JCEEJנ/zhWx1LI蟽qs5Q^ Cs|gor-|nnc>- X[׃Byr5*{1V/8VD^!=C x@u?E+kRDZڵq阐Ν;ξYAyy9 q2"/PKw/sL#vҮuh?tLNNÅ !!3"/eD^33{x#Ǹm)5@ B7gw@pi @ . D%A8ɶ@ @ A_t ^#4!@ BTupOl/w7HTIENDB`jacktrip-1.1/documentation/img/jack_main_settings.jpg0000644000175000017500000033611512531357105023412 0ustar zmoelnigzmoelnigJFIFHHExifMM*JR(iZHH02100100C     C  "  w  !1A"Qa 2VWq#RSuv$6789BTUb %34&dfr'5CDet(EcsFGC !1Qaq4ASr3"5T#2B$%DRbC ?n]XRb}DRr L~jLK$]K`Qq4O蚷Va[s WmTJPD=a# )&⽒ZRD1E v8EwQQvf]WNmjrL^aU {c(қK./52Y*S̓уGsp]Ý%z#_a>~t|FF"GlKԳe̥ڰTOr%hڒxtQi׍8Ȣ{S˭!It||36<#Vr5Řkv+709wGoc_APkjӧ)sYmM8@pǏ8)6UOBzJ'OԆ'f Z% ')9dvT,Jc MY^aMc8֚ΰ~+?(ߨư(ߨUT[K—Lӓr֦f;#9+YsR$9 7ڹ!KbG^q d$q,Ʉ6Raȋ5:%?QQw71MY7dweW`fY,&]<6'8v֩c]Z\Z۬d/3.%G9Ge \?c+Kg^5~.и-TJֆ焳 YRʒ,!EI. Cuj9HKip4ʐRXb [Դo)̉-l{Eםa)~~h~̤ZZV%RfZTċa9J *;0D`,buZBߖfqPsEܘR[;NIh`N$/hwZ:uC s^uuJưkEwv;71_ipiܼ5Jr&Ҿim,)ixv >x0jjd0_YWQbcpLFn;z70>wOoc_!ê|7_ !4홰=5ێ;k=u^Ú_a>%3`{k=wn;z71W095|Kfn;z70>wOoc_wa{sk}#ĺ>wOoaX}ߨƿu^ê|G9.ٛX}ߨÿưq?ѿQ~9U9s]6ưq?ѿQa~~{ss_ !Ļfla~~tFF5|W0B9ωvtFF5ێkU:wa|/s홰=5ێ;k=u^Ú_a>%3`{k=wn;z71W095|Kfn;z70>wOoc_wa{sk}#ĺ>wOoaX}ߨƿu^:wk}#l̓9خz6~Y(9Upk 4/>HԒ@&&,ߺ޷2.5-1>RTTHɎ:G&fdRRԄC+x5`ߔtl.wBhτ3DUx5bnT*qzGizI0PJF\{YXqd RH8I1Q &o~dtSŌΕ mjZk=ʥ )TO&83#U:ž <("#C'Ș}bӷI[Xϓ>xO_u)5)YJ>UeG6THNqqQ`dIY:qSbg9pΊ2zC2u#E^yFOG%>8RimgϩU5DVdrnJL$*6;3JYK95%%/6²1!^ QH!!)MƚijBeVw̠c9I8ĺ/e4Rl}-s莣ZG18ۮsi ǖysH2CAPUE:l0][zjR%6WgskHZTZGDmT+rϫQNun)!3ժ<JUb!3q s菑}@SbqXŠN8vc sVگjg6uMc'iBkfK^-ˎQU90RTXML2V+ rֺj-.joU3ZmHne )ed$m;PU9$ťzW=mu8G"-j p={ BP=T\;Gp?!aYq7RwZÍb*R&2o/ ;TJԵk̿v 7M3)GR|,7^/hzWv\ׄ>rfd!8 KE_lRco [Kwt,kLjoO'+QfLjO`Qm.v HAQ;yb.CR9vً1 N*w|܊m6EЛZPJvIP @8< zPe ̥rl*a ?6d$l *c6]@9s# Ga]vK#GUAej[4KI)%?`z?U{Q.\ Ρ%$sdlx)FW)i=F ޕ8HY4t6o!K' D,)$%)N) RO<)&>0K=;>1ّEu7ng fSY+fкUKb+MP7I3'>BNI^toTgCeiSgwgRYe+H0ҝBՀK^2;Hq &Bt@8M18G\ #VDYںӚ痈V:duI[מijim!;Dʐ8ˇGzqvC[Trp]I~H2BP(U+On'7݌odssJ Ťkπ6:L9ѪoY)Jѥ/ZUNyM[CӬ)k֋j*K:%=PiUm(XJ@Vp"܃ZB'+7K ^Sm`ۅ jIH*5!K:*'[jq96N1 (,)Z}t䒯JגH>8E{\Ɵ]) PM-`Đp DAap8YZVbfcV4>X(ڦT o *]R8QZsq>3J-5䅵SW^;R{w(u C<?v,J $М-c;Hz\q7`1s> 4~!hLū?NkfMj%2[1Ԧ+M[>TURm-4fRxD![ 88]vx>fZ柪JPJYݵA 01SZyt-!!>)y#ŜgbJu M>#4>>)8Rq?W{Vm^OwcԗT56ߪJ2-DU~j}KN% R'$-GN1mbiW٦U5-gK/a3!s%<PTR%&9^8tPPR4BSKXvBҵ}cKv HRa KC}e#.s#}ɉ~~ s`Wo}ʴ=>epmgdL:&$ 5Ϣ5kD9V̽&vOP>èrbHa߃\!Q]Ch{X|rmgdL:&$ 5Ϣ5kD9V̽&vOP>èrbHa߃\!Q]Ch{X|rmgdL:&$ 5Ϣ5kD9V̽&vOP>èrbHa߃\!Q]Ch{X|rmgdL:&$ 5Ϣ5kD9V̽&vOP>èrbHa߃\!Q]Ch{X|rmgdL:&$ 5Ϣ5kD9V̽&vOP>èrbHa߃\!Q]Ch{X|rmgdL:&$ 5Ϣ5kD9V̽&vOP>èrbHa߃\!Q]Ch{X|rmgd-Dqڗm?iBÛ2ޱvDp u:a`WYծ}ޑ5nNrQ#2x㎳ ^**q7Tq:CbX+ERf' sb]j%mEIC4@$I炌Sr m]e!2% '-7`\TɊ$z8 7VO <M-A]5~X/)5͸⋒! TJ;N#b)M^q $IxvG?RT4I;grUSy3J-9֫i:f*)16fdYZ>Y ''s!`NZVN Biғ;p! $/HRNA㘲ZTqMط*:1Jq(A0y;{"OY6%*N( )K=p8 #*IBSwYZ8L9oRK7kj3z$[W1'Irmn0Fvr>/7'>gx8dwQSb!!@B!!@B!!@B!!@B!!@Bt c5:yt:JZEss-ԥ.-!)BVĶ%Xq{#"1WmxHD KS(0&|̽.fmP =#4o}dcjI89CzTg+SmbORlT]߱Rfդ;m vhXBrv.Xe#o-۞o<N)KL%rx*1D c,pQ}>ٸI22OrGև&*8cAƑqtIc3WE[A[X%8I0L*YD}Y?Rq3bY܊햤H> 8A<3^µf> 3#&u3l[5=lIBɑI}3S06%/.ޣ8 w209xd:ރF֗U4zṾ ?88H 7UL֘mCOpp`qt%kqf%l4L-22VT@#'#5^ʨw=P:Zm߲%Y7dgHRzg]@ yNUmIzTVY`(*aFO8S^;ʨw1j~t#MT}RK39, +e6V!Ji{ mĂRpF끩񸣯O<ÇStʨ_|#U *s`;j'ppqېO>sg2\m衩jPJR&2O!>gP(%wM HNxq$e:괎k08(,_^L(KB6lJ6qc):leT?΄;ʨ)jBvz2UQfj )by.R+^:n-:JL벋TҊT6HVCOQd{*t!UCGu5φ]zxp}܌Fz+zhҍS$ KeTAx gXz 쪅Ї|#U O aIN6 y1wv #yËAI z)&9;Ym8P@B6n# |T'.CJĜ}:KE{Uq%DCir_ʨ_w=P:wڴS>gIZu7̽Ǥz@kuƒZI;^QsnuԚRRϩUjT')ӳI̦YA*^iN*H$ofj1*Q*7{ʨ_w=P:)M%DrFA'r {nqdUu@ɛHN(9%0Գˈ `s|hA}G"A,Y,M46.! ڑyu𔣓(lpFw xrHF9Z! BJW>A R9JW>A R2Vg"aϐC>A3 |!dEsϐE! %j+ |)f+Q\3HC0Z! BJW>A R2Vg"aϐC>A3 |!dC'@ӟz8A$z94Rdn#GA0rϔ;sBȲ>>>YnP;\?Q{-f?Ѫ7R7^D{F:8O>?|oh?80sp`OaEYp!@B!!@B!!@B!!@B!!@B!h~ šG^6JOXH) (NCrlκCSyjnAtjM:J|^T񦴝XB\-dB%u=bY*مu:=;@W멹ev3K-2b+2='PRK͞H#u4۱b9EDҒQ~iŽS+uM:8_ JB[.%S}י|mS[wep3;R2-5ҋO/1K&%hw-+1se1AujyN32ͩU!j ` a"-4W.~y(۟t±f t)YY:]౑\F7)ԑR TŔnpLYd?4vOrǯJBQ jץA:$wU׉v/+( e -Vu 'nm =n-3LJ*=Qz] et^ǰ?nH^5}c=75?}*v9@%[6M&\ja.yBޟͅ-Rl(#& P;2b\8Rj)t柙,]5թYlnTfZMfJV%lZ[3*.NS[(J gcH%%ġ-?b4[~ysUȶvf+pJIRaiR rq)H*{ :t#ONqΝX$k c~S(Tzb~&}f_{mKl HJF#"U-ijᛗXL2Q.>RI>)R~Xz3bs8`7,(֤zjmZ˺elIУ3IR&!pJv6v.N]Sљ *e1fHJJK u4:{Z`aBک6s&el}mOI J28{յh3{F9`nf#+3Gŋ8nim~i]9NMWߒ $6}mHq+JM8vF;^d12fVlwDcs`+qj469EMj+l\s05jŵ"S5KQrbRztVK=KOtKe -a r7 \ڥY7 $ɚlxUgf=\Zȩ?D 2x?Jd1/; {Ss1\3iz-Ǟ LJP]/H2z>0 3S =R⻯jr7閩KO>g[$+;=ȍoS. H m&݌ݞ~XBU,o%DNwg8fxDY!g)WnX5>S\^a2Ҵ*hruK`;,IYR#lޥCNr]v!VąF~^M`;* JqՖi zNgbg&ܜLh>F&.$\fJ>.{f5hGK5Xfo**!\۱I8;}'q+<3_BxwErnRf6Kf8{w}z}R;oDGEEkq|xN n\&ٚ=l!KEݚ9*X*ۦY/l~qS~BkT^P#I'DL,ܺpFh8b0WV?lTC?}G*VwS|?u8l3.Qkрzsb-;e9rХҜh9:Jx‡k$+L#c{0o釫مLj$ˡمL=_-bB1W7 kz[_7t#c{0o釫مLB1W7 kz[_7t#c{0o釫مLB1W7 kz[_77`eЌGAof 1).b>of 0c{0o2F#am| W7 k.b>of 0c{0o2F#am| W7 k.b>of 0c{0o2F#am| W7 k.b>of 0c{0o2F#am| W7 k.b>of 0c{0o2F#am| W7 k.b>of 0c{0o2Yno1Qjc$ j-|ܭOEjjNagiS m֔%xA?ؼ57wԠzMG{@ˆ×r8sǴq#WtȾvrϲ)5*RҗPӎ)HZr4ϒVq*J@)P> 3 %%w_bT2w_bT2'??>w_bT2w_bT2GL;\31ʇ?y5%L+y5%L+/=?>w_dXbwM}Sw_bT2w_bT2cuO另,tؕ3̯tؕ3̯_dXac7y5%L+y5%L+/=?>w_dXbwM}SzO+=RkV.E*9HQ#7=q#*\b@2Wp"AD17JN%RTN 0b؋UvM=%cXky`==) O|s%. ' % pd@TEqՇT" Ŋ]z홠ҮlY6w8YJV(Bِ'TlZTګFZP#Pm\P8 *\a?Ȯ?_x) *\a?Ȯ?_nzMgV%)q*8r#f]o;4)D@d?Ȯ?_JWX%{JXۃ9Pd-+i@iP \*\`.DWMwx9} Er;BT;AY,9664Tӈ'U2;DD #N5Y۫N@H9iGoW*JBsL8_ Z-96cu7̓z9T[$#x;}߽P_ooO?/!.!BB !BB !BB !BB !BB x?(;I5$O='|ѭ/ux)?OO;FOIz}еGu*޲ҋqyKe \VҥlNr3EbpWwOgh}>fmiV=JII%8HBuAAK;j7280ydpI9ڔqؐ*'2z] xtf-j0/%SSfrUڤ u[XWVV s-P,1'-iΜ@u*Ti>KB*2_u4 kZ-A&=VK-%NAR̜ JSԴ !qq +$JEwNkT*ȤMۮH3Ejb1ե30z7$e&lޖ#ÄPo9{.bbE=NTUG ǡE+[F3{]z+o]zvLArZUZdJ[JD m xH*?9̫T6@=v׽.cERq\j[ (~86Fxb%$_׍?Mj7LH+pL}ZTD%YJ[ڔ $E{Zi RVX8U_ͩ+qNԧHR@o H)O|qv\0 q! $jd!|Y4"(4J]:\1V RII]lg }x4a}lg }xwŰ}Z 1 Ű}Z 1qh|0׀3HFqh|0ׇ|[š^!_|[š^lg }x4a}lg }xwŰ}Z 1 Ű}Z 1qh|0׀3HFqh|0ׇ|[š^!_|[š^lg }x4a}lg }xwŰ}Z 1 Ű}Z 1qh|0׀3HFqh|0ׇ|[š^!_|[š^lg }x4a}lg }xwŰ1wUv ?OQ=8^4lhK}#mNWTtQ) rrTbFJRJp R8k˚%-WZI+jfd1$+a7-Z;R8O<ĕ!(;C2ɦ6WE%M|pcPJE$LKnP?&<O]`[2lLlp$B2 F#ʦP6<CZatDxyxԫC~Kj1s[ Sn6oZu+RS d\U( kқPqd$-¨딩ڛLr4nSq⡜sԃր[QJ|DqP[ DN3˂G@Iy'&2k=L–%Yr|@v}I4^qhJ#BG"[;J8x%Jo ';A<;xhMWZ*e%K!G8RVdPQQ<O8dK2~=c1e9g# diU(()sFIO``)IAGnxԽcJ^%iX̜.۳YP)(HR^Y)H۞+@G=.i܉*VG^q2i%>RN"aTZrܔph4%FRT@8VpwM"˅2,5J*l'[jRGV 3'^wǁ1ܯ9lzK}f{*CkܦpNJ|RZgRn d:Y*6E!k(I <9Ǹ#׭!a AQ A.ܞ&-nCKaL8Tӱ9)6׵8F *JJ3'1c8l;rA#0^@ g2RԐPU܎=b^ѵ {T'0GS+''LDBjiϙ?VHHˤ.c;;c~5kٍ: Qndg;0G~B}v>nVⰄ" !BB !BB !BB !BB !BB4?S\%'\\??NCrl].%<HOSw}J ' Iy039dm-问wƇۚm;nP[]{e}!m.)dL[U/G6R5}}diN)Aueu#)R"7 KOKSPnn!oISC:LHiHTu*JAzqݠV/D.izUmX57-׉v]jв!I8` Ԅh :EFw%.)_fߓ)5Ib& P/#-'ތc-{Z6S$ЮRhjN(N:zۣqx#TB0WuZnPԋN~U31.r]Z%IRAnZG%# H G~-#ߌ^GO~0K}xG)I#N$ d b)<"#ZG%;i`r46#Sh oZG%"Gܧ v#~-#ߌ^GO~0K}xF؜H؏O#ZG%;i`r46#Sh oZG%"Gܧ :Ť~:{[ÿh oH)Cb>>hi`Ť~:{[.}|؏O#ZG%;i`r46#Sh oZG%"Gܧ :Ť~:{[ÿh oH)Cb>>hi`Ť~:{[.}|؏O#ZG%;i`r46#Sh oZG%"Gܧ :Ť~:{[ÿh oH)Cb>>hi`Ť~:{[.}|؏O#ZG%;i`r4 h G]GO~0K}xw?m=- @Q! ?@g&#ZG%;i`BR1𛕗vVi컀A$s9$`A~-#ߌ^GO~0K}x%=95YH6cō̈ud0S߈;i`Ť~:{[' _ c+&֌gh oZG%W\U6?uэ?m=-ߋHt׀2_Suυ1OW\Q~-#ߌ^GO~0K}x%1OW\QkS VSx1V c]GO~0K}xw?m=- ZBRN)vܲSf2yd,զ!;Yv.I Qy焥`qkh oZG%z#nFIox zWZ?~-#ߌ^GO~0K}x%1OW\Q/OU[Q~-#ߌ^GO~0K}x%1  o}hzfooG5_ʋs#>߽P_oo?;0w!e!!@B!!@B!!@B!!@B!!@B/ux9UO3"wkf šG^6FLw8P6? "v/1>M(0S-w}\|~HX(*&-U;;R();A'A:GNR^RoyUJy-aF].Fx)ӌ`N>ôPHN8g(Sk. \7P/R RXHZ7>kXK,$Gg]:#iH o"i?ͥΰ4TA@;; @* $r bz<+ŬsﮫJRNjf#i}R@Jp7>h2lsj S(YAMDM=NLq9KS02lO @)8?EJʁ_#s Ȟ.@w۴>:~<|V R'<:RBl}H9u.uK] wïw ꗰcC/at/Ԁ3߾/߾/7[^_ TоRz~<:~<`n{ |6>:RBl}H9u.uK] wïw ꗰcC/at/Ԁ3߾/߾/7[^_ TоRz~<:~<`n{ |6>:RBl}H9u.uK] wïw ꗰcC/at/Ԁ3߾/߾/7[^_ TоRz~<:~<`n{ |6>:RBl}H9u.uK] wïw ꗰcC/at/Ԁ3߾/߾/7[^_ TоRz~<:~<`n{ |6>PN,q6>u~xu"IWfڑ:KgU#k*uײhQ"3P6Cfi׻{=˻/_6'!4eQC/_6,]^^//_6z!>b:~<:~9;Aiϙ$,|!V N|͘Mގz3 HFv{~~> ~;qޟ7y~_+qXBV\B!!@B!!@B!!@B!!@B!!@h~.QwW/󱭺/ux)?OO;FOIz㎳Jy1 $|@jKW-:!FfI\jș#3AZ66N(${TVgӡ K![N8+<](3EӘMnIuԐ_\G ǹF_ݫ7K-CU*jdISjU:Ӓh!ix+S@"Mk's[]EDU;6RZ`6Zҹ|.0rUmYT^\U KrL (uܥAR'rzf-7)$m5amZ/m&$ziݳ%R]Z;2>a S)u]PHfQ,Ӆ,i‰%E0mɢ5}!41a.) B2a3.mq˭ i/L`~<O;cCŘTmٖ+pLiqWf%uAOmV7$Mjhf ؛ֻOH(e%j2ssi9 JNx`DX5UZm7GuRuvP8ҧxR830aϐ>CVL 30aϐ>CVL 30aϐ>CVL 30aϐ>CVL 30aϐ>CVL 30aϐ>CVL 30aϐ>CVL 30aϐ>CVL 30aϐ I' g(V_^IdvvƇ=[MtN} W.Ot!Nu\rsz$:Ů%eA9P곡ad%@H}bƪ]Ww/nQRmԋ`%(/ 'o,CEu*V!JFezRJmޱ7$unCJJw8RnI}iw%^R霝rͧքVm0_ZyY(So)$ff,M.eNiNR횅)@R\zaJ7m< WYĘd:4[򶾁&gm{ViCJHLRHcNCSzLIXvMbA%}˪Lwb x#g6@ZEOPK&A is L2uM!) K8ХT꭫R7ei;cܦR*(\\[/Xja֬lI[wJnyk§J%)K-H˺Dl8 3[ة(;8 GZ+Ӵ\YJwZX,zsMgB+R{"B=“<91)1j5P.+zE}MթJuywgXڔ@A9Ct#UQ* NĻS]uo}+nLKwi-I!&Z~Viw]fHӢjKzI6^THI9P1niǤTEW\E.٘fMu .z>2rU=F6}rOzv٩fR\m( <]QRRs3uejM%fZO6 ,G߻yh牲2*xlbS!:HV4UJ?#f? ~4wo/a!n;+n+B*ˁBB !BB !BB !BB !BB !C;^-?Rv5S\#&qw??q;FGIzgdH %[xdqCNI 8G@gg(Áp<9vg< NFryBJ*@Jx>9@) FcA8R|A˄TrA<yaJ=ӘBC{B@N01ÑϗG._w>xQ5y dKCy OgPPڞmc>W^x2ҁ rx悂;1ì|^oNq Ư<߳zw$:ٽ;~2MƯ<77_~#/fH7|jQ}7|CӾG#$|jqc}Eޝ?IoNq Ư<߳zw$:ٽ;~2MƯ<77_~#/fH7|jQ}7|CӾG#$|jqc}Eޝ?IoNq Ư<߳zw$:ٽ;~2MƯ<77_~#/fH7|jQ}7|CӾG#$|jqc}Eޝ?IoNq Ư<߳zw$:ٽ;~2MƯ<77_~#/fH7|jQ}7|CӾG#$|jqc}Eޝ?IoNq Ư<߳zw$:ٽ;~2MƯ<7W;a\5FMΩ e I؍"Z|jy31 8eD\9p=H9 ogix0-Kfj0!:ׯBҤ G$˗ӪY^ĂIT)XuϿrrq1B}I¯(F>0{8rN`J#ii<{g{K{^xn>5yrÕ @c~)->5y`{׽WRIW^x{9YI$uUVYrn]L%B)Ql 7|jĨX"QUrI$}iuW+73y6]KY2IkHs.~h %$xbNI98"~5kىz _>fmoG5_ʋs#>߽P_oo?;0w!e!!@B!!@B!!@B!!@B!!@B/ux)1u‰xȝqFwZdw;'bR 5=Cή!s5Q+&pV)3m\%2:L#'펤q9\ަtky'kugiצdcT9*q;3qw_s[T~ؙvbߝos J$9vGkSmZMlӮttK:czTP8(jNEsK-v;⭪wXEq2ʦ"o˻0ufmiZRb㯽&kKmj.֛ڏcXRDR.]4\}3 gÅlJq q 'mh>ܸzڥ&5\M̡L̥YXYhX^u?\trߑfQZ~x'@펙:uQEV,:N۽d$'h:Tf/˸@Z)2DtԫsTn\zPK%I)eJKSPǗ h(M6CNmY +Bܶ?OqmSդdpI9mn\6E VASnѧgSR0%Ps(FӞ~>0m?\IҺގU<ܜfC3<V  =!uSB= J&SSn}BQhZB%[:z@gCs}C[Z T7ƁFmzJo3>òetai3ۜҟ?J`P%l٧! D  :Jt,W˴u_R,$VfOqL 5N)<Hah#@!FP E1^NKBi)@aVԄo-I ‘[m-<տZEj!n $%/H $sys߯h{.4LRڥ%(@pZd+Hip7|-xp((B9p;oP?hDx!/ ՝7_!/"cc;gvM_z} /HYݾzBiB&< < #Vwoޡ~EЇ;oP?hD/!/ ՝7_!/"cc;gvM_z} /HYݾzBiB&< < #Vwoޡ~EЇ;oP?hD/!/ ՝7_!/"cx<y/gvM_q!@׫;oP?hC՝7_"ECqG^iBVwoޡ~EЉq} /=Y]zB$A$(Oۓs0!:gvM_iBBH9 < )߫;oP?hC՝7_"DǗǗw/gvM_11} /=YݾzB$Ly~A y~AG~iBVwoޡ~EЉ_C_@߫;oP?hC՝7_"DǗǗw/gvM_11} /=YݾzB$N88@ y^} /=YݾzB$\Hy1!u/gvM_q!@׫;oP?hC՝7_"ECqG^iBVwoޡ~EЉq Hyz} /=YݾzB$\HySo ՝7_!/"b`՝7_!/! xsr՝7_#3kP .O>=xcN sٌA=@jN_uJL:&s`tv "?~5kىfooG5_ʋs#>߽P_oo?;0w!e!!@B!!@B!!@B!!@B!!@B/ux)?OO;۩h~.QwW8K 'bR 5BXKmٺ/.6xbW96ש&uSKU.Q̟ݳYB@@H/]I(RÀ0/<ڍBɬHW*K<5% ]jE򰇺 [ZBf(׻zuٔu(s0*[% y:ЧV 8GfK 5e6UuA$vεi07^Zs%n7LטUk}32}hRNvpu dZ[nr-UtgY!]be6p7m!QQej4J>iܣ֞lI`T2>C8 Q)//h'oW?K-{LKիUTBjۘveS]6B0MM)Bcp53ћOS"NLkB ME>-&qrܞt!ʺJ#q Ωfғ\%7Nqhbp%dpDETi˛iYd=*_K͇uM>w6n6䔭' y 9Dd[Ӧ곍T7qݍiyRV8[Bp f3ӖHOS.JImM2fnmHi(R'qr2Bg3 <1}8vԬ^yHENB"Br븮E4gUDmedUU;Vݛk\M2JQzzNem&a/(!) u-% P%Ia!!@B!!@8@', JR ȭ@dq<9 I98C6+ptj-CMPBv^ETFP듆ąRn< JRg1C෕>Nf4vջSA5˺&Jv5Z{&rq+,wh.!h֫7Z2 嬪%Lmtg6fck(3Ip6[V26679#2@N2{ rB*Bx)XxdC÷y|]}\6d^J%ҢnxRY(a!.*+FZzt;r*PWOuȰ=L˩p+_ >(*L%*v#ũO?FnïK#sꒂ* z1Bs 7ERb^oڨ8uJ*IL4T.x%8Y'93`)ddSG$c&$~p5*$s<Ͻo&}-~^W59Noz1jL5)[;i*ܢ^ NgtIwdݟ~7gow_$T(ats*}ݖmnZ֢JJI8Ș?7#S;:>] Ħv 1 H#Ҋ騚qV!ʫ{|JZ%Ց( 1gIu@hzw(uӭwE92iQڜ%JJԝ+5nZFSS4mJE$JbYmv !d㞶2V4Ff5Q-ܕBRu(8 mےb66ϩNU4RjyhrmK:jeHaW2XKVJ)v+R { %=s5k!!@pN 8嘴פYzb*5*CRnsjbTZҤi$@$P'  l8$c'<<|rv4'EuVWG-z-Zh&Wovunu}Q VOgiYiH˦dy<ޅSiT/Xsz[/R#*Ċe۠JԥL@^ @Ӑ8cqG`#Ǟ4m]ӝ3GZN? YWR&i[z4cey[6O=lnCn>ԳJeN@RBm"tvޟ&㖔z":ZU zs3@M2BI$9ft4ZmCS*41BjfIɧܠH厵|Wom*v5bj&.OKT{ҧscYW [) +wol$p3gFWCRS'|ytιu(#RtW[1i2JYjERb;RTCs,W7]RVYGJITTnV)gõ)iSndÑG;W tۨHEWDn#F˶5_Kn^>ߥͶ-)'%iQF %_[UF=d%_-yT1O 9@NulQCo&}/x0Ǯ[6Cf4Tm+>jǗ@"0=K%:G2~b%نt>Lqj #|]:$,]\6\mhI c1=)n>urbϮ)%תjʗ*[yַ-(+zJT 6.ޑU:G5eQ:t .¯-s-K)ovJDSA%Am3U}Jԩs]ssG8[R;'%@IfHR Q"2W#8GbliaQ(_T$FcR!| @g]qā<UMBצ5Wbe/Py.A8dE]]ZFjIZ땘JRU)J B8uGI5x p᏷dV&L9GD;Bjiϙ#'!V N|͘eioFF}{~~> ~4wo/a!f;80n+B*ˁBB !BB !BB !BB !BB !C;^-?Rv5S\%'_N'ѩj{ޅνI>z̪f@8dg9_HT.[HzВ\Ffm'Vy3GL :(8@1c[kꚁZ*N0FQMHɦHy skq2tyJ|<(` oG(I_w;NSq2)uHRTW06'9ጾNHyM2f89{"03uϷ3-=+M7%wꜵCa[|-4%ʔ +ݝ^ڙbbݢ?/$i$)`#>1|T@)=ˎJ>@8C89ܒIO,{AI*I3fE8*1ۀ?k>̣tĈ*#β3FDWyҤ̛>WF^at_.\s-Q>t4G,wa_Lں5 q f#p; S3i&x\-&9WO tR^seŦD ԞgAտԬ+23yRM;vQQG]P3HKC+)Pi)~rx X2{TԲ)4!,>&V RJ8'>n'7K $=RcdT:> !u]LL:b5}R =,Fiv@;"E1P-110sL)VP:ӄ EwMU4iiXi%kxL4.0[Qd˶6vmۅnoj^.$Nq_N&uJk8dxxMO0ҝ#r8uRG!1_DLB+1!RՉ~=*#ރ戇xDZ1Vb= INJDx$?NҺDWk|UC%}4"CSf b9Rl?!b}ϗ_-ߢ$)k!BXJI=^@YLpx=t/n:!_1uWI9WX48)t;{HZ9JeT/p/>\{^ti.0/ysI-($.UӴ< '$+Rx?WCS;޹453q֪7%аDt2R #rԢl 'Ri;*Gڅ.e/$xXi y +Ao+=,y< |f;%tOLvWOF5-78VvK*R=8ݒ U81cb5Do9i}tZS-Mf} %`e.=ib`JҶjJNfg9Ofޚ9#S ފ-fR. X.&W@hޗW/E'VTd7&Xi!t퀢 {iWf Jmwm7\eـ[ nWk)8GQ ՚BvzwRz)2rrEM6ì2̈́Ӭ4Ppj'-WPwQ5Z9*.j/0⊔ˠu#z9&lIkp3V~3/7?0=cnINT-$28dc+R4O݃17gYi=`$cj(Ļ_2ZhXPP$0;!rl[(fQ,=%LStYwSqLJT߷3QH&LߧIɦiD& KnVÁ[>xc/zsOKtD7/5mV\-7-Nr꘹$+Rͪz8Wq6\q BB<4z&sKB9p67֧m!'/#}M~ɷOp>O-[YPH{ްmM[dBDPZ  ^'H/XBdh+r' D9GTG)ߥ6mXNRR AXJ$%(8%)}Œq)G}kLLT_Y\NyW)NZ )Ex.9Ix{іwwױ-Z!/:epQBI#8CG>`#'PhzZsmDNsI8~uk?ӟ3f3wڍU_&-̌~B}=4ws =? 1׍^Ӄ_3 {t4Cmk AV #U6w$e{;clH9B>%K31y%q=1"v!7xWw&蒸 Pԫ4m7^6U2ݘ4 8TLӄ?+#O`|patIXE="J8=rRO#ȋ$B8YC9 )` 0v-v^Vʼn ( zSLRG*RG#֝^ K\ KYLUSK8NRٜ"EIy\*P y#"$WG8G@23drq!# kc#'1^>H=hpLQg *!$ƤR#v:=&kiU&1\A%V ,cW ;6s>psI !BB4?S\'$qNƶjwZd??yjnAtNB29z~کt#j^xTq}#7U:}ţopN$eDG@Iw]uזlkfMCkQbĔZIȥdp0PRp 8~VPuE4Үvݖ*N&܋2DK{$5mRf5 ,+32KuL;z ŏF{M:-<Ăzm-%ޱ%$W`otVuZY ߳]"N?luV.ukխEjP3W+BU5.SuJVYN f 2OvbM8Xs RS$qE=>ᚶX]35Y 1ؖaeD@6\gDѻBcecFʻ6R5';2LiK\ ZZٷ*> 5_]:eEn?@]BrM |CDL$[*,[[nܓj%KKKɹ4S/% Ş{#q?y:#dKqIי2jCjvfU[TJ tff*3zjC}B7Gc,jT ZeJ[Y St5"񓿧&ө2ʛkfTK GsHLsqFTjT5SoTR*VtaLUB&eq[ Tv0DOu]ˎ51[w}&BfNS(d) I5EK;F#{9h-=oMg(suT IeR~ w @ǭ3 ܶ[r*<"v#='STqRLԥ!*;Te 8ޗ+ޙWD.ZkVi(f*rM RIq3ATѝJXSSHZNDK 8 qHARRCSիn[-VN%SMLLe^iIF;L\[̦޷J$˩e%N)J rJɌfzti}7) XSjmRJ3=*IC.N%[VI "Ti"j[=5t"fE̫%rz %cjHV2ٷ5pYիD*J/,eej)B)90葩t$˵DRz[wr8[x*MBZR7,J(NΧM-Y %\Kh q嬄H qK;@ Dy%zȡOiZ*ꔝIOLJj].ˮ.۪܆Gၱ]Η3[ViiwT[iU$%$QU2($Y`M`nqS:m;LZ,;Svg'l <(oCvzWYurY(.VO#72TU{h'MhNѭr*v$C* eGoNJKR+P-Ӎۛ N$0=I*|Zoj.J)Eu+>O*}:5iv}7@/i*tQ+P-K;.ˎ8f3LI>xcolJPifi-qI ziOe);6()--@b(-=.V cRKrVEj[]λ9L[A%q`8RS,ERuXNP;mR$lZQ:楥f[œe*ZXw+%R nGΫ1Btآ:J^N$uśRb,ZKHJsjwI Aܤ1t_mb)N]R5.feed:e6nS|G͊ì[4)IMzNQBxԒP%%g\*8R}R~&{Vb֯=[U2'HVd]VgTttYuc77RRi}hO0a"C`o"O0#J!c+V&jpR%M:4s{{ژ;9i%A6m++?_g'n(5e L%P[$(Jɑ-EmKާӲ}^M6RYUIMTVL{B(r7A/.iI Rҫ#PleUJdQ[yնڥfRհZpl--M^֝A2o4ث0ˮ-6gjДĐdM2sd;NI59[zB;(dmy츖i*8`ƝU5m"NNr( f^M=.4YsRV(JD(wĵSQ4*M2F^i^TI  W.ԡ+$)v<-rM62jVQ*;B+ Zऒ1Pԍ&Үv4V,)fMqbLĪSN0v-j`G*TҩfZv쒝)ʾRPd֢9(.m&j'T~޹9YUARThSι*͐'1|%IX -ᨬC |p'bc\v7F&XPnV0(}Wj4%0POW!'=dO&,n;%z2 ebVjcEBᦴ\K4w-.GVKjK]f2>{Ʈc'ӷEģe6\E!$8I$l3zH֣[OӪe{~V]KP4diYgI.2MuJyiwj7Mo]43u)1Z3*W\y%ox!Ba6f.%u?v{O>>?DPi2jYI ϒ#)t-R|FSy6t&[RA iHK3y";~}z{vje*&@m!]SձԌ$ E?n%#X΄wނiHO\%ّo@,zThU$K=0ΰr t(g-"OiV]VTʃ3Zg1 5OTVJf jv) <+<N~V{8:DhXl'# k5zίiL>ZWtI>\ 2 YTG*|Xmݿp(':$U2g1! N;ʿ^x. Ӣ{Z?эj)xKBTĕ @3\ u8I\e+NB@q#8Vs=%Ѫ *ZUpSUGV&gaEfZOZr^̀,8QSۮqyIoR\:Dulpo*J[*3x*c=OE1SI[;zsU2;7]^pP]&N϶S'z囖ޤ󀝧&Z^5nrNW5io ٽٔUfBTr,G=fOE1SI[;o>'!{vr-/9_~Vz%ӣ|b~:;{i*w#m+۰|OCJ[^#x%g']:;G}='NH.nm%:\59R &R$$3} 9<~ʯi\2& (J2Gu+89GX\[<$5GF02n` o1O}Z0jA )As98ǝ5/Cj}Lz'7RmU(ŭԴ>fPp @^Ȧs!nc$$܉ UI Yu$Δڨ62u%/ݒaA)yag܄2A=4tc;sЙ:֦ cUqi"s$v/޶?#S{b> yzKK!ۯI~6~<ތ|Nlcod>?Wr?]٤Ke\t]'TwW4L*/R#%AJܑƠzw`Ĥg4;u,6 lZ!e;_퍲k^:3Qn.*4Y6-̕ѪynͿ{5+ZYo4dd@u [EIX1g1dGIKft ֭8kVmJ)&X/.ZH2N1Cǎbn?wvq岿cŢtJ" x!4"p33Gg\t q&,y+Th;zi&MT Zeun*a%%ҊJЯl[E;P}DƱJק4z7t*^Gju(J㻌I9Z9g!]*xz `X&o6Kٮn>%iPˆhE)-U.ˍ6{IAsO(ּԕuUGH,RKx8˜~ktw.kٖطTpU b8;A/MXh08=N_rKeFy(ڈP,gB eFz\t?YB=EQRMə.mzpW-5IMҧ\pNHRq5R,L Kri7CZy90q-(Ҧ(mL^ p)<)=>zUck5~~^t!o:&e aͻwwm0jy,שh޲ޚX醒T+#0(n@(3 r\{>.vJX~+JPm-JiD%X'b4<> 5ҲOSzFSԍZ)MN}朤J:Ve+K^e $ĨK{6v gև);rQMqK82沙i)صr@)hoevzVcܻVZuVۗ[N%+N^qx)SJt)%@jWKM@fhIj!qfiff R4ֹ JW]\E+!8>JOM.9L)J,"VX(djp\u; f6άz7;'AڱZ4RzB(Q,Ä6$pvぴ@GO8_w9|t3Fnjn\FLmpl#k|,EtD U\ܷ sUjmeakeS2և6HV J-ŝ-f,zKKj>ISYF- }uqfEZE} l rT*&itrau4pww}ԾNCꔊ,0)~Ydv)w>8(4>E7V1|{a6͇Z}voQ{JåL>bn૳"ӎuhSKTp$?F}z}WPJ5Je~yKZl9hAeZ^}iǒfY"?|]BõlljjilKU{Vam] .̺@e(Rc gD4oHһJeX*rޗSRg&)uD.(?) 6D0u2HCKj{uz7%hЦL,Ά0Ir`7lg6zlXy%ܡ]R6SJp)+PPڕNGG=Ұ4YVVԮJ=;s*RYgVWYӽ%JFΘ讀:|O{HU'V꿄FXF[3[[ڮV]XSaCŴI]aiBօ#=13-;mO)!jڒv'8X--pP'mKU4qɪM㕚i-!)RR$FHςz@]>GA( ->MK˖dj`J5Ct7UoE-I+5*̫M6ٗ,eJt:T%KwR+-mx#\Q:alZkӱ)/=Ե9_"Ӯ%N+<2xHS6Ʒ^ eE߶:NT|{aZmvTSdSqWqjTJSo[rXzh3]4CČꞑHnעޭ&L+iQ )ZF4(%I={Zv3r7=f[) ~]#*ҖO:k:-DSK+FMܬP'*S(u|҂SKI)[jFڟވїMҩ0rsv$\2 m}QK$Y)cy=k,QmeѫTjd %)LhB$$Èۑhׁ+(**JIg<uZ4@ǓsVA!!@BwZdwK'c[u;^-?Rv/1>M(0Q2,d'nxE|!/)R,cW}BjzTH BBR3wob6a*#Q# dz4Q8)e${=11U3=D-4̤ˮ;ښ )F?+9!kh}!tU3ej/_u^m)^n/rq" )V TZE3BO^5`)TΡ PmqQJHORֵ܉UbtFƦץKɘ[)w(Ym6*qb'^[ZO-{9uraKi+yN%)$ 3+GY$3OPym%D4Zd+Aܥ8^Z@Fl H)RW9g ukMkHgmU(-TCrN;6V[%~voŦ7i\ԍMפiƊd\*)-6l4X6z@L<|!y:3M~%,9Tʉ[d8C(SbtҡS_DTJaNX!G}jTX "<@#F5M4IKVnj&c K>˩e8uҠd)ӵ ̪3M]p%H۵QV`s @drخx#<?܊sq{ǻYsTT_}m )e٦?d1H[gb3|cVzjû{&d-;@t{FjΕГRƅk2KdOē1Ǣ_ߵ )ҮI;:όEF m%B1ܝOОz{ں'OBNb3֖HQ BF #9P/#m&-4ZiVo!7X&/)YcS 4jsm_py~ɘ)e^@g?M-WzKVْ 2 y1HԦϦTU1/{'/k=N}\HwwvRS@!\85'6CkYxSׁ'^?'# G[aze]@DPux#1S-Pj 7pI& ociGa=p'?2$*oCsT䆢QV[X~<|S/ӳ^^Mu0㎴ڔ) jJBHNp1S} (IjoJPq@>~Ũԕ%O\3Ϗu#E[8>瘪\ S>ާ^cFYq΀F:N!ØXEz<0IeoE)x '/"3d8E~p8ttw}@&I%X#|4(948>J\ 7[W I KZ>]{ JK#ZȌx1^.SZ֭G0<$ $!`#'WF>3vNhmiS8S0dvߎ DwMqvkF$s Xp}،r%w1c|:~rlzՊ̽WUn74jV.n?I'D73L h.MKNѬ y)5j@)clP,i)v5 d23*CP!+0[:$nNt]O^jWPd&!f8*9*([t+s|}?l.%8 ̓B|E^x PIIpFxv C WW1hY<3>xXǬ)>S<<Y<7r|8Ç'*-`3`rH8>>}┥* `C;G?݊`Ǒ`=֋wS#PPni7٦zR7qXE&&9Ox7.LI g슩!* (<x{Y#8cv08g2"k_2%&f˦MN2uHYu+ RvӐ r$9XIܒjm9:f&C ! ԰s mLL.+  i9RjV{<ڎiE9}Ljwq"jw9=l]V-\Kwd۔71*HR6r{Z n>_<7{x=2AZ4Y-Oqq*}m)|- g$xbg5i@x_q;Cy7G?(KMKI3.o˺maIP<##Q Cxu 8x>8v'KN:EۆkH*-*]켴-YmkaiOr4d'#>n|JɪJm6ѳ۸ jJܙ%).[ mCv,xXҎoRtvSꊦ9L(}(Kv !@   MyƏ:hhN􊹵&j?\%$VAS!w<5FҤ**ޅ9utqCJmz^TiiĺnK8Mqntd+BTTIm?v+[-nShXKOo?"7vjInZ=~綮[fo}jOR{BqKm-(ZЭh9ڠ {]g=ujjŹ1^K7Uzi 12;+.m ;TJuFq(\h[e#(]K߱ڹvzh:9 gi~^nH۳M6;م<ӥе-JZ8(U3ؚgEuV]TK&nVeșJZԐt`!h8*xcҞ3zk恽g1Fvi5%:t!+m!YF~Y6iH3CPhwe%!(DiT):[A%(^8m*XHp` Dxކ]vϻUKUQ3he m RTB $ǡoڝlK"Ǵ;"e-$8:j28?C2FhtWkS\3J:$IH.\V1%C-a1(F4tvVr"ʡT]3 萘K}i ] 4X噖iC %HJ pcv} p>أʘе[A }8q#MzGtNnf5J''ܪ*[Ҳf$K&۔\F*%c xy9$+D#=!AUW*T?Kd٘x` !e)@DEarp1Km@=j-Җn^lg/;qOL&Zny}PX=Ɵ xp mYc _һ.UaM#$SyBTHg1]:DNt_KթM-ʌԧT P^am{6񘄮6"^qћ]sW)+wN˱7&SQ. %/bʀum'`q15X77Qt6ο* TyjǪ}N9d%ZfjwZdwK?/ux)?OO;؟FOIz@ :7]Js|`֋ꤦj2n1h.) $Z2Ҋz_:Ǖȩ:r1z dQ$t"gLԉi4Zd8TJe6L6KJRT2yeE>U+E|\d$|1>r9 ztIF+d[Z%2U'ze/BNrA vU_7j&f+tU-}MB䩝Kr.˩YJGW=ϯs*3&4#EKv{E+7TRq(Vp2*VmDzX-fZ vT#e&vsWӥDrFmkrץר1d9IweN-F]9Ë KORF )Qf̕_%U%&bI9(XR!Ka* )GfoA&矻朐]INR NڔB{y# Rl]NBiw P.:,A:˿8T~%R Rw ՜\6eo/m?oI|jr2݊C"^rWeLR='y m(^д)XcIHJmrRT6-:\ԴR&kE%)ڕ%AHJ&#mbK7h_7mJ;5C&ڮHU_~f^zV5=\Laq2}='.86Vt%T2Y9veTR/S(H )q:f9\/CKkzXәJT%2KsPINTd j5:AC*=Nu,%r*-._2([Z'; i&N:tkNZDūCoHi3:w':5zetgLU3A-V!X/Kw&oR{R\r-Nj3JS)#w]xHMyPu"N75 3j N"íK2QX !^:~Z^Zݓ1fd}MIS,HN2{ښIZ;Oi՗-IOgy33.>H*O8/@t``! YKf;Eǔ)-[23< RR6aۤ&+Q.m&oJh "bb ,iv)@*k5E5m:U(i$%\ 2bt=G%d #E{!8JR[RINqʩ0CIԸ]l*LOܵEۧ\ - B|Z&3`f5T})3[3Ze^ E~%K/+;N&ٴӡoq*^69~8fsImiMi2}4i. 3j<Ҝ ǜxa-=%69D q;/:QDN4nCUhGDE`⺬ *F[Y1gl.5Nu&Jw}A?MRČHZ-) }z 'G8fV~'b*Ru pQ_ҟ?8ҟgz'=|R>ǽoRyJ\b??8})h~9ǢcށJi7)?{?iM&'8|+OgJ9qO?ϽyǢcށJi7)?t|4(趓'wNK\> B\b=-GޠJNUTe|0[DJyG-vK$dVqaX;JSwwYMPT=:QnkpBQ/iOw+,&^rVːLMH[QT]FA2ʒe/2˫'d՜D|- sV1y~\놓lb-;v*ҙSfV҃S' j)XBSĎ/?*umO&SI\Y(aN IXR҅%*^(@!"l &]Atڕ[iSӆqD˦$J\[/$JpіLt#%]BwL+Ujn+|JE23n>둇e4ۊFmbݺ+[b3]Ϣ245dY1āb[4z&W2 `+y8qG{. t3Ok6M5 Omm>ڑ)Є*Iz =[fU;M -z* u\x5C~xr1yMGg'b3 ST SʹI qHR*tH S%Z$ۋam-%+ k)XA +n &.'3cDU^x$IEy3_FZ{nW t2NKɥL(9.^Smyܐ,[CN2j|NOu+;;qyivAR|%^Oc! ^o N'zs~C$l5_Nn _~h̄ݣ*d !MfU*Z v-%*LKCt✏X8%Ɠ|oH|sdf?HݞZ;?EZ;?E?Px%Ɠ|oH|sdf?HݞZ;?EZ;?E?Px%Ɠ|oH|sdf?HݞZ;?EZ;?E?Px%ƒDlN;sxiw'])\:./NvMLࢵħgV8GCiI9/EcL%K4^OaW[/c#qaXv\ic*nXu .`ќĺ,^=xq /1yX>4'CY) pÈFK]=Rks?a)orq{#Ay](<UNK>䈲(嗩;`jqV%!)=^@1ȘIj{~Ls*TWsWOiKޡJg?su[-+p'y|3xW'6<SK WGg+ӻ~^}&jVÈhHȫJ!pʔq7nG_$Ex.Q+;80w1A>,,cͯNA?cY:N*%.wI+t-\ԦФ'OC- dx~:g}ZsdNI7xu4)K׎=6ӖH56ӭ)=`QiDu %{F:`toqbt&t_=%Y{-'4j]N y][.4[T[qK)5tޑzoVWL(L[~I mͽbs&݊S\FptV5C}tbtzZDqӫJ[Ai jN!W*+;- TPFkO'צ,G{_ux0^L 9zskF:uEa% mu$ [:?z ] 4萠=9\]rsLBJҴ!JV8`u=_W^jU-KZXse$0 b[) ?oԪZ-y19V2IF6O)'K^  qo;2nlٌjM.kضE=rVاjJS $ r+19xUz5vG[i2S( r-)rs *B5ۢb:k%5C 4pǴ+8|:RrE!IFQ&JrNIN"ekWI.tE}nҫA,j7V&کU6Í)6Ri]zSoA #:C.:c:/7ޱxmZHKM<6-mܲ@mqԥa*~;oB6*Sk˩ eЕ<LFzů4?Ddtu&͹]<0ʉzgVīJ.c wJ7a *cf7'.@:K!iaM+JRԍ5CW gflrwJBrm,\JHp+Aq IIҳ7ihsYuhL-ZS&XIQCCd^Jp3=1Zm?ܥR[ ni)I ; 'u+tT02%Ҷ&NX [Zl[:P6(\PAŵ8u;o9 C}rs_D>iFDtiF̦}.5OmyX\8X*Kǧ9:MtE6^⑰)Y eV.T]2*x6T0)HkY dGkC_hn?:eCXME+p^CE5UT3!1/'$zC} c֍DOƔTnYˢݧN,Kmot/hCgs  IVY]}&Vj^BSPh'lة쫞 w >HBhjΠtAtOe-Ҫç Y\R,$[=̾8-36em7P6W$%:j#Ĭ˩KKp:6^z ]&*!ΏԶO[T񝜛qb=s2J-BTH;+Š~z 6,P5wZ!bLץa1"Ԝ_JTPJ|Dem8\ƥLq9@ )c8`Vz1kw^?ҭC:j91O4g/kr]!†*&e XO;L]w ^LOv^kqH*S (CrJ+P V+zkW:k Jۻ],ߗUm  G\sK5 K)+ mtpEHet&h U+t)K͂0g BԂCjQT”Q?e[Q-'&=>5h ~VߖXZ*N9p擟<=uڈ"z%i;?taJV+2+3>9 Ԛ c8XLW]qwz5oG hϭʺd.GK! )ןt%†ҕ+R @R#}7i]"u;O+3J~kU:- V[*k6uV )rKPXJ~4΢\֞Yp%\w% ہ\tk5ǥ h]~r٤V*=O*mRNܡ-i %%Dw;[: gPJm9]mXur~ qh9(oܬWX1B]SmB[[9( s$nDŽ4KoMojP \Vա wjF{8F8n3XO1?s/JmW=&K[6nj YJ\NQ:(6ݓM^fRQ mw2IK>TRAHN=4-k:#fCS յiSKN۪R҄! N,*Ni.d$$$G8E&z#eY{L-{u4;{X]5J pIQovԩCjZwe^fB]vikHZ=΅6h]ՠ2S.*3qIoTO*u**Hǯ')OBZݭiLqʭ6n'[ikC8XdH)lQ< -A:R+ rlj$ ܵI*JibaHK mHQS R/DHtuDMFBӋ O9eS)խxZVր0pp1>2Bh)j}:SX2Ғ)mihBPvۮQm [nJOUOVfNq(>iM(Q.(VuB&c QV#:WX-zVϘ8pl[5F  ?I x+RI3C(/*^Zbשsr>j*Z( :cק{tEfH[Ҿ=279πxX}O}ITtM"u\tyS4ɢi .jZT6 `i<` ٺ(!sZ6kI)8Twjօm Q<pD`-eis֙Rq\]ΟJCCS.6 Ns:uX95fo[Nט_U3m&i?cw%h4cN-#Nc%@툊hg*lrT ttrSdu-k_sej.rت1<|^k,oi}rViiEAs(mhBԝ]lk ̱PJL71(Iq[PRV q+4Qf=/ q 6i *;u\8' 84qix$%et_6\"}{md-jgul}/: ESԛ2Gcԉ“'=>u*Iİ-7J?&ە<BVJHsUAU9T2ܲB."]`SR ;U1XL|Kw_K% kgž9LNTL}]ȫtyy*ze['>c;^4{Pov:Wn6Iʇ79ՂW) Y*%)Q 1ޗu:Zne >%$,J qĥ Vs"y>~s zp~3^,ZG2Mug3UmlZy3Q5z];rfνJP̳3Lk .\)tŇ wF:n Ga~@sb wT#@(5)QvxU}=OcC穕i%IJ+So[`.EI![x=Y{|$ gPKA%A8*XZ}&_6,/+2:swƜ9'R3ON.3Yf^}̢I=nӝ)~}r[H[M%cxq $ӑѤAbW=#.s`.ճǁҧM_7ҏBG"֫%5*9 Z-:[R_8׽6shҿ 1CnN}H g>|,*k|O L>Zvy-/MLnMԯ&HLpn_yI4*zEP31:DG:;HP_l4 i#18O]wMn-%FiNM-{[m' rEzHy:Y%)Qa ?.K+nNԕ$]`Tx88uVsZlmz/9giŧLa#z^C,ph؜p<@c#$G ((r0۞ԙTTM*]Jrڈ$) (q#j'j5)C/Z|qǹ5rlKYF$E>,M?-첏*OP?KFC|> c xG%㸏U|i-T7ݧ4;)U)J=(KV̴ڒv'֨P0Qk xc>᢯?8{?7b2ClкяOG |$=n8y>2OKv;n8)%$9-o "BN(QFfLcNO _>fM7΃z422 HF HFvێ [ Oj]6JVZ@pψymoYBOăPw_D|NifeV āϙ%#٫؋:KL7a>[\HP(Дmh`b(Sp01 7 cfA_0<ڟ|z05>նXS\APm2! n8Ԥ}C ;INJsp2$g07cmdBYp xvqGߒTOb,`ք/i~[ᆃN8dphu)9v>IɎX0, `<1H/'-*U*2;Z@@'ǔbV>jld ƌWYo(*[ALچv$&- VI%* zqՀ+'0YRucv]S./4nY4lr@y1)1W:*rialԀTy{ GӫFr88rG ,'6n0F1U.ҙ(ZCp#q쏾"uZaCl4MHJlGbFVoffCn%%rT #skcE[\ m4›IqRV QB Jsc9 q#")Ͱm:K0jmgB>#rCm)JBFp9W;y1] ҳ=q##9<R9%rMM闡ןH΂YWu̝ͰRUZee {mWxJV6r'=3)c2T]BJ78KAjےp2I#= e^ps1HCt x?(;Iw yNCrlq~/q;FXOIz.Zmˈbid;#6W$}#/?98% RYRyNZMn];SeQڙk ~[e.2WpHms9aS?ދ25e*MM̤)%!X$cw4~ AVxe$A?90s Qb9Ǐufj}u-Kv)i)Jp74B=VrTI1%p7g1FA=GCם3KP;Jfh nsm[۲lIjDmRo;A}cE+Dh⒴c#ud?+^bU?/<ƒ\OKw`ˮx0Y)-x%%<"j\*p> y` #:l'L}c }P[EFM2ӎ9rePq5 Jr k۪ɤ!VO#@Im*2y#uBKc @*Z5_=:m ^ CBVIg5?X6V󈓘Q>`}pA(goM"9je=/L[i]6\WեEO XԹM+)72rS)5.ZX[XNHRRH 9F[zQ Jӽ:E6$ڜV7>\y{@'@ JR)N 6;NPQ\KeG q1F`KLiU=3L)|%Cj&q9C4ij!F$<>;cXNTiR:JS4K~i*:Ĕ _9ǰdkhSWv.BORnue eJHi$JFtlI%o:tX-b6i7V&S{ԦJ Jgt85FFb٫%KͰ (l_^pI åi=֏ҭJ*2lHauU>j.6y,ţjS#(oRTէjq/ԼY}!ܩEx9\ פnRKӛ&HfQ^xٻGmP^ScGH*i"F>Y%צW*S1Xbh0S2XI ۸nOȦ5)ىdf'*WFvke0}n%mJT ̮]2.z&G%}ݔ[k{y3%䩤VHjӨޏwmZ̅->>JnRz~yBXp0n2=#xvaZt{“_R-Wн#}3.KzBTn#vG>'=&WE70ԺS a)DvqXw}zsJt1z_SĴò35Tm5,i. @dZMS j[Ƣ])E60ڂeUPam!Fa ~yḔ;*&h&je'NQ2ܳuPP Ge:Y-e)4Tj-PT“6_ pq׽6W4JԑlZ$J^nA\ԔTa fL|A p}pLC/~17΃z5|@ߨ_B7ߍ HFvێ[B !BB !BB !BB !BB !BNCrlq~mx?(;I?H)8OSw}J 'w%AIi;H8#I9S!/IRRp8G0+1Zʝ:LLL4: }R8ŕϧVUNR3 l⌿.ٍj&<@ǂ<)avwYّa jt<Of1Ieݶ,LhqgnZDijeiyp 1@Nj-/w?Qv^ݯ:=SKY N)ygB JNB$kFkHHVkש/):h)9DR IĮ^NV+Z7DZ.|R:ܘv_c˨892~Gt_NQQgSHOU&8:̭imyK)JN4JLz=.=Jo[MT]>W& ˸i)XBqvmQBúy5CҹydRfQ3\{z¤W{eS2.%$RYƞn6CNԋ,:Âj6TIN§Kk-"QWf$6nY)Kؘ3W[3 ԖVs`m;Pk3q%88'bRN8|9 **C [!V'%3iiE%=W;S᫏@n%ߦJRܫU'7iF6 Ē1176ۯ4S%J9>? ?,`aCN~E;v R2)Mh|>uKbRWQL(/0ZPޡ 󏅙4K79BMMRԩm ^ ʎ])we0d*SmmhZI)M3^QO#(w/T@)y͓Y6 uPmZk)jb:ܺ{@RĎxD.KJJo$*J>Xs~F ] c)<9 rr|)<,踨NuilSQrOt"QnRq PFxo( # 9偐@NpImVGJJV*2tf* H2fJ7>iAqjBJP-w}h3s%շCs*XmKPm9Z\RQq&cj+v}I) 8L:e/GŔ<{qFFI#HpoyR%e%O7$Kˠh RFN2N"2vm'e|ԇ)+f]8/Ӓt`=)e3iJ7czG7Q)48DHԠ҃,J< v$c 1lKZ>}2 v :D]r\+dwTc˨#+YG/s<9hBVR 8g82# .Xeg qexJi]jpQHM"T#8sh_ Z-96bcDŽO#~5kىoFF}{~~> ~4wo/a!n;80n+B*ˁBB !BB !BB !BB !BB !C;^-?RxqxOq;۩h~.QwW㮷N'ѩj{ކWĀ60@L H(GՌgsȏ.@S2ZYXyi!%4'%#.~-%OG1xw1۴s0i-ZJSfKreYH[NZzCkEsKu'L(4˯Lj}陛^OKe[Sm6f]P$O68bT'K-LE*ZoD}sa_\m; W8 G\V٧NHА*TKfIOvr^qJސr z_N虢ߒH[4džu(%eBh'1l‰}>ytt!/#c-Ӎ:!8ړ I2ry9 q*Brxq6#'R7߶i;n b*rLV_2f8Nѽ+RBFcN*UK@hW*Xڨ$r][[gc)aC#7.Ji*<gFm47_&2IgBNĦ_ KJ 6‹SMjԥՓ%S6{.X`8-;P< {r !-u6a58)S TT~H)@)I(YÎ %3iSM(*93&AǟCSuBR!,xgxG8'iH[䟘ؕD)y'qr]z.첩wUn%YiHl}3Ǚq&9_Y:˔>a.I $xuJ%H-&TCa F?pǏ9DUx02sOT'ּdY[ 2@nrEɖ]Do4i B)h$W℟ #<;qH-,YfLˈ) )бcw $ -D2s ?ߗ^nV}*TiKAJҔ-)RU#(cw֧ˌKHYsq;JHG#Lv^q*[-K,;j|wic{EH 3V'>cdav1]ʭ?X.V@=rk(8DsߏG"X]^v.}Z=7dGa/ |̔3RYuмxxJSV̝",ԍ6Qܻ ! gVTrwI*$UMr*Mx?pz]zW-׷gɗy4c䚔X8-jRh\ToycR8cQ1rˎ<v0` [ޟi 9E}nc0@# xp,vgX+eV5Kv-U\& RuN[MԜ)w~/Nd,Mlt9L\L{=[J5RQ#~Q;1'2>y1nD09 gdc<` }i;}irn+-jfiʥi5$Ó`M8d֡ȘOs|%+vңIs`UMҵߺ̄>^IIe.>@tҽl֙.6fTwÊQeiz[|rhZT7$5N~Κ#oӝ+ee&} '%VV}`߄GTl&(vuO5NTE&eL򊔵(BTJ87# WN:?Dojevwߗ6?#SJW4NKT20TڈRHV kwQMǭ-VWl*]nrji5+vP;;DuIu+IOJq681BnV@U ۞}'usfHuo&Vݕq4=a_)Wsr>x"7]ܵ+eCLV圭12cfջĥrIk~޸Ng)S!\J^hX #jO:3hC#N[sWS!60J ޥ)R+q.ʉ9z>.TFYĢлzҜDpSFcf]$OEiSdM1Ufdj)mxaJjdžnbJ6e*vUƦ_mq/2 ck-)D՘ideiR>,BfBZrdN颮J>LGd:PTRdBZ !C`o7XO ay@aZa g|[uF^FEFyƈD8D@Iß?!V N|͘ǯ9m&!V N|͘oj1n#>߽P_oo?;0w!e!!@B!!@B!!@B!!@B!!@B/ux)??~NCrlq?O^b}Pa="pqZ;^J}@W(O?Piv$b3,]Dmۻ8ϳ>QnLyRWQڹ'+un.SMBZa)q)K(NBQ**Q$8'Ӆ1 Exm@(O,0+*7{˚/I eUkM 0Jvg|(Z*t9 ٖu4ԢpFI#1p +;sg)` '3!@B'm JHq%$$H =z-5%WB|" R 9%XᏌKSΥu6qY GRz|o xcMɒӯ&a6WU!9cHyF=qKmN8q=%uyP[orHIQ)TNqԩT]}k.nN$+nqYǏrpr?{wrrmg$g2wx 0cfr*QS)iFYX)!ͩ>O?*Fpk凷2@㵯<^|pح=|>S:~zA mߦ2-Vfae**{{ZvC>!B.Mmͩx'bTV9X TaA>o *>l;߶@"H82OGt)THe&2I+ xڳS3VS+}ɗzbW^7-L-*jN"şd3;v2x|geev*ZG =ڔK!R]CkBԐd%I$d#4tNJV5ao *֛~ZVNu9OXQ) *V N@ ޔXdOɵ3\.XMK0Hmf;Ԭdbq+-Zh᣽xef\UA5W)%slSctt)lf[vm'hSFHʾ<$9*TVܣOdǏd);H`xI#;Æ|Qv%z@֞IpnB멪$2f;5;ѷ|hvS.stD4JA\bp!ٵK:te-  kMJf6̔ufbR}D7.CBX# QUv闩ܽRvCT'-Еvl6b^Fslw' ಓ`r1{ YΑy/K^^4Uꖤ<^oRT7 1zH)UVSRfS=74&(pKuo ` 9#=s;IH)fl)ƤsbIܲ q"Dԋnzm?LT[BjbMM.6j <(sgTߵC8zŒ׹Rm)"fh y6ªy5e4 IksM:|WKΡPܷI='~Dx\9ye朖u/%hPG;JjSR'i !\x r&jB^DJ.u<.Z@RTn R29-:G9'ҢJibQ)e )--([^ g{bz嘣~Tt2̥kv(,%# 1b24&M[ē3yHՌ*8(~H 0 0kw$$E84! [AP8yI}"Vs PP;F8P}w8 @x!V N|͘пhZslosU23 HFv{~~> ~;qޟ7y~_+qXBV\B!!@B!!@B!!@B!!@B!!@h~.QwWK 'c[5;^-?Rgq<(_^b}Pa=&\.:,Y)P%;qDz>>j6x)'R9}g`[WRTO |4FxQ4V0=E~a,\TH9BLﰹB‚Cu8֥wimZ5 ű]745E.&U(SەFOfcTyo륮4TA1؃N:kz)]rzmgnJeRq HYA;  bhj-BU35:uVA$؝i԰ٚe@TA]ū!Zve)BL̳/M9(JYpas bkOM"XL$C058yq*p$$l!=Lҋ*mMz%MNnQtҶ伉7_<̓n.kwU>BmOn&Je.JO9v"[GCv܏4q3ȧ<:zw |2]okw=&zvsJ6mU>K$ L xl88w5dz33_bEju#0{!EO)-ˣ)q+Q0h)ZG8}="ֳ`jk/UnDgZF ܐ W?֭CgGe-ꦊU(r4eٙkg0 {IJOXa$ew;T )J*a‚9&7`0RpxdAuiQqn NTp@'z 4*9 -5Gݕ)+qa릀18%{ *Yh0jkV/sib/Sm Ize9'PLJ8en$@PJGݸz{2y#<3c8<@b8B!|Tғa66ډRNB$$^qf8CK-ac|Xr`-1&ûٓmCRYHQ!AC>)>T6kZ-d R$B]FRmNt%'r>#Oci2R% . G}) .`cke%I9px>H*]70-2R!)ZNH Bڂ)I=>Bμ* V7 9pqē H P8^8Ʈ#oiWTweDZ*J0ydd_ۥ)νo*(TˊNH`vp"13p|xD?ƑӍ#-#a_%El0&RaD=zFsL6-AG ]Ӎ#-#a_GZG¿GM-I^U 3.@ڐx%93L}3%J,-J*aָEA)<8{xԒ]q#'3Z⛵/ E5kTyӴׅA*k/m/-%8A$^įG*&ܧIeZT 2ə)[!@ BObDK}Q*J)G#/fii4;&v](%t1SfA8gڔ~Un$GԎvw"VwMghu)D-&^]!T$R]68Hx~IwgĜ}Ӄ@ nt@ҵY7Kc@uٛ}Kn\]*AkyJ*S:R(GUWӍ. #"qJ )*Ns8s8#F}?yq'9O<^3h RTU7IZfTԝVVDcޟq7@&m:%f(08H .%TɖV obFVeJ*W(>/$qˀ5-kܞj7 zi7LD\ {oc$%ю$Gx=-@L|H%ܠ<>@3jߠߋa8w#ɒCqтV>9 <ɊE.K ;Q 11) cZ0;bԪSn5ʄ\e[RB\#8{F8D!BB \"~5kىz _>fooG5_ʋs#>߽P_oo?;0w!e!!@B!!@B!!@B!!@B!!@B/ux)?OO;۩h~.QwWq;FOIzB+TS[/IQpy|3\R ?s_Hs*4BqeIpT$ ň]LmϤW8G1F_J]GՄg&=Pzh*jۜ^!.| x9rs<$%^Jmmm~XqN?# msi{q'Q3mRn#3SHͺV\ %{SiC 3Ig98q ^BՊ6$Y594i۔<8yIRB !BB^`^Qǜ<9Ism˥ǥTa%6iIQjKSyPn9!IPP, E3Li,%3Nӻ̻&qliZv<|]qTJ9.+;HϮ2ȩN:z8ԴW;s{08 u=y0RrZZەg'W5o$GsR&]o4'jc ''?,[٧̊WV]zP! \ʸ DNZ*^ _eKmeIЕ$'v[JQUĂx'0^˸i<$`uB0{+g2.IJrT=Fp5 ?Z3Fݝ,!eJ@QkjUO; $C?"  i5ĢS.'=F5z"R <4qq܀,z7|7j[Y) u6e;R2@hiՊ}.%qSH*' [o)M(nP8S7WL 흾L>߶\ ~n)BX2m<%nVϬRBMJtN\DbKHgO hN,_nQc*>'9=vpCRBЬ+[OO8O`R1#T6ߚ%.iy/\4)(i>oG*3JeƜ*m"d٘iFq`zjxJrHz#XJ`| V`qB3e# (`< <!G=$.^{4,Vȩ3+06nfQmsmmi'dXWrW-kj[UޞgM4,*A&Pis%&\,:/+c%.a#n0q$8ǰ{8(XZR)PPA 9|X1'YU~1t b\-Tw?NK!8 # ݏ992M1aU;v.rƤ*䴳J6$Mm+ZRsaJ96yS E%Ԡ⠓vOi9$u)UiS+jQnnB$uNB ڠHVpyƞhMG<kLLp\iJ /GW3@ NADًǦL3Kv,QړCIueIJs){$sH=Y7 \ ݔ=Ncyu^56b\թIeM^LMqADICi-nq`9THS޳%?Ewis7SZzvܜՆKEhq' }O|-i5tWlP K*S'\u"ZZ)e# ss%~`Pp=\ƗJa$ݗ߅Ϲ >=_Wr:7wޕLnVⰄ" !BB !BB !BB !BB !BB4?S\%'_)c[u;^-?RO^b}Pa=&|u@?jN$&(g><# .,z!ӯ Qp{@=8Jq՝q'%N{Rb/iv\A<-|@rqEZ|ե5s&q\.K2[&RF3ʻ"{RnyR_rJK:H['Ƅ3d4jLLMS$&j:|$(R׌(6 =vU׽\)Ik}L+ڷ`4i!#q^;3Yսv]gn9;"v/'%*ڹր4pNUުkEz)3nε).ʥؗK/ۗe)%\b4膠յ6JR}@QDueU%n6G2I$ɩkILf* 7H%$rZURy&sJvTI:Q/ ]$}舛DkݹKD&RJ}=D;rYn5(ܥm c֐ڄ!9``vz]DUXFd[.-nzٍF` +M{:TONShvRfeL{qD;z2RvNaB ׹|Щ=%%H~a4ܭ5,PLzσ/3m9Mv1{!(9GaS`>S Lf-*=֦L^;*b}.:qD%V%NKmo3.9Եm!@(6=k(jԷ+|ӚcJ԰v$a)[G yc' !E}<"YFx$Æ$ˈ`q<! Gh$yw%JJܓ>9xC@$(cDycyiڧ]#x;=g ,O)~SA}-opN>n$KN>n$KoRWcd~9g߃[V$m?ŖUJ6ZЬfjX%*\wn=/HԝMziw=ʶ[R8x#{G2*u>VSBO]9󏄼vVEik.n][ 5(60JWÀ1T/d$l y8mEPdN|,CeܧyYМ[L<*u,Ef/|`V6ڜy Rym)@q.<VZK'hV7dM̩)5V)!mC }Ӟj~h=XikoSei+ܶT7@ې 18<F PNyD0VҔ[JyA<&1ks[q_hr+3K~,]/NiG:̳M /"'18>8`[x?Gi6lΜXON5:aݘeAM>J7Rd 񌊃iַڷ(v[$~PO:PqYV@KW W&4 oL?x8G^˳)ԌnI[@fmz\%#OUx;INpHmDэ Rߩ-ӺXu[.0t!֤J!2Yqr٠.LfUU=6¥ҝ”Q2@%{J鴝\uc4'']>C\)^-%ŭjRFTI$VtK.:2ӋN\RIwyR– ӀD/@K\2yz-1p18d%J̺W[ Ac+ej-QJn^s]$=qgbweFҕK] N™rL8 %*8ZsgjS]Qb Qsr*f[pOT$D1}ݣa=M/~ P{Y;GES=@QI3Ti'%SOVg姤e mm*RH  hV7>%6T 6v-N+%HmC| ͦYVm'NrOgm9rH>_}rѭB˵lLܿVL!IS J$q*D_KtÍ AX3n<9q|WM0#=`CRRC9j}ҥ;X4I*Q ĒbI C1sxSi~[sYݓzReNLHܬ2Є[Hʊqd2GZn^vܛ::e+O^,`>щ"QC/~11\"~5kٍ: Qndg;0G~B}v>nVⰄ" !BB !BB !BB !BB !BB4?S\'%Y??NƶjwZdw;?<^b}Pa=&e` Q^~ $1*(ӺN2d#c*ms*W$Jxse~9*7`"<  bӵl].S b?)T^K*A8$. xC8(x$xxŽsǺQ-0&`6 V mĻ0_Bӹ!Y.ȨgqbCe9JВHχϴyu+Rӊt֠'Ot6 IH8 *<H;!+#ˎљT^A \iΰ),Vpa)8:?0D'mjn]ڛP܄@G/8F7pÏ}m$eՏw\S{SmȄ`(VB W>pO@A D@Ҳ9@tbꤡ˾ٕ\EIrs(2Ran % a#$KQ=Ju^Fe:p֥:)99:M'; H1wUC-ZE( zQ̌3;ql9JA1ӽKwP-6) u/2IuA)W0Y}%ƜTP$)@@fݷ۶$A*:ԩғIgsRf׏^ςrY =ZԙM~ElӯNͥMXK(2u-rQ+-:.5L3n)kjew[ܕeJuYY8RYwm6zBYjTGUM5O%5ۺVJc٪] fnF}tkyte-wBJAQE!}(41t|ܴ8sү;0fd%KS @wvٌ‘{BS&鴉fi2.)W<ҽ)KqxHrIϥv5Ưԓjt 2˶6{| V#6z~I»I-ΑضrبPioJL9Z4,Ԧ؜(P*NsrZ|BGi%,+>A,ۨzaTCڴ %Lv]\RAݾEhܝ$%1Uwkޝ"q˦ mL 5*L!A 냍;23$֤:W'Q*jehQ*L\x#ZйB]S iߚYʻ*vFAmͥ]Dv] q+WvʻzIVև :bBQO>yǥ]SM-)m"]IuHŤ[V6_,CioطUrWo xV$g+3KəK1$T·cr&&L?䠜\GڠU9ĨOrOSRKaHtS2 qSr]X*H#nxtZ _֭]:qFJeI(rړbqf0մ$wcbZ+%JRFG,sϏs0zVUmi5r2M<%Z^W[N}{ce%ZG(AU=;G9LjH,g1 B+b 6EKG!CT%t> Ρ$pTq$]7ӭm .ͭSQyms/5KJ¢7XE+ri W9G~^2RҨg'7y=P^[[N!@$@bjuz:lz[&ݚқq̴g~(. =3.M#nu^ >ܵPoQl1΄kKIbjbfӺ1Zr^m(S%me!+IRBx\;.z-r}X:Yj}M7*Դ[Rsh#1edYs7%rPm+v\LU+3JBp؉5Zzjo˪>ΙoVhZfaZIuT O^-[YM5 (5US5qeI bpU<Fȝ%nU-NuϮ]MmOa-:"sRթVu vQKv\ve!Yi VJI)#RH5GJ"[YU j5JL+Kҙ~Ԛu]&RrF b #E*)OLfd촒Ԗy(WXi@R1CZ5UPjW5ɉvz Xucn%x"{WCSkrWbi !}Zlgsad%JJU&5Y4VRg۔V+ײjSu|ROXī> 9Pc{֣MNiơUFd캦!4ӬN+qhQe6QO4ծS\t*õ*gIOd%=ҍcr$drz[ 9)a@g##Bz4tXmKDɳrgvK3 O Im=f3Pœ[[q"1xlk^]NTJ>i:hq3dJIfLCh_ Z-96c}7΃z9T[$#x;}߽P_ooO?/!.!BB !BB !BB !BB !BB x?(;I~?/ux)?OO;FGIzf:,4Ω+pOGEU拘2)aqTVOsˇB0Rjyp'wx+(PTF.||@S#3\yYVTrA#9p N2rx9=3EI$"G,H@ 0x9Wn|t;+1&ӈC+ IZ* }p p_kNzJJvq&94q zxd@@p9<=ZV\I~1R4Ԯ1 0Kҵ KiZp~}%xƽJ꯳oR4/Jװ+/եk+mL=J꯳odfs`ǔH1±rWYLWjaS :L6?,x[J#MfooG5_ʋs#>߽P_oo?;0w!e!!@B!!@B!!@B!!@B!!@B/ux)?OO;۩h~.QwW/yjnAB2B !BB !vv\8JLV횅%/RT%Nq%6ڕ$GE9k]_cM&տp*vg>BuwS_ri(SԶJ :)D/VΞ>.a7L$?{jF<^PߐgD][m .mT,KT)y-j7 pሗI;J2}) %3HPaU7(J/i۸8$s:wVt~װj^[5]LMޖb^078K3eS)X$Ձqy<IOo.\"% d<8crv5=u"VQ([%vd䴼Y-IC3,撜)JR7٭Gvˡ[Vu6vQSW)OLB)aBx$Q ̏.OJRd'%$SOKSn6R4Xn|ݒVBz%BVEWfGdÉxvjxcyX5J5jzkťh;kMSf[4˩˂UAix32T6=C)j.'/N0%ma)}Oc˓`3k{tِ\8+Tm6)pXbPKȥN"i.9OXT^KۖiѯSYw5QS-7O @u -%q)$ TtmZFVOt-0j O @5EhL5s&չ#Ye 4IGL$0+ vr83`<)C=񚴄4LhBPJ@8r<\1Mt;N՛:zRq\3RӂZ]sCDV=a޳NuBթ)v+CUa.595/)nuiZYz81V&%.Mizhe*UkKQiC{3PXlf7ݦV\q^IJ㜒%xDe2W:JTEP'?g-֨o?Ki֥');Jة7*,Ti\L(X([f[ԝsZuxq/n}QF0 R)=0Gg_kTuykzC^dىm6-񸅧K#܍@f5dp>f8N(Waʊ懄x {I'7e#2Fӓ*#3?cfXfJi﭅fwñc2$ @:S*ZiEOڒSTk2.iPZX zqN@ȍz#'?" &l56׷E'+7$*f%*CI9b "vэ|{L_V9XMOəS2!n(opx}t UOUIhjuԻCl V$ YxLj[C**3%2\N2ap[v8H"Eӧ}3)Pظ&jƦ+f&++*`,rQ0K{LK #S?f5Qfav_T`'qց8p%J@ǚB{˴/[rvԍ*e_RL5oQlC{ڰ<@Vk5ۂVU^ۂFR%;58.@BӐDwz*i<Ăf*Y:}W[rvU%ۋoQ00cZ.B =c&ij%:ɬLTzq7a2/KRW9<<՛=i=DqťN[`I8Y$`Y-MZkf7K=?UqeRW(F.t{O ^ZWq씬oTp(PfiG{i!8 ;f0i~~-52JUnz-n;(+e\Ẕo%+m_ ZEI+~͖w(I;8t?OLh!*e[98[wڕ*?MjvtL=< N: ^GŶw(7%jnUXORAE:Aw%zVҝVA^vU=m_N圧M[ԺjqSu Zk}n2T{m'zBrBF˫^ӇhJ՚#;G~zRzqK4ɗo{ yeÒncx|uJۓoꢒTt52e!d%9,WVl;ИW[^;;qT\|ʺ^h78K*݌6"-R[m\TBrIjtT̐o-E)y+K{k8sυꋢ :^1TW) !mx'jZ<ɡqc"]4]HZ=v\,-M R5zǁn[q* I#c[}ٜkg˦I.av0뭧rS!0?_,2!0?_,2!0?_,2!1ö#Rt1%4-K Jx@' {SI0N }08s5 ybQ \v]J&2msmSe%|8- +ֹo1] ݅U4ډJhLF)=6/TJ8>Gץv֦:#W.5տMM>䤵X-82ϔI eI۝F Tˣˈ֭S5CP35'j5SRir ⥻<~3fj^QV-Oh(P{Il`jwh$w={Ȏ1Ƙ$!F$D;Bjiϙ!V N|͘MގjF}{~~> ~4wo/a!n;80n+B*ˁBB !BB !BB !BB !BB !C;^-?Rv5S\%'_)bR 5C*!e!!@B!!@B!!@B!#Op8r}ܒc "B?!舤 YW-rÄqC*8g |`! x2H,ߊBZ^Ú@Pݎ[vE!_{P"9 $I'<2{8 !`aC +g VJBTw@ǏG@U5e^ysRs&^nM͂c@E (U{,~v2@U{,~vUqʡbUqzWc*WaU^/+U^-R.N~&VRVӕ TR$!H1#KJrj({xn`t‚w%u ֕m!C."2q0u)IJ4%FО@g1؄""QC/~11\"~5kٍ: Qndg;0G~B}v>nVⰄ" !BB !BB !BB !BB !BB4?S\%&@KBO/ux)>:q?O^b}Pa=&e;}ȠNAYLc77Ljn II%#?Fw'?qF_9'(pojvSuON݈Oݫ9!.[c[]Ɖzj-0[RR6~C\ v56#U˝MԧSw~U*.*ZT}mHkjU >\Eo?PJE>e6g.Ի%ĥaD%EIR@)x}DVWU3)+FRQQLrNe9Kx9bJҼ(K596줽92A*uC BVzeDMv<1yjh>y%Yu qfCRF3{RSEnMHUtiJdKeKr8)'8< RIkt*bzV(Ý2yZz\)!BPY7+w_1UʑjA6H0O~J?8$"(k[b[RUK~yDBèeM0Rx}i56rОhrSz~BIC] ty۞g$&C?BSҎ]/M m&8ps":ꭕ]OUi |U5jf\YL-8XBzm\)zgk7k%0~EYioQ2س!;RLqn;y9uIT5)¾否@jRQwͥO *!aeYO#Yxwf;]ߧ|kA[ܳm;;mQqJI94%t8BPvyFs^uyZtnࠉU:":gDS-d0˾ ޠm-xwͥO<ӫ]0^ 4ň%.JUH0.i*dZ.)Km(K6-`5jY7 hWu597?/$[gi rWnrswP6<;]ߧ]ۮz]cjRz_DӌLʹZ+u J(n#Hϋ0ޕrI֖Z[VBY[ i$ ޠm-xwͥOydӹ n֦Fp(AĻ0mdx1uu&ϻ5l:3=2pSANR4%-(g@.3{Π64}*2Rӻ2( NʶRYNNѹ@1 /0CZCUlF}@ xho _>fL\ہ"!V N|͘MގjF}{~~> ~4wo/a!n;80n+B*ˁBB !BB !BB !BB !BB !C;^-?Rv5S\#'\13oq;FOIz-@Ѻ!,H.79ځg Q0O `vF)))$p99pIg=+?Sb0dWUmU$jHb8\V%lRH7Jws@ҲseܚsU+O^ gg Èنai) O11q<h%eFI[Vf+vSܡ lZz@IJdx%)tce6f.# -m/Xإ(zI6%-m|xsg>r״KSo{}\/XʢRi rK}\i;0b6;@"K\t3w#QK3ERdd$gOW$F'x^nQ_>2Iyx tHw :VRi9;$&%dueemZ!N(06-a.ZGuKSN'*o14%[Fœ8I9ԟ\\O,|dPʀ*ӮT)SmS2{ꖝQD€l*⓰`/jpI$m}2KJu$Ri曔_P'̒[#$px1Ð#Uܢr2Hp%}se'.wwy 6*JMU,i32ifP\V2QcP'<0pB8 $q9yf9dxsx<` $4KZR-ۢ* KE2RSLԭvUh)C[* RrȤ*O]nV)XTjL^ =[Bdp% ;A)၃ p$TNNNxxIHKPչZ{Ch=4#&0ef`%Gk,s1^}zUoFNcJOjRUjjv\* jw-J 9>ʀ 'K Op<Ïۓ̜>(t& Qn6W:ubWJ/ t0O!:R4oeyUr/.Q +2AzRFĎOs*<̌`ˉ5Rb^FXR߮-ڔYܖ`R׸ 7CKCel5/sK+jK:HKΙeͼP .w9l;e;3N)cxcvx1UdHJyXʹc$"׮kkC:eWon֙Rn`%S ЩZPO=ԓ˄fzHՁ71VNjj{fA% Td&[ Q;Jkn69Qn;Vj+Fդ'joO2AwQ֨I)C+ ; ^n[88ɏ{@T"9A}R/vDKI\RF׉ʮJ F'(?!<{k.GJ$$m1{f%u '%璘+`Z wcx9bj R6[Px%D'DCFt] /뻣IYR\.T*L][wPNvN[i6-KERx! $wI1"ܾK-JZL"Pxyc>Pljs ML<$:ZҌ)N|CK4/#Q"#-kUtj*Ti甠oL:jp*P@RVԨ86+vA)>CZ.OZuGܖޗj֪<^P$!MB*JKNo))9}]!-5?r!=In6Լ>[5w.hڞq n16Y;* Rs˟yy 8Y]7NҋΤY>mjL_NOSf$ҧ,h)Oqak!;ʈZr&[5N"%i?QjXJ {F%.ByEi8{A0 ze_z}%TDL9@z&>aٟ q5̔ %/%?3NI}fɖ.jiB&B1ॎ3;i-pr xg8ٞ89|ADxvrq\R/7 <{#h ߹)Ej_gg]SM>@'zP%aM'13I!^oqwL۹M)gݒVVl>oxBjIV31Zh)vN杫;&&ЪZ<z:ݛ1Gu_#vzNxܘ8pLBoZ\Nq9ܘ8` rh%VUK6Zl#wVu%}{PmJRhU%u'(bFܱ[KHËuJoܛv_YRhك̧zACe~f)Ka{(~Y!*QLFG>zACe~f;X^)~wʰS?,ACe~fPi1Vg埢/e?Dd~Pi臤?ZGh sU좙g| E3?ZGh!>a{(~Y!*QLFG>zACe~f;X^)~wʰS?,ACe~fPi1Vg埢/e?Dd~Pi臤?ZGh sU좙g| E3?ZGh!>a{(~Y!*QLFG>zACe~f;X^)~wʰS?,ACe~fPi1Vg埢/e?Dd~Pi臤?ZGh sU좙g| E3?ZGh!>a{(~Y!*QLFG>zACe~f;X^)~wʰS?,ACe~fPi1Vg埢/e?Dd~Pi臤?ZGh sU좙g| E3?ZGh!>a{(~Y!*QLFG>zACe~f;X^)~wʰS?,ACe~fPi1Vg埢*5. Ⱥi?ZGh!>aǪz^'ya{(?ZGh!>a{(6mTd~Pi臤?ZGh teF=TG-?ZGh!>a>iE{؇$TO2\$ -#4}HDwʰS?,a{(~Y##HDS-$;qc/e?D;X^)~)%ԛU.$RXcqcͩ,@Vg埢/e?Dwsj~Kcͩ,@Vg埢/e?Dwsj~Kcͩ,@Vg埢Rl2=TGݟ;l?%,`2E `!VU4ѐAädM(0S)B(BB !BB !BB !BB !BB !BB Y'ΟW^mE"A!@ ^"t~Ѫ7K5*iȔ< NIDg'IIdÌsd}71dg [jn꟮9U fYo7KY<bE1Fm[\)V $~*8Gw ͑D'6XQ NWG6ʡ88 P%\ N{}; )LgY9c叹^q'1mQ+[-гC$6pKri '29C~ 莤 yޭuj뱌qIǎ.[+ňpIHȪԜo3,ZJT!%I Z.d&a"R`u5TCjWYQ7 "S/w YȨjWDR BԶ~H`54ls$$I#(Pnݔ)zo) pNP!)v䍇V08BGKfP3u}BPwP`@#<ĪUxX6u3Qz]OwAʓJǑy0pi_>-<̙ 5\/OR=6ZL=@ɽL2J3}0^-d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:??9~O[d:?,7F;\=Ѫ2>J"r3$(qv[d:φc֎8px!Bk.Rߝgkw+ݑy%HB$ǁ$p]4 &MSa rLjeTIO>!2eOek*RI#>1\c|ix|eH-Cdq.g]G]d6TǗt=,)hzYr sa]w5ե;\'%\X+"eH-E 2f[ !]*,wr.q=U#{hI?$b0)Bւ0SN(FG(\VtyFXU>zQFg`ZRB%+RH8ds2}dE RjRX4'%4$r!{C֜[Xk2\SvҚeRī-x '$x[_Q7ivQ=YYAǒ=?3ݭ/(8QPq\06kafvI7EFv6a( ^ܠ s:AX "A%y g/Q7nm?o.D.Z+B81NlNyK,R仑^|56IP%E HݻIY s=ZS I$/>^UࡴWYHtCqM4$*~^rF`InA~qc*q-4焼h[kٯ檎&1}nVB*BB !BB !BB !BB !BB !JGhۗ֓ܔ }hԽ9y[dDcf0A&jgi:+_\}!4\j\ YkIu<1̉;ŦǗ_cwMOm?T^cwMOm?T^=DFscO6(sϱΟc~Οc~^Û{|_SgO1?WSgO1?WPsa͊=/n꟫n꟫Ǩp9vyWyy7tuO7tuO8C:>:zsg{lQ~痟cwMOm?T^cwMOm?T^=C9[ʽ6(sϱΟc~Οc~^Û{|_SgO1?WSgO1?WPsa͊=/n꟫n꟫Ǩp9vyWyy7tuO7tuO8C:>:zsg{lQ~痟cwMOm?T^cwMOm?T^=C9[ʽ6(sϱΟc~Οc~^Û{|_SgO1?WSgO1?WPsa͊=/n꟫n꟫Ǩp9vyWyy7tuO7tuO8C:>:zsg{lQ~痟cwMOm?T^(z7t)#֟uOHC|- _pTqSZ^> ۯŚѻIj=t+~FuT.ȸW)) RS ݑO?󣰨LݷrDeP,9Bg@!!@B!!@B!!@B!!@B!!@Bjacktrip-1.1/documentation/img/jack_routing.png0000644000175000017500000015725212531357105022244 0ustar zmoelnigzmoelnigPNG  IHDRQaƓ_sRGBbKGD pHYs  tIME   IDATxwE&n^%  ࡀňsx, 1 "$#y,4y~̲rg>t׷իWo^WUwK&MpGaۻBB I6Y[[[|8^UU5nZ!=9fUUCCXʴxxxxo$YfcٳgOϟzfVtMJVd%+YJ-'EUhoxCMOO4@ [[[[F --mjFg!D҂b i쮨`W_svW$ɥ>DrڶEUU$IbO2k*+YgZNGN@NJ(VYU]ɜdݖmF\ޟQCO"͞dSC} ]`@I#cl6[Bp86Wyj_]Zнs?xOHFgiѢEbìY^|taKܱo W\!CbIԄB!~aleoś|hGV|A(cٸW"k;Fqرq:IŒ`{!t]q}D鏽MK_;K W\}mbm??25N=3>y?;z# 1(^qŌ\ؒbS6KK?׎ђۘ;c N?6=i^~˿+YsH.xrgX9ikryC.:rC+xYpǓ㬘XH^5 lN$ MX,9كO<O9C4~lb'C CQ%*DM?.`н0Ca$k-Śek01dY#'-3]4-_?Bp>d<@w+?a񚹬Z?xIˀfB?=;6PU Ulz{M%L5UlvN_]Gfmʼnjy{&ru6Y̋ayg;W`򅔧_zytUݷOgTOPy,~>UGCՖ8jӚż4v6sm-ɺ0 !||5b*_rzȟR~[ qjܮN:IG5 /]B?M럝Kᑨ]S!co$Yt[LÏs9`םck5k$kOG$JÕ/ [ [)kd'%f_|B_@{Me%K_yٙˢ,pWܾ +dƁ`бwoڴkPNr!lyG~wмUHCjL{gW#gor۵oVf{euSgLs< Nlt[2!/Oa=Y`+SgLG^k۾'?Y^3{݁a6_'{t擛Io2gm5 syf4j#g %:v]oPOcw32]x}&u?wKY0z~~ζGf1jԿX_r2$sVyݗpLw;Z";V yY8޷_*D&5kk6ίBU<>ҞYih#h9ǯa4}9wl:eZyl:#LEnߣ~$,?r /Ggr|W;0`Ws^_L͡0Cs^uMu0#W%[ytP!Wjsg3 uZ2utI &K;TjF͐8bH qd*(F]\žUCz%S˜x6 ݇8ҡ^/esb-' U]laOOVWkY|,&M6Y8*z Cn2mMŬT2\Жy9&ezweA.zAr了OCUUB4.G3tPʴFd<]ifذas*jC{.[O?mqߙ<֦p}1SСCve[_'u ?cꟿM߾? = J<0q_.X5az$ndl[7m?鿎g<y7 ;i[~Z<˶պDu le7]7ܒa~}|+C-/.=;1b7Vfyt09f7>23faO蚆{_Mw>:4/SdOt1-9߿ŹWLՏ{li.{3.{U^: |h.;NႬi2 {ZB/x&a맸xh8O¤_ҿ3֡<9#Jfr7qX P[W>!9!JwP 9Gjf *w<ƫO.ⴡd !K }X9)Ma(1q椺ߢK_F123+`B$ô C m(@r]|7*օrG ??rOse_\{L% Md S3r.i&+{vz/xv: n>+h\jv*Dxy_3.;^ɳqXn|-S&O_;9YZIϽO`DL;[ٸ]Lv䧏#{33HWNc̽{ުKdQJ71IZզ;Ӆ8jh㴑vVUTUE@QM'c!y&!n,,pfpcs;*COC 9PvdUm<$,=6Y҅47*2$F ]躁æӱK>+vngOO&-~*uABVE!&%(jl)8t] %'>'ɥo09ŏXu\;_?Aow8;B0aCW5+7R>t隁DuSkG79o[:WJ/kcQ5n[ 1d6M&TV{̥Ôv$6N!~Lqn!̶a:69 JrHqQ2"O{ 4]CDD0b28O.u>]լ__;i)OH戝v7+(5=a뿅IaGE>'_@ )Mfr;Y>P0֣;N(Ktޕ| 6)OܑzL}~^`N Bו$L\^".ƝQ]M dI" DF]XlOTBUnJr8Ht$ $  ^$(alaS\8qdQvdH FHC (i.tUiVf|*Å*bWTuB8vl=Mha|Gcǃk6ˍjMm;|,BUM6ROYW] sƉٱz1F@`Wy!Z4@ѡ0Ǝj#u0"f7 :;;ѐy@mށe.Kr  ÏDuy d0h}DB7 CM{&W32Wt:܊9u]'QL}r.pO\/.=}q ruHqH%-ܒ'ꩨ6ǑAu:$)#IB1}bvz[ah44fw5D}4c6pml&*:h$I"#`-͍v8lHvI5 $ TYd jߦ vņ]Uq*NŁ@Z_8i0tbOO&:0J#3́VvUKln/|MAָ͙WDKN ':m7P{I2$ 0ˑl쒎冼 <5.S52a bNə?q|wwŻz|lM_Gh!Zrni ݰ& &b=>zkNU߽͹7A>dx&d=?p}̓;QTpV~s|]28FOoPtt]rdJc'qWb\axϜsR>2mD  Ĵdm`n=1}=zmN.[@ :Bh~υ@ӣN/!0A=#9e@$ a3ة´S%'fh]iĎ>/"͉0H@n`N[xyIA2vL}DwҚ0{U"!N7 j8;5*ةWƧ"@7BCw1|򭌹^0G@@"n&h_׼lDG?BJFfJn|vQڵGn5Uuvogi,^K~QWz/xA*_1h?aԃAB?"4!H+mn:Qf߱f0~lZj=u*BƝogRR#60D~vt{%`=}:۠ am43wbW\VtI/Lyi۷o(SWK 5 wP?NNbڝHi\הT{UѽzW5#nGk Owߤ/0j`?Aq\t |.EԬY?ӓD$f|Ng{p}/آ(KWK.Z2Dh]޼uzؾ/_CbbN/C[Qg@Ĉ񁎹C`a:~*5n,_ 7a7 qwm9n)S43IūEty4bgJ[S\ci#rRW[AZsx Lbm<Șȸh)f_Ǽj,m45X9<+9;93e.aXPU׎vQ|&C۾Q4'6n)^J6$!+y*lڲ_:|,`8exh!l(^5 ERPe"ERQe ڌSuqX#{-o-e^(n<4jOFy.B#R[Iڦy Onݛj8AAL\iܪ$^@KnȩSngvcwE-A,G {I^FKȊ IVNfqǪsyč8ȹ {;fsw!޾K~&aHzz}'r}Z Q٠#4\9Rw-B]7^cOi2:5hcȲOudzʩ+ږݟ:hNO1A}('t!Jd=!O=Am ?xTVT&YrXu&cr=1):i Jᛙ . c^Le}<5v,Vt UUZ=v;;uĞdw,!AB l޹[?h% $-/ZVSD햏;7ծmߒ:֗zz8%KcMt:Vú۝^;taa#7`^>,gYf O"Tŧ䫫wf*;ݛΝ;Xvի ڧ_&+9nc=4]ts6C(.첟JB@R3)oe U^ Ѯs!`5 ǾCfδIbHt:Js*iߞ4t௢Gf> *}&PJCTkQW]hX[UTxm)s7vΝcl//XOSYͣL縛^֣;ҦKP-Ee5Mx'`>JH+]TG:i*wR)\-XEqy4:v& jwPhS]ytjzɯsaoe2!.Z0!t)Bup%tՁRRR&ӥs[|U%TzRKCj)')n۷E5.2v{uD~n.in (@mmmҫ،gFꊾS<:umGf$z;)RMF ?0Vf0*geC)+Τ}~&yddz/e;+UQO6E9[m߁Uv 08) B"ڐ%֚!9kz/2傮1ԟ,* M3~u /^r=e=18kTw*v{I N-u_.#c)z8Q zV%Vߴ_ZՁ2Z(fhgǏ*|fC- :a؃Y/y?KeAO;K0^.7~%MSR[[]tAONNn.&#vi1Q93ݬظZV5hNkkNrҺѿp<>jjj:Wy݆YH o ҟ[/RQp:䐙y=d(D}}=w& 52-RcSTfMM `T xxx-^zwD RY)Y%ݩ5eFZ3T!TEݣ-{#[[[y ͞)k[IdOp3>E$57{#[[[رc'JVd%+YJIHT<;-7W[ٶ*:InRG؟_dAϞ=4heo%| *Yv[˯9%|$(ʟ߰elo!^j/YLrƏoٯ[/UWWTZc-8~?R[ഁ*eҚ [v2f@7|ghFYk9Þ|+Yɦ,-ZwQ]ӥUeO`>ܹ% B4;G;c,xߏy6-(qX[ka ON h 䈞o$7ٶ\Ӟ L6eo4,8T M__s1{o`/͹/ :~|7]NPuwK,/?Wtμ.(tĝ3yi;zJ mM/&>&;g#zr8 D=Rî8;徨 .I-xoydAU4Voar;e:@fׂ0B>|6<-MI> sh/j>C|OX /s3eǣ9u^}?k30ع%>Cd9%>f;h {"YNvO<^yqED !p8B-?^NԚFh[13́-~5˘>|ywd[@r4 )EtC.N{˄}ٚ a>!; `l&7P#aᇦJ:e f" %BFq)]v7~j*t0eY Da$Yr% -~=Mmxcxہ,B#ifݛlN+w>x>f#p)i0"yX}!kٌ֭3kJ$%ș 7CTptwwf-ïX&? tC0ثmF|7՚]|zV! ,z0+O7}DS7b)]s:ZZ-ͫMbJ=[{>.dn® |a=)mQο'FfmAi`7wgku !t6nX®5:*>ї=ĽR3^z:]C_DJgqv 9a-Ճ|J>w-ߦLN-E,Ϗ&3 [ye~xÓ9O %u:@ P0V\}xz{ @(Wo" ,xy5YDV^^ήr~oz3rdo^A]]H("KI)rD[6廠SB;&ɦ5 I6eIAH6$-W>5_EiС7s]OR@{$Ο"D{17>' #$DƦrǝS$)S,Iu?=br3 IFeEB%@BeYF%$YFQE FȊ-]t6p|r=[[^}v7 ==+YJ$Hs I$69Fb|`{HHj6vS5(',Te4UQ:qF)H2vUiQYq*~Eao"RddB $ zf)Ȳw:Lg 8qYTVZØ %У>?u; ){ ey*F%nxF2glvTUBQnsӹCk>C H-k*r?J5%x DX'pӐ`Ctmfd$TdFO WFzޘ0^_C.E'Z/DEqri,x|tpPUUW㯐)33m^WI!eo9^ p]6dIF@&(].e™g>^^ཙj?8n{\fݱ/o nbS<*buxgん9{[ -[RUuu QJK澅o0'^U>%t'˦d.I@uU ;Ґѩض7fKcd2%ٔ/t )kN'NwYoyvHN3a&jK,#!}v6oafٿ[~'Kjk25-F,I+,-?ëyݧS?p2sߘZ.]̙39묳b{[|yу wY xxxe0wɦVKNݩlioooo3rKʚ `oooo;4.228xxxO{ R4[[[[#/4*K5foooɎWR-i----~SD[x՚oooom|ʍ孙[[[[+RU2&{h)[>_]Hd蚧>K%7-mOΗ؁d/vt'jԞ`48a@5O xߏjE !A ˽wWiv37:Xk?j*G㏇usoѪ !%);"+/'_J]Ok˟%^ju~%^j K-RPW x鰅K-R$ 0~7 ͐ BVkŋ$5LjKRK<0Zn/V7Zn/Vѱ˜3̔ORSeHf!_~"L '--~o5;cּ[7=eob~횝jjZ`_Χ'FILvǢǵ,^/FMY*=lհ|J=lWt]8d@h,*VuBJa[7$?bP,ecrۏ^ò8Wh|FӎԬ;߫olwټ]dz~ WFGg9xϖoOuJ^KcysO8^ /B[mK[9[>;-mnM@a{KΓ#pԔ- N^5죺!>upNB~ +:I/olX߸?7[c1[ɽOoɘ˶ןV?#*aOTKN#9_ޯNW!<O[ဠp$5qFhzNOh<璧ݼ?8I_B ;@;1tz{!׼#6]+fR=SphG^kMdfmy͑|>,B/rGfkC8VZ)OqI'B:u11Xh@^z<\:ӮE%z~M@aKюc2Xj;?P/ZQ:z5#Ir[?b7t<Kw>::,])7D%{>B*gMnxBzC`s9ХTl ቜ/^)] u&`Ƥq?U~O o$W:qB.݁7bKdz<,xx},hb}KdK5Xu@~BOHP{[(p, \N[OfßQ4xuS`UE_3S 1Y<~nwS.O oO,nrJqM_MK=_MYH㬋UǾ aQ*P__Gu>QTw\w.|je{yq~j&p+#^]~BaL6~݊Bk/fЈBdy8 I3ۭ(j[|7C'xw2kG ဟcqݝ[OePbcql5iwdcwFa=aA}]rK61f7]*o|O|KWm7wu3}&/_ uJD?7[p3i<[Bڽ=y;YQ">s2yy DHnk_k^?f <#wƿڒcHa7$o1 <{D>e)=#/,˿~ 2s?@ xav'(C܏?$PXׄ!( q9$8p$KTstACDd]LY}~N"z7(ZCιPXjb06"AK̹E|D 0!r\u ^W\ WQq5Аc&K+MR"ASqWOC([8rֵC.]N Sq9ta@Dh{&n`]nߊ@eۦzP3S^܊F4-u||`2~YrTZjgD[:J mN A(h`Xrdo/(I|cǞWA1(~J_Bh%M~Vi>Ƭ @ i}Pq IOѲ8[D P&[Üu5#]V]GLrHIIÍ>xWϋn77оAjO,O@!ǭpɭ85Х)BvsȶSy&o1wΤdj+ 5U4TI"0@_$ed j|!UFA} djh\lixμ9&W]D4) 7\٤ll_}f#hpc9Pؠ #hө#  H9q:4uO9Gt^>t;(fkb($@@#( XQ + "?T,bA/6D ņ"EIB BI"yJ~(h^Q@-ڨb(J>|EР5P&"iKRŔBGĪ (z;STΝ+B/J劉a zFzN>ANXDՉ (- Zp%FMZ8h-:Yw#1lDDJwc~?c Q:f[M[Qk:Tt8XAAg9=a!p lƨrd?(ɀAq)NMݦ=b+֢RNȒpÄ!Ԋ`ЪXSZTO=VBQӨfq^/>?g >4|F4iW]ANMJ^4׍bhQit֗sd7ܞ8u!ܘa=%ٹW`205DJ9`ըhD{/LL Y+'JϣӪ]Q!&&ޅsvj+Zlj?( :QjOVKGw{`\}4zTCU]Tzb0F눋i|.g2nDOyUf=ya_Y͢-Gԯyﵯ2w3;nDuFCL&->70h4K4`3Q1FH_~gҦ|adT*o_ŵOw(jtĨTDk>gD=1*(zjb!&Z\uP^nXwyQNYөFDi;h uduag`_wy?еK*jjB, B`??l//l꿀 |/q>wLߗtg99mM\ty6)O?ˋ:,u:1JGY:ޡ%:Lҹԟ"s'ee&roGU{=-]"kU^zo} E*bmPzͮ$Mm5%.Zq{Tpef[btvP0FK<8{Our 10qYʠw7zĈ:bU7J'!7bA%qc+7]'7ӕbUi[JG A>yޚ&F#wLup^k'NtW^>;y{ .n|Y~wK~divN}]l>//% MݺuQ=z\dٴ<(LY΀Z%?(=2V(XLhNj zJՂJQN;(ZM4z#^97jAMt3w}2Zފ7^,`4Mƀo*.贞הrsR~^c&|xk Vq2 FUKEe:玗ס(Z(3]Z(  %x͂lh}<:rƤ'u4Njp?N^MU~\ڽnZ-%nTyUZtM@'"j(jr~TBpo)3JF-V̥&x76|#2n 0`@;YJi i1xptj1(8Rhu%6_JE(Tu_Jv[撚q0tj5F ٍy1yGߵ,FEPhyCto=& ԁopևї>mSQ#Pb>N'օ0ehmGPbrѯ! u`4:BPiJ>F}T}8#78StƊ!^xW΍^.X΋{t_aՏ~׺_2 ׹|?XkoNG!OM٪ ~KoK?}&*:Ȑ.^ut?{eO"]Ҡ*~Ko!pRC;L46 n֡mpŏ70V#z|1(ͥjl J8_ƻ7Д?Nt"1g9u ^+:%ۿӉ ~:k|wkGJa6/-]y;35 ^U>*H/^N'ǫrWxu; Op{m6?EJRpe T8ۛE?^BCֶ߾/ǶGrk7AfF{K"\Yן 7{).֟D?^(@';֟}x~ WJ 5.>G#:"\SN2ow?6' "D_jrC͛6vĉVNEնŠ"*:T >x—(RSS4\ +S233Clڴj+{dlBJSbq(|x/%oL1I4NE[Lx V4\UK7tܩun!}gGR_nIM|/ϧo1]r`(DZ(JE{' A=Yt/]DiTz*@|_ 3EpKZ;bt37|jIifj6kIF7s(XzYOiߊT8TՍb `E_%Ly'+'@_HlIxjhK ǭskbbTCȝl_|䑘ѣ+ zJ#++wCSN/ϠSIw/`?0|*Y PNe >_\pp(ԫW/bRDAKU|w{gx|C{_t~Q]w—TR38/RP *Ê+}8qbHWđred~om,f.wvid'Ģlޝ4#/Qqs4һz|hv~7&vq,Bo/gfh>e⯳_ّ|i76tjoZ{S>]7mxa)^`Fޝ/W\/BD3`Cp>;$y%wv~^ġ]iw]`+:~Ʈ.|Yf._R }M;&=a ﲧkohelAr X@u"AGٿ_/4oܫӠAJ}L\(ovC, C%_r錘ʼnĪ˿P*3ОS3F!yx7I}@޾y:-Ӈǯ9^l8TzEDCç5iU̪oT@ |f';w2t<̌B{6mϋ;W +oyj^[))RQ ~tg3|I"-ϝi̶5x}CgáR 5zaN]wu:t2huH$qio[aX0Jm ̜2^=.*p%S`*)>&{bʷU|?f{0/~w^Ͻ7 [lzQZ"^۶]ETٿΗy]tt4=zt9&o+z0K 'DlvT2[/0`YA.G6nɗSd仧@|t~?WZc ۿʽJa]vet%khϞ=1fe8VT>c1Bn/N4vw&>.ʫ[{o`9<8/nU+ZoW倠}Ej,-L|ٿ/xϞ=1~ܱHU)WB(E<}M-;R?pb+/}1hFdWU!YE ~徑*8}k׮Ԯ]AHIIm`RI4IgӕOz|xė(V_J&$?W9)3ݻb7cfe|ir+4&ɧD3s!뷙TdS—T۸_}boV"JPUxs*eHϚ(ϵ 9s7^;њOA^FxlןhJv?;SיK{)nNH W7 k }E 2lO+~=NFVi?r(Ǒ>kyjj* 6yP%E5 ķČx-A̕;~?Fy4;{a/ރEX[OqMC(-&}~:Nu )(M'3//O}vv6uԹ.IIU *:=ȦEc1Qз"t8tj(+.r.G1T9ppC~ jJ~A1ZC (CrrrX-3&=kkH%6P_cǏ %Fe񱝼4- !nԇ&vQTP`te1%8٘YQAVQW|ib7ߠK _*/%u™3gHMMuy<4\NUÄ$d/S-QV^X PW2z,xBb\_ hSZ"Aʃ |8a?2-瞽ZN0e|iKꔒ"{qq}Ο_saCU!޿`Wӹ2HnܮE~_j>Q|t9EhZ8 >||HVV#%%د5p] JRFq|pH &i&&NGzt,8ۏk"p5RsUE VdVVwFFEXֽ4]z'>Z:[{M`mBd5ʲG6B4_t"QE9I S*mW$R̤sb„ mY񣹯 u wӬ]w{Ŷ"pN v/WO4t S7?|5nɊ(BmlEnL;ٿ*Ue޼y+@A4cmT*9J۷њXVD{Rvh['_(HERDj.v )_@IP9T1=3?OFwg_挮c껳J(όuU]jokA>~'ϭyOwabݜ!LZ>~u.ӣ޿Վ^o2=.^{G %cFvp1#X7vdH,&N:tPMAXԡ-+Ѩvߗ  W̧ҧv+ab:h4Td׿!B=grs.(Tr%tjl9P~ o"lB})JogsudՙFƞbFz:3ᏻ$r dfV|']H޾I.Uzn"E _?C VK:߿֎H2p7_OYgqP>_`^.d_=V$wK(^TD}ΐ(ұKˆ/ w$׎ o-z>X5ё8TW`8fc22歎l|w+Yy?AG[h_+- \{N(y$Z:a vFVTvʛދ' i6?MqqG9ܨ>ϣeǖ- >O7I؏q2eΘJ!btN IDAT.đRXƟ_{T(@q׎`ITGJswJq\L$F5oYDMiMȾc9k&^rrr1+TrpxE2 R8gd(o+h=Nʕ͟w|_i ylhuO;{Wcw4 FwvRDh?cm 1WGN'MYe0%˜Z,d|8Q>U?ܡ MJY3Z|1JWułFV+_k.!pZ!Xn>7 Tl]⽓ܨLyyy`vv6uEV{\z[.2꒔(|(#UL*F#Ez 9[)-6a!htJ ;ĉ gLzhi\GCjmmX.G _Q_;f6R;~dkts3^iQfCM" V qәSeTv.m4HIJnn..y\ԑm虙NI߬zC,q>G*.\=Hh4c-_S .v-Lf2ef+l|EW9yeծ&EHĵJӁ z#qr lE{&$SxO6j`ȷe<_JK)"%{EFFEXֽ4]:,]&ڙ?쩥(T|l8Ck,ZÙ=/"k3Tu| :w_Z{'w_Elr‘7ʁͫ2rZhA-ߧ8r_:RkKqfffҹsgg]\^񣹯*P>b5ya'sQ<\_H ~lx}˘Կv}~OU~U3l7Dz] Q+KcϯިR~ʺi>o$j%=ޟkyap<g$9PͽJu w1OVfoQqK"Fo{d;sJ9W~i߻,8x \d}ϗ#u1jR F~h;5i0\ r-")}*59isjs<>x7D3}XLߝUO9,}fsLUo٥z^pǝvteǶO9 G:ӺukZf<̪&>(ʹĩ#)OzX57?eTDO_NeSO1Q+>Y?3䏴9͙יM 4qē<ժ ^̑בUWgƎi kvxZǨo0,R4`N)ޯA<̖K9.:K;" }saw'C D^(zL:MAPyLH/ԈT09*Cj|+09|(.T8oLK_cd ÁjvK&'6.eؠX󇴌_x~—43>[c< ?8VXx {;ߠ%qcxh?ߖhQ\OuH)G%~dTHF.+|+]gsVmdfNcohOI c:?X>l`FNoA[Ԙݺ 62#1ڣ/=&vw~ %g!c#P<jqoJղXɋ|Hvv$GGW:؞;^sx5knj\g_:I3O̗1|JU2tT>Æ~}rMz4pD} <+{Dohئ O""ɝ/*k,ZkoGRͼkaƌalHj<` ]}WvkzD?_yxo6gB5.T\͉r{V^r" _V*Z?q['vb٘:]28&RG?i!M?\1@gJy_x_r`Ґ! `hg4grG VErZh~A߈ߝG_NϚo=c\=kb'իuV^ՆǣX,Š iOÄr^Ƿu|\H{ bxowITI"w܁0>yJtDQf8/+-u {S>SOo亱O3x%3Y7W& ?qѼU6ox# >?^@~`R2{ï;eh0T|E[/}17tn_ [bX.rnI>Mݧs `3[,e`| j;s3SE 7ktY-#%׈ËZk܁ʮ OIZ^#vr蜉^Е+<?ە>!~$w&Q_!vrWlNEO*_o2iP }ZR;mܘyuphݨQ$`'S{IyJ_s)? ]WYyk :6NKD*(=年`;$devuW눙u儼o4W9;}5JFX=M'we) y֕n@:8JD<ƹU6hޫ)OKLL O %9j%77T&|1r?ŎS4X|~q>qQXDo+kje3RQBXh\r|ϓ ]ݝ*E|.4WjȰN>ՄRB^2d-㯿ӥmUMXi 6%ǏfC3^O˟ Pnx.-n8<$oʢ%!闯z/K9s?+!Q6Owq.򫉉q+Opq?R]EQL&q{K<jS#T"͛+NE"@rrrg(Cu0޼yJ)"VC fp$J.*EKEΦ_ wR'!333d̦M"s:T-I{ /͕}{K4dkR;^/Q2;ٿ1\ Cy]os*ًQĻ'jrK$~ؑ^U FСw _*H$w<M00XV(h4Ugzd$rYAV7O:JkT"U'`?o=|x-\Y3z8lxb?VWCsq=߭h\gtLVU/Š󄼼{d=:쇐:nx|VM5+-RLN8_ؓmҸ=٦(J0΃?? ڜ+K:PUfC;~d'wKoz]|iʹxV5wQV/$v*O)?0ŁJPѧOV\}ӿ}VCɷh FBYoecߌo2?ѭYmpojǂ7\z4;?ΉZnfAr sk[|JY ~k=9ctӃXHoyåǰgwcu o5 sʔyƧ>斎@1N;MMR}bn ߳6}ջύP$l fUJIOOzvѠAJ}w$VMrgGԎQ?ցG+{z#{/?::={V#uz`%8VV{i-ӦsKjQk+cfj5l9av܈.*7j@H{xPdlT&$}sC ]Aߐ#quf>=؊)(K mC3Z2%xapSJ?\\wuN\{@=gHUcmr@.]HK?S~VnOwӪ[4{N$6*zp'LgX4#{J.'noYbDSVc?!"_E Qjg| ml2wCmtG;NP$#,0x):k=ibx8'oxiu.s򥄩.twu}Sf|1IoGhzl=X@}f+.gw7.;_Fq.8`u,N=mCD6r%@+Z&=ηCE/S*'c]vvж]JĕΒX~D5H]XDu--7%ׇ^#@, j7z9pcɺ %Y8G&7=06^^@^JYo׎^F~R_3z8,qf6U:|~_"(UƗ69;K1隰ͬ)2qbzZ7~tkl1ydwelNgvƼ%Nʗ|Q?Mq"lfKDWE*TqZvNۯ/*AU"9R;MC\g\HFByvy,F`dJwLBr"\p*b`9p⛵;nS\\̑8t7N Ғ ]Q/wvVR#:jrTT—ޜ8P8_ȉs&RȈW#rO$iYGt:˝|)JIHIs"+9pΗ )6?MqqG9'xSTz 9~/!m_s)SSS]c ם^m&iVGjBAjEB/?9Qx)*  ZU<0T Tm|YSJY;')bo'=]e >CoaqWb)kF^Ľ|2`];j'Iǻ/ h=6W6Gjz[v)^B,ڂS"4yD(!//υ >LbbK7iY'*))20YEbqSr'AX 1(01V26o*.dQaRR3G X Z;fKP⾑}*R#3uɲJ{~orqH,-*lX+IS8= _;x%UDF^,-lS{WDKnjri7nѸD0lV濫7{srvskVfɃ87'FE4(]8*ےFڋ2_R|e4/<>7 z#qzoRDOov0٦B:XviYtҚ &CZ}T(@#$ϬK|#,u—Vu"_=)HI0v?X6'%nCޠ4.Ejf*@4@OKh⥕R~5߫H/U1$oy~}Cu0?-_[\{nM?oTZ"5Uy|rwe;4%Q/ͽo\Qh.o"Cob/;R 7r:,WvQs]ןzRZ"5pN d8)B*>PlؑR&roۺTT k{%Ռ| IDAT^E&+ 5zQZh\y{=ѣG8yҚ!g$_N2}E2s"EQEkx/X|Mv&OEMd֭դJ?0Ҽa\Ӕ *|1Mh ^ ?Tj=z={*~vء$1sϟhKkp~/V} ?s9hQ}ʢ%sJ5ŁHTFFG4Sf>w%8XyL&|qgr©5/M^tnaœPgϲo>nƋV|q1)h7uQl-b@lw2|pOBB׈b+ C)\|aP/%tJ1gϞ/`ҥ.ZS)ɁB' lݺ c„ ݂b -JWѸDi*~+p_NU|/珀)jzKr4- pװA6pED8j0f9Q*csN-A}f5;X\~>8oVˠA|rӃ뗆'_ A.믿f[#yeJ(S_I{N ju$H U+ES  __۾8P}5K0[3~O?e+f3cd7#&#YBrā*x |y}w~~O3cU5}(uġ+d=u:Vmvޗ JHb#>رc9yarqluO&/ 0bxX@'0n8}Q^rrrjFl2}T@isׇ@s$=oI*a^LZ Vٵ?,gJ|uoL[ܳa&ZAD{é_{ r}c'̼.|.vtG4B /Ԟ=ΑFPi;Jy]7R^͌@;P o}/2^ٳ'%5v:X1A< 'g0[fw|?W<߾Ӛ?ekx2zbNmC۶-8XKkgG>gn" 8%3rʘC{Li{Ѕgsan#r¡ro^Mc[l!>Z(`Bqׂ#A7ўL3W493X;yq&u 7â+nƪUHIIAйաW&xo@QpGAw;ۇԲzm];[0~ߋke27w=5oK"!J (tZpԩ<"SU+=UAUZRXXXcI,&2OvDy۰"DU򯓟ٸ|C98TD hvb0[Q< 6m';SǖSIƧ$-}_#vr)3q:'Cǎp"99ANTc?AEgj ~oxoΫS~@DQd#̛pu^bgxJ_I~CWεs}]Nl\tޅ_K-)%V{NwKhMbֵ+ ^9լP? Uw 6_4Nny1RK4BSad6Gjz[v ي0Y}z ?)$7!%A\dD?۱p9V߅~,s.5HTVV9Q&L#6ߍV8xOV'mԑ^+2@Hb6nSv/^}T &]~+3N,qNOE^2 ``{Y@OTraSNg(/Ç[.grS#>O?m۶1k,> +G1KX-5ġSCYq%8MoP3 Dap{ 3c{(1C\1 0YU{Y1z ej4zt:eT$JNNrƤ'uԤy|$Z)T|EUcǏ %ɚۈ86m2*|Q5n9ө(RAOAXYLޘ™$^lFPc&ԋĂجpjdob!77c[)䛑"|:\/X @&Mx| 댉#4cF 5gibjb=(Ao$N@hB!y/[^Ih4:bb_S\7PK~5RIJ*K#V5!"5;wpT{} +㜎N鉍~Y nuq4:7u>NeKl| LIjP`jӦM:^%\?R?*cK֟æVJZhu$7ʏcO"p,}A((^Zwx\ՙ?)FXՒqr6c eRi)$ dC`MH!l$ZJ^nIzI#M33 <~<{9yh[!kuC=\nb޲ VmJMxS䳍Δޮ(((@Q0yuW7Tho9u3&*qx" 3ϠQh[e<]4V***yaF!?牠$s]tg $^IwJѣGٯ]T|gw'a}gPis-t)Wj8h^HT @;>IRq}p˔I2g60{l4|_Dz>jXH{{o(F*n|>eOM;l9cʔ8z_;䨿 ~,~,#O#E_0=Nс $}}P=L):x=rgx&e9'0,Sx0 o&ǻ!ڦShCgˑf#N]&S}(55u@._koN7?0Y.ߟ|=W3(4T~t1=~;KŁůoG¢Q?:krߛ9.^v^ׇS?Υ凞y>\Pi+l7ϐC}HuysH}}}45Pu٥=/qǪzɻ|ļ6ܭv\V7owt%G/?Tc.<yu8oRgᖟ>ؗGT"H8 qVyo6Ƨ /ʑg.'sW-;  Q 4BFpc+54Iv$ 'hNKcDNvW,Y2gdR}duWInizCHPG÷SpބKF~󡔟lwrc4<ǃd2qaGyɷ/;cC`$G w`cGxhp_y#-|a8l3yi|15m᫟,Jnv{QX zh35OtNof͛|zu PϧD3_[Ϛ/փ$ꏍ2m 5_CGFr$^)^Rl" SD|qS=(Y?'Ft]COmHML:}!ڛ_eZo??>8^O*[1n3& n~vL{Ǿyx-[hkX؃r.O-FZ욧 )N͡iX%^+h3O+& 'e9J6>ɨDZJohc%~|+4sE=TlM屻Jȓto+4I!ݪsKuɗe3kcuD^yvoKs?o`C؛$d*SMTRg8xw{/ս)?Ou0~.W]O:=0#>izR0{JD9e񖼒D%Oz~6@y#Q;v숸sp׼>mvܹS=\5.4V4@wO6bF?;n{Uʴ9E|[, s/ak-g#;^bjMܦC,Fݩ2M[L/ߎ}_w{T_yJDFD2JtDfѡH/W :PyydOL}\Kȓ|yt*KruHx:m:{W :c̣wg>.>ј)Ú߶P̜[HgzѓTd3~ 5h^~\wRQgY>('9|7ЄD*~.i8i64^xƍƪZ7\A^ k+Mխn~En[R@qqeuwN8~۔Zoky333B#SQCS GqDg508--\iQhؿcj²2UOcrG3 C^l_>YV@GՌ= %$Sf1N =09@nD)cgʠ"FCuW6;yvZάb і?q~2⾯yC,[A<.jja^ `e1K<"7ڊן+C|}l~B )Ԉ5L-/_x.&؆D׾4KggbF`$9N?>Qa3hF=-"0at?Vt? [VFk׽ 7 |w@UUWXٷ ?drnݧy6ܣxnd|41&kH )=u:5f)3m4m/&/.u<Ic'sTjZ930-8mɠXz~U8[L|6 )w{iv}lZrt_NGK,qk'gsob]suq; 53ﰼG| &mkS$%w|me9##4YLFk+\8[[1=ç/w01ӮCbƌ><[l&O⺉@"+K:K:y|cq~s)iGBѱeqX3V I1)oi]ʗ9M,)枪T&Y,>1k*_ؤծ+ t)yqdJcOEuZUmܷ k“_4rvS"i0߿j8.=v῿jaXf2[k#E,߯{x5q9oց3;[ٷ};΀zIuk|zA!/n߾z_I&vnv# Ÿ ˉq"-AE>>,om$/'~wXv4X`Xk~߳ʹ,*(clXaӾNZ0u^1_:ndV4+S1N$,*g_-gc;) tfn4>:9٪"):ϷD󀙥K]|f1O6H^9UY1?sM"_|)_gH?uۑҁ13| kJsy|br/c/z%XCW w>8hiiW3~p,))AX-R1=tVYda5 __έs y@MX,.;srݽös^ooo(;h ƟCo힏8k+w/5:k׾Dms.b[]wəXhCS''BrC{+ol/0L_LSP+tp3+ɵcѨmЫK*#,\Mاm6qf*Mis#bN/..5ϟ(羯l5<}{n/oއٖIj <80ebQa iV[O[}\H N=Ni"=k {쨲͚S*NO#D,(+- JyyypNT3bBÕ) 86|n*{?E߽ȡ2V6w-<7"!uwɄ V?ԡ&Wp7^]Cov^%7aLDimyA''Y0]?hHyJ5߽(. =zΏʮ}]t'<]=<*,jp/¡hّ )+{^꧚'iVS;y3ޮzTL $Cxe4bXujԃ?=\P;_fGAaiiR 99Cߔ|km7^|'[ћ ױ6_@@`tDbk>F&vlLsK +^1VJ&Q0m$8_@@ e/ǻ!7 ::Xp!ٹguuu7% 0_@@ e/s.֙3gr6mľ}eY,/Nn?oN$zDhvÅ-i;X[2ޒ$(u3~{IcǸy #*_L<Ԡ?+Ԧ?s+$~lо1~g`8{%%$"?J@`4#hiO@(Gfe3?DIQ>w~Vn?ZztЮ DXoX/|?-M 3&JiOM>wB"& Y6jgxgX~(a;PZo |Ke/;9h\˸{OK5sOo7[=Wjů*k㳁o>ʍ3>Qeqmvn<COzܱeR.ʟ2~N]G YE73z8g;_}OVdY[\~BCSBZ6'~FsE%e q7F7޼P\\jc*.| k+MiqU]6~8-}Oi~-W9y+2qWLg)@F\,À]ew|ͯu]<Ɏ\68xgϞuaI&CG_@@ yg6?dšyf,|ɘܨQ n ptE.1zݳj:%c(\NNJ"OO>߸2 tm"H|๙q?qȜtEM_76ZAskuͿK/1uTv;?]tWjإXz~U8[L|kf 12ēf4 )wZξ-ʠ9`a;7MMJ{P%G4J`|7cN_$ $s(uXU2cǶMb9|5rJKK~~֤x]|ʾiuBpz촟 tNO]f*<@.go<,SW](8هKPh6Ǯc mMgM\΃fnaoANw:yسږn\ߴYH-OhzB%;~CN䦡;.UiCAp-o2YX@n ϱ hll䕝97So"&h"ޤIsҤmnJkk+AΔk5fXk_ s̎yx59233rL2  l3pl}0@H42(%m@-LYZfcӍꠗ`̡@8PI7ÊY9|۾M_>Z%7 %IUUUlٲVV #.v4EAZ7o5ChTP&JGO<\@@ Y'B`&ρXuqN޽{j0u B 99BsF'<:$ ~b|dxF:owCu1_]ٺ}oEoF"V&'C#' RO([w1̲vlLsK +^1^\(6u9 d/[\L3..\uv쮢VѳpxKZĖFw(9sj/gӦM۷O(hw;E[$;u-蓀hFX$*:|ᕭn^}/ ^~-ya}1`$Ǚ E:8 ~|p"y&QFu3J'жc;YKU()3-lW MwKJ.`=x+3q[ $͌1ˣ;:%8xϰfq?xvqݺ?o\?|k_ 2?{c|6pG>ϭ@.Xj?cwX9v'yc&fU/}ӟXm.8L=9_\^'sI4ФI|<غf2=٬[װ:}Xs ~~XsIOR&7{੝i~fwǀ|GvlkG5Ys w|[.ʟ3A]G YMna_G|SoJ-,%+?dhp~H!d|cO .;oszӋܶptJJy{A^ k+MipUύ{2f?$럞 r՝ӨZb:3gN> Y90.;>|#)@~_]w)q-c>S]oܬff=Fl ։㑾 G狨@r:MCS"w+վc;[OTGy;vK/QZZ0o!?[6GdeF`Or9KyEӊVŭx.79#JլIfqN=sbrRbg7uVG{Gyo-jR>b8X! }=\lΡ)MdGskuL饗:u*vg];t'J]^c]ub["%Սў.gxrTϼ-oYPAsf.=MSw|ҊyfLjй}e1Q[%K4 =J '-3Bdd7;mY\UZZn&.b3w]M:p{g+o vc΁n῿jpr O~Gl8ЀgqɀR+ϭ+':zq4a˖6Ǯe .+^[!ێpPw ;codɩd,Xg׀e W-alYhZ򘄘&I';K@`4" G6@YIOaI:#iXp;zBFo/c-[m{I IU]z: S'369=8U,db;B%#yeg+V7Qg:iz<A9[_OYiiK^6T5MHTYhڅo}9sfǼ_`|owιL[F&A-pid9E dI#30y*igi}GXAzz:6-Oڕ3,D",_ $/Sh2 {b˖-65/3pi69@G4d%ԞT^P_ $317E_E 8P5ɻ{/@ ML]̜9vVH{,!ѣXf//Tze $10|_H&{тJra $/'?OTx|䱗^>w, Df"d(ֵD",/&ahr$R8Hk_GIㅬ \_Ůf=3Ѱmf F'G=EC $' -_@@`O9!@@ z]"[F41Hٔ nǜ$'$tEX7r0x585L|a.y&1Hj3:BHDCŦqj8'=1Yzda ^wk%v-:T_O4H։gb/F'u_@@ '} Fb8/^&rĦqo B7&_H&{mq83,G'ạ'r'H{7xK/@RXh f+,m%'F8HX/MfQm3c,n+=\@@`Ϣ1 /J@`T83<A@`tͬ.|g?fOajѐɪ&C }3>QajѨɄ Dύ/ 02远׌@ͦVw=D%< $s(/Ǻ)C% \ѝa ( pqpC5@RЉ8?|E"v4N@Y !6\>W" 3,P}|PI$Ib^jC%(M?Dsdx&r}:EEEG Yƺ^SSդ0s̸bMFqIqrD<q[uu5VYfEDA# Ncc#G b@馄.u;3gEh/##]9Qa(]שJds\.#5-5KCgŋ]l vڍl3q:arL'Buegf33f̈=I<##vÍ0qUUiȒQ7#=:pu{@un*fl\4P#|9Q>%Z+WS[KOOjk:UT4@5dp FU(qVQAj2m4vEmmmXR$Y`_N:O\f $Y._W4FsfŚ[xQGׂcX:uj̃ znj.Cܣx IU }d|Ƴm gp[P:Х$J ~lvϟOJJ xhΙ~8bqNvF nw P{Мݴ[hZ{Zk9] fcǏ''U9Oׯ (K;o1[:u*;w *S=_n7TTT`6|?d]ơv,wԲu{+i&##˖Qms䇽- ={,fK'Jf35Ce:8r)[tM} ՞Ѐځ}̾S~9št%2&MM~7Eט,V0"BH2?s_ωxarFo s+([2: 8jkߴ85h8)X&kcJd~`[t2 0qUL-N `{8=].ߟ\46'ud;~G뺎5&):4N+xYnY1ptMP2 KWX1,jMuH~*ϬYH_G^~ <%d'q{2n|/X,g,KT,Q'*P%Xmo --T'*i%;'nwO먚:*HpcY)X@p2rc#۷e4#v΋9Je,wߠ=k Y݇8֢7QSHLBy!"ԫ4 Ʉٻ= :N\gb\0p4رcn 3 ΦӤ0Ro[/EzM)ET]>;90+8ϻ|> v\+ȜOvo}Ք .2BWG/I#m$30fMÚІA,H=7L&>Z$9eAN[,]q= DImkT^Զʢ7ؘ0.ܚo~cրGVeŴi<TLytv'h;qKǐNu/?J&&CQ~/h(/y)qÉL<.~ʒ(Ζ:lF*Ɩ?4_L$7)f5T`ɂK~iQUo m3ٛS=/kwB?= tӸc% WDOvPmkߞ ,ŋCl|9/(ӄxjkP&̑2}=S2f-d$qO$H] iF7&2릮v[褚 @^RiٳE+/z8ى&s^91OYi2ף,J"\)sz/]C #hV]Rı3,l,w##["טHG%/tX84x KƤy-iw|KGCFsk a so(1 붼C$PdSgHAv&HщѺXwK`?o$EI~Q=7wp{| 2noMo?\2se> Y#|V5;=~+=*UMEUAC_TK.Furvz{p;Kw֎:: Eeރ>TYx}pw7퍻c_WyKWenD<Ȭ2u 5/aB^*.UT1c{o>NS$;ɒ$^Q~sΘ~ۡDi?Hr Y6Ew5}Ly\ Z_/ز>\gC, j^+~ snXCBƴ@"lL]C !mHv֫.1㱡жA9Qxd# ɁXQh)+|de)8L)2\&kyIcG +/fl 5{ Utov6pۘHwhe# S16%"Dz eCߣIgkBډ8ŀ5i2d’}5մ:h~睍 r@"DNg{g؁rқ0ph6:&SRe+iTБNTwzCT~Nڻ:^n#=hRI~.ۡO$dBQlv?u#G;m۫J~ھgPۂe.yeL}2%e^$s_NlT/̭!2O"ܷD'K6pU򆲣([,.dl Lze򋊹iR%nMgb{mDd[2W|WG'vrʼ^|2A=D@wkAz*k:qT-ӹt,Oy@ةv,~uECth?gEi? ۾nۿc:v.6ZS>=._.F3;4Ԏ:2nJKb?Dr'-b 7ϯg6GwLjkݡz[FPݱMaL6Rֺ:h}v a#O@KH?wZh "jbyx3dلbvrL ]RuwtsRZZj$eM*N']~)yq~x@-3Ȗt ~=Xg\,E,J,7~$aRLɬ & `2Ȧl{M>xg_sc2z vv{ ILtֺ斦&Csc$ɻ\.nh6l6a6唚ArT2֌5kMhL_b$Ibʏsɖ۹>Qih%{x절]ʼȶ"΋Uݷ={zV&U"m:n@%?@'e~r̍A?P]9@>M+yﭷd~[͊+&HINp̫_xM@7 l6c6fV5L7kiƀۈP8u8ң3}RJJJz97wle'c #&c!,?Ko6**g FGѧ,k+owsuݿꯣ!s)@~4(sIɊD$+eM˩*Qd QL/F84u% }"V }:/#iXǜ$/vnЁzFe.-54pI~JA6i(f UWtn;˫B&+?33\A__{0U~X_$?U [udS6+KAJMBWyh|͉oIg$Eԧ DUU6JJKl2G6CNa#Tט29s朓}:s9[9^g ː^L|UZ;wGMFO`8dC'^ncCCCdeeF4-|޳Gy&M*+dpu $n UTkD~`ʀÉ.)~pʏUH2*)Xin'27H2VJʂdn%)Qd.Q6q Uv~e V^atjDU]q DOSSO=c?Xce mȸmI; )حك#M+wpJ1-q=/>Om2דz߱joo׃r"8Q*:N&dYLzP=0TU<EEEXyNFEIHNwFT>B/y$$@LKKd2H@[ZNGQ,_}:}ȏ9nzu3.7NP؁qfb\y)횷;y%@7%'P](r ,R@X"4J/gU---G82~\hfnFbf|쪅|,[rcA{ykkk9EGjkkʦSCss3fqaق*~m뱊]XOrQXXHhadL&Ə##=#r=D<ӧq\oPJ/eB;}4?>>9[ݧl|Y7~A䯉|J%`A`ZB@JN˲^zf||it@0` ]Ti085$UkӶlƼ瑮ƢyO4|g䁼=^uwQނZ ث>ݣp M@"?GwA (;~VV9esg}tmm91r9_~E\,yNm% Q)i _⟎9ǯ=p.~7W$+ oǪ[zlJiMY#`[%{Ms"돖[MITYVVoN ITV?2W, u] 8De[~)@JLҏ*6ow]@)&`^Jn Gww+C)XՀx_qћݺooo1prr4\/㊔Fect࿾}(======}t:x<Rfq%D0J]J)(vzA=======>\__RJS7Q|[ray^􃞞~ĝ`4 f%ĝrxEkdjE=======>(Aڬ8e|zzn

 

Documentation generated by Doxygen $doxygenversion on $datetime

© 2008 by Juan-Pablo Caceres (jcaceres at ccrma dot stanford dot edu) and Chris Chafe
SoundWIRE Group at CCRMA - Stanford University

SourceForge.net Logo

jacktrip-1.1/documentation/documentation.cpp0000644000175000017500000001073612531357105021653 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file documentation.cpp * \author Juan-Pablo Caceres * \date July 2008 */ // Main Page Documentation //----------------------------------------------------------------- /** \mainpage JackTrip Documentation \section intro_sec About JackTrip JackTrip: A System for High-Quality Audio Network Performance over the Internet. JackTrip is a Linux and Mac OS X-based system used for multi-machine network performance over the Internet. It supports any number of channels (as many as the computer/network can handle) of bidirectional, high quality, uncompressed audio signal steaming. You can use it between any combination of Linux and Mac OS X (i.e., one end using Linux can connect to the other using Mac OS X). It is currently being developed and actively tested at CCRMA by the SoundWIRE group. \section install_sec Installation Download the latest release: Download Please read the documentation inside the packet to install. \subsection install_subsec_osx Mac OS X Requirements You'll need: Jack OS X. The documentation explains how to install it and set it up, and it's highly recommended. Jack OS X comes with JackPilot to do the audio routing. An alternative is qjackctl, which I find easier to use. You can find the binary here: qjackctl mac binary If you use Leopard, you won't need to configure the UDP ports manually. \subsubsection install_linux Linux Requirements Please read the documentation inside the packet to compile and install in linux.\n The older version of JackTrip is documented here. Please follow those instructions to configure the firewall under \section using Using JackTrip Type jacktrip in a terminal window to display a help list with all the options. JackTrip uses Jack as its audio server. You have to make sure that the settings in Jack are the same in the local and remote machine. There are two parameters that you want to tweak: Frames/Period and Sample Rate. The Lower the Frames/Period, the lower the latency. The higher the Sampling Rate, the higher the bandwidth requirements. You have to make sure these settings match in both machines. \image html jack_main_settings.jpg You also may want to look at the internal buffering -q, --queue parameter in JackTrip. If your connection is very unstable, with a lot of jitter, you should increase this number at the expense of a higher latency. The audio bit resolution parameter, -b, --bitres, can be use to decrease (or increase) the bandwidth requirements, at the expense of a lower audio quality. A basic connection will have one of the nodes as a server: jacktrip -s And the other as a client jacktrip -c [SERVER-IP-NUMBER] You'll see a JackTrip client in Jack. Everything you connect into the send ports will be transmitted to your peer. You'll receive what your peer sends you on the receive ports. \image html jack_routing.png */ jacktrip-1.1/CHANGESLOG.txt0000644000175000017500000000366012531357105015576 0ustar zmoelnigzmoelnig--- 1.1 - (added) Support for RtAudio. Jacktrip can now be used without Jack - (added) DNS Look-up support, now one machine can have a private IP (but still needs to have UDP ports open) - (added) New port to Windows XP and Windows Vista (experimental and not tested for a long time, only when using jacktrip as a library) - (added) Multiclient Server (experimental and not exposed in the executable) --- 1.0.5 - (added) Compatibility with JamLink boxes (restricted at the moment to 48KHz, 64 buffer size and 1 channel) - (added) New port structure that allows the communication between a public server and a local client - (added) Option for packets without header - (added) Option to change default client name - (fixed) General optimizations and code cleanup - (added) Improved, now cross-platform build script --- 1.0.4 - (fixed) Buss error caused when no physical inputs or outputs ports are available --- 1.0.3 - (added) Redundancy Algorithm for UDP Packets to to avoid glitches with packet losses - (fixed) Now compiles on 64bits machines - (fixed) Improved exceptions handling - (added) Basic Karplus-Strong model added as Plug-in - (added) Some functionality reimplemented using signals and slots for more flexibility - (added) Multiple-Client-Server in alpha testing, expect it working in the next release --- 1.0.2 Alpha - (added) Port offset mode, to use a different UDP port than the default one. - (fixed) Improved thread behavior --- 1.0.1 Alpha - (added) jamlink mode to connect with jamlink boxes - (fixed) thread priority in both Linux and Mac OS X (still need some work on the Mac OS X version) - (fixed) Bug that was causing plug-ins not to behave correctly - (added) Loopback mode - (added) Underrun Modes: Wavetable (default) and set to zeros - (added) Check for peer audio settings, program exists if they don't match - (added) Automatically connect ports to available physical audio interface. --- 1.0 Alpha - initial release jacktrip-1.1/src/0000755000175000017500000000000012544047133014206 5ustar zmoelnigzmoelnigjacktrip-1.1/src/RtAudioInterface.cpp0000644000175000017500000003362412531357105020111 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file RtAudioInterface.cpp * \author Juan-Pablo Caceres * \date July 2009 */ #include "RtAudioInterface.h" #include "JackTrip.h" #include "jacktrip_globals.h" #include using std::cout; using std::endl; //******************************************************************************* RtAudioInterface::RtAudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, audioBitResolutionT AudioBitResolution) : AudioInterface(jacktrip, NumInChans, NumOutChans, AudioBitResolution), mJackTrip(jacktrip), mRtAudio(NULL) {} //******************************************************************************* RtAudioInterface::~RtAudioInterface() { delete mRtAudio; } //******************************************************************************* void RtAudioInterface::setup() { // Initialize Buffer array to read and write audio and members mNumInChans = getNumInputChannels(); mNumOutChans = getNumOutputChannels(); mInBuffer.resize(getNumInputChannels()); mOutBuffer.resize(getNumOutputChannels()); cout << "Settin Up Default RtAudio Interface" << endl; cout << gPrintSeparator << endl; mRtAudio = new RtAudio; if ( mRtAudio->getDeviceCount() < 1 ) { cout << "No audio devices found!" << endl; std::exit(0); } // Get and print default devices RtAudio::DeviceInfo info_input; RtAudio::DeviceInfo info_output; int deviceId_input; int deviceId_output; // use default devices deviceId_input = mRtAudio->getDefaultInputDevice(); deviceId_output = mRtAudio->getDefaultOutputDevice(); cout << "DEFAULT INPUT DEVICE : " << endl; printDeviceInfo(deviceId_input); cout << gPrintSeparator << endl; cout << "DEFAULT OUTPUT DEVICE : " << endl; printDeviceInfo(deviceId_output); cout << gPrintSeparator << endl; RtAudio::StreamParameters in_params, out_params; in_params.deviceId = mRtAudio->getDefaultInputDevice(); out_params.deviceId = mRtAudio->getDefaultOutputDevice(); in_params.nChannels = getNumInputChannels(); out_params.nChannels = getNumOutputChannels(); RtAudio::StreamOptions options; //The second flag affects linux and mac only options.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_SCHEDULE_REALTIME; #ifdef __WIN_32__ options.flags = options.flags | RTAUDIO_MINIMIZE_LATENCY; #endif //linux only options.priority = 99; unsigned int sampleRate = getSampleRate();//mSamplingRate; unsigned int bufferFrames = getBufferSizeInSamples();//mBufferSize; try { // IMPORTANT NOTE: It's VERY important to remember to pass this // as the user data in the process callback, otherwise memeber won't // be accessible mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &RtAudioInterface::wrapperRtAudioCallback, this, &options); } catch ( RtError& e ) { std::cout << '\n' << e.getMessage() << '\n' << std::endl; exit( 0 ); } // Setup parent class AudioInterface::setup(); } //******************************************************************************* void RtAudioInterface::listAllInterfaces() { RtAudio rtaudio; if ( rtaudio.getDeviceCount() < 1 ) { cout << "No audio devices found!" << endl; } else { for (unsigned int i = 0; i < rtaudio.getDeviceCount(); i++) { printDeviceInfo(i); cout << gPrintSeparator << endl; } } } //******************************************************************************* void RtAudioInterface::printDeviceInfo(unsigned int deviceId) { RtAudio rtaudio; RtAudio::DeviceInfo info; int i = deviceId; info = rtaudio.getDeviceInfo(i); std::vector sampleRates; cout << "Audio Device [" << i << "] : " << info.name << endl; cout << " Output Channels : " << info.outputChannels << endl; cout << " Input Channels : " << info.inputChannels << endl; sampleRates = info.sampleRates; cout << " Supported Sampling Rates: "; for (unsigned int ii = 0; ii(userData)->RtAudioCallback(outputBuffer,inputBuffer, nFrames, streamTime, status); } //******************************************************************************* int RtAudioInterface::startProcess() const { try { mRtAudio->startStream(); } catch ( RtError& e ) { std::cout << '\n' << e.getMessage() << '\n' << std::endl; return(-1); } return(0); } //******************************************************************************* int RtAudioInterface::stopProcess() const { try { mRtAudio->closeStream(); } catch ( RtError& e ) { std::cout << '\n' << e.getMessage() << '\n' << std::endl; return(-1); } return 0; } // OLD CODE // ============================================================================= /* int RtAudioInterface::processCallback(jack_nframes_t nframes) { mJackTrip->printTextTest(); return JackAudioInterface::processCallback(nframes); } */ //******************************************************************************* //int RtAudioInterface::RtAudioCallback(void *outputBuffer, void *inputBuffer, // unsigned int nFrames, // double /*streamTime*/, RtAudioStreamStatus /*status*/) //{ /* mInBuffer[0] = (sample_t*) inputBuffer; mOutBuffer[0] = (sample_t*) outputBuffer; //AudioInterface::callback(mInBuffer, mOutBuffer, mInputPacket, mOutputPacket, // nFrames, mInProcessBuffer, mOutProcessBuffer); // Output Process (from NETWORK to JACK) // ---------------------------------------------------------------- // Read Audio buffer from RingBuffer (read from incoming packets) //mOutRingBuffer->readSlotNonBlocking( mOutputPacket ); mJackTrip->receiveNetworkPacket( mOutputPacket ); // Extract separate channels to send to Jack for (int i = 0; i < getNumOutputChannels(); i++) { //-------- // This should be faster for 32 bits //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = mOutBuffer[i]; //sample buffer for channel i for (unsigned int j = 0; j < nFrames; j++) { //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); // Change the bit resolution on each sample //cout << tmp_sample[j] << endl; AudioInterface::fromBitToSampleConversion(&mOutputPacket[(i*getSizeInBytesPerChannel()) + (j*BIT16)], &tmp_sample[j], BIT16); } } // Input Process (from JACK to NETWORK) // ---------------------------------------------------------------- // Concatenate all the channels from jack to form packet for (int i = 0; i < getNumInputChannels(); i++) { //-------- // This should be faster for 32 bits //std::memcpy(&mInputPacket[i*getSizeInBytesPerChannel()], mInBuffer[i], // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = mInBuffer[i]; //sample buffer for channel i sample_t tmp_result; for (unsigned int j = 0; j < nFrames; j++) { // Add the input jack buffer to the buffer resulting from the output process tmp_result = tmp_sample[j]; AudioInterface::fromSampleToBitConversion(&tmp_result, &mInputPacket[(i*getSizeInBytesPerChannel()) + (j*BIT16)], BIT16); } } // Send Audio buffer to RingBuffer (these goes out as outgoing packets) //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); mJackTrip->sendNetworkPacket( mInputPacket ); */ //mTestJackTrip->printTextTest(); //if (mJackTrip != NULL) // cout << "(mJackTrip != NULL)" << endl; //if (mJackTrip == NULL) { cout << " === JACKTRIPNULL === " << endl; } //const int8_t* caca; //mJackTrip->sendNetworkPacket( mInputPacket ); //in_buffer = mInBuffer.data(); //mInBuffer.data() = (float*) inputBuffer; //mInBuffer[0] = static_cast(outputBuffer); //mOutBuffer[0] = static_cast(inputBuffer); //float* in_buffer = static_cast(inputBuffer); //float* out_buffer = static_cast(outputBuffer); //cout << "nFrames = ==================== = = = = = = = ======== " << this->getBufferSizeInSamples() << endl; //int8_t* input_packet = new int8_t[nFrames*2]; //tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 //JackAudioInterface::fromSampleToBitConversion(in_buffer, input_packet, BIT16); //for (int i = 0; isendNetworkPacket(input_packet); //cout << mJackTrip->getRingBuffersSlotSize() << endl; //delete[] input_packet; //mOutputPacket = static_cast(inputBuffer); //mInputPacket = static_cast(outputBuffer); // Allocate the Process Callback //------------------------------------------------------------------- // 1) First, process incoming packets // ---------------------------------- /* mJackTrip->receiveNetworkPacket( mOutputPacket ); // Extract separate channels to send to Jack for (int i = 0; i < getNumInputChannels(); i++) { sample_t* tmp_sample = mOutBuffer[i]; //sample buffer for channel i for (int j = 0; j < mBufferSize; j++) { fromBitToSampleConversion(&mOutputPacket[(i*getSizeInBytesPerChannel()) + (j*BIT16)], &tmp_sample[j], BIT16); } } */ // 3) Finally, send packets to peer // -------------------------------- // Input Process (from JACK to NETWORK) // ---------------------------------------------------------------- // Concatenate all the channels from jack to form packet /* for (int i = 0; i < getNumOutputChannels(); i++) { //-------- // This should be faster for 32 bits //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], // mSizeInBytesPerChannel); //-------- float* tmp_sample = in_buffer; //sample buffer for channel i //sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process sample_t tmp_result; for (int j = 0; j < mBufferSize; j++) { //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); // Change the bit resolution on each sample // Add the input jack buffer to the buffer resulting from the output process //tmp_result = tmp_sample[j] + tmp_process_sample[j]; fromSampleToBitConversion(tmp_sample, &mInputPacket[(i*getSizeInBytesPerChannel()) + (j*BIT16)], BIT16); } } // Send Audio buffer to RingBuffer (these goes out as outgoing packets) //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); mJackTrip->sendNetworkPacket( mInputPacket ); */ //return 0; //} jacktrip-1.1/src/LoopBack.cpp0000644000175000017500000000377512531357105016417 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file LoopBack.cpp * \author Juan-Pablo Caceres * \date July 2008 */ #include "LoopBack.h" #include "jacktrip_types.h" #include // for memcpy #include using std::cout; using std::endl; //using namespace JackTripNamespace; //******************************************************************************* void LoopBack::compute(int nframes, float** inputs, float** outputs) { for ( int i = 0; i < getNumInputs(); i++ ) { // Everything that comes out, copy back to inputs //memcpy(inputs[i], outputs[i], sizeof(sample_t) * nframes); memcpy(outputs[i], inputs[i], sizeof(sample_t) * nframes); } } jacktrip-1.1/src/PacketHeader.cpp0000644000175000017500000003112312531357105017231 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file PacketHeader.cpp * \author Juan-Pablo Caceres * \date July 2008 */ #include "PacketHeader.h" #include "JackTrip.h" #include #include #include #include using std::cout; using std::endl; // below is the gettimeofday definition for windows: this function is not defined in sys/time.h as it is in unix // for more info check: http://www.halcode.com/archives/2008/08/26/retrieving-system-time-gettimeofday/ #if defined __WIN_32__ #ifdef __cplusplus void GetSystemTimeAsFileTime(FILETIME*); inline int gettimeofday(struct timeval* p, void* tz /* IGNORED */) { union { long long ns100; /*time since 1 Jan 1601 in 100ns units */ FILETIME ft; } now; GetSystemTimeAsFileTime( &(now.ft) ); p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); return 0; } #else /* Must be defined somewhere else */ int gettimeofday(struct timeval* p, void* tz /* IGNORED */); #endif #endif //####################################################################### //####################### PacketHeader ################################## //####################################################################### //*********************************************************************** PacketHeader::PacketHeader(JackTrip* jacktrip) : mSeqNumber(0), mJackTrip(jacktrip) {} //*********************************************************************** uint64_t PacketHeader::usecTime() { struct timeval tv; gettimeofday (&tv, NULL); return ( (tv.tv_sec * 1000000) + // seconds (tv.tv_usec) ); // plus the microseconds. Type suseconds_t, range [-1, 1000000] } //####################################################################### //####################### DefaultHeader ################################# //####################################################################### //*********************************************************************** DefaultHeader::DefaultHeader(JackTrip* jacktrip) : PacketHeader(jacktrip), mJackTrip(jacktrip) { mHeader.TimeStamp = 0; mHeader.SeqNumber = 0; mHeader.BufferSize = 0; mHeader.SamplingRate = 0; mHeader.BitResolution = 0; //mHeader.NumInChannels = 0; //mHeader.NumOutChannels = 0; mHeader.NumChannels = 0; mHeader.ConnectionMode = 0; } //*********************************************************************** void DefaultHeader::fillHeaderCommonFromAudio() { mHeader.TimeStamp = PacketHeader::usecTime(); mHeader.BufferSize = mJackTrip->getBufferSizeInSamples(); mHeader.SamplingRate = mJackTrip->getSampleRateType (); mHeader.BitResolution = mJackTrip->getAudioBitResolution(); mHeader.NumChannels = mJackTrip->getNumChannels(); mHeader.ConnectionMode = static_cast(mJackTrip->getConnectionMode()); //printHeader(); } //*********************************************************************** void DefaultHeader::checkPeerSettings(int8_t* full_packet) { bool error = false; DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); // Check Buffer Size if ( peer_header->BufferSize != mHeader.BufferSize ) { std::cerr << "ERROR: Peer Buffer Size is : " << peer_header->BufferSize << endl; std::cerr << " Local Buffer Size is : " << mHeader.BufferSize << endl; std::cerr << "Make sure both machines use same buffer size" << endl; std::cerr << gPrintSeparator << endl; error = true; } // Check Sampling Rate if ( peer_header->SamplingRate != mHeader.SamplingRate ) { std::cerr << "ERROR: Peer Sampling Rate is : " << AudioInterface::getSampleRateFromType ( static_cast(peer_header->SamplingRate) ) << endl; std::cerr << " Local Sampling Rate is : " << AudioInterface::getSampleRateFromType ( static_cast(mHeader.SamplingRate) ) << endl; std::cerr << "Make sure both machines use the same Sampling Rate" << endl; std::cerr << gPrintSeparator << endl; error = true; } // Check Audio Bit Resolution if ( peer_header->BitResolution != mHeader.BitResolution ) { std::cerr << "ERROR: Peer Audio Bit Resolution is : " << static_cast(peer_header->BitResolution) << endl; std::cerr << " Local Audio Bit Resolution is : " << static_cast(mHeader.BitResolution) << endl; std::cerr << "Make sure both machines use the same Bit Resolution" << endl; std::cerr << gPrintSeparator << endl; error = true; } // Exit program if error if (error) { //std::cerr << "Exiting program..." << endl; //std::exit(1); //throw std::logic_error("Local and Peer Settings don't match"); emit signalError("Local and Peer Settings don't match"); } /// \todo Check number of channels and other parameters } //*********************************************************************** void DefaultHeader::printHeader() const { cout << "Default Packet Header:" << endl; cout << "Buffer Size = " << static_cast(mHeader.BufferSize) << endl; // Get the sample rate in Hz form the AudioInterface::samplingRateT int sample_rate = AudioInterface::getSampleRateFromType ( static_cast(mHeader.SamplingRate) ); cout << "Sampling Rate = " << sample_rate << endl; cout << "Audio Bit Resolutions = " << static_cast(mHeader.BitResolution) << endl; //cout << "Number of Input Channels = " << static_cast(mHeader.NumInChannels) << endl; //cout << "Number of Output Channels = " << static_cast(mHeader.NumOutChannels) << endl; cout << "Number of Channels = " << static_cast(mHeader.NumChannels) << endl; cout << "Sequence Number = " << static_cast(mHeader.SeqNumber) << endl; cout << "Time Stamp = " << mHeader.TimeStamp << endl; cout << "Connection Mode = " << static_cast(mHeader.ConnectionMode) << endl; cout << gPrintSeparator << endl; //cout << sizeof(mHeader) << endl; } //*********************************************************************** uint64_t DefaultHeader::getPeerTimeStamp(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return peer_header->TimeStamp; } //*********************************************************************** uint16_t DefaultHeader::getPeerSequenceNumber(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return peer_header->SeqNumber; } //*********************************************************************** uint16_t DefaultHeader::getPeerBufferSize(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return peer_header->BufferSize; } //*********************************************************************** uint8_t DefaultHeader::getPeerSamplingRate(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return peer_header->SamplingRate; } //*********************************************************************** uint8_t DefaultHeader::getPeerBitResolution(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return peer_header->BitResolution; } //*********************************************************************** uint8_t DefaultHeader::getPeerNumChannels(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return peer_header->NumChannels; } //*********************************************************************** uint8_t DefaultHeader::getPeerConnectionMode(int8_t* full_packet) const { DefaultHeaderStruct* peer_header; peer_header = reinterpret_cast(full_packet); return static_cast(peer_header->ConnectionMode); } //####################################################################### //####################### JamLinkHeader ################################# //####################################################################### //*********************************************************************** JamLinkHeader::JamLinkHeader(JackTrip* jacktrip) : PacketHeader(jacktrip), mJackTrip(jacktrip) { mHeader.Common = 0; mHeader.SeqNumber = 0; mHeader.TimeStamp = 0; } //*********************************************************************** void JamLinkHeader::fillHeaderCommonFromAudio() { // Check number of channels int num_inchannels = mJackTrip->getNumInputChannels(); if ( num_inchannels != 1 ) { //std::cerr << "ERROR: JamLink only support ONE channel. Run JackTrip using only one channel" // << endl; //std::exit(1); //std::cerr << "WARINING: JamLink only support ONE channel. Run JackTrip using only one channel" << endl; //throw std::logic_error("JamLink only support ONE channel. Run JackTrip using only one channel"); emit signalError("JamLink only support ONE channel. Run JackTrip using only one channel"); } // Sampling Rate int rate_type = mJackTrip->getSampleRateType(); if ( rate_type != AudioInterface::SR48 ) { //std::cerr << "WARINING: JamLink only support 48kHz for communication with JackTrip at the moment." << endl; //throw std::logic_error("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); emit signalError("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); } // Check Buffer Size int buf_size = mJackTrip->getBufferSizeInSamples(); if ( buf_size != 64 ) { //std::cerr << "WARINING: JamLink only support 64 buffer size for communication with JackTrip at the moment." << endl; //throw std::logic_error("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); emit signalError("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); } mHeader.Common = (ETX_MONO | ETX_16BIT | ETX_XTND) + 64; switch (rate_type) { case AudioInterface::SR48 : mHeader.Common = (mHeader.Common | ETX_48KHZ); break; case AudioInterface::SR44 : mHeader.Common = (mHeader.Common | ETX_44KHZ); break; case AudioInterface::SR32 : mHeader.Common = (mHeader.Common | ETX_32KHZ); break; case AudioInterface::SR22 : mHeader.Common = (mHeader.Common | ETX_22KHZ); break; default: //std::cerr << "ERROR: Sample rate not supported by JamLink" << endl; //std::exit(1); //throw std::out_of_range("Sample rate not supported by JamLink"); emit signalError("Sample rate not supported by JamLink."); break; } } //####################################################################### //####################### EmptyHeader ################################# //####################################################################### //*********************************************************************** EmptyHeader::EmptyHeader(JackTrip* jacktrip) : PacketHeader(jacktrip), mJackTrip(jacktrip) {} jacktrip-1.1/src/Settings.h0000644000175000017500000000637612531357105016172 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file Settings.h * \author Juan-Pablo Caceres * \date July 2008 */ #ifndef __SETTINGS_H__ #define __SETTINGS_H__ #include #include "DataProtocol.h" #ifndef __NO_JACK__ #include "JackAudioInterface.h" #endif //__NO_JACK__ #include "JackTrip.h" /** \brief Class to set usage options and parse settings from input */ class Settings : public QThread { Q_OBJECT; public: Settings(); virtual ~Settings(); /// \brief Parses command line input void parseInput(int argc, char** argv); void startJackTrip(); void stopJackTrip(); /// \brief Prints usage help void printUsage(); bool getLoopBack() { return mLoopBack; } public slots: void slotExitProgram() { std::cerr << "Exiting JackTrip..." << std::endl; std::exit(1); } private: JackTrip* mJackTrip; ///< JackTrip class JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol int mNumChans; ///< Number of Channels (inputs = outputs) int mBufferQueueLength; ///< Audio Buffer from network queue length AudioInterface::audioBitResolutionT mAudioBitResolution; QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode int mBindPortNum; ///< Bind Port Number int mPeerPortNum; ///< Peer Port Number char* mClientName; ///< JackClient Name bool mUnderrrunZero; ///< Use Underrun to Zero mode bool mLoopBack; ///< Loop-back mode bool mJamLink; ///< JamLink mode bool mEmptyHeader; ///< EmptyHeader mode bool mJackTripServer; ///< JackTrip Server mode QString mLocalAddress; ///< Local Address unsigned int mRedundancy; ///< Redundancy factor for data in the network bool mUseJack; ///< Use or not JackAduio bool mChanfeDefaultSR; ///< Change Default Sampling Rate bool mChanfeDefaultBS; ///< Change Default Buffer Size unsigned int mSampleRate; unsigned int mAudioBufferSize; }; #endif jacktrip-1.1/src/UdpDataProtocolPOSIX.h.tmp0000644000175000017500000000522512531357105021050 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file UdpDataProtocol.h * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __UDPDATAPROTOCOL_H__ #define __UDPDATAROTOCOL_H__ #include "DataProtocol.h" /** \brief UDP implementation of DataProtocol class * * * */ class UdpDataProtocol : public DataProtocol { public: /** \brief The class constructor * \param runmode Sets the run mode, use either SENDER or RECEIVER * \param peerHostOrIP IPv4 number or host name */ UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP); /** \brief The class destructor */ virtual ~UdpDataProtocol() {}; /** \brief Receives a packet * * This function makes sure we recieve a complete packet * of size n * \param buf Buffer to store the recieved packet * \param n size of packet to receive * \return number of bytes read, -1 on error */ virtual size_t receivePacket(char* buf, size_t n); /** \brief Sends a packet * * This function meakes sure we send a complete packet * of size n * \param buff Buffer to send * \param n size of packet to receive * \return number of bytes read, -1 on error */ virtual size_t sendPacket(const char* buff, size_t n); //virtual void run(); private: void setBindSocket(); int mSockFd; ///< Socket file descriptor }; #endif jacktrip-1.1/src/ThreadPoolTest.h0000644000175000017500000000307512531357105017264 0ustar zmoelnigzmoelnig/** * \file ThreadPoolTest.h * \author Juan-Pablo Caceres * \date October 2008 */ #ifndef __THREADPOOLTEST_H__ #define __THREADPOOLTEST_H__ #include #include #include #include #include #include "NetKS.h" #include "JackTripWorkerMessages.h" class ThreadPoolTest : public QObject, public QRunnable //class ThreadPoolTest : public QThread { Q_OBJECT; public: ThreadPoolTest() { setAutoDelete(false); } void run() { JackTripWorkerMessages jtm; QThread testThread; //jtm.moveToThread(&testThread); //QObject::connect(&jtm, SIGNAL(signalTest()), &jtm, SLOT(slotTest()), Qt::QueuedConnection); testThread.start(); jtm.play(); //testThread.wait(); //std::cout << "--------------- BEFORE ---------------" << std::endl; //NetKS netks; //netks.play(); //std::cout << "--------------- AFTER ---------------" << std::endl; QEventLoop loop; //QObject::connect(this, SIGNAL(stopELoop()), &loop, SLOT(quit()), Qt::QueuedConnection); loop.exec(); //std::cout << "--------------- EXITING QRUNNABLE---------------" << std::endl; /* while (true) { std::cout << "Hello world from thread" << std::endl; sleep(1); } */ } void stop() { std::cout << "--------------- ELOOP STOP---------------" << std::endl; emit stopELoop(); } signals: void stopELoop(); private slots: void fromServer() { std::cout << "--------------- SIGNAL RECEIVED ---------------" << std::endl; } }; #endif //__THREADPOOLTEST_H__ jacktrip-1.1/src/jacktrip_types.h0000644000175000017500000000627412531357105017422 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file jacktrip_types_jacktrip.h * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __JACKTRIP_TYPES_H__ #define __JACKTRIP_TYPES_H__ //#include #include //For QT4 types //namespace JackTripNamespace //{ //------------------------------------------------------------------------------- /** \name Audio typedefs * */ //------------------------------------------------------------------------------- //@{ /// Audio sample type //typedef jack_default_audio_sample_t sample_t; typedef float sample_t; //@} //------------------------------------------------------------------------------- /** \name Typedefs that guaranty some specific bit length * * It uses the QT4 types. This can be changed in the future, keeping * compatibility for the rest of the code. */ //------------------------------------------------------------------------------- //@{ /// Typedef for unsigned char. This type is guaranteed to be 8-bit. typedef quint8 uint8_t; /// Typedef for unsigned short. This type is guaranteed to be 16-bit. typedef quint16 uint16_t; /// Typedef for unsigned int. This type is guaranteed to be 32-bit. typedef quint32 uint32_t; /// \brief Typedef for unsigned long long int. This type is guaranteed to /// be 64-bit. //typedef quint64 uint64_t; /// Typedef for signed char. This type is guaranteed to be 8-bit. typedef qint8 int8_t; /// Typedef for signed short. This type is guaranteed to be 16-bit. typedef qint16 int16_t; /// Typedef for signed int. This type is guaranteed to be 32-bit. typedef qint32 int32_t; /// \brief Typedef for long long int. This type is guaranteed to /// be 64-bit. //typedef qint64 int64_t; //@} //} // end of namespace JackTripNamespace #endif jacktrip-1.1/src/DataProtocolPOSIX.cpp.tmp0000644000175000017500000001645112531357105020735 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file DataProtocol.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #include "DataProtocol.h" #include "jacktrip_globals.h" #include "JackAudioInterface.h" #include "PacketHeader.h" #include #include #include #include using std::cout; using std::endl; //******************************************************************************* DataProtocol::DataProtocol(const runModeT runmode, const packetHeaderTypeT headertype) : mRunMode(runmode), mStopped(false), mHasPacketsToReceive(false), mHeader(NULL) { //--------PROTOTYPE------------------------- if ( headertype == DEFAULT ) { mHeader = new DefaultHeader; } else if ( headertype == JAMLINK ) { mHeader = new JamLinkHeader; } //------------------------------------------ // Base ports gInputPort_0 and gOutputPort_0defined at globals.h if (mRunMode == RECEIVER) { mLocalPort = gInputPort_0; mPeerPort = gOutputPort_0; } else if (mRunMode == SENDER) { mLocalPort = gOutputPort_0; mPeerPort = gInputPort_0; } this->setLocalIPv4Address(); } //******************************************************************************* DataProtocol::~DataProtocol() { delete mHeader; } //******************************************************************************* void DataProtocol::stop() { mStopped = true; } //******************************************************************************* void DataProtocol::setLocalIPv4Address() { bzero(&mLocalIPv4Addr, sizeof(mLocalIPv4Addr)); mLocalIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol mLocalIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address mLocalIPv4Addr.sin_port = htons(mLocalPort);//set local port //std::cout << "mLocalPort = " << mLocalPort << std::endl; } //******************************************************************************* void DataProtocol::setPeerIPv4Address(const char* peerHostOrIP) { const char* peerAddress; // dotted decimal address to use in the struct below /// \todo Improve this to make it work also with local ip numbers, in a LAN, /// that don't have an assigned host name /* // Resolve Peer IPv4 with either doted integer IP or hostname //---------------------------------------------------------- std::cout << "Resolving Peer IPv4 address..." << std::endl; QHostInfo info = QHostInfo::fromName(peerHostOrIP); if ( !info.addresses().isEmpty() ) { std::cout << "Peer Address Found" << std::endl; QHostAddress address = info.addresses().first(); // use the first address in list peerAddress = address.toString().toLatin1(); } else { std::cerr << "ERROR: Could not set Peer IP Address" << std::endl; std::cerr << "Check that it's public or that the hostname exists" << std::endl; std::exit(1); } */ // temporary implementation to make this work /// \todo change this peerAddress = peerHostOrIP; // Set the Peer IPv4 Address struct //--------------------------------- bzero(&mPeerIPv4Addr, sizeof(mPeerIPv4Addr)); mPeerIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol mPeerIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address mPeerIPv4Addr.sin_port = htons(mPeerPort);//set Peer port //std::cout << "mPeerPort = " << mPeerPort << std::endl; int nPeer = inet_pton(AF_INET, peerAddress, &mPeerIPv4Addr.sin_addr); if ( nPeer == 1 ) { std::cout << "Successful Set Peer Address" << std::endl; } else if ( nPeer == 0 ) { std::cout << "Error: Incorrect presentation format for address" << std::endl; std::exit(1); } else { std::cout << "Error: Could not set Peer Address" << std::endl; std::exit(1); } } //******************************************************************************* void DataProtocol::setRingBuffer(std::tr1::shared_ptr RingBuffer) { mRingBuffer = RingBuffer; } //******************************************************************************* void DataProtocol::run() { std::cout << "Running DataProtocol Thread" << std::endl; std::cout << gPrintSeparator << std::endl; size_t packet_size = getAudioPacketSize(); int8_t packet[packet_size]; switch ( mRunMode ) { case RECEIVER : //----------------------------------------------------------------------------------- // Wait for the first packet to be ready and obtain address // from that packet /// \todo here is the place to read the datagram and check if the settings match /// the local ones. Extract this information from the header std::cout << "Waiting for Peer..." << std::endl; this->receivePacket( (char*) packet, packet_size); // This blocks waiting for the first packet std::cout << "Received Connection for Peer!" << std::endl; while ( !mStopped ) { //std::cout << "RECEIVING PACKETS" << std::endl; /// \todo Set a timer to report packats arriving too late //std::cout << "RECIEVING THREAD" << std::endl; this->receivePacket( (char*) packet, packet_size); /// \todo Change this to match buffer size //std::cout << "PACKET RECIEVED" << std::endl; mRingBuffer->insertSlotBlocking(packet); //std::cout << buf << std::endl; } break; case SENDER : //----------------------------------------------------------------------------------- while ( !mStopped ) { //std::cout << "SENDING PACKETS --------------------------" << std::endl; /// \todo This should be blocking, since we don't want to send trash mRingBuffer->readSlotBlocking(packet); //std::cout << "SENDING PACKETS" << std::endl; this->sendPacket( (char*) packet, packet_size); //std::cout << "SENDING PACKETS DONE!!!" << std::endl; //this->sendPacket( sendtest, 64); } break; } } void DataProtocol::setAudioPacketSize(size_t size_bytes) { mAudioPacketSize = size_bytes; } size_t DataProtocol::getAudioPacketSize() { return(mAudioPacketSize); } jacktrip-1.1/src/UdpDataProtocolPOSIX.cpp.tmp0000644000175000017500000001024112531357105021375 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file UdpDataProtocol.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #include "UdpDataProtocol.h" #include #include #include #include // NOTE: It's better not to use // using namespace std; // because some functions (like exit()) get confused with QT functions //******************************************************************************* UdpDataProtocol::UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP) : DataProtocol(runmode) { setPeerIPv4Address(peerHostOrIP); setBindSocket(); } //******************************************************************************* void UdpDataProtocol::setBindSocket() { // UDP socket creation mSockFd = socket(AF_INET, SOCK_DGRAM, 0); if ( mSockFd < 0 ) { std::cerr << "ERROR: UDP Socket Error" << std::endl; std::exit(0); } // Bind local address and port /// \todo Bind to a different port in case this one is used by a different instance /// of the program struct sockaddr_in LocalIPv4Addr = getLocalIPv4AddressStruct(); int nBind = bind(mSockFd, (struct sockaddr *) &LocalIPv4Addr, sizeof(LocalIPv4Addr)); if ( nBind < 0 ) { std::cerr << "ERROR: UDP Socket Bind Error" << std::endl; std::exit(0); } std::cout << "Successful socket creation and port binding" << std::endl; //Connected UDP struct sockaddr_in PeerIPv4Addr = getPeerIPv4AddressStruct(); int nCon = ::connect(mSockFd, (struct sockaddr *) &PeerIPv4Addr, sizeof(PeerIPv4Addr)); if ( nCon < 0) { std::cerr << "ERROR: UDP Socket Connect Error" << std::endl; std::exit(0); } } //******************************************************************************* // Adapted form Stevens' "Unix Network Programming", third edition // Page 88 (readn) size_t UdpDataProtocol::receivePacket(char* buff, size_t n) { size_t nleft; ssize_t nread; char* ptr; ptr = buff; nleft = n; while (nleft > 0) { if ( (nread = ::read(mSockFd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; // and call read() again else return(-1); } else if (nread == 0) break; // EOF nleft -= nread; ptr += nread; } return(n - nleft); } //******************************************************************************* // Adapted form Stevens' "Unix Network Programming", third edition // Page 88 (writen) // Write "n" bytes to a descriptor size_t UdpDataProtocol::sendPacket(const char* buff, size_t n) { size_t nleft; ssize_t nwritten; const char* ptr; ptr = buff; nleft = n; while (nleft > 0) { if ( (nwritten = ::write(mSockFd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; // and call write() again else return(-1); // error } nleft -= nwritten; ptr += nwritten; } return(n); } jacktrip-1.1/src/LoopBack.h0000644000175000017500000000446012531357105016054 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file LoopBack.h * \author Juan-Pablo Caceres * \date July 2008 */ /** \brief Connect Inputs to Outputs * */ #ifndef __LOOPBACK_H__ #define __LOOPBACK_H__ #include "ProcessPlugin.h" /** \brief This Class just copy audio from its inputs to its outputs. * * It can be use to do loopback without the need to externally connect channels * in JACK. Note that if you do connect the channels in jack, you'll * be effectively multiplying the signal by 2. */ class LoopBack : public ProcessPlugin { public: /// \brief The class constructor sets the number of channels to connect as loopback LoopBack(int numchans) { mNumChannels = numchans; }; /// \brief The class destructor virtual ~LoopBack() {}; virtual int getNumInputs() { return(mNumChannels); }; virtual int getNumOutputs() { return(mNumChannels); }; virtual void compute(int nframes, float** inputs, float** outputs); private: int mNumChannels; }; #endif jacktrip-1.1/src/jacktrip_globals.cpp0000644000175000017500000001315512531357105020230 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file globals.cpp * \author Juan-Pablo Caceres * \date August 2008 */ #include #include #include #include "jacktrip_globals.h" #include "jacktrip_types.h" #if defined ( __LINUX__ ) #include #include #include #endif //__LINUX__ #if defined ( __MAC_OSX__ ) #include #include //#include //#include //#include //#include //#include //#include //m#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include #endif //__MAC_OSX__ #if defined ( __MAC_OSX__ ) //******************************************************************************* //http://developer.apple.com/DOCUMENTATION/Darwin/Conceptual/KernelProgramming/scheduler/chapter_8_section_4.html //http://lists.apple.com/archives/darwin-dev/2007/Sep/msg00035.html int set_realtime(int period, int computation, int constraint) { //AbsoluteTime time; //clock_get_uptime((uint64_t *)&time); //uint64_t result; //clock_get_uptime(&result); //clock_get_system_microtime(&result,&result); struct thread_time_constraint_policy ttcpolicy; int ret; ttcpolicy.period=period; // HZ/160 ttcpolicy.computation=computation; // HZ/3300; ttcpolicy.constraint=constraint; // HZ/2200; ttcpolicy.preemptible=1; if ((ret=thread_policy_set(mach_thread_self(), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&ttcpolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS) { fprintf(stderr, "set_realtime() failed.\n"); return 0; } return 1; } #endif //__MAC_OSX__ #if defined ( __LINUX__ ) //******************************************************************************* int get_fifo_priority (bool half) { int min, max, priority; min = sched_get_priority_min (SCHED_FIFO); max = sched_get_priority_max (SCHED_FIFO); if (half) { priority = (max - (max - min) / 2); } else { priority = max; } //priority=min; return priority; } #endif //__LINUX__ #if defined ( __LINUX__ ) //******************************************************************************* int set_fifo_priority (bool half) { struct sched_param p; int priority; // scheduling priority if (true) // (!getuid () || !geteuid ()) { priority = get_fifo_priority (half); p.sched_priority = priority; if (sched_setscheduler (0, SCHED_FIFO, &p) == -1) { fprintf (stderr, "\ncould not activate scheduling with priority %d\n", priority); return -1; } seteuid (getuid ()); //fprintf (stderr, // "\nset scheduling priority to %d (SCHED_FIFO)\n", // priority); } else { fprintf (stderr, "\ninsufficient privileges to set scheduling priority\n"); priority = 0; } return priority; } #endif //__LINUX__ #if defined ( __LINUX__ ) //******************************************************************************* int set_realtime_priority (void) { struct sched_param schp; memset (&schp, 0, sizeof (schp)); schp.sched_priority = sched_get_priority_max (SCHED_FIFO); if (sched_setscheduler (0, SCHED_FIFO, &schp) != 0) { perror ("set_scheduler"); return -1; } return 0; } #endif //__LINUX__ #if defined ( __WIN_32__ ) int win_priority() { if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == 0) { printf("set Priority Class failed \n"); return -1; } if(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == 0) { printf("set Thread Priority failed \n"); return -1; } return GetThreadPriority(GetCurrentThread()); } #endif //__WIN_32__ void set_crossplatform_realtime_priority() { #if defined ( __LINUX__ ) set_fifo_priority (false); #endif //__LINUX__ #if defined ( __MAC_OSX__ ) set_realtime(1250000,60000,90000); #endif //__MAC_OSX__ #if defined __WIN_32__ win_priority(); #endif } jacktrip-1.1/src/DataProtocol.cpp0000644000175000017500000000406112531357105017305 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file DataProtocol.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #include "DataProtocol.h" #include "jacktrip_globals.h" #include "JackTrip.h" #include #include #include #include using std::cout; using std::endl; //******************************************************************************* DataProtocol::DataProtocol(JackTrip* jacktrip, const runModeT runmode, int /*bind_port*/, int /*peer_port*/) : mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip) {} //******************************************************************************* DataProtocol::~DataProtocol() {} jacktrip-1.1/src/jacktrip_globals.h0000644000175000017500000001060112531357105017666 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file globals.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __JACKTRIP_GLOBALS_H__ #define __JACKTRIP_GLOBALS_H__ #include "AudioInterface.h" //#include "JackAudioInterface.h" /// \todo Add this namespace //namespace JackTrip const char* const gVersion = "1.1"; ///< JackTrip version //******************************************************************************* /// \name Default Values //@{ const int gDefaultNumInChannels = 2; const int gDefaultNumOutChannels = 2; //const JackAudioInterface::audioBitResolutionT gDefaultBitResolutionMode = // JackAudioInterface::BIT16; const AudioInterface::audioBitResolutionT gDefaultBitResolutionMode = AudioInterface::BIT16; const int gDefaultQueueLength = 4; const int gDefaultOutputQueueLength = 4; const uint32_t gDefaultSampleRate = 48000; const uint32_t gDefaultBufferSizeInSamples = 128; const QString gDefaultLocalAddress = QString(); const int gDefaultRedundancy = 1; const int gTimeOutMultiThreadedServer = 5000; // seconds const int gWaitCounter = 60; //@} //******************************************************************************* /// \name Network related ports //@{ const int gDefaultPort = 4464; ///< Default JackTrip Port //const int gInputPort_0 = 4464; ///< Input base port //const int gOutputPort_0 = 4465; ///< Output base port //const int gDefaultSendPort = 4464; ///< Default for to use to send packet //@} //******************************************************************************* /// \name Separator for terminal printing //@{ const char* const gPrintSeparator = "---------------------------------------------------------"; //@} //******************************************************************************* /// \name Global flags //@{ extern int gVerboseFlag; ///< Verbose mode flag declaration //@} //******************************************************************************* /// \name JackAudio //@{ const int gJackBitResolution = 32; ///< Audio Bit Resolution of the Jack Server //@} //******************************************************************************* /// \name Global Functions void set_crossplatform_realtime_priority(); //@{ // Linux Specific Functions #if defined ( __LINUX__ ) /// \brief Returns fifo priority int get_fifo_priority (bool half); /// \brief Set fifo priority (if user has sufficient privileges). int set_fifo_priority (bool half); int set_realtime_priority (void); #endif //__LINUX__ //@} //@{ // Mac OS X Specific Functions #if defined ( __MAC_OSX__ ) int set_realtime(int period, int computation, int constraint); #endif //__MAC_OSX__ //@} //@{ // Windows Specific Functions #if defined ( __WIN_32__ ) int win_priority(); #endif //__WIN_32__ //@} //******************************************************************************* /// \name JackTrip Server parameters //@{ /// Maximum Threads that can be run at the same time const int gMaxThreads = 290; // some pthread limit around 297? /// Public well-known UDP port to where the clients will connect const int gServerUdpPort = 4464; //@} #endif jacktrip-1.1/src/JackAudioInterface.h0000644000175000017500000001705612531357105020042 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackAudioInterface.h * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __JACKAUDIOINTERFACE_H__ #define __JACKAUDIOINTERFACE_H__ #include //#include //for shared_ptr #include //for mem_fun_ref #include #include #include #include #include "jacktrip_types.h" #include "ProcessPlugin.h" #include "AudioInterface.h" //class JackTrip; //forward declaration /** \brief Class that provides an interface with the Jack Audio Server * * \todo implement srate_callback * \todo automatically starts jack with buffer and sample rate settings specified by the user */ class JackAudioInterface : public AudioInterface { public: /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param NumInChans Number of Input Channels * \param NumOutChans Number of Output Channels * \param AudioBitResolution Audio Sample Resolutions in bits * \param ClientName Client name in Jack */ JackAudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16, const char* ClientName = "JackTrip"); /// \brief The class destructor virtual ~JackAudioInterface(); /// \brief Setup the client virtual void setup(); /** \brief Tell the JACK server that we are ready to roll. The * process-callback will start running. This runs on its own thread. * \return 0 on success, otherwise a non-zero error code */ virtual int startProcess() const; /** \brief Stops the process-callback thread * \return 0 on success, otherwise a non-zero error code */ virtual int stopProcess() const; /// \brief Connect the default ports, capture to sends, and receives to playback void connectDefaultPorts(); //--------------SETTERS--------------------------------------------- /// \brief Set Client Name to something different that the default (JackTrip) virtual void setClientName(const char* ClientName) { mClientName = ClientName; } virtual void setSampleRate(uint32_t /*sample_rate*/) { std::cout << "WARING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } virtual void setBufferSizeInSamples(uint32_t /*buf_size*/) { std::cout << "WARING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } //------------------------------------------------------------------ //--------------GETTERS--------------------------------------------- /// \brief Get the Jack Server Sampling Rate, in samples/second virtual uint32_t getSampleRate() const; /// \brief Get the Jack Server Buffer Size, in samples virtual uint32_t getBufferSizeInSamples() const; /// \brief Get the Jack Server Buffer Size, in bytes virtual uint32_t getBufferSizeInBytes() const { return (getBufferSizeInSamples() * getAudioBitResolution()/8); } /// \brief Get size of each audio per channel, in bytes virtual size_t getSizeInBytesPerChannel() const; //------------------------------------------------------------------ private: /** \brief Private method to setup a client of the Jack server. * \exception std::runtime_error Can't start Jack * * This method is called by the class constructors. It does the following:\n * - Connects to the JACK server * - Sets the shutdown process callback * - Creates the appropriate number of input and output channels */ void setupClient(); /// \brief Creates input and output channels in the Jack client void createChannels(); /** \brief JACK calls this shutdown_callback if the server ever shuts down or * decides to disconnect the client. */ static void jackShutdown(void*); /** \brief Set the process callback of the member function processCallback. * This process will be called by the JACK server whenever there is work to be done. */ void setProcessCallback(); /** \brief JACK process callback * * This is the function to be called to process audio. This function is * of the type JackProcessCallback, which is defined as:\n * typedef int(* JackProcessCallback)(jack_nframes_t nframes, void *arg) * \n * See * http://jackaudio.org/files/docs/html/types_8h.html#4923142208a8e7dacf00ca7a10681d2b * for more details */ int processCallback(jack_nframes_t nframes); /** \brief Wrapper to cast the member processCallback to a static function pointer * that can be used with jack_set_process_callback * * jack_set_process_callback needs a static member function pointer. A normal * member function won't work because a this pointer is passed under the scenes. * That's why we * need to cast the member funcion processCallback to the static function * wrapperProcessCallback. The callback is then set as:\n * jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, * this) */ // reference : http://article.gmane.org/gmane.comp.audio.jackit/12873 static int wrapperProcessCallback(jack_nframes_t nframes, void *arg) ; int mNumInChans;///< Number of Input Channels int mNumOutChans; ///< Number of Output Channels int mNumFrames; ///< Buffer block size, in samples //int mAudioBitResolution; ///< Bit resolution in audio samples AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode jack_client_t* mClient; ///< Jack Client const char* mClientName; ///< Jack Client Name QVarLengthArray mInPorts; ///< Vector of Input Ports (Channels) QVarLengthArray mOutPorts; ///< Vector of Output Ports (Channels) QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel QVector mProcessPlugins; ///< Vector of ProcesPlugins JackTrip* mJackTrip; ///< JackTrip mediator class static QMutex sJackMutex; ///< Mutex to make thread safe jack functions that are not }; #endif jacktrip-1.1/src/DataProtocol.h0000644000175000017500000001557312531357105016764 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file DataProtocol.h * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __DATAPROTOCOL_H__ #define __DATAPROTOCOL_H__ #ifdef __WIN_32__ #include #endif #ifndef __WIN_32__ #include //sockaddr_in{} and other Internet defns #include //inet(3) functions #include //#include //for shared_ptr #endif #include #include #include #include #include class JackTrip; // forward declaration /** \brief Base class that defines the transmission protocol. * * This base class defines most of the common method to setup and connect * sockets using the individual protocols (UDP, TCP, SCTP, etc). * * The class has to be constructed using one of two modes (runModeT):\n * - SENDER * - RECEIVER * * This has to be specified as a constructor argument. When using, create two instances * of the class, one to receive and one to send packets. Each instance will run on a * separate thread. * * Redundancy and forward error correction should be implemented on each * Transport protocol, cause they depend on the protocol itself * * \todo This Class should contain definition of jacktrip header and basic funcionality to obtain * local machine IPs and maybe functions to manipulate IPs. * Redundancy and forward error correction should be implemented on each * Transport protocol, cause they depend on the protocol itself * * \todo The transport protocol itself has to be implemented subclassing this class, i.e., * using a TCP or UDP protocol. * * Even if the underlined transmission protocol is stream oriented (as in TCP), * we send packets that are the size of the audio processing buffer. * Use AudioInterface::getBufferSize to obtain this value. * * Each transmission (i.e., inputs and outputs) run on its own thread. */ class DataProtocol : public QThread { Q_OBJECT; public: //----------ENUMS------------------------------------------ /// \brief Enum to define packet header types enum packetHeaderTypeT { DEFAULT, ///< Default application header JAMLINK, ///< Header to use with Jamlinks EMPTY ///< Empty Header }; /// \brief Enum to define class modes, SENDER or RECEIVER enum runModeT { SENDER, ///< Set class as a Sender (send packets) RECEIVER ///< Set class as a Receiver (receives packets) }; //--------------------------------------------------------- /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param runmode Sets the run mode, use either DataProtocol::SENDER or * DataProtocol::RECEIVER * \param headertype packetHeaderTypeT header type to use for packets * \param bind_port Port number to bind for this socket (this is the receive or send port depending on the runmode) * \param peer_port Peer port number (this is the receive or send port depending on the runmode) */ DataProtocol(JackTrip* jacktrip, const runModeT runmode, int bind_port, int peer_port); /// \brief The class destructor virtual ~DataProtocol(); /** \brief Implements the thread loop * * Depending on the runmode, with will run a DataProtocol::SENDER thread or * DataProtocol::RECEIVER thread */ virtual void run() = 0; /// \brief Stops the execution of the Thread virtual void stop() { QMutexLocker lock(&mMutex); mStopped = true; } /** \brief Sets the size of the audio part of the packets * \param size_bytes Size in bytes */ void setAudioPacketSize(const size_t size_bytes){ mAudioPacketSize = size_bytes; } /** \brief Get the size of the audio part of the packets * \return size_bytes Size in bytes */ size_t getAudioPacketSizeInBites() { return(mAudioPacketSize); } /** \brief Set the peer address * \param peerHostOrIP IPv4 number or host name * \todo implement here instead of in the subclass UDP */ virtual void setPeerAddress(const char* peerHostOrIP) = 0; /** \brief Set the peer incomming (receiving) port number * \param port Port number * \todo implement here instead of in the subclass UDP */ virtual void setPeerPort(int port) = 0; //virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, // uint16_t& port) = 0; signals: void signalError(const char* error_message); void signalReceivedConnectionFromPeer(); protected: /** \brief Get the Run Mode of the object * \return SENDER or RECEIVER */ runModeT getRunMode() const { return mRunMode; } /// Boolean stop the execution of the thread volatile bool mStopped; /// Boolean to indicate if the RECEIVER is waiting to obtain peer address volatile bool mHasPeerAddress; /// Boolean that indicates if a packet was received volatile bool mHasPacketsToReceive; QMutex mMutex; private: int mLocalPort; ///< Local Port number to Bind int mPeerPort; ///< Peer Port number to Bind const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct /// Number of clients running to check for ports already used /// \note Unimplemented, try to find another way to check for used ports static int sClientsRunning; size_t mAudioPacketSize; ///< Packet audio part size /// \todo check a better way to access the header from the subclasses protected: //PacketHeader* mHeader; ///< Packet Header JackTrip* mJackTrip; ///< JackTrip mediator class }; #endif jacktrip-1.1/src/UdpMasterListener.cpp0000644000175000017500000003434712531357105020336 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file UdpMasterListener.cpp * \author Juan-Pablo Caceres and Chris Chafe * \date September 2008 */ #include #include #include #include #include #include #include #include #include "UdpMasterListener.h" #include "JackTripWorker.h" #include "jacktrip_globals.h" using std::cout; using std::endl; //******************************************************************************* UdpMasterListener::UdpMasterListener(int server_port) : //mJTWorker(NULL), mServerPort(server_port), mStopped(false), mTotalRunningThreads(0) { // Register JackTripWorker with the master listener //mJTWorker = new JackTripWorker(this); mJTWorkers = new QVector; for (int i = 0; iinsert(i, NULL); } //mJTWorkers = new JackTripWorker(this); mThreadPool.setExpiryTimeout(3000); // msec (-1) = forever // Inizialize IP addresses for (int i = 0; iat(i); } delete mJTWorkers; } //******************************************************************************* // Now that the first handshake is with TCP server, if the addreess/peer port of // the client is already on the thread pool, it means that a new connection is // requested (the old was desconnected). So we have to remove that thread from // the pool and then connect again. void UdpMasterListener::run() { mStopped = false; QHostAddress PeerAddress; // Object to store peer address int peer_udp_port; // Peer listening port int server_udp_port; // Server assigned udp port // Create and bind the TCP server // ------------------------------ QTcpServer TcpServer; if ( !TcpServer.listen(QHostAddress::Any, mServerPort) ) { std::cerr << "TCP Socket Server ERROR: " << TcpServer.errorString().toStdString() << endl; std::exit(1); } const int tcpTimeout = 5*1000; cout << "JackTrip MULTI-THREADED SERVER: TCP Server Listening in Port = " << TcpServer.serverPort() << endl; while ( !mStopped ) { cout << "JackTrip MULTI-THREADED SERVER: Waiting for client connections..." << endl; cout << "=======================================================" << endl; while ( !TcpServer.waitForNewConnection(1000) ) { if (mStopped) { return; } } // block until a new connection is received cout << "JackTrip MULTI-THREADED SERVER: Client Connection Received!" << endl; // Control loop to be able to exit if UDPs or TCPs error ocurr for (int dum = 0; dum<1; dum++) { QTcpSocket *clientConnection = TcpServer.nextPendingConnection(); if ( !clientConnection->waitForConnected(tcpTimeout) ) { std::cerr << clientConnection->errorString().toStdString() << endl; break; } PeerAddress = clientConnection->peerAddress(); cout << "JackTrip MULTI-THREADED SERVER: Client Connect Received from Address : " << PeerAddress.toString().toStdString() << endl; // Get UDP port from client // ------------------------ peer_udp_port = readClientUdpPort(clientConnection); if ( peer_udp_port == 0 ) { break; } cout << "JackTrip MULTI-THREADED SERVER: Client UDP Port is = " << peer_udp_port << endl; // Check is client is new or not // ----------------------------- // Check if Address is not already in the thread pool // check by comparing 32-bit addresses int id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port); // If the address is not new, we need to remove the client from the pool // before re-starting the connection if (id == -1) { int id_remove; id_remove = getPoolID(PeerAddress.toIPv4Address(), peer_udp_port); // stop the thread mJTWorkers->at(id_remove)->stopThread(); // block until the thread has been removed from the pool while ( isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port) == -1 ) { cout << "JackTrip MULTI-THREADED SERVER: Removing JackTripWorker from pool..." << endl; QThread::msleep(10); } // Get a new ID for this client //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port); id = getPoolID(PeerAddress.toIPv4Address(), peer_udp_port); } // Assign server port and send it to Client server_udp_port = mBasePort+id; if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) { clientConnection->close(); delete clientConnection; releaseThread(id); break; } // Close and Delete the socket // --------------------------- clientConnection->close(); delete clientConnection; cout << "JackTrip MULTI-THREADED SERVER: Client TCP Socket Closed!" << endl; // Spawn Thread to Pool // -------------------- // Register JackTripWorker with the master listener delete mJTWorkers->at(id); // just in case the Worker was previously created mJTWorkers->replace(id, new JackTripWorker(this)); // redirect port and spawn listener cout << "---> JackTrip MULTI-THREADED SERVER: Spawning Listener..." << endl; { QMutexLocker lock(&mMutex); mJTWorkers->at(id)->setJackTrip(id, mActiveAddress[id][0], server_udp_port, mActiveAddress[id][1], 1); /// \todo temp default to 1 channel } //send one thread to the pool cout << "---> JackTrip MULTI-THREADED SERVER: Starting Thread..." << endl; mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority); // wait until one is complete before another spawns while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); } //mTotalRunningThreads++; cout << "JackTrip MULTI-THREADED SERVER: Total Running Threads: " << mTotalRunningThreads << endl; cout << "===============================================================" << endl; QThread::msleep(100); } } /* // Create objects on the stack QUdpSocket MasterUdpSocket; QHostAddress PeerAddress; uint16_t peer_port; // Ougoing Peer port, in case they're not using the default // Bind the socket to the well known port bindUdpSocket(MasterUdpSocket, mServerPort); char buf[1]; cout << "Server Listening in UDP Port: " << mServerPort << endl; cout << "Waiting for client..." << endl; cout << "=======================================================" << endl; while ( !mStopped ) { //cout << "WAITING........................." << endl; while ( MasterUdpSocket.hasPendingDatagrams() ) { cout << "Received request from Client!" << endl; // Get Client IP Address and outgoing port from packet int rv = MasterUdpSocket.readDatagram(buf, 1, &PeerAddress, &peer_port); cout << "Peer Port in Server ==== " << peer_port << endl; if (rv < 0) { std::cerr << "ERROR: Bad UDP packet read..." << endl; } /// \todo Get number of channels in the client from header // check by comparing 32-bit addresses /// \todo Add the port number in the comparison cout << "peer_portpeer_portpeer_port === " << peer_port << endl; int id = isNewAddress(PeerAddress.toIPv4Address(), peer_port); //cout << "IDIDIDIDIDDID === " << id << endl; // If the address is new, create a new thread in the pool if (id >= 0) // old address is -1 { // redirect port and spawn listener sendToPoolPrototype(id); // wait until one is complete before another spawns while (mJTWorker->isSpawning()) { QThread::msleep(10); } mTotalRunningThreads++; cout << "Total Running Threads: " << mTotalRunningThreads << endl; cout << "=======================================================" << endl; } //cout << "ENDDDDDDDDDDDDDDDDDd === " << id << endl; } QThread::msleep(100); } */ } //******************************************************************************* // Returns 0 on error int UdpMasterListener::readClientUdpPort(QTcpSocket* clientConnection) { // Read the size of the package // ---------------------------- //tcpClient.waitForReadyRead(); cout << "Reading UDP port from Server..." << endl; while (clientConnection->bytesAvailable() < (int)sizeof(uint16_t)) { if (!clientConnection->waitForReadyRead()) { std::cerr << "TCP Socket ERROR: " << clientConnection->errorString().toStdString() << endl; return 0; } } cout << "Ready To Read From Socket!" << endl; // Read UDP Port Number from Server // -------------------------------- int udp_port; int size = sizeof(udp_port); char port_buf[size]; clientConnection->read(port_buf, size); std::memcpy(&udp_port, port_buf, size); return udp_port; } //******************************************************************************* int UdpMasterListener::sendUdpPort(QTcpSocket* clientConnection, int udp_port) { // Send Port Number to Client // -------------------------- char port_buf[sizeof(udp_port)]; std::memcpy(port_buf, &udp_port, sizeof(udp_port)); clientConnection->write(port_buf, sizeof(udp_port)); while ( clientConnection->bytesToWrite() > 0 ) { if ( clientConnection->state() == QAbstractSocket::ConnectedState ) { clientConnection->waitForBytesWritten(-1); } else { return 0; } } return 1; cout << "Port sent to Client" << endl; } //******************************************************************************* /* void UdpMasterListener::sendToPoolPrototype(int id) { mJTWorker->setJackTrip(id, mActiveAddress[id][0], mBasePort+(2*id), mActiveAddress[id][1], 1); /// \todo temp default to 1 channel mThreadPool.start(mJTWorker, QThread::TimeCriticalPriority); //send one thread to the pool } */ //******************************************************************************* void UdpMasterListener::bindUdpSocket(QUdpSocket& udpsocket, int port) throw(std::runtime_error) { // QHostAddress::Any : let the kernel decide the active address if ( !udpsocket.bind(QHostAddress::Any, port, QUdpSocket::DefaultForPlatform) ) { //std::cerr << "ERROR: could not bind UDP socket" << endl; //std::exit(1); throw std::runtime_error("Could not bind UDP socket. It may be already binded."); } else { cout << "UDP Socket Receiving in Port: " << port << endl; } } //******************************************************************************* // check by comparing 32-bit addresses int UdpMasterListener::isNewAddress(uint32_t address, uint16_t port) { QMutexLocker lock(&mMutex); bool busyAddress = false; int id = 0; /* while ( !busyAddress && (id #include using std::cout; using std::endl; //******************************************************************************* AudioInterface::AudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, audioBitResolutionT AudioBitResolution) : mJackTrip(jacktrip), mNumInChans(NumInChans), mNumOutChans(NumOutChans), mAudioBitResolution(AudioBitResolution*8), mBitResolutionMode(AudioBitResolution), mSampleRate(gDefaultSampleRate), mBufferSizeInSamples(gDefaultBufferSizeInSamples), mInputPacket(NULL), mOutputPacket(NULL) { // Set pointer to NULL for (int i = 0; i < mNumInChans; i++) { mInProcessBuffer[i] = NULL; } for (int i = 0; i < mNumOutChans; i++) { mOutProcessBuffer[i] = NULL; } } //******************************************************************************* AudioInterface::~AudioInterface() { delete[] mInputPacket; delete[] mOutputPacket; for (int i = 0; i < mNumInChans; i++) { delete[] mInProcessBuffer[i]; } for (int i = 0; i < mNumOutChans; i++) { delete[] mOutProcessBuffer[i]; } } //******************************************************************************* void AudioInterface::setup() { // Allocate buffer memory to read and write mSizeInBytesPerChannel = getSizeInBytesPerChannel(); int size_input = mSizeInBytesPerChannel * getNumInputChannels(); int size_output = mSizeInBytesPerChannel * getNumOutputChannels(); mInputPacket = new int8_t[size_input]; mOutputPacket = new int8_t[size_output]; // Initialize and asign memory for ProcessPlugins Buffers mInProcessBuffer.resize(mNumInChans); mOutProcessBuffer.resize(mNumOutChans); int nframes = getBufferSizeInSamples(); for (int i = 0; i < mNumInChans; i++) { mInProcessBuffer[i] = new sample_t[nframes]; // set memory to 0 std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); } for (int i = 0; i < mNumOutChans; i++) { mOutProcessBuffer[i] = new sample_t[nframes]; // set memory to 0 std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); } } //******************************************************************************* size_t AudioInterface::getSizeInBytesPerChannel() const { return (getBufferSizeInSamples() * getAudioBitResolution()/8); } //******************************************************************************* void AudioInterface::callback(QVarLengthArray& in_buffer, QVarLengthArray& out_buffer, unsigned int n_frames) { // Allocate the Process Callback //------------------------------------------------------------------- // 1) First, process incoming packets // ---------------------------------- computeProcessFromNetwork(out_buffer, n_frames); // 2) Dynamically allocate ProcessPlugin processes // ----------------------------------------------- // The processing will be done in order of allocation /// \todo Implement for more than one process plugin, now it just works propertely with one. /// do it chaining outputs to inputs in the buffers. May need a tempo buffer for (int i = 0; i < mNumInChans; i++) { std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * n_frames); std::memcpy(mInProcessBuffer[i], out_buffer[i], sizeof(sample_t) * n_frames); } for (int i = 0; i < mNumOutChans; i++) { std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames); } for (int i = 0; i < mProcessPlugins.size(); i++) { mProcessPlugins[i]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data()); } // 3) Finally, send packets to peer // -------------------------------- computeProcessToNetwork(in_buffer, n_frames); ///************PROTORYPE FOR CELT************************** ///******************************************************** /* CELTMode* mode; int* error; mode = celt_mode_create(48000, 2, 64, error); */ //celt_mode_create(48000, 2, 64, NULL); //unsigned char* compressed; //CELTEncoder* celtEncoder; //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); ///******************************************************** ///******************************************************** } //******************************************************************************* // Before sending and reading to Jack, we have to round to the sample resolution // that the program is using. Jack uses 32 bits (gJackBitResolution in globals.h) // by default void AudioInterface::computeProcessFromNetwork(QVarLengthArray& out_buffer, unsigned int n_frames) { /// \todo cast *mInBuffer[i] to the bit resolution // Output Process (from NETWORK to JACK) // ---------------------------------------------------------------- // Read Audio buffer from RingBuffer (read from incoming packets) mJackTrip->receiveNetworkPacket( mOutputPacket ); // Extract separate channels to send to Jack for (int i = 0; i < mNumOutChans; i++) { //-------- // This should be faster for 32 bits //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = out_buffer[i]; //sample buffer for channel i for (unsigned int j = 0; j < n_frames; j++) { // Change the bit resolution on each sample fromBitToSampleConversion( &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], &tmp_sample[j], mBitResolutionMode ); } } } //******************************************************************************* void AudioInterface::computeProcessToNetwork(QVarLengthArray& in_buffer, unsigned int n_frames) { // Input Process (from JACK to NETWORK) // ---------------------------------------------------------------- // Concatenate all the channels from jack to form packet for (int i = 0; i < mNumInChans; i++) { //-------- // This should be faster for 32 bits //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = in_buffer[i]; //sample buffer for channel i sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process sample_t tmp_result; for (unsigned int j = 0; j < n_frames; j++) { // Change the bit resolution on each sample // Add the input jack buffer to the buffer resulting from the output process tmp_result = tmp_sample[j] + tmp_process_sample[j]; fromSampleToBitConversion( &tmp_result, &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], mBitResolutionMode ); } } // Send Audio buffer to Network mJackTrip->sendNetworkPacket( mInputPacket ); } //******************************************************************************* // This function quantize from 32 bit to a lower bit resolution // 24 bit is not working yet void AudioInterface::fromSampleToBitConversion (const sample_t* const input, int8_t* output, const AudioInterface::audioBitResolutionT targetBitResolution) { int8_t tmp_8; uint8_t tmp_u8; // unsigned to quantize the remainder in 24bits int16_t tmp_16; sample_t tmp_sample; sample_t tmp_sample16; sample_t tmp_sample8; switch (targetBitResolution) { case BIT8 : // 8bit integer between -128 to 127 tmp_sample = floor( (*input) * 128.0 ); // 2^7 = 128.0 tmp_8 = static_cast(tmp_sample); std::memcpy(output, &tmp_8, 1); // 8bits = 1 bytes break; case BIT16 : // 16bit integer between -32768 to 32767 tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 tmp_16 = static_cast(tmp_sample); std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes break; case BIT24 : // To convert to 24 bits, we first quantize the number to 16bit tmp_sample = (*input) * 32768.0; // 2^15 = 32768.0 tmp_sample16 = floor(tmp_sample); tmp_16 = static_cast(tmp_sample16); // Then we compute the remainder error, and quantize that part into an 8bit number // Note that this remainder is always positive, so we use an unsigned integer tmp_sample8 = floor ((tmp_sample - tmp_sample16) //this is a positive number, between 0.0-1.0 * 256.0); tmp_u8 = static_cast(tmp_sample8); // Finally, we copy the 16bit number in the first 2 bytes, // and the 8bit number in the third bite std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes std::memcpy(output+2, &tmp_u8, 1); // 8bits = 1 bytes break; case BIT32 : std::memcpy(output, input, 4); // 32bit = 4 bytes break; } } //******************************************************************************* void AudioInterface::fromBitToSampleConversion (const int8_t* const input, sample_t* output, const AudioInterface::audioBitResolutionT sourceBitResolution) { int8_t tmp_8; uint8_t tmp_u8; int16_t tmp_16; sample_t tmp_sample; sample_t tmp_sample16; sample_t tmp_sample8; switch (sourceBitResolution) { case BIT8 : tmp_8 = *input; tmp_sample = static_cast(tmp_8) / 128.0; std::memcpy(output, &tmp_sample, 4); // 4 bytes break; case BIT16 : tmp_16 = *( reinterpret_cast(input) ); // *((int16_t*) input); tmp_sample = static_cast(tmp_16) / 32768.0; std::memcpy(output, &tmp_sample, 4); // 4 bytes break; case BIT24 : // We first extract the 16bit and 8bit number from the 3 bytes tmp_16 = *( reinterpret_cast(input) ); tmp_u8 = *( reinterpret_cast(input+2) ); // Then we recover the number tmp_sample16 = static_cast(tmp_16); tmp_sample8 = static_cast(tmp_u8) / 256.0; tmp_sample = (tmp_sample16 + tmp_sample8) / 32768.0; std::memcpy(output, &tmp_sample, 4); // 4 bytes break; case BIT32 : std::memcpy(output, input, 4); // 4 bytes break; } } //******************************************************************************* void AudioInterface::appendProcessPlugin(ProcessPlugin* plugin) { /// \todo check that channels in ProcessPlugins are less or same that jack channels if ( plugin->getNumInputs() ) {} mProcessPlugins.append(plugin); } //******************************************************************************* AudioInterface::samplingRateT AudioInterface::getSampleRateType() const { uint32_t rate = getSampleRate(); if ( rate == 22050 ) { return AudioInterface::SR22; } else if ( rate == 32000 ) { return AudioInterface::SR32; } else if ( rate == 44100 ) { return AudioInterface::SR44; } else if ( rate == 48000 ) { return AudioInterface::SR48; } else if ( rate == 88200 ) { return AudioInterface::SR88; } else if ( rate == 96000 ) { return AudioInterface::SR96; } else if ( rate == 19200 ) { return AudioInterface::SR192; } return AudioInterface::UNDEF; } //******************************************************************************* int AudioInterface::getSampleRateFromType(samplingRateT rate_type) { int sample_rate = 0; switch (rate_type) { case SR22 : sample_rate = 22050; return sample_rate; break; case SR32 : sample_rate = 32000; return sample_rate; break; case SR44 : sample_rate = 44100; return sample_rate; break; case SR48 : sample_rate = 48000; return sample_rate; break; case SR88 : sample_rate = 88200; return sample_rate; break; case SR96 : sample_rate = 96000; return sample_rate; break; case SR192 : sample_rate = 192000; return sample_rate; break; default: return sample_rate; break; } return sample_rate; } jacktrip-1.1/src/UdpDataProtocol.h0000644000175000017500000001637112531357105017432 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file UdpDataProtocol.h * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __UDPDATAPROTOCOL_H__ #define __UDPDATAPROTOCOL_H__ #include #include #include #include #include #include "DataProtocol.h" #include "jacktrip_types.h" #include "jacktrip_globals.h" /** \brief UDP implementation of DataProtocol class * * The class has a bind port and a peer port. The meaning of these * depends on the runModeT. If it's a SENDER, bind port is the source port and * peer port is the destination port for each UDP packet. If it's a RECEIVER, * the bind port destination port (for incoming packets) and the peer port * is the source port. * * The SENDER and RECEIVER socket can share the same port/address pair (for compatibility * with the JamLink boxes). This is achieved setting * the resusable property in the socket for address and port. You have to * externaly check if the port is already binded if you want to avoid re-binding to the * same port. */ class UdpDataProtocol : public DataProtocol { Q_OBJECT; public: /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param runmode Sets the run mode, use either SENDER or RECEIVER * \param bind_port Port number to bind for this socket (this is the receive or send port depending on the runmode) * \param peer_port Peer port number (this is the receive or send port depending on the runmode) * \param udp_redundancy_factor Number of redundant packets */ UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, int bind_port, int peer_port, unsigned int udp_redundancy_factor = 1); /** \brief The class destructor */ virtual ~UdpDataProtocol(); /** \brief Set the Peer address to connect to * \param peerHostOrIP IPv4 number or host name */ void setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument); /** \brief Receives a packet. It blocks until a packet is received * * This function makes sure we recieve a complete packet * of size n * \param buf Buffer to store the recieved packet * \param n size of packet to receive * \return number of bytes read, -1 on error */ //virtual int receivePacket(char* buf, const size_t n); virtual int receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n); /** \brief Sends a packet * * This function meakes sure we send a complete packet * of size n * \param buf Buffer to send * \param n size of packet to receive * \return number of bytes read, -1 on error */ virtual int sendPacket(QUdpSocket& UdpSocket, const QHostAddress& PeerAddress, const char* buf, const size_t n); /** \brief Obtains the peer address from the first UDP packet received. This address * is used by the SERVER mode to connect back to the client. * \param peerHostAddress QHostAddress to store the peer address * \param port Receiving port */ virtual void getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, QHostAddress& peerHostAddress, uint16_t& port); /** \brief Sets the bind port number */ void setBindPort(int port) { mBindPort = port; } /** \brief Sets the peer port number */ void setPeerPort(int port) { mPeerPort = port; } /** \brief Implements the Thread Loop. To start the thread, call start() * ( DO NOT CALL run() ) * * This function creats and binds all the socket and start the connection loop thread. */ virtual void run(); private slots: void printUdpWaitedTooLong(int wait_msec); signals: /// \brief Signals when waiting every 10 milliseconds, with the total wait on wait_msec /// \param wait_msec Total wait in milliseconds void signalWatingTooLong(int wait_msec); //private: protected: /** \brief Binds the UDP socket to the available address and specified port */ void bindSocket(QUdpSocket& UdpSocket) throw(std::runtime_error); /** \brief This function blocks until data is available for reading in the * QUdpSocket. The function will timeout after timeout_msec microseconds. * * This function is intended to replace QAbstractSocket::waitForReadyRead which has * some problems with multithreading. * * \return returns true if there is data available for reading; * otherwise it returns false (if an error occurred or the operation timed out) */ bool waitForReady(QUdpSocket& UdpSocket, int timeout_msec); /** \brief Redundancy algorythm at the receiving end */ virtual void receivePacketRedundancy(QUdpSocket& UdpSocket, int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size, uint16_t& current_seq_num, uint16_t& last_seq_num, uint16_t& newer_seq_num); /** \brief Redundancy algorythm at the sender's end */ virtual void sendPacketRedundancy(QUdpSocket& UdpSocket, QHostAddress& PeerAddress, int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size); private: int mBindPort; ///< Local Port number to Bind int mPeerPort; ///< Peer Port number const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER QHostAddress mPeerAddress; ///< The Peer Address int8_t* mAudioPacket; ///< Buffer to store Audio Packets int8_t* mFullPacket; ///< Buffer to store Full Packet (audio+header) unsigned int mUdpRedundancyFactor; ///< Factor of redundancy static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process }; #endif // __UDPDATAPROTOCOL_H__ jacktrip-1.1/src/Settings.cpp0000644000175000017500000004500712531357105016517 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file Settings.cpp * \author Juan-Pablo Caceres * \date July 2008 */ #include "Settings.h" #include "LoopBack.h" #include "NetKS.h" #include "UdpMasterListener.h" #include "JackTripWorker.h" #include "jacktrip_globals.h" #include #include // for command line parsing #include #include "ThreadPoolTest.h" using std::cout; using std::endl; int gVerboseFlag = 0; //******************************************************************************* Settings::Settings() : mJackTrip(NULL), mJackTripMode(JackTrip::SERVER), mDataProtocol(JackTrip::UDP), mNumChans(2), mBufferQueueLength(gDefaultQueueLength), mAudioBitResolution(AudioInterface::BIT16), mBindPortNum(gDefaultPort), mPeerPortNum(gDefaultPort), mClientName(NULL), mUnderrrunZero(false), mLoopBack(false), mJamLink(false), mEmptyHeader(false), mJackTripServer(false), mLocalAddress(gDefaultLocalAddress), mRedundancy(1), mUseJack(true), mChanfeDefaultSR(false), mChanfeDefaultBS(false) {} //******************************************************************************* Settings::~Settings() { stopJackTrip(); delete mJackTrip; } //******************************************************************************* void Settings::parseInput(int argc, char** argv) { // If no command arguments are given, print instructions if(argc == 1) { printUsage(); std::exit(0); } // Usage example at: // http://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html#Getopt-Long-Option-Example // options descriptor //---------------------------------------------------------------------------- static struct option longopts[] = { // These options set a flag, has to be sepcified as a long option --verbose { "verbose", no_argument, &gVerboseFlag, 1 }, // These options don't set a flag. { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels { "server", no_argument, NULL, 's' }, // Run in server mode { "client", required_argument, NULL, 'c' }, // Run in client mode, set server IP address { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464 { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464 { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464 { "queue", required_argument, NULL, 'q' }, // Queue Length { "redundancy", required_argument, NULL, 'r' }, // Redundancy { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode { "srate", required_argument, NULL, 'T' }, // Set Sample Rate { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size { "version", no_argument, NULL, 'v' }, // Version Number { "help", no_argument, NULL, 'h' }, // Print Help { NULL, 0, NULL, 0 } }; // Parse Command Line Arguments //---------------------------------------------------------------------------- /// \todo Specify mandatory arguments int ch; while ( (ch = getopt_long(argc, argv, "n:sc:SC:o:B:P:q:r:b:zljeJ:RT:F:vh", longopts, NULL)) != -1 ) switch (ch) { case 'n': // Number of input and output channels //------------------------------------------------------- mNumChans = atoi(optarg); break; case 's': // Run in server mode //------------------------------------------------------- mJackTripMode = JackTrip::SERVER; break; case 'S': // Run in jacktripserver mode //------------------------------------------------------- mJackTripServer = true; break; case 'c': // Client mode //------------------------------------------------------- mJackTripMode = JackTrip::CLIENT; mPeerAddress = optarg; break; case 'L': // set optional local host address //------------------------------------------------------- mLocalAddress = optarg; break; case 'C': // Ping to server //------------------------------------------------------- mJackTripMode = JackTrip::CLIENTTOPINGSERVER; mPeerAddress = optarg; break; case 'o': // Port Offset //------------------------------------------------------- mBindPortNum += atoi(optarg); mPeerPortNum += atoi(optarg); break; case 'B': // Bind Port //------------------------------------------------------- mBindPortNum = atoi(optarg); break; case 'P': // Peer Port //------------------------------------------------------- mPeerPortNum = atoi(optarg); break; case 'b': //------------------------------------------------------- if ( atoi(optarg) == 8 ) { mAudioBitResolution = AudioInterface::BIT8; } else if ( atoi(optarg) == 16 ) { mAudioBitResolution = AudioInterface::BIT16; } else if ( atoi(optarg) == 24 ) { mAudioBitResolution = AudioInterface::BIT24; } else if ( atoi(optarg) == 32 ) { mAudioBitResolution = AudioInterface::BIT32; } else { std::cerr << "--bitres ERROR: Wrong bit resolutions: " << atoi(optarg) << " is not supported." << endl; printUsage(); std::exit(1); } break; case 'q': //------------------------------------------------------- if ( atoi(optarg) <= 0 ) { std::cerr << "--queue ERROR: The queue has to be equal or greater that 2" << endl; printUsage(); std::exit(1); } else { mBufferQueueLength = atoi(optarg); } break; case 'r': //------------------------------------------------------- if ( atoi(optarg) <= 0 ) { std::cerr << "--redundancy ERROR: The reduncancy has to be a positive integer" << endl; printUsage(); std::exit(1); } else { mRedundancy = atoi(optarg); } break; case 'z': // underrun to zero //------------------------------------------------------- mUnderrrunZero = true; break; case 'l': // loopback //------------------------------------------------------- mLoopBack = true; break; case 'e': // jamlink //------------------------------------------------------- mEmptyHeader = true; break; case 'j': // jamlink //------------------------------------------------------- mJamLink = true; break; case 'J': // Set client Name //------------------------------------------------------- mClientName = optarg; break; case 'R': // RtAudio //------------------------------------------------------- mUseJack = false; break; case 'T': // Sampling Rate //------------------------------------------------------- mChanfeDefaultSR = true; mSampleRate = atoi(optarg); break; case 'F': // Buffer Size //------------------------------------------------------- mChanfeDefaultBS = true; mAudioBufferSize = atoi(optarg); break; case 'v': //------------------------------------------------------- cout << "JackTrip VERSION: " << gVersion << endl; cout << "Copyright (c) 2008-2009 Juan-Pablo Caceres, Chris Chafe." << endl; cout << "SoundWIRE group at CCRMA, Stanford University" << endl; cout << "" << endl; std::exit(0); break; case 'h': //------------------------------------------------------- printUsage(); std::exit(0); break; default: //------------------------------------------------------- printUsage(); std::exit(0); break; } // Warn user if undefined options where entered //---------------------------------------------------------------------------- if (optind < argc) { cout << gPrintSeparator << endl; cout << "WARINING: The following entered options have no effect" << endl; cout << " They will be ignored!" << endl; cout << " Type jacktrip to see options." << endl; for( ; optind < argc; optind++) { printf("argument: %s\n", argv[optind]); } cout << gPrintSeparator << endl; } } //******************************************************************************* void Settings::printUsage() { cout << "" << endl; cout << "JackTrip: A System for High-Quality Audio Network Performance" << endl; cout << "over the Internet" << endl; cout << "Copyright (c) 2008-2015 Juan-Pablo Caceres, Chris Chafe." << endl; cout << "SoundWIRE group at CCRMA, Stanford University" << endl; cout << "VERSION: " << gVersion << endl; cout << "-----------------------------------------------------------------------------" << endl; cout << "" << endl; cout << "Usage: jacktrip [-s|-c host] [options]" << endl; cout << "" << endl; cout << "Options: " << endl; cout << "REQUIRED ARGUMENTS: " << endl; cout << "===================" << endl; cout << " -s, --server Run in Server Mode" << endl; cout << " -c, --client Run in Client Mode" << endl; cout << endl; cout << "OPTIONAL ARGUMENTS: " << endl; cout << "===================" << endl; cout << " -n, --numchannels # Number of Input and Output Channels (default " << 2 << ")" << endl; cout << " -q, --queue # (2 or more) Queue Buffer Length, in Packet Size (default " << gDefaultQueueLength << ")" << endl; cout << " -r, --redundancy # (1 or more) Packet Redundancy to avoid glitches with packet losses (defaul 1)" << endl; cout << " -o, --portoffset # Receiving port offset from base port " << gDefaultPort << endl; cout << " --bindport # Set only the bind port number (default to 4464)" << endl; cout << " --peerport # Set only the Peer port number (default to 4464)" << endl; cout << " -b, --bitres # (8, 16, 24, 32) Audio Bit Rate Resolutions (default 16)" << endl; cout << " -z, --zerounderrun Set buffer to zeros when underrun occurs (defaults to wavetable)" << endl; cout << " -l, --loopback Run in Loop-Back Mode" << endl; cout << " -j, --jamlink Run in JamLink Mode (Connect to a JamLink Box)" << endl; cout << " --clientname Change default client name (default is JackTrip)" << endl; cout << " --localaddress Change default local host IP address (127.0.0.1)" << endl; cout << endl; cout << "ARGUMENTS TO USE IT WITHOUT JACK:" << endl; cout << "=================================" << endl; cout << " --rtaudio Use defaul sound system instead of Jack" << endl; cout << " --srate # Set the sampling rate, works on --rtaudio mode only (defaults 48000)" << endl; cout << " --bufsize # Set the buffer size, works on --rtaudio mode only (defaults 128)" << endl; cout << endl; cout << "HELP ARGUMENTS: " << endl; cout << "===============" << endl; cout << " -v, --version Prints Version Number" << endl; cout << " -h, --help Prints this Help" << endl; cout << "" << endl; } //******************************************************************************* void Settings::startJackTrip() { /// \todo Change this, just here to test if ( mJackTripServer ) { UdpMasterListener* udpmaster = new UdpMasterListener; udpmaster->start(); //---Thread Pool Test-------------------------------------------- /* cout << "BEFORE START" << endl; ThreadPoolTest* thtest = new ThreadPoolTest(); // QThreadPool takes ownership and deletes 'hello' automatically QThreadPool::globalInstance()->start(thtest); cout << "AFTER START" << endl; sleep(2); thtest->stop(); QThreadPool::globalInstance()->waitForDone(); */ //--------------------------------------------------------------- } else { //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans, // mBufferQueueLength, mAudioBitResolution); mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, mBufferQueueLength, mRedundancy, mAudioBitResolution); // Connect Signals and Slots QObject::connect(mJackTrip, SIGNAL( signalProcessesStopped() ), this, SLOT( slotExitProgram() )); // Change client name if different from default if (mClientName != NULL) { mJackTrip->setClientName(mClientName); } // Set buffers to zero when underrun if ( mUnderrrunZero ) { cout << "Setting buffers to zero when underrun..." << endl; cout << gPrintSeparator << std::endl; mJackTrip->setUnderRunMode(JackTrip::ZEROS); } // Set peer address in server mode if ( mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER ) { mJackTrip->setPeerAddress(mPeerAddress.toLatin1().data()); } // if(mLocalAddress!=QString()) // default // mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data())); // else // mJackTrip->setLocalAddress(QHostAddress::Any); // Set Ports //cout << "SETTING ALL PORTS" << endl; mJackTrip->setBindPorts(mBindPortNum); mJackTrip->setPeerPorts(mPeerPortNum); // Set in JamLink Mode if ( mJamLink ) { cout << "Running in JamLink Mode..." << endl; cout << gPrintSeparator << std::endl; mJackTrip->setPacketHeaderType(DataProtocol::JAMLINK); } // Set in EmptyHeader Mode if ( mEmptyHeader ) { cout << "Running in EmptyHeader Mode..." << endl; cout << gPrintSeparator << std::endl; mJackTrip->setPacketHeaderType(DataProtocol::EMPTY); } // Set RtAudio #ifdef __RT_AUDIO__ if (!mUseJack) { mJackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO); } #endif // Chanfe default Sampling Rate if (mChanfeDefaultSR) { mJackTrip->setSampleRate(mSampleRate); } // Chanfe default Buffer Size if (mChanfeDefaultBS) { mJackTrip->setAudioBufferSizeInSamples(mAudioBufferSize); } // Add Plugins if ( mLoopBack ) { cout << "Running in Loop-Back Mode..." << endl; cout << gPrintSeparator << std::endl; //std::tr1::shared_ptr loopback(new LoopBack(mNumChans)); //mJackTrip->appendProcessPlugin(loopback.get()); LoopBack* loopback = new LoopBack(mNumChans); mJackTrip->appendProcessPlugin(loopback); // ----- Test Karplus Strong ----------------------------------- //std::tr1::shared_ptr loopback(new NetKS()); //mJackTrip->appendProcessPlugin(loopback); //loopback->play(); //NetKS* netks = new NetKS; //mJackTrip->appendProcessPlugin(netks); //netks->play(); // ------------------------------------------------------------- } // Start JackTrip mJackTrip->startProcess(); mJackTrip->start(); /* sleep(10); cout << "Stoping JackTrip..." << endl; mJackTrip->stop(); */ } } //******************************************************************************* void Settings::stopJackTrip() { mJackTrip->stop(); } jacktrip-1.1/src/PacketHeader.h0000644000175000017500000002575012531357105016707 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file PacketHeader.h * \author Juan-Pablo Caceres * \date July 2008 */ #ifndef __PACKETHEADER_H__ #define __PACKETHEADER_H__ #include //#include // for shared_ptr #include #include #include #include "jacktrip_types.h" #include "jacktrip_globals.h" class JackTrip; // Forward Declaration /// \brief Abstract Header Struct, Header Stucts should subclass it struct HeaderStruct{}; /// \brief Default Header Struct struct DefaultHeaderStruct : public HeaderStruct { public: // watch out for alignment... uint64_t TimeStamp; ///< Time Stamp uint16_t SeqNumber; ///< Sequence Number uint16_t BufferSize; ///< Buffer Size in Samples uint8_t SamplingRate; ///< Sampling Rate in JackAudioInterface::samplingRateT uint8_t BitResolution; ///< Audio Bit Resolution //uint8_t NumInChannels; ///< Number of Input Channels //uint8_t NumOutChannels; ///< Number of Output Channels uint8_t NumChannels; ///< Number of Channels, we assume input and outputs are the same uint8_t ConnectionMode; }; //--------------------------------------------------------- //JamLink UDP Header: /************************************************************************/ /* values for the UDP stream type */ /* streamType is a 16-bit value at the head of each UDP stream */ /* Its bit map is as follows: (b15-msb) */ /* B15:reserved, B14:extended header, B13 Stereo, B12 not 16-bit */ /* B11-B9: 0-48 Khz, 1-44 Khz, 2-32 Khz, 3-24 Khz, */ /* 4-22 Khz, 5-16 Khz, 6-11 Khz, 7-8 Khz */ /* B8-0: Samples in packet */ /************************************************************************/ const unsigned short ETX_RSVD = (0<<15); const unsigned short ETX_XTND = (1<<14); const unsigned short ETX_STEREO = (1<<13); const unsigned short ETX_MONO = (0<<13); const unsigned short ETX_16BIT = (0<<12); //inline unsigned short ETX_RATE_MASK(const unsigned short a) { a&(0x7<<9); } const unsigned short ETX_48KHZ = (0<<9); const unsigned short ETX_44KHZ = (1<<9); const unsigned short ETX_32KHZ = (2<<9); const unsigned short ETX_24KHZ = (3<<9); const unsigned short ETX_22KHZ = (4<<9); const unsigned short ETX_16KHZ = (5<<9); const unsigned short ETX_11KHZ = (6<<9); const unsigned short ETX_8KHZ = (7<<9); // able to express up to 512 SPP //inline unsigned short ETX_SPP(const unsigned short a) { (a&0x01FF); } /// \brief JamLink Header Struct struct JamLinkHeaderStuct : public HeaderStruct { // watch out for alignment -- need to be on 4 byte chunks uint16_t Common; ///< Common part of the header, 16 bit uint16_t SeqNumber; ///< Sequence Number uint32_t TimeStamp; ///< Time Stamp }; //####################################################################### //####################### PacketHeader ################################## //####################################################################### /** \brief Base class for header type. Subclass this struct to * create a new header. */ class PacketHeader : public QObject { Q_OBJECT; public: /// \brief The class Constructor PacketHeader(JackTrip* jacktrip); /// \brief The class Destructor virtual ~PacketHeader() {} /// \brief Return a time stamp in microseconds /// \return Time stamp: microseconds since midnight (0 hour), January 1, 1970 static uint64_t usecTime(); /// \todo Implement this using a JackTrip Method (Mediator) member instead of the /// reference to JackAudio virtual void fillHeaderCommonFromAudio() = 0; /// \brief Parse the packet header and take appropriate measures (like change settings, or /// quit the program if peer settings don't match) virtual void parseHeader() = 0; virtual void checkPeerSettings(int8_t* full_packet) = 0; virtual uint64_t getPeerTimeStamp(int8_t* full_packet) const = 0; virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const = 0; virtual uint16_t getPeerBufferSize(int8_t* full_packet) const = 0; virtual uint8_t getPeerSamplingRate(int8_t* full_packet) const = 0; virtual uint8_t getPeerBitResolution(int8_t* full_packet) const = 0; virtual uint8_t getPeerNumChannels(int8_t* full_packet) const = 0; virtual uint8_t getPeerConnectionMode(int8_t* full_packet) const = 0; /// \brief Increase sequence number for counter, a 16bit number virtual void increaseSequenceNumber() { mSeqNumber++; } /// \brief Returns the current sequence number /// \return 16bit Sequence number virtual uint16_t getSequenceNumber() const { return mSeqNumber; } /// \brief Get the header size in bytes virtual int getHeaderSizeInBytes() const = 0; virtual void putHeaderInPacketBaseClass(int8_t* full_packet, const HeaderStruct& header_struct) { std::memcpy(full_packet, reinterpret_cast(&header_struct), getHeaderSizeInBytes() ); } /// \brief Put the header in buffer pointed by full_packet /// \param full_packet Pointer to full packet (audio+header). Size must be /// sizeof(header part) + sizeof(audio part) virtual void putHeaderInPacket(int8_t* full_packet) = 0; signals: void signalError(const char* error_message); private: uint16_t mSeqNumber; JackTrip* mJackTrip; ///< JackTrip mediator class }; //####################################################################### //####################### DefaultHeader ################################# //####################################################################### /** \brief Default Header */ class DefaultHeader : public PacketHeader { public: DefaultHeader(JackTrip* jacktrip); virtual ~DefaultHeader() {} virtual void fillHeaderCommonFromAudio(); virtual void parseHeader() {} virtual void checkPeerSettings(int8_t* full_packet); virtual void increaseSequenceNumber() { mHeader.SeqNumber++; } virtual uint16_t getSequenceNumber() const { return mHeader.SeqNumber; } virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } virtual void putHeaderInPacket(int8_t* full_packet) { putHeaderInPacketBaseClass(full_packet, mHeader); } void printHeader() const; uint8_t getConnectionMode() const { return mHeader.ConnectionMode; } uint8_t getNumChannels() const { return mHeader.NumChannels; } virtual uint64_t getPeerTimeStamp(int8_t* full_packet) const; virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const; virtual uint16_t getPeerBufferSize(int8_t* full_packet) const; virtual uint8_t getPeerSamplingRate(int8_t* full_packet) const; virtual uint8_t getPeerBitResolution(int8_t* full_packet) const; virtual uint8_t getPeerNumChannels(int8_t* full_packet) const; virtual uint8_t getPeerConnectionMode(int8_t* full_packet) const; private: DefaultHeaderStruct mHeader;///< Default Header Struct JackTrip* mJackTrip; ///< JackTrip mediator class }; //####################################################################### //####################### JamLinkHeader ################################# //####################################################################### /** \brief JamLink Header */ class JamLinkHeader : public PacketHeader { public: JamLinkHeader(JackTrip* jacktrip); virtual ~JamLinkHeader() {} virtual void fillHeaderCommonFromAudio(); virtual void parseHeader() {} virtual void checkPeerSettings(int8_t* /*full_packet*/) {} virtual uint64_t getPeerTimeStamp(int8_t* /*full_packet*/) const { return 0; } virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; } virtual uint16_t getPeerBufferSize(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerSamplingRate(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerBitResolution(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerNumChannels(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerConnectionMode(int8_t* /*full_packet*/) const { return 0; } virtual void increaseSequenceNumber() {} virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } virtual void putHeaderInPacket(int8_t* full_packet) { putHeaderInPacketBaseClass(full_packet, mHeader); } private: JamLinkHeaderStuct mHeader; ///< JamLink Header Struct JackTrip* mJackTrip; ///< JackTrip mediator class }; //####################################################################### //####################### EmptyHeader ################################# //####################################################################### /** \brief Empty Header to use with systems that don't include a header. */ class EmptyHeader : public PacketHeader { public: EmptyHeader(JackTrip* jacktrip); virtual ~EmptyHeader() {} virtual void fillHeaderCommonFromAudio() {} virtual void parseHeader() {} virtual void checkPeerSettings(int8_t* /*full_packet*/) {} virtual void increaseSequenceNumber() {} virtual int getHeaderSizeInBytes() const { return 0; } virtual uint64_t getPeerTimeStamp(int8_t* /*full_packet*/) const { return 0; } virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; } virtual uint16_t getPeerBufferSize(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerSamplingRate(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerBitResolution(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerNumChannels(int8_t* /*full_packet*/) const { return 0; } virtual uint8_t getPeerConnectionMode(int8_t* /*full_packet*/) const { return 0; } virtual void putHeaderInPacket(int8_t* /*full_packet*/) {} private: JackTrip* mJackTrip; ///< JackTrip mediator class }; #endif //__PACKETHEADER_H__ jacktrip-1.1/src/UdpDataProtocol.cpp0000644000175000017500000005570312531357105017767 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file UdpDataProtocol.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #include "UdpDataProtocol.h" #include "jacktrip_globals.h" #include "JackTrip.h" #include #include #include #include #include #include #ifdef __WIN_32__ #include #endif #if defined (__LINUX__) || (__MAC__OSX__) #include // for POSIX Sockets #endif using std::cout; using std::endl; // NOTE: It's better not to use // using namespace std; // because some functions (like exit()) get confused with QT functions // sJackMutex definition QMutex UdpDataProtocol::sUdpMutex; //******************************************************************************* UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, int bind_port, int peer_port, unsigned int udp_redundancy_factor) : DataProtocol(jacktrip, runmode, bind_port, peer_port), mBindPort(bind_port), mPeerPort(peer_port), mRunMode(runmode), mAudioPacket(NULL), mFullPacket(NULL), mUdpRedundancyFactor(udp_redundancy_factor) { mStopped = false; if (mRunMode == RECEIVER) { QObject::connect(this, SIGNAL(signalWatingTooLong(int)), jacktrip, SLOT(slotUdpWatingTooLong(int)), Qt::QueuedConnection); } } //******************************************************************************* UdpDataProtocol::~UdpDataProtocol() { delete[] mAudioPacket; delete[] mFullPacket; wait(); } //******************************************************************************* void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument) { // Get DNS Address QHostInfo info = QHostInfo::fromName(peerHostOrIP); if (!info.addresses().isEmpty()) { // use the first IP address mPeerAddress = info.addresses().first(); //cout << "UdpDataProtocol::setPeerAddress IP Address Number: " // << mPeerAddress.toString().toStdString() << endl; } // check if the ip address is valid if ( mPeerAddress.isNull() ) { QString error_message = "Incorrect presentation format address\n '"; error_message.append(peerHostOrIP); error_message.append("' is not a valid IP address or Host Name"); //std::cerr << "ERROR: Incorrect presentation format address" << endl; //std::cerr << "'" << peerHostOrIP <<"' does not seem to be a valid IP address" << endl; //throw std::invalid_argument("Incorrect presentation format address"); throw std::invalid_argument( error_message.toStdString()); } /* else { std::cout << "Peer Address set to: " << mPeerAddress.toString().toStdString() << std::endl; cout << gPrintSeparator << endl; usleep(100); } */ } //******************************************************************************* void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) throw(std::runtime_error) { QMutexLocker locker(&sUdpMutex); #if defined __WIN_32__ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { // Tell the user that we couldn't find a useable // winsock.dll. return; } // Confirm that the Windows Sockets DLL supports 1.1. or higher if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { // Tell the user that we couldn't find a useable // winsock.dll. WSACleanup( ); return; } // Creat socket descriptor SOCKET sock_fd; SOCKADDR_IN local_addr; #endif #if defined ( __LINUX__ ) || (__MAC_OSX__) int sock_fd; //Set local IPv4 Address struct sockaddr_in local_addr; #endif // Creat socket descriptor sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //::bzero(&local_addr, sizeof(local_addr)); std::memset(&local_addr, 0, sizeof(local_addr)); // set buffer to 0 local_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address local_addr.sin_port = htons(mBindPort); //set local port // Set socket to be reusable, this is platform dependent int one = 1; #if defined ( __LINUX__ ) ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); #endif #if defined ( __MAC_OSX__ ) // This option is not avialable on Linux, and without it MAC OS X // has problems rebinding a socket ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); #endif #if defined (__WIN_32__) //make address/port reusable setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); #endif // Bind the Socket #if defined ( __LINUX__ ) || ( __MAC_OSX__ ) if ( (::bind(sock_fd, (struct sockaddr *) &local_addr, sizeof(local_addr))) < 0 ) { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } #endif #if defined (__WIN_32__) //int bound; //bound = bind(sock_fd, (SOCKADDR *) &local_addr, sizeof(local_addr)); if ( (bind(sock_fd, (SOCKADDR *) &local_addr, sizeof(local_addr))) == SOCKET_ERROR ) { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } #endif // To be able to use the two UDP sockets bound to the same port number, // we connect the receiver and issue a SHUT_WR. if (mRunMode == SENDER) { // We use the sender as an unconnected UDP socket UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::BoundState, QUdpSocket::WriteOnly); } else if (mRunMode == RECEIVER) { #if defined (__LINUX__) || (__MAC_OSX__) // Set peer IPv4 Address struct sockaddr_in peer_addr; bzero(&peer_addr, sizeof(peer_addr)); peer_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address peer_addr.sin_port = htons(mPeerPort); //set local port // Connect the socket and issue a Write shutdown (to make it a // reader socket only) if ( (::inet_pton(AF_INET, mPeerAddress.toString().toLatin1().constData(), &peer_addr.sin_addr)) < 1 ) { throw std::runtime_error("ERROR: Invalid address presentation format"); } if ( (::connect(sock_fd, (struct sockaddr *) &peer_addr, sizeof(peer_addr))) < 0) { throw std::runtime_error("ERROR: Could not connect UDP socket"); } if ( (::shutdown(sock_fd,SHUT_WR)) < 0) { throw std::runtime_error("ERROR: Could suntdown SHUT_WR UDP socket"); } #endif #if defined __WIN_32__ // Set peer IPv4 Address SOCKADDR_IN peer_addr; std::memset(&peer_addr, 0, sizeof(peer_addr)); // set buffer to 0 peer_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address peer_addr.sin_port = htons(mPeerPort); //set local port // Connect the socket and issue a Write shutdown (to make it a // reader socket only) peer_addr.sin_addr.s_addr = inet_addr(mPeerAddress.toString().toLatin1().constData()); int con = (::connect(sock_fd, (struct sockaddr *) &peer_addr, sizeof(peer_addr))); if ( con < 0) { fprintf(stderr, "ERROR: Could not connect UDP socket"); throw std::runtime_error("ERROR: Could not connect UDP socket"); } //cout<<"connect returned: "<getPacketSizeInBytes(); //cout << "full_packet_size: " << full_packet_size << endl; mFullPacket = new int8_t[full_packet_size]; std::memset(mFullPacket, 0, full_packet_size); // set buffer to 0 bool timeout = false; // Time out flag for packets that arrive too late // Put header in first packet mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); // Redundancy Variables // (Algorithm explained at the end of this file) // --------------------------------------------- int full_redundant_packet_size = full_packet_size * mUdpRedundancyFactor; int8_t* full_redundant_packet; full_redundant_packet = new int8_t[full_redundant_packet_size]; std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0 // Set realtime priority (function in jacktrip_globals.h) set_crossplatform_realtime_priority(); switch ( mRunMode ) { case RECEIVER : { // Connect signals and slots for packets arriving too late notifications QObject::connect(this, SIGNAL(signalWatingTooLong(int)), this, SLOT(printUdpWaitedTooLong(int)), Qt::QueuedConnection); //----------------------------------------------------------------------------------- // Wait for the first packet to be ready and obtain address // from that packet std::cout << "Waiting for Peer..." << std::endl; // This blocks waiting for the first packet while ( !UdpSocket.hasPendingDatagrams() ) { if (mStopped) { return; } QThread::msleep(100); } int first_packet_size = UdpSocket.pendingDatagramSize(); // The following line is the same as int8_t* first_packet = new int8_t[first_packet_size]; /// \todo fix this to avoid memory leaks // but avoids memory leaks //std::tr1::shared_ptr first_packet(new int8_t[first_packet_size]); receivePacket( UdpSocket, reinterpret_cast(first_packet), first_packet_size); // Check that peer has the same audio settings mJackTrip->checkPeerSettings(first_packet); mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); std::cout << "Received Connection for Peer!" << std::endl; emit signalReceivedConnectionFromPeer(); // Redundancy Variables // -------------------- // NOTE: These types need to be the same unsigned integer as the sequence // number in the header. That way, they wrap around in the "same place" uint16_t current_seq_num = 0; // Store current sequence number uint16_t last_seq_num = 0; // Store last package sequence number uint16_t newer_seq_num = 0; // Store newer sequence number while ( !mStopped ) { // Timer to report packets arriving too late // This QT method gave me a lot of trouble, so I replaced it with my own 'waitForReady' // that uses signals and slots and can also report with packets have not // arrive for a longer time //timeout = UdpSocket.waitForReadyRead(30); timeout = waitForReady(UdpSocket, 60000); //60 seconds // OLD CODE WITHOUT REDUNDANCY---------------------------------------------------- /* // This is blocking until we get a packet... receivePacket( UdpSocket, reinterpret_cast(mFullPacket), full_packet_size); mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); // ...so we want to send the packet to the buffer as soon as we get in from // the socket, i.e., non-blocking //mRingBuffer->insertSlotNonBlocking(mAudioPacket); mJackTrip->writeAudioBuffer(mAudioPacket); */ //---------------------------------------------------------------------------------- receivePacketRedundancy(UdpSocket, full_redundant_packet, full_redundant_packet_size, full_packet_size, current_seq_num, last_seq_num, newer_seq_num); } break; } case SENDER : { //----------------------------------------------------------------------------------- while ( !mStopped ) { // OLD CODE WITHOUT REDUNDANCY ----------------------------------------------------- /* // We block until there's stuff available to read mJackTrip->readAudioBuffer( mAudioPacket ); mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); // This will send the packet immediately //int bytes_sent = sendPacket( reinterpret_cast(mFullPacket), full_packet_size); sendPacket( UdpSocket, PeerAddress, reinterpret_cast(mFullPacket), full_packet_size); */ //---------------------------------------------------------------------------------- sendPacketRedundancy(UdpSocket, PeerAddress, full_redundant_packet, full_redundant_packet_size, full_packet_size); } break; } } } //******************************************************************************* bool UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec) { int loop_resolution_usec = 100; // usecs to wait on each loop int emit_resolution_usec = 10000; // 10 milliseconds int timeout_usec = timeout_msec * 1000; int ellaped_time_usec = 0; // Ellapsed time in milliseconds while ( ( !(UdpSocket.hasPendingDatagrams()) && (ellaped_time_usec <= timeout_usec) ) && !mStopped ){ if (mStopped) { return false; } QThread::usleep(loop_resolution_usec); ellaped_time_usec += loop_resolution_usec; if ( !(ellaped_time_usec % emit_resolution_usec) ) { emit signalWatingTooLong(static_cast(ellaped_time_usec/1000)); } } if ( ellaped_time_usec >= timeout_usec ) { emit signalWatingTooLong(ellaped_time_usec/1000); return false; } return true; } //******************************************************************************* void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec) { int wait_time = 30; // msec if ( !(wait_msec%wait_time) ) { std::cerr << "UDP waiting too long (more than " << wait_time << "ms)..." << endl; } } //******************************************************************************* void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size, uint16_t& current_seq_num, uint16_t& last_seq_num, uint16_t& newer_seq_num) { // This is blocking until we get a packet... receivePacket( UdpSocket, reinterpret_cast(full_redundant_packet), full_redundant_packet_size); // Get Packet Sequence Number newer_seq_num = mJackTrip->getPeerSequenceNumber(full_redundant_packet); current_seq_num = newer_seq_num; //cout << current_seq_num << " "; int redun_last_index = 0; for (unsigned int i = 1; igetPeerSequenceNumber( full_redundant_packet + (i*full_packet_size) ); //cout << current_seq_num << " "; } //cout << endl; last_seq_num = newer_seq_num; // Save last read packet // Send to audio all available audio packets, in order for (int i = redun_last_index; i>=0; i--) { memcpy(mFullPacket, full_redundant_packet + (i*full_packet_size), full_packet_size); mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); mJackTrip->writeAudioBuffer(mAudioPacket); } } //******************************************************************************* void UdpDataProtocol::sendPacketRedundancy(QUdpSocket& UdpSocket, QHostAddress& PeerAddress, int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size) { mJackTrip->readAudioBuffer( mAudioPacket ); mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); // Move older packets to end of array of redundant packets std::memmove(full_redundant_packet+full_packet_size, full_redundant_packet, full_packet_size*(mUdpRedundancyFactor-1)); // Copy new packet to the begining of array std::memcpy(full_redundant_packet, mFullPacket, full_packet_size); // 10% (or other number) packet lost simulation. // Uncomment the if to activate //--------------------------------------------------------------------------------- //int random_integer = rand(); //if ( random_integer > (RAND_MAX/10) ) //{ sendPacket( UdpSocket, PeerAddress, reinterpret_cast(full_redundant_packet), full_redundant_packet_size); //} //--------------------------------------------------------------------------------- mJackTrip->increaseSequenceNumber(); } /* The Redundancy Algorythmn works as follows. We send a packet that contains a mUdpRedundancyFactor number of packets (header+audio). This big packet looks as follows ---------- ------------ ----------------------------------- | UDP[n] | | UDP[n-1] | ... | UDP[n-(mUdpRedundancyFactor-1)] | ---------- ------------ ----------------------------------- Then, for the new audio buffer, we shift everything to the right and send: ---------- ------------ ------------------------------------- | UDP[n+1] | | UDP[n] | ... | UDP[n-(mUdpRedundancyFactor-1)+1] | ---------- ------------ ------------------------------------- etc... For a redundancy factor of 4, this will look as follows: ---------- ---------- ---------- ---------- | UDP[4] | | UDP[3] | | UDP[2] | | UDP[1] | ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- | UDP[5] | | UDP[4] | | UDP[3] | | UDP[2] | ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- | UDP[6] | | UDP[5] | | UDP[4] | | UDP[3] | ---------- ---------- ---------- ---------- etc... Then, the receiving end checks if the firs packet in the list is the one it should use, otherwise it continure reding the mUdpRedundancyFactor packets until it finds the one that should come next (this can better perfected by just jumping until the correct packet). If it has more than one packet that it hasn't yet received, it sends it to the soundcard one by one. */ jacktrip-1.1/src/RingBuffer.h0000644000175000017500000001257212531357105016416 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file RingBuffer.h * \author Juan-Pablo Caceres * \date July 2008 */ #ifndef __RINGBUFFER_H__ #define __RINGBUFFER_H__ #include #include #include #include "jacktrip_types.h" //using namespace JackTripNamespace; /** \brief Provides a ring-buffer (or circular-buffer) that can be written to and read from * asynchronously (blocking) or synchronously (non-blocking). * * The RingBuffer is an array of \b NumSlots slots of memory * each of which is of size \b SlotSize bytes (8-bits). Slots can be read and * written asynchronously/synchronously by multiple threads. */ class RingBuffer { public: /** \brief The class constructor * \param SlotSize Size of one slot in bytes * \param NumSlots Number of slots */ RingBuffer(int SlotSize, int NumSlots); /** \brief The class destructor */ virtual ~RingBuffer(); /** \brief Insert a slot into the RingBuffer from ptrToSlot. This method will block until * there's space in the buffer. * * The caller is responsible to make sure sizeof(WriteSlot) = SlotSize. This * method should be use when the caller can block against its output, like * sending/receiving UDP packets. It shouldn't be used by audio. For that, use the * insertSlotNonBlocking. * \param ptrToSlot Pointer to slot to insert into the RingBuffer */ void insertSlotBlocking(const int8_t* ptrToSlot); /** \brief Read a slot from the RingBuffer into ptrToReadSlot. This method will block until * there's space in the buffer. * * The caller is responsible to make sure sizeof(ptrToReadSlot) = SlotSize. This * method should be use when the caller can block against its input, like * sending/receiving UDP packets. It shouldn't be used by audio. For that, use the * readSlotNonBlocking. * \param ptrToReadSlot Pointer to read slot from the RingBuffer */ void readSlotBlocking(int8_t* ptrToReadSlot); /** \brief Same as insertSlotBlocking but non-blocking (asynchronous) * \param ptrToSlot Pointer to slot to insert into the RingBuffer */ void insertSlotNonBlocking(const int8_t* ptrToSlot); /** \brief Same as readSlotBlocking but non-blocking (asynchronous) * \param ptrToReadSlot Pointer to read slot from the RingBuffer */ void readSlotNonBlocking(int8_t* ptrToReadSlot); protected: /** \brief Sets the memory in the Read Slot when uderrun occurs. By default, * this sets it to 0. Override this method in a subclass for a different behavior. * \param ptrToReadSlot Pointer to read slot from the RingBuffer */ virtual void setUnderrunReadSlot(int8_t* ptrToReadSlot); /** \brief Uses the last read slot to set the memory in the Read Slot. * * The last read slot is the last packet that arrived, so if no new packets are received, * it keeps looping the same packet. * \param ptrToReadSlot Pointer to read slot from the RingBuffer */ virtual void setMemoryInReadSlotWithLastReadSlot(int8_t* ptrToReadSlot); private: /// \brief Resets the ring buffer for reads under-runs non-blocking void underrunReset(); /// \brief Resets the ring buffer for writes over-flows non-blocking void overflowReset(); /// \brief Helper method to debug, prints member variables to terminal void debugDump() const; const int mSlotSize; ///< The size of one slot in byes const int mNumSlots; ///< Number of Slots const int mTotalSize; ///< Total size of the mRingBuffer = mSlotSize*mNumSlotss int mReadPosition; ///< Read Positions in the RingBuffer (Tail) int mWritePosition; ///< Write Position in the RingBuffer (Head) int mFullSlots; ///< Number of used (full) slots, in slot-size int8_t* mRingBuffer; ///< 8-bit array of data (1-byte) int8_t* mLastReadSlot; ///< Last slot read // Thread Synchronization Private Members QMutex mMutex; ///< Mutex to protect read and write operations QWaitCondition mBufferIsNotFull; ///< Buffer not full condition to monitor threads QWaitCondition mBufferIsNotEmpty; ///< Buffer not empty condition to monitor threads }; #endif jacktrip-1.1/src/RtAudioInterface.h0000644000175000017500000000743012531357105017552 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file RtAudioInterface.h * \author Juan-Pablo Caceres * \date July 2009 */ #ifndef __RTAUDIOINTERFACE_H__ #define __RTAUDIOINTERFACE_H__ #include "RtAudio.h" #include "AudioInterface.h" #include "jacktrip_globals.h" class JackTrip; // Forward declaration /// \brief Base Class that provides an interface with RtAudio class RtAudioInterface : public AudioInterface { public: /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param NumInChans Number of Input Channels * \param NumOutChans Number of Output Channels * \param AudioBitResolution Audio Sample Resolutions in bits */ RtAudioInterface(JackTrip* jacktrip, int NumInChans = gDefaultNumInChannels, int NumOutChans = gDefaultNumOutChannels, audioBitResolutionT AudioBitResolution = BIT16); /// \brief The class destructor virtual ~RtAudioInterface(); /// \brief List all avialable audio interfaces, with its properties virtual void listAllInterfaces(); virtual void setup(); virtual int startProcess() const; virtual int stopProcess() const; /// \brief This has no effect in RtAudio virtual void connectDefaultPorts() {} //--------------SETTERS--------------------------------------------- /// \brief This has no effect in RtAudio virtual void setClientName(const char* /*ClientName*/) {} //------------------------------------------------------------------ //--------------GETTERS--------------------------------------------- //------------------------------------------------------------------ private: int RtAudioCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); static int wrapperRtAudioCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData); void printDeviceInfo(unsigned int deviceId); JackTrip* mJackTrip; ///< JackTrip Mediator Class pointer int mNumInChans;///< Number of Input Channels int mNumOutChans; ///< Number of Output Channels QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK RtAudio* mRtAudio; ///< RtAudio class }; #endif // __RTAUDIOINTERFACE_H__ jacktrip-1.1/src/RingBuffer.cpp0000644000175000017500000002055512531357105016751 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file RingBuffer.cpp * \author Juan-Pablo Caceres * \date July 2008 */ #include "RingBuffer.h" #include #include #include #include using std::cout; using std::endl; //******************************************************************************* RingBuffer::RingBuffer(int SlotSize, int NumSlots) : mSlotSize(SlotSize), mNumSlots(NumSlots), mTotalSize(mSlotSize*mNumSlots), mReadPosition(0), mWritePosition(0), mFullSlots(0), mRingBuffer(new int8_t[mTotalSize]), mLastReadSlot(new int8_t[mSlotSize]) { //QMutexLocker locker(&mMutex); // lock the mutex // Verify if there's enough space to for the buffers if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) { //std::cerr << "ERROR: RingBuffer out of memory!" << endl; //std::cerr << "Exiting program..." << endl; //std::exit(1); throw std::length_error("RingBuffer out of memory!"); } // Set the buffers to zeros /* for (int i=0; i #include using std::cout; using std::endl; //******************************************************************************* void JackTripThread::run() { JackTrip jacktrip(mJackTripMode); jacktrip.setAllPorts(mPortNum); if ( mJackTripMode == JackTrip::CLIENT ) { jacktrip.setPeerAddress(mPeerAddress); } NetKS netks; jacktrip.appendProcessPlugin(&netks); //netks.play(); //QThread::sleep(1); jacktrip.start(); //netks.play(); jacktrip.wait(); cout << "******** AFTER JACKTRIPTHREAD START **************" << endl; //QThread::sleep(9999999); /* jack_client_t* mClient; const char* client_name = "JackThread"; const char* server_name = NULL; jack_options_t options = JackNoStartServer; jack_status_t status; mClient = jack_client_open (client_name, options, &status, server_name); if (mClient == NULL) { fprintf (stderr, "jack_client_open() failed, " "status = 0x%2.0x\n", status); if (status & JackServerFailed) { fprintf (stderr, "Unable to connect to JACK server\n"); } std::exit(1); } if (status & JackServerStarted) { fprintf (stderr, "JACK server started\n"); } if (status & JackNameNotUnique) { client_name = jack_get_client_name(mClient); fprintf (stderr, "unique name `%s' assigned\n", client_name); } */ } jacktrip-1.1/src/ProcessPlugin.cpp0000644000175000017500000000140612531357105017507 0ustar zmoelnigzmoelnig//#include "ProcessPlugin.h" /* //---------------------------------------------------------------------------- // Jack Callbacks //---------------------------------------------------------------------------- int srate(jack_nframes_t nframes, void *arg) { printf("the sample rate is now %u/sec\n", nframes); return 0; } void jack_shutdown(void *arg) { std::cout << "" << std::endl; std::exit(1); } int process (jack_nframes_t nframes, void *arg) { for (int i = 0; i < gNumInChans; i++) { gInChannel[i] = (float *)jack_port_get_buffer(input_ports[i], nframes); } for (int i = 0; i < gNumOutChans; i++) { gOutChannel[i] = (float *)jack_port_get_buffer(output_ports[i], nframes); } DSP.compute(nframes, gInChannel, gOutChannel); return 0; } */ jacktrip-1.1/src/JackTripWorkerMessages.h0000644000175000017500000000442012531357105020747 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTripWorkerMessages.h * \author Juan-Pablo Caceres * \date October 2008 */ #ifndef __JACKTRIPWORKERMESSAGES_H__ #define __JACKTRIPWORKERMESSAGES_H__ #include #include #include class JackTripWorkerMessages : public QObject { Q_OBJECT; public: JackTripWorkerMessages() {}; virtual ~JackTripWorkerMessages() {}; void play() { std::cout << "********** PALYING ***********************************" << std::endl; QTimer *timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(slotTest()), Qt::QueuedConnection); timer->start(300); } public slots: void slotTest() { std::cout << "---JackTripWorkerMessages slotTest()---" << std::endl; } signals: void signalTest(); /// Signal to stop the event loop inside the JackTripWorker Thread void signalStopEventLoop(); }; #endif //__JACKTRIPWORKERMESSAGES_H__ jacktrip-1.1/src/JackTripWorker.h0000644000175000017500000001004612531357105017260 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTripWorker.h * \author Juan-Pablo Caceres * \date September 2008 */ #ifndef __JACKTRIPWORKER_H__ #define __JACKTRIPWORKER_H__ #include #include #include #include #include #include #include "JackTrip.h" //class JackTrip; // forward declaration class UdpMasterListener; // forward declaration /** \brief Prototype of the worker class that will be cloned through sending threads to the * Thread Pool * * This class can be send to the ThreadPool using the start() method. Each time * it is sent, it'll became "independent" of the prototype, which means * that the prototype state can be changed, and used to send and start * another thread into the pool. setAutoDelete must be set to false * in order for this to work. */ // Note that it is not possible to start run() as an event loop. That has to be implemented // inside a QThread class JackTripWorker : public QObject, public QRunnable { Q_OBJECT; // QRunnable is not a QObject, so I have to inherit from QObject as well public: /// \brief The class constructor JackTripWorker(UdpMasterListener* udpmasterlistener); /// \brief The class destructor virtual ~JackTripWorker(); /// \brief Implements the Thread Loop. /// To start the thread, call start() ( DO NOT CALL run() ). void run(); /// \brief Check if the Thread is Spawning /// \return true is it is spawning, false if it's already running bool isSpawning(); /// \brief Sets the JackTripWorker properties /// \param id ID number /// \param address void setJackTrip(int id, uint32_t client_address, uint16_t server_port, uint16_t client_port, int num_channels); /// Stop and remove thread from pool void stopThread(); int getID() { return mID; } private slots: void slotTest() { std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; } signals: void signalRemoveThread(); private: int setJackTripFromClientHeader(JackTrip& jacktrip); JackTrip::connectionModeT getConnectionModeFromHeader(); UdpMasterListener* mUdpMasterListener; ///< Master Listener Socket //QHostAddress mClientAddress; ///< Client Address uint32_t mClientAddress; uint16_t mServerPort; ///< Server Ephemeral Incomming Port to use with Client /// Client Outgoing Port. By convention, the receving port will be mClientPort -1 uint16_t mClientPort; /// Thread spawning internal lock. /// If true, the prototype is working on creating (spawning) a new thread volatile bool mSpawning; QMutex mMutex; ///< Mutex to protect mSpawning int mID; ///< ID thread number int mNumChans; ///< Number of Channels }; #endif //__JACKTRIPWORKER_H__ jacktrip-1.1/src/JackTripWorker.cpp0000644000175000017500000002327412531357105017622 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTripWorker.cpp * \author Juan-Pablo Caceres * \date September 2008 */ #include #include #include #include #include #include "JackTripWorker.h" #include "JackTrip.h" #include "UdpMasterListener.h" #include "NetKS.h" #include "LoopBack.h" #ifdef __JAMTEST__ #include "JamTest.h" #endif using std::cout; using std::endl; //******************************************************************************* JackTripWorker::JackTripWorker(UdpMasterListener* udpmasterlistener) : mUdpMasterListener(NULL), mSpawning(false), mID(0), mNumChans(1) { /* From the QT Documentation: QThreadPool supports executing the same QRunnable more than once by calling tryStart(this) from within QRunnable::run(). If autoDelete is enabled the QRunnable will be deleted when the last thread exits the run function. Calling start() multiple times with the same QRunnable when autoDelete is enabled creates a race condition and is not recommended. */ mUdpMasterListener = udpmasterlistener; setAutoDelete(false); // stick around after calling run() //mNetks = new NetKS; //mNetks->play(); } //******************************************************************************* JackTripWorker::~JackTripWorker() { //delete mUdpMasterListener; } //******************************************************************************* void JackTripWorker::setJackTrip(int id, uint32_t client_address, uint16_t server_port, uint16_t client_port, int num_channels) { { //Start Spawning, so lock mSpawning QMutexLocker locker(&mMutex); mSpawning = true; } mID = id; // Set the jacktrip address and ports //mClientAddress.setAddress(client_address); mClientAddress = client_address; mServerPort = server_port; mClientPort = client_port; mNumChans = num_channels; } //******************************************************************************* void JackTripWorker::run() { /* NOTE: This is the message that qt prints when an exception is thrown: 'Qt Concurrent has caught an exception thrown from a worker thread. This is not supported, exceptions thrown in worker threads must be caught before control returns to Qt Concurrent.'*/ { QMutexLocker locker(&mMutex); mSpawning = true; } QHostAddress ClientAddress; // Try catching any exceptions that come from JackTrip try { // Local event loop. this is necesary because QRunnables don't have their own as QThreads QEventLoop event_loop; // Create and setup JackTrip Object //JackTrip jacktrip(JackTrip::SERVER, JackTrip::UDP, mNumChans, 2); cout << "---> JackTripWorker: Creating jacktip objects..." << endl; #ifndef __JAMTEST__ JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, 2); #endif #ifdef __JAMTEST__ JamTest jacktrip(JackTrip::SERVERPINGSERVER); // ########### JamTest ################# //JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, 2); #endif // Connect signals and slots // ------------------------- cout << "---> JackTripWorker: Connecting signals and slots..." << endl; // Connection to terminate JackTrip when packets haven't arrive for // a certain amount of time QObject::connect(&jacktrip, SIGNAL(signalNoUdpPacketsForSeconds()), &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); // Connection to terminate the local eventloop when jacktrip is done QObject::connect(&jacktrip, SIGNAL(signalProcessesStopped()), &event_loop, SLOT(quit()), Qt::QueuedConnection); QObject::connect(this, SIGNAL(signalRemoveThread()), &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); ClientAddress.setAddress(mClientAddress); // If I don't type this line, I get a bus error in the next line. // I still haven't figure out why ClientAddress.toString().toLatin1().constData(); jacktrip.setPeerAddress(ClientAddress.toString().toLatin1().constData()); jacktrip.setBindPorts(mServerPort); //jacktrip.setPeerPorts(mClientPort); cout << "---> JackTripWorker: setJackTripFromClientHeader..." << endl; int PeerConnectionMode = setJackTripFromClientHeader(jacktrip); if ( PeerConnectionMode == -1 ) { mUdpMasterListener->releaseThread(mID); { QMutexLocker locker(&mMutex); mSpawning = false; } return; } // Start Threads and event loop cout << "---> JackTripWorker: startProcess..." << endl; jacktrip.startProcess(); cout << "---> JackTripWorker: start..." << endl; jacktrip.start(); // ########### JamTest Only ################# // Thread is already spawning, so release the lock { QMutexLocker locker(&mMutex); mSpawning = false; } event_loop.exec(); // Excecution will block here until exit() the QEventLoop //-------------------------------------------------------------------------- { QMutexLocker locker(&mMutex); mSpawning = true; } // wait for jacktrip to be done before exiting the Worker Thread jacktrip.wait(); } catch ( const std::exception & e ) { std::cerr << "Couldn't send thread to the Pool" << endl; std::cerr << e.what() << endl; std::cerr << gPrintSeparator << endl; mUdpMasterListener->releaseThread(mID); { QMutexLocker locker(&mMutex); mSpawning = false; } return; } { QMutexLocker locker(&mMutex); mUdpMasterListener->releaseThread(mID); } cout << "JackTrip ID = " << mID << " released from the THREAD POOL" << endl; cout << gPrintSeparator << endl; { // Thread is already spawning, so release the lock QMutexLocker locker(&mMutex); mSpawning = false; } } //******************************************************************************* // returns -1 on error int JackTripWorker::setJackTripFromClientHeader(JackTrip& jacktrip) { //QHostAddress peerHostAddress; //uint16_t peer_port; QUdpSocket UdpSockTemp;// Create socket to wait for client // Bind the socket if ( !UdpSockTemp.bind(QHostAddress::Any, mServerPort, QUdpSocket::DefaultForPlatform) ) { std::cerr << "in JackTripWorker: Could not bind UDP socket. It may be already binded." << endl; throw std::runtime_error("Could not bind UDP socket. It may be already binded."); } // Listen to client QWaitCondition sleep; // time is in milliseconds QMutex mutex; int sleepTime = 100; // ms int udpTimeout = gTimeOutMultiThreadedServer; // gTimeOutMultiThreadedServer mseconds int elapsedTime = 0; { QMutexLocker lock(&mutex); while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { sleep.wait(&mutex,sleepTime); elapsedTime += sleepTime; //cout << "---------> ELAPSED TIME: " << elapsedTime << endl; } } // Check if we time out or not if (!UdpSockTemp.hasPendingDatagrams()) { std::cerr << "--->JackTripWorker: is not receiving Datagrams (timeout)" << endl; UdpSockTemp.close(); return -1; } int packet_size = UdpSockTemp.pendingDatagramSize(); char packet[packet_size]; UdpSockTemp.readDatagram(packet, packet_size); UdpSockTemp.close(); // close the socket int8_t* full_packet = reinterpret_cast(packet); int PeerBufferSize = jacktrip.getPeerBufferSize(full_packet); int PeerSamplingRate = jacktrip.getPeerSamplingRate(full_packet); int PeerBitResolution = jacktrip.getPeerBitResolution(full_packet); int PeerNumChannels = jacktrip.getPeerNumChannels(full_packet); int PeerConnectionMode = jacktrip.getPeerConnectionMode(full_packet); cout << "--->JackTripWorker: getPeerBufferSize = " << PeerBufferSize << endl; cout << "--->JackTripWorker: getPeerSamplingRate = " << PeerSamplingRate << endl; cout << "--->JackTripWorker: getPeerBitResolution = " << PeerBitResolution << endl; cout << "--->JackTripWorker: getPeerNumChannels = " << PeerNumChannels << endl; cout << "--->JackTripWorker: getPeerConnectionMode = " << PeerConnectionMode << endl; jacktrip.setNumChannels(PeerNumChannels); return PeerConnectionMode; } //******************************************************************************* bool JackTripWorker::isSpawning() { QMutexLocker locker(&mMutex); return mSpawning; } //******************************************************************************* void JackTripWorker::stopThread() { QMutexLocker locker(&mMutex); emit signalRemoveThread(); } jacktrip-1.1/src/JackTrip.cpp0000644000175000017500000006370612531357105016434 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTrip.cpp * \author Juan-Pablo Caceres * \date July 2008 */ #include "JackTrip.h" #include "UdpDataProtocol.h" #include "RingBufferWavetable.h" #include "jacktrip_globals.h" #include "JackAudioInterface.h" #ifdef __RT_AUDIO__ #include "RtAudioInterface.h" #endif #include //#include // for usleep, sleep #include #include #include #include #include using std::cout; using std::endl; //the following function has to remain outside the Jacktrip class definition //its purpose is to close the app when control c is hit by the user in rtaudio/asio4all mode #if defined __WIN_32__ void sigint_handler(int sig) { exit(0); } #endif //******************************************************************************* JackTrip::JackTrip(jacktripModeT JacktripMode, dataProtocolT DataProtocolType, int NumChans, int BufferQueueLength, unsigned int redundancy, AudioInterface::audioBitResolutionT AudioBitResolution, DataProtocol::packetHeaderTypeT PacketHeaderType, underrunModeT UnderRunMode, int receiver_bind_port, int sender_bind_port, int receiver_peer_port, int sender_peer_port) : mJackTripMode(JacktripMode), mDataProtocol(DataProtocolType), mPacketHeaderType(PacketHeaderType), mAudiointerfaceMode(JackTrip::JACK), mNumChans(NumChans), mBufferQueueLength(BufferQueueLength), mSampleRate(gDefaultSampleRate), mAudioBufferSize(gDefaultBufferSizeInSamples), mAudioBitResolution(AudioBitResolution), mDataProtocolSender(NULL), mDataProtocolReceiver(NULL), mAudioInterface(NULL), mPacketHeader(NULL), mUnderRunMode(UnderRunMode), mSendRingBuffer(NULL), mReceiveRingBuffer(NULL), mReceiverBindPort(receiver_bind_port), mSenderPeerPort(sender_peer_port), mSenderBindPort(sender_bind_port), mReceiverPeerPort(receiver_peer_port), mTcpServerPort(4464), mRedundancy(redundancy), mJackClientName("JackTrip"), mConnectionMode(JackTrip::NORMAL), mReceivedConnection(false), mTcpConnectionError(false), mStopped(false) { createHeader(mPacketHeaderType); } //******************************************************************************* JackTrip::~JackTrip() { wait(); delete mDataProtocolSender; delete mDataProtocolReceiver; delete mAudioInterface; delete mPacketHeader; delete mSendRingBuffer; delete mReceiveRingBuffer; } //******************************************************************************* void JackTrip::setupAudio() { // Check if mAudioInterface has already been created or not if (mAudioInterface != NULL) { // if it has been created, disconnet it from JACK and delete it cout << "WARINING: JackAudio interface was setup already:" << endl; cout << "It will be errased and setup again." << endl; cout << gPrintSeparator << endl; closeAudio(); } // Create AudioInterface Client Object if ( mAudiointerfaceMode == JackTrip::JACK ) { #ifndef __NO_JACK__ mAudioInterface = new JackAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); mAudioInterface->setClientName(mJackClientName); mAudioInterface->setup(); mSampleRate = mAudioInterface->getSampleRate(); mAudioBufferSize = mAudioInterface->getBufferSizeInSamples(); #endif //__NON_JACK__ #ifdef __NO_JACK__ /// \todo FIX THIS REPETITION OF CODE #ifdef __RT_AUDIO__ cout << "Warning: using non jack version, RtAudio will be used instead" << endl; mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); mAudioInterface->setSampleRate(mSampleRate); mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); mAudioInterface->setup(); #endif #endif } else if ( mAudiointerfaceMode == JackTrip::RTAUDIO ) { #ifdef __RT_AUDIO__ mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); mAudioInterface->setSampleRate(mSampleRate); mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); mAudioInterface->setup(); #endif } std::cout << "The Sampling Rate is: " << mSampleRate << std::endl; std::cout << gPrintSeparator << std::endl; int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t); std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl; std::cout << " or: " << AudioBufferSizeInBytes << " bytes" << std::endl; std::cout << gPrintSeparator << std::endl; cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl; std::cout << gPrintSeparator << std::endl; QThread::usleep(100); } //******************************************************************************* void JackTrip::closeAudio() { //mAudioInterface->close(); if ( mAudioInterface != NULL ) { mAudioInterface->stopProcess(); delete mAudioInterface; mAudioInterface = NULL; } } //******************************************************************************* void JackTrip::setupDataProtocol() { // Create DataProtocol Objects switch (mDataProtocol) { case UDP: std::cout << "Using UDP Protocol" << std::endl; std::cout << gPrintSeparator << std::endl; QThread::usleep(100); mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER, //mSenderPeerPort, mSenderBindPort, mSenderBindPort, mSenderPeerPort, mRedundancy); mDataProtocolReceiver = new UdpDataProtocol(this, DataProtocol::RECEIVER, mReceiverBindPort, mReceiverPeerPort, mRedundancy); break; case TCP: throw std::invalid_argument("TCP Protocol is not implemented"); break; case SCTP: throw std::invalid_argument("SCTP Protocol is not implemented"); break; default: throw std::invalid_argument("Protocol not defined or unimplemented"); break; } // Set Audio Packet Size //mDataProtocolSender->setAudioPacketSize // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); //mDataProtocolReceiver->setAudioPacketSize // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); mDataProtocolSender->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); mDataProtocolReceiver->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); } //******************************************************************************* void JackTrip::setupRingBuffers() { // Create RingBuffers with the apprioprate size /// \todo Make all this operations cleaner //int total_audio_packet_size = getTotalAudioPacketSizeInBytes(); int slot_size = getRingBuffersSlotSize(); switch (mUnderRunMode) { case WAVETABLE: mSendRingBuffer = new RingBufferWavetable(slot_size, gDefaultOutputQueueLength); mReceiveRingBuffer = new RingBufferWavetable(slot_size, mBufferQueueLength); /* mSendRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, gDefaultOutputQueueLength); mReceiveRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, mBufferQueueLength); */ break; case ZEROS: mSendRingBuffer = new RingBuffer(slot_size, gDefaultOutputQueueLength); mReceiveRingBuffer = new RingBuffer(slot_size, mBufferQueueLength); /* mSendRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, gDefaultOutputQueueLength); mReceiveRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, mBufferQueueLength); */ break; default: throw std::invalid_argument("Underrun Mode undefined"); break; } } //******************************************************************************* void JackTrip::setPeerAddress(const char* PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } //******************************************************************************* void JackTrip::appendProcessPlugin(ProcessPlugin* plugin) { mProcessPlugins.append(plugin); //mAudioInterface->appendProcessPlugin(plugin); } //******************************************************************************* void JackTrip::startProcess() throw(std::invalid_argument) { //signal that catches ctrl c in rtaudio-asio mode #if defined (__WIN_32__) if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("signal"); exit(1); } #endif // Check if ports are already binded by another process on this machine // ------------------------------------------------------------------ checkIfPortIsBinded(mReceiverBindPort); checkIfPortIsBinded(mSenderBindPort); // Set all classes and parameters // ------------------------------ setupAudio(); createHeader(mPacketHeaderType); setupDataProtocol(); setupRingBuffers(); // Connect Signals and Slots // ------------------------- QObject::connect(mPacketHeader, SIGNAL(signalError(const char*)), this, SLOT(slotStopProcesses()), Qt::QueuedConnection); QObject::connect(mDataProtocolReceiver, SIGNAL(signalReceivedConnectionFromPeer()), this, SLOT(slotReceivedConnectionFromPeer()), Qt::QueuedConnection); QObject::connect(this, SIGNAL(signalUdpTimeOut()), this, SLOT(slotStopProcesses()), Qt::QueuedConnection); //QObject::connect(mDataProtocolSender, SIGNAL(signalError(const char*)), // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); //QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)), // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); // Start the threads for the specific mode // --------------------------------------- switch ( mJackTripMode ) { case CLIENT : clientStart(); break; case SERVER : serverStart(); break; case CLIENTTOPINGSERVER : if ( clientPingToServerStart() == -1 ) { // if error on server start (-1) we return inmediatly mTcpConnectionError = true; slotStopProcesses(); return; } break; case SERVERPINGSERVER : if ( serverStart(true) == -1 ) { // if error on server start (-1) we return inmediatly slotStopProcesses(); return; } break; default: throw std::invalid_argument("Jacktrip Mode undefined"); break; } // Start Threads mAudioInterface->startProcess(); for (int i = 0; i < mProcessPlugins.size(); ++i) { mAudioInterface->appendProcessPlugin(mProcessPlugins[i]); } mAudioInterface->connectDefaultPorts(); mDataProtocolReceiver->start(); QThread::msleep(1); mDataProtocolSender->start(); } //******************************************************************************* void JackTrip::stop() { // Stop The Sender mDataProtocolSender->stop(); mDataProtocolSender->wait(); // Stop The Receiver mDataProtocolReceiver->stop(); mDataProtocolReceiver->wait(); // Stop the audio processes //mAudioInterface->stopProcess(); closeAudio(); cout << "JackTrip Processes STOPPED!" << endl; cout << gPrintSeparator << endl; // Emit the jack stopped signal emit signalProcessesStopped(); } //******************************************************************************* void JackTrip::waitThreads() { mDataProtocolSender->wait(); mDataProtocolReceiver->wait(); } //******************************************************************************* void JackTrip::clientStart() throw(std::invalid_argument) { // For the Client mode, the peer (or server) address has to be specified by the user if ( mPeerAddress.isEmpty() ) { throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); } else { mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl; cout << gPrintSeparator << endl; } } //******************************************************************************* int JackTrip::serverStart(bool timeout, int udpTimeout) throw(std::invalid_argument, std::runtime_error) { // Set the peer address if ( !mPeerAddress.isEmpty() ) { std::cout << "WARNING: SERVER mode: Peer Address was set but will be deleted." << endl; //throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); mPeerAddress.clear(); //return; } // Get the client address when it connects cout << "Waiting for Connection From Client..." << endl; QHostAddress peerHostAddress; uint16_t peer_port; QUdpSocket UdpSockTemp;// Create socket to wait for client // Bind the socket if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, QUdpSocket::DefaultForPlatform) ) { std::cerr << "in JackTrip: Could not bind UDP socket. It may be already binded." << endl; throw std::runtime_error("Could not bind UDP socket. It may be already binded."); } // Listen to client int sleepTime = 100; // ms int elapsedTime = 0; if (timeout) { while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { if (mStopped == true) { emit signalUdpTimeOut(); UdpSockTemp.close(); return -1; } QThread::msleep(sleepTime); elapsedTime += sleepTime; } if (!UdpSockTemp.hasPendingDatagrams()) { emit signalUdpTimeOut(); cout << "JackTrip Server Timed Out!" << endl; return -1; } } else { while ( !UdpSockTemp.hasPendingDatagrams() ) { if (mStopped == true) { emit signalUdpTimeOut(); return -1; } QThread::msleep(sleepTime); } } char buf[1]; // set client address UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port); UdpSockTemp.close(); // close the socket mPeerAddress = peerHostAddress.toString(); cout << "Client Connection Received from IP : " << qPrintable(mPeerAddress) << endl; cout << gPrintSeparator << endl; // Set the peer address to send packets (in the protocol sender) mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() ); mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() ); // We reply to the same port the peer sent the packets from // This way we can go through NAT // Because of the NAT traversal scheme, the portn need to be // "symetric", e.g.: // from Client to Server : src = 4474, dest = 4464 // from Server to Client : src = 4464, dest = 4474 mDataProtocolSender->setPeerPort(peer_port); mDataProtocolReceiver->setPeerPort(peer_port); setPeerPorts(peer_port); return 0; } //******************************************************************************* int JackTrip::clientPingToServerStart() throw(std::invalid_argument) { //mConnectionMode = JackTrip::KSTRONG; //mConnectionMode = JackTrip::JAMTEST; // Set Peer (server in this case) address // -------------------------------------- // For the Client mode, the peer (or server) address has to be specified by the user if ( mPeerAddress.isEmpty() ) { throw std::invalid_argument("Peer Address has to be set if you run in CLIENTTOPINGSERVER mode"); return -1; } // Creat Socket Objects // -------------------- QTcpSocket tcpClient; QHostAddress serverHostAddress; serverHostAddress.setAddress(mPeerAddress); // Connect Socket to Server and wait for response // ---------------------------------------------- tcpClient.connectToHost(serverHostAddress, mTcpServerPort); cout << "Connecting to TCP Server..." << endl; if (!tcpClient.waitForConnected()) { std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; //std::exit(1); return -1; } cout << "TCP Socket Connected to Server!" << endl; emit signalTcpClientConnected(); // Send Client Port Number to Server // --------------------------------- char port_buf[sizeof(mReceiverBindPort)]; std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort)); tcpClient.write(port_buf, sizeof(mReceiverBindPort)); while ( tcpClient.bytesToWrite() > 0 ) { tcpClient.waitForBytesWritten(-1); } cout << "Port sent to Client" << endl; // Read the size of the package // ---------------------------- cout << "Reading UDP port from Server..." << endl; while (tcpClient.bytesAvailable() < (int)sizeof(uint16_t)) { if (!tcpClient.waitForReadyRead()) { std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; //std::exit(1); return -1; } } cout << "Ready To Read From Socket!" << endl; // Read UDP Port Number from Server // -------------------------------- uint32_t udp_port; int size = sizeof(udp_port); //char port_buf[size]; tcpClient.read(port_buf, size); std::memcpy(&udp_port, port_buf, size); //cout << "Received UDP Port Number: " << udp_port << endl; // Close the TCP Socket // -------------------- tcpClient.close(); // Close the socket //cout << "TCP Socket Closed!" << endl; cout << "Connection Succesfull!" << endl; // Set with the received UDP port // ------------------------------ setPeerPorts(udp_port); mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); mDataProtocolSender->setPeerPort(udp_port); mDataProtocolReceiver->setPeerPort(udp_port); cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl; cout << gPrintSeparator << endl; return 0; /* else { // Set the peer address mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); } // Start the Sender Threads // ------------------------ mAudioInterface->startProcess(); mDataProtocolSender->start(); // block until mDataProtocolSender thread starts while ( !mDataProtocolSender->isRunning() ) { QThread::msleep(100); } // Create a Socket to listen to Server's answer // -------------------------------------------- QHostAddress serverHostAddress; QUdpSocket UdpSockTemp;// Create socket to wait for server answer uint16_t server_port; // Bind the socket //bindReceiveSocket(UdpSockTemp, mReceiverBindPort, // mPeerAddress, peer_port); if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, QUdpSocket::ShareAddress) ) { //throw std::runtime_error("Could not bind PingToServer UDP socket. It may be already binded."); } // Listen to server response cout << "Waiting for server response..." << endl; while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::msleep(100); } cout << "Received response from server!" << endl; char buf[1]; // set client address UdpSockTemp.readDatagram(buf, 1, &serverHostAddress, &server_port); UdpSockTemp.close(); // close the socket // Stop the sender thread to change server port mDataProtocolSender->stop(); mDataProtocolSender->wait(); // Wait for the thread to terminate cout << "Server port now set to: " << server_port << endl; cout << gPrintSeparator << endl; mDataProtocolSender->setPeerPort(server_port); // Start Threads //mAudioInterface->connectDefaultPorts(); mDataProtocolSender->start(); mDataProtocolReceiver->start(); */ } //******************************************************************************* /* void JackTrip::bindReceiveSocket(QUdpSocket& UdpSocket, int bind_port, QHostAddress PeerHostAddress, int peer_port) throw(std::runtime_error) { // Creat socket descriptor int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); // Set local IPv4 Address struct sockaddr_in local_addr; ::bzero(&local_addr, sizeof(local_addr)); local_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address local_addr.sin_port = htons(bind_port); //set bind port // Set socket to be reusable, this is platform dependent int one = 1; #if defined ( __LINUX__ ) ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); #endif #if defined ( __MAC_OSX__ ) // This option is not avialable on Linux, and without it MAC OS X // has problems rebinding a socket ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); #endif // Bind the Socket if ( (::bind(sock_fd, (struct sockaddr *) &local_addr, sizeof(local_addr))) < 0 ) { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } // To be able to use the two UDP sockets bound to the same port number, // we connect the receiver and issue a SHUT_WR. // Set peer IPv4 Address struct sockaddr_in peer_addr; bzero(&peer_addr, sizeof(peer_addr)); peer_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address peer_addr.sin_port = htons(peer_port); //set local port // Connect the socket and issue a Write shutdown (to make it a // reader socket only) if ( (::inet_pton(AF_INET, PeerHostAddress.toString().toLatin1().constData(), &peer_addr.sin_addr)) < 1 ) { throw std::runtime_error("ERROR: Invalid address presentation format"); } if ( (::connect(sock_fd, (struct sockaddr *) &peer_addr, sizeof(peer_addr))) < 0) { throw std::runtime_error("ERROR: Could not connect UDP socket"); } if ( (::shutdown(sock_fd,SHUT_WR)) < 0) { throw std::runtime_error("ERROR: Could suntdown SHUT_WR UDP socket"); } UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::ConnectedState, QUdpSocket::ReadOnly); cout << "UDP Socket Receiving in Port: " << bind_port << endl; cout << gPrintSeparator << endl; } */ //******************************************************************************* void JackTrip::createHeader(const DataProtocol::packetHeaderTypeT headertype) { delete mPacketHeader; //Just in case it has already been allocated switch (headertype) { case DataProtocol::DEFAULT : mPacketHeader = new DefaultHeader(this); break; case DataProtocol::JAMLINK : mPacketHeader = new JamLinkHeader(this); break; case DataProtocol::EMPTY : mPacketHeader = new EmptyHeader(this); break; default : throw std::invalid_argument("Undefined Header Type"); break; } } //******************************************************************************* void JackTrip::putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet) { mPacketHeader->fillHeaderCommonFromAudio(); mPacketHeader->putHeaderInPacket(full_packet); int8_t* audio_part; audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); //std::memcpy(audio_part, audio_packet, mAudioInterface->getBufferSizeInBytes()); //std::memcpy(audio_part, audio_packet, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); std::memcpy(audio_part, audio_packet, getTotalAudioPacketSizeInBytes()); } //******************************************************************************* int JackTrip::getPacketSizeInBytes() { //return (mAudioInterface->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); //return (mAudioInterface->getSizeInBytesPerChannel() * mNumChans + //mPacketHeader->getHeaderSizeInBytes()); return (getTotalAudioPacketSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); } //******************************************************************************* void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet) { int8_t* audio_part; audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); //std::memcpy(audio_packet, audio_part, mAudioInterface->getBufferSizeInBytes()); //std::memcpy(audio_packet, audio_part, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes()); } //******************************************************************************* void JackTrip::checkPeerSettings(int8_t* full_packet) { mPacketHeader->checkPeerSettings(full_packet); } //******************************************************************************* void JackTrip::checkIfPortIsBinded(int port) { QUdpSocket UdpSockTemp;// Create socket to wait for client // Bind the socket if ( !UdpSockTemp.bind(QHostAddress::Any, port, QUdpSocket::DontShareAddress) ) { UdpSockTemp.close(); // close the socket throw std::runtime_error( "Could not bind UDP socket. It may already be binded by another process on your machine. Try using a different port number"); } UdpSockTemp.close(); // close the socket } jacktrip-1.1/src/TestRingBuffer.h0000644000175000017500000000161512531357105017252 0ustar zmoelnigzmoelnig#ifndef __TESTRINGBUFFER__ #define __TESTRINGBUFFER__ #include "RingBuffer.h" #include #include static RingBuffer rb(2,100); class TestRingBufferWrite : public QThread { public: void run() { int8_t* writeSlot; writeSlot = new int8_t[2]; writeSlot[0] = *"a"; writeSlot[1] = *"b"; while (true) { //std::cout << "writing BEFORE" << std::endl; rb.insertSlotBlocking(writeSlot); //std::cout << "writing AFTER" << std::endl; } } }; class TestRingBufferRead : public QThread { public: void run() { int8_t* readSlot; readSlot = new int8_t[2]; while (true) { //std::cout << "reading BEFORE" << std::endl; rb.readSlotBlocking(readSlot); //std::cout << "reading AFTER" << std::endl; //std::cout << *(readSlot) << std::endl; //std::cout << *(readSlot+1) << std::endl; } } }; #endif jacktrip-1.1/src/NetKS.h0000644000175000017500000000777112531357105015356 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTrip.h * \author Juan-Pablo Caceres * \date October 2008 */ #ifndef __NETKS_H__ #define __NETKS_H__ #include //#include #include #include "ProcessPlugin.h" /** \brief A simple (basic) network Karplus Strong. * * This plugin creates a one channel network karplus strong. */ class NetKS : public ProcessPlugin { Q_OBJECT; public: /* void play() { std::cout << "********** PALYING ***********************************" << std::endl; QTimer *timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(exciteString())); timer->start(300); } */ private slots: /// \brief Stlot to excite (play) the string void exciteString() { std::cout << "========= EXTICING STRING ===========" << std::endl; fbutton0 = 1.0; //std::cout << fbutton0 << std::endl; QThread::usleep(280000); /// \todo Define this number based on the sampling rate and buffer size fbutton0 = 0.0; //std::cout << fbutton0 << std::endl; } //=========== FROM FAUST =================================================== private: float fbutton0; float fVec0[2]; float fRec0[2]; int iRec1[2]; float fVec1[2]; public: virtual int getNumInputs() { return 1; } virtual int getNumOutputs() { return 1; } static void classInit(int /*samplingFreq*/) {} virtual void instanceInit(int samplingFreq) { fSamplingFreq = samplingFreq; fbutton0 = 0.0; for (int i=0; i<2; i++) fVec0[i] = 0; for (int i=0; i<2; i++) fRec0[i] = 0; for (int i=0; i<2; i++) iRec1[i] = 0; for (int i=0; i<2; i++) fVec1[i] = 0; } virtual void init(int samplingFreq) { classInit(samplingFreq); instanceInit(samplingFreq); } /* virtual void buildUserInterface(UI* interface) { interface->openVerticalBox("excitator"); interface->addButton("play", &fbutton0); interface->closeBox(); } */ virtual void compute (int count, float** input, float** output) { float* input0 = input[0]; float* output0 = output[0]; float fSlow0 = fbutton0; for (int i=0; i 0.000000f) + fRec0[1]) - (3.333333e-03f * (fRec0[1] > 0.000000f))); iRec1[0] = (12345 + (1103515245 * iRec1[1])); float fTemp0 = ((4.190951e-10f * iRec1[0]) * (fRec0[0] > 0.000000f)); float fTemp1 = input0[i]; fVec1[0] = (fTemp1 + fTemp0); output0[i] = (0.500000f * ((fTemp0 + fTemp1) + fVec1[1])); // post processing fVec1[1] = fVec1[0]; iRec1[1] = iRec1[0]; fRec0[1] = fRec0[0]; fVec0[1] = fVec0[0]; } } //============================================================================ }; #endif // __NETKS_H__ jacktrip-1.1/src/build0000755000175000017500000000201512531357105015230 0ustar zmoelnigzmoelnig#!/bin/bash ## Created by Juan-Pablo Caceres # Check for Platform platform='unknown' unamestr=`uname` if [[ "$unamestr" == 'Linux' ]]; then echo "Building on Linux" platform='linux' elif [[ "$unamestr" == 'Darwin' ]]; then echo "Building on Mac OS X" platform='macosx' fi # Set qmake command name if [[ $platform == 'linux' ]]; then if hash qmake-qt5 2>/dev/null; then echo "Using qmake-qt5" QCMD=qmake-qt5 elif hash qmake-qt4 2>/dev/null; then echo "Using qmake-qt4" QCMD=qmake-qt4 elif hash qmake 2>/dev/null; then #in case qt was compiled by user echo "Using qmake" QCMD=qmake fi QSPEC=linux-g++ elif [[ $platform == 'macosx' ]]; then QCMD=qmake QSPEC=macx-g++ fi # Build if [[ $1 == 'nojack' ]]; then echo "Building without Jack" $QCMD -spec $QSPEC -config nojack jacktrip.pro make clean $QCMD -spec $QSPEC -config nojack jacktrip.pro make release else $QCMD -spec $QSPEC jacktrip.pro make clean $QCMD -spec $QSPEC jacktrip.pro make release fi jacktrip-1.1/src/DataProtocolPOSIX.h.tmp0000644000175000017500000001567612531357105020412 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file DataProtocol.h * \author Juan-Pablo Caceres * \date June 2008 */ #ifndef __DATAPROTOCOL_H__ #define __DATAPROTOCOL_H__ //#include //basic socket definitions #include //sockaddr_in{} and other Internet defns #include //inet(3) functions #include #include //for shared_ptr #include #include "RingBuffer.h" //#include "PacketHeader.h" class PacketHeader; // forward declaration /** \brief Base class that defines the transmission protocol. * * This base class defines most of the common method to setup and connect * sockets using the individual protocols (UDP, TCP, SCTP, etc). * * The class has to be constructed using one of two modes (runModeT):\n * - SENDER * - RECEIVER * * This has to be specified as a constructor argument. When using, create two instances * of the class, one to receive and one to send packets. Each instance will run on a * separate thread. * * Redundancy and forward error correction should be implemented on each * Transport protocol, cause they depend on the protocol itself * * \todo This Class should contain definition of jacktrip header and basic funcionality to obtain * local machine IPs and maybe functions to manipulate IPs. * Redundancy and forward error correction should be implemented on each * Transport protocol, cause they depend on the protocol itself * * \todo The transport protocol itself has to be implemented subclassing this class, i.e., * using a TCP or UDP protocol. * * Even if the underlined transmission protocol is stream oriented (as in TCP), * we send packets that are the size of the audio processing buffer. * Use AudioInterface::getBufferSize to obtain this value. * * Each transmission (i.e., inputs and outputs) run on its own thread. */ class DataProtocol : public QThread { public: /// \brief Enum to define packet header types enum packetHeaderTypeT { DEFAULT, ///< Default application header JAMLINK ///< Header to use with Jamlinks }; /// \brief Enum to define class modes, SENDER or RECEIVER enum runModeT { SENDER, ///< Set class as a Sender (send packets) RECEIVER ///< Set class as a Receiver (receives packets) }; /** \brief The class constructor * \param runmode Sets the run mode, use either SENDER or RECEIVER */ DataProtocol(const runModeT runmode, const packetHeaderTypeT headertype = DEFAULT); /// \brief The class destructor virtual ~DataProtocol(); /** \brief Sets the peer (remote) IPv4 address struct * \param peerHostOrIP Either an IPv4 dotted integer number or a hostname */ virtual void setPeerIPv4Address(const char* peerHostOrIP); /** \brief Receive a packet from the UDPSocket * * This method has to be implemented in the sub-classes * \param buf Location at which to store the buffer * \param n size of packet to receive in bytes * \return number of bytes read, -1 on error */ virtual size_t receivePacket(char* buf, size_t n) = 0; /** \brief Sends a packet * * This method has to be implemented in the sub-classes * \param buff Buffer to send * \param n size of packet to receive in bytes * \return number of bytes read, -1 on error */ virtual size_t sendPacket(const char* buff, size_t n) = 0; /** \brief Implements the thread loop * * Depending on the runmode, with will run a RECEIVE thread or * SEND thread */ virtual void run(); /** \brief Set the pointer to the RingBuffer that'll be use to read * or write */ void setRingBuffer(std::tr1::shared_ptr RingBuffer); /// \brief Stops the execution of the Thread void stop(); /** \brief Sets the size of the audio part of the packets * \param size_bytes Size in bytes */ void setAudioPacketSize(size_t size_bytes); /** \brief Get the size of the audio part of the packets * \return size_bytes Size in bytes */ size_t getAudioPacketSize(); //virtual void getIPAddressFromFirstPacket() = 0; protected: /** \brief Sets the local IPv4 address struct * * It uses the default active device. */ virtual void setLocalIPv4Address(); /** \brief Get the Run Mode of the object * \return SENDER or RECEIVER */ runModeT getRunMode() const { return mRunMode; }; /** \brief Returns the Local machine IPv4 socket address stuct * \return Socket address stuct */ const sockaddr_in& getLocalIPv4AddressStruct() const { return mLocalIPv4Addr; }; /** \brief Returns the Peer IPv4 socket address stuct * \return Socket address stuct */ const sockaddr_in& getPeerIPv4AddressStruct() const { return mPeerIPv4Addr; }; private: int mLocalPort; ///< Local Port number to Bind int mPeerPort; ///< Peer Port number to Bind const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct /// Smart Pointer to RingBuffer to read (for SENDER) or write (for RECEIVER) std::tr1::shared_ptr mRingBuffer; /// Boolean stop the execution of the thread volatile bool mStopped; /// Boolean to indicate if the RECEIVER is waiting to obtain peer address volatile bool mHasPeerAddress; /// Boolean that indicates if a packet was received volatile bool mHasPacketsToReceive; /// Number of clients running to check for ports already used /// \note Unimplemented, try to find another way to check for used ports static int sClientsRunning; size_t mAudioPacketSize; ///< Packet audio part size PacketHeader* mHeader; ///< Packet Header }; #endif jacktrip-1.1/src/jacktrip_tests.cpp0000644000175000017500000000571512531357105017752 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file jacktrip_tests.cpp * \author Juan-Pablo Caceres * \date September 2008 */ #include #include #include "JackTripThread.h" using std::cout; using std::endl; const int num_jacktrips = 5; const int base_port = 4464; void main_tests(int argc, char** argv); void test_threads_server(); void test_threads_client(const char* peer_address); void main_tests(int /*argc*/, char** argv) { if (argv[1][0] == 's' ) { test_threads_server(); } else if (argv[1][0] == 'c' ) { test_threads_client("171.64.197.209"); } } // Test many servers running at the same time void test_threads_server() { QVector jacktrips; jacktrips.resize(num_jacktrips); int port_num; for (int i = 0; i < num_jacktrips; i++) { port_num = base_port + i*10; cout << "Port Number: " << port_num << endl; jacktrips[i] = new JackTripThread(JackTrip::SERVER); jacktrips[i]->setPort(port_num); jacktrips[i]->start(QThread::NormalPriority); //sleep(1); } } // Test many servers running at the same time void test_threads_client(const char* peer_address) { QVector jacktrips; jacktrips.resize(num_jacktrips); int port_num; for (int i = 0; i < num_jacktrips; i++) { port_num = base_port + i*10; cout << "Port Number: " << port_num << endl; jacktrips[i] = new JackTripThread(JackTrip::CLIENT); jacktrips[i]->setPort(port_num); jacktrips[i]->setPeerAddress(peer_address); //sleep(1); jacktrips[i]->start(QThread::NormalPriority); //sleep(1); } } jacktrip-1.1/src/ProcessPlugin.h0000644000175000017500000000547512531357105017166 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file ProcessPlugin.h * \author Juan-Pablo Caceres * \date July 2008 */ #ifndef __PROCESSPLUGIN_H__ #define __PROCESSPLUGIN_H__ #include #include /** \brief Interface for the process plugins to add to the JACK callback process in * JackAudioInterface * * This class contains the same methods of the FAUST dsp class. A mydsp class can inherit from * this class the same way it inherits from dsp. Subclass should implement all methods * except init, which is optional for processing that are sampling rate dependent or * that need specific initialization. */ class ProcessPlugin : public QThread { public: /// \brief The Class Constructor ProcessPlugin() {}; /// \brief The Class Destructor virtual ~ProcessPlugin() {}; /// \brief Return Number of Input Channels virtual int getNumInputs() = 0; /// \brief Return Number of Output Channels virtual int getNumOutputs() = 0; //virtual void buildUserInterface(UI* interface) = 0; /** \brief Do proper Initialization of members and class instances. By default this * initializes the Sampling Frequency. If a class instance depends on the * sampling frequency, it should be initialize here. */ virtual void init(int samplingRate) { fSamplingFreq = samplingRate; }; /// \brief Compute process virtual void compute(int nframes, float** inputs, float** outputs) = 0; protected: int fSamplingFreq; ///< Faust Data member, Sampling Rate }; #endif jacktrip-1.1/src/UdpMasterListener.h0000644000175000017500000001043212531357105017770 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file UdpMasterListener.h * \author Juan-Pablo Caceres and Chris Chafe * \date September 2008 */ #ifndef __UDPMASTERLISTENER_H__ #define __UDPMASTERLISTENER_H__ #include #include #include #include #include #include #include #include #include #include "jacktrip_types.h" #include "jacktrip_globals.h" class JackTripWorker; // forward declaration /** \brief Master UDP listener on the Server. * * This creates a server that will listen on the well know port (the server port) and will * spawn JackTrip threads into the Thread pool. Clients request a connection. */ class UdpMasterListener : public QThread { Q_OBJECT; public: UdpMasterListener(int server_port = gServerUdpPort); virtual ~UdpMasterListener(); /// \brief Implements the Thread Loop. To start the thread, call start() /// ( DO NOT CALL run() ) void run(); /// \brief Stops the execution of the Thread void stop() { mStopped = true; } int releaseThread(int id); private slots: void testReceive() { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; } signals: void Listening(); void ClientAddressSet(); void signalRemoveThread(int id); private: /** \brief Binds a QUdpSocket. It chooses the available (active) interface. * \param udpsocket a QUdpSocket * \param port Port number */ static void bindUdpSocket(QUdpSocket& udpsocket, int port) throw(std::runtime_error); int readClientUdpPort(QTcpSocket* clientConnection); int sendUdpPort(QTcpSocket* clientConnection, int udp_port); /** \brief Send the JackTripWorker to the thread pool. This will run * until it's done. We still have control over the prototype class. * \param id Identification Number */ //void sendToPoolPrototype(int id); /** \brief Check if address is already handled, if not add to array * \param IPv4 address as a number * \return -1 if address is busy, id number if not */ int isNewAddress(uint32_t address, uint16_t port); /** \brief Returns the ID of the client in the pool. If the client * is not in the pool yet, returns -1. */ int getPoolID(uint32_t address, uint16_t port); //QUdpSocket mUdpMasterSocket; ///< The UDP socket //QHostAddress mPeerAddress; ///< The Peer Address //JackTripWorker* mJTWorker; ///< Class that will be used as prototype QVector* mJTWorkers; ///< Vector of JackTripWorker s QThreadPool mThreadPool; ///< The Thread Pool int mServerPort; //< Server known port number int mBasePort; uint32_t mActiveAddress[gMaxThreads][2]; ///< Active addresses pool numbers (32 bits IPv4 numbers) QHash mActiveAddresPortPair; /// Boolean stop the execution of the thread volatile bool mStopped; int mTotalRunningThreads; ///< Number of Threads running in the pool QMutex mMutex; }; #endif //__UDPMASTERLISTENER_H__ jacktrip-1.1/src/jacktrip.pro0000644000175000017500000000736312531357105016547 0ustar zmoelnigzmoelnig#****************************** # Created by Juan-Pablo Caceres #****************************** CONFIG += qt thread debug_and_release build_all CONFIG(debug, debug|release) { TARGET = jacktrip_debug } else { TARGET = jacktrip } QT -= gui QT += network # http://wiki.qtcentre.org/index.php?title=Undocumented_qmake#Custom_tools DEFINES += __RT_AUDIO__ # Configuration without Jack nojack { DEFINES += __NO_JACK__ } !win32 { INCLUDEPATH+=/usr/local/include LIBS += -L/usr/local/lib -ljack -lm nojack { message(Building NONJACK) LIBS -= -ljack } } macx { message(MAC OS X) QMAKE_CXXFLAGS += -D__MACOSX_CORE__ #-D__UNIX_JACK__ #RtAudio Flags QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 QMAKE_MAC_SDK = macosx10.9 CONFIG -= app_bundle #CONFIG += x86 #ppc #### If you have both libraries installed, you # can change between 32bits (x86) or 64bits(x86_64) Change this to go back to 32 bits (x86) LIBS += -framework CoreAudio -framework CoreFoundation DEFINES += __MAC_OSX__ } linux-g++ { message(Linux) LIBS += -lasound QMAKE_CXXFLAGS += -D__LINUX_ALSA__ #-D__LINUX_OSS__ #RtAudio Flags QMAKE_CXXFLAGS += -g -O2 DEFINES += __LINUX__ } linux-g++-64 { message(Linux 64bit) LIBS += -lasound QMAKE_CXXFLAGS += -fPIC -D__LINUX_ALSA__ #-D__LINUX_OSS__ #RtAudio Flags QMAKE_CXXFLAGS += -g -O2 DEFINES += __LINUX__ } win32 { message(win32) CONFIG += x86 console QMAKE_CXXFLAGS += -D__WINDOWS_ASIO__ #-D__UNIX_JACK__ #RtAudio Flags LIBS += -lWs2_32 -lOle32 #needed by rtaudio/asio LIBS += "../externals/includes/QTWindows/libjack.lib" DEFINES += __WIN_32__ DEFINES -= UNICODE #RtAudio for Qt } DESTDIR = . QMAKE_CLEAN += -r ./jacktrip ./jacktrip_debug ./release ./debug target.path = /usr/bin INSTALLS += target #INCLUDEPATH += ../externals/includes/rtaudio-4.0.7 #DEPENDPATH += ../externals/includes/rtaudio-4.0.7 win32 { INCLUDEPATH += ../externals/includes/rtaudio-4.0.7/include INCLUDEPATH += ../externals/includes DEPENDPATH += ../externals/includes/rtaudio-4.0.7/include DEPENDPATH += ../externals/includes } # Input HEADERS += DataProtocol.h \ JackTrip.h \ jacktrip_globals.h \ jacktrip_types.h \ JackTripThread.h \ JackTripWorker.h \ JackTripWorkerMessages.h \ LoopBack.h \ NetKS.h \ PacketHeader.h \ ProcessPlugin.h \ RingBuffer.h \ RingBufferWavetable.h \ Settings.h \ TestRingBuffer.h \ ThreadPoolTest.h \ UdpDataProtocol.h \ UdpMasterListener.h \ AudioInterface.h \ RtAudioInterface.h #JamTest.h !nojack { SOURCES += JackAudioInterface.h } SOURCES += DataProtocol.cpp \ JackTrip.cpp \ jacktrip_globals.cpp \ jacktrip_main.cpp \ jacktrip_tests.cpp \ JackTripThread.cpp \ JackTripWorker.cpp \ LoopBack.cpp \ PacketHeader.cpp \ ProcessPlugin.cpp \ RingBuffer.cpp \ Settings.cpp \ #tests.cpp \ UdpDataProtocol.cpp \ UdpMasterListener.cpp \ AudioInterface.cpp \ RtAudioInterface.cpp !nojack { SOURCES += JackAudioInterface.cpp } # RtAduio Input HEADERS += ../externals/includes/rtaudio-4.0.7/RtAudio.h \ ../externals/includes/rtaudio-4.0.7/RtError.h SOURCES += ../externals/includes/rtaudio-4.0.7/RtAudio.cpp win32 { HEADERS += asio.h \ asiodrivers.h \ asiolist.h \ asiodrvr.h \ asiosys.h \ ginclude.h \ iasiodrv.h \ iasiothiscallresolver.h SOURCES += asio.cpp \ asiodrivers.cpp \ asiolist.cpp \ iasiothiscallresolver.cpp } jacktrip-1.1/src/AudioInterface.h0000644000175000017500000002142312531357105017242 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file AudioInterface.h * \author Juan-Pablo Caceres * \date July 2009 */ #ifndef __AUDIOINTERFACE_H__ #define __AUDIOINTERFACE_H__ #include "ProcessPlugin.h" #include "jacktrip_types.h" #include #include //#include "jacktrip_globals.h" // Forward declarations class JackTrip; //using namespace JackTripNamespace; /** \brief Base Class that provides an interface with audio */ class AudioInterface { public: /// \brief Enum for Audio Resolution in bits enum audioBitResolutionT { BIT8 = 1, ///< 8 bits BIT16 = 2, ///< 16 bits (default) BIT24 = 3, ///< 24 bits BIT32 = 4 ///< 32 bits }; /// \brief Sampling Rates supported by JACK enum samplingRateT { SR22, ///< 22050 Hz SR32, ///< 32000 Hz SR44, ///< 44100 Hz SR48, ///< 48000 Hz SR88, ///< 88200 Hz SR96, ///< 96000 Hz SR192, ///< 192000 Hz UNDEF ///< Undefined }; /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param NumInChans Number of Input Channels * \param NumOutChans Number of Output Channels * \param AudioBitResolution Audio Sample Resolutions in bits */ AudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16); /// \brief The class destructor virtual ~AudioInterface(); /** \brief Setup the client. This function should be called just before * * starting the audio processes, it will setup the audio client with * the class parameters, like Sampling Rate, * Packet Size, Bit Resolution, etc... Sub-classes should also call the parent * method to ensure correct inizialization. */ virtual void setup(); /// \brief Tell the audio server that we are ready to roll. The /// process-callback will start running. This runs on its own thread. /// \return 0 on success, otherwise a non-zero error code virtual int startProcess() const = 0; /// \brief Stops the process-callback thread /// \return 0 on success, otherwise a non-zero error code virtual int stopProcess() const = 0; /** \brief Process callback. Subclass should call this callback after obtaining the in_buffer and out_buffer pointers. * \param in_buffer Array of input audio samplers for each channel. The user * is reponsible to check that each channel has n_frames samplers * \param in_buffer Array of output audio samplers for each channel. The user * is reponsible to check that each channel has n_frames samplers */ virtual void callback(QVarLengthArray& in_buffer, QVarLengthArray& out_buffer, unsigned int n_frames); /** \brief Append a ProcessPlugin. The order of processing is determined by * the order by which appending is done. * \param plugin a ProcesPlugin smart pointer. Create the object instance * using something like:\n * std::tr1::shared_ptr loopback(new ProcessPluginName); */ virtual void appendProcessPlugin(ProcessPlugin* plugin); virtual void connectDefaultPorts() = 0; /** \brief Convert a 32bit number (sample_t) into one of the bit resolution * supported (audioBitResolutionT). * * The result is stored in an int_8 array of the * appropriate size to hold the value. The caller is responsible to allocate * enough space to store the result. */ static void fromSampleToBitConversion(const sample_t* const input, int8_t* output, const AudioInterface::audioBitResolutionT targetBitResolution); /** \brief Convert a audioBitResolutionT bit resolution number into a * 32bit number (sample_t) * * The result is stored in an sample_t array of the * appropriate size to hold the value. The caller is responsible to allocate * enough space to store the result. */ static void fromBitToSampleConversion(const int8_t* const input, sample_t* output, const AudioInterface::audioBitResolutionT sourceBitResolution); //--------------SETTERS--------------------------------------------- virtual void setNumInputChannels(int nchannels) { mNumInChans = nchannels; } virtual void setNumOutputChannels(int nchannels) { mNumOutChans = nchannels; } virtual void setSampleRate(uint32_t sample_rate) { mSampleRate = sample_rate; } virtual void setBufferSizeInSamples(uint32_t buf_size) { mBufferSizeInSamples = buf_size; } /// \brief Set Client Name to something different that the default (JackTrip) virtual void setClientName(const char* ClientName) = 0; //------------------------------------------------------------------ //--------------GETTERS--------------------------------------------- /// \brief Get Number of Input Channels virtual int getNumInputChannels() const { return mNumInChans; } /// \brief Get Number of Output Channels virtual int getNumOutputChannels() const { return mNumOutChans; } virtual uint32_t getBufferSizeInSamples() const { return mBufferSizeInSamples; } virtual size_t getSizeInBytesPerChannel() const; /// \brief Get the Jack Server Sampling Rate, in samples/second virtual uint32_t getSampleRate() const { return mSampleRate; } /// \brief Get the Jack Server Sampling Rate Enum Type samplingRateT /// \return AudioInterface::samplingRateT enum type virtual samplingRateT getSampleRateType() const; /** \brief Get the Audio Bit Resolution, in bits * * This is one of the audioBitResolutionT set in construction */ virtual int getAudioBitResolution() const { return mAudioBitResolution; } /** \brief Helper function to get the sample rate (in Hz) for a * JackAudioInterface::samplingRateT * \param rate_type JackAudioInterface::samplingRateT enum type * \return Sample Rate in Hz */ static int getSampleRateFromType(samplingRateT rate_type); //------------------------------------------------------------------ private: /// \brief Compute the process to receive packets void computeProcessFromNetwork(QVarLengthArray& out_buffer, unsigned int n_frames); /// \brief Compute the process to send packets void computeProcessToNetwork(QVarLengthArray& in_buffer, unsigned int n_frames); JackTrip* mJackTrip; ///< JackTrip Mediator Class pointer int mNumInChans;///< Number of Input Channels int mNumOutChans; ///< Number of Output Channels int mAudioBitResolution; ///< Bit resolution in audio samples AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode uint32_t mSampleRate; ///< Sampling Rate uint32_t mBufferSizeInSamples; ///< Buffer size in samples size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel QVector mProcessPlugins; ///< Vector of ProcesPlugins QVarLengthArray mInProcessBuffer;///< Vector of Input buffers/channel for ProcessPlugin QVarLengthArray mOutProcessBuffer;///< Vector of Output buffers/channel for ProcessPlugin int8_t* mInputPacket; ///< Packet containing all the channels to read from the RingBuffer int8_t* mOutputPacket; ///< Packet containing all the channels to send to the RingBuffer }; #endif // __AUDIOINTERFACE_H__ jacktrip-1.1/src/JackTrip.h0000644000175000017500000004220112531357105016064 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTrip.h * \author Juan-Pablo Caceres * \date July 2008 */ #ifndef __JACKTRIP_H__ #define __JACKTRIP_H__ //#include //for shared_ptr #include #include #include #include #include "DataProtocol.h" #include "AudioInterface.h" #ifndef __NO_JACK__ #include "JackAudioInterface.h" #endif //__NO_JACK__ #include "PacketHeader.h" #include "RingBuffer.h" #include /** \brief Main class to creates a SERVER (to listen) or a CLIENT (to connect * to a listening server) to send audio streams in the network. * * All audio and network settings can be set in this class. * This class also acts as a Mediator between all the other class. * Classes that uses JackTrip methods need to register with it. */ class JackTrip : public QThread { Q_OBJECT; public: //----------ENUMS------------------------------------------ /// \brief Enum for the data Protocol. At this time only UDP is implemented enum dataProtocolT { UDP, ///< Use UDP (User Datagram Protocol) TCP, ///< NOT IMPLEMENTED: Use TCP (Transmission Control Protocol) SCTP ///< NOT IMPLEMENTED: Use SCTP (Stream Control Transmission Protocol) }; /// \brief Enum for the JackTrip mode enum jacktripModeT { SERVER, ///< Run in Server Mode CLIENT, ///< Run in Client Mode CLIENTTOPINGSERVER, ///< Client of the Ping Server Mode SERVERPINGSERVER ///< Server of the MultiThreaded JackTrip }; /// \brief Enum for the JackTrip Underrun Mode, when packets enum underrunModeT { WAVETABLE, ///< Loops on the last received packet ZEROS ///< Set new buffers to zero if there are no new ones }; /// \brief Enum for Audio Interface Mode enum audiointerfaceModeT { JACK, ///< Jack Mode RTAUDIO ///< RtAudio Mode }; /// \brief Enum for Connection Mode (useful for connections to MultiClient Server) enum connectionModeT { NORMAL, ///< Normal Mode KSTRONG, ///< Karplus Strong JAMTEST ///< Karplus Strong }; //--------------------------------------------------------- /** \brief The class Constructor with Default Parameters * \param JacktripMode JackTrip::CLIENT or JackTrip::SERVER * \param DataProtocolType JackTrip::dataProtocolT * \param NumChans Number of Audio Channels (same for inputs and outputs) * \param BufferQueueLength Audio Buffer for receiving packets * \param AudioBitResolution Audio Sample Resolutions in bits * \param redundancy redundancy factor for network data */ JackTrip(jacktripModeT JacktripMode = CLIENT, dataProtocolT DataProtocolType = UDP, int NumChans = gDefaultNumInChannels, int BufferQueueLength = gDefaultQueueLength, unsigned int redundancy = gDefaultRedundancy, AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16, DataProtocol::packetHeaderTypeT PacketHeaderType = DataProtocol::DEFAULT, underrunModeT UnderRunMode = WAVETABLE, int receiver_bind_port = gDefaultPort, int sender_bind_port = gDefaultPort, int receiver_peer_port = gDefaultPort, int sender_peer_port = gDefaultPort); /// \brief The class destructor virtual ~JackTrip(); /// \brief Starting point for the thread virtual void run() {} /// \brief Set the Peer Address for jacktripModeT::CLIENT mode only virtual void setPeerAddress(const char* PeerHostOrIP); /** \brief Append a process plugin. Processes will be appended in order * \param plugin Pointer to ProcessPlugin Class */ //void appendProcessPlugin(const std::tr1::shared_ptr plugin); virtual void appendProcessPlugin(ProcessPlugin* plugin); /// \brief Start the processing threads virtual void startProcess() throw(std::invalid_argument); /// \brief Stop the processing threads virtual void stop(); /// \brief Wait for all the threads to finish. This functions is used when JackTrip is /// run as a thread virtual void waitThreads(); /// \brief Check if UDP port is already binded /// \param port Port number virtual void checkIfPortIsBinded(int port); //------------------------------------------------------------------------------------ /// \name Getters and Setters Methods to change parameters after construction //@{ // /// \brief Sets (override) JackTrip Mode after construction virtual void setJackTripMode(jacktripModeT JacktripMode) { mJackTripMode = JacktripMode; } /// \brief Sets (override) DataProtocol Type after construction virtual void setDataProtocoType(dataProtocolT DataProtocolType) { mDataProtocol = DataProtocolType; } /// \brief Sets the Packet header type virtual void setPacketHeaderType(DataProtocol::packetHeaderTypeT PacketHeaderType) { mPacketHeaderType = PacketHeaderType; delete mPacketHeader; mPacketHeader = NULL; createHeader(mPacketHeaderType); } /// \brief Sets (override) Buffer Queue Length Mode after construction virtual void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; } /// \brief Sets (override) Audio Bit Resolution after construction virtual void setAudioBitResolution(AudioInterface::audioBitResolutionT AudioBitResolution) { mAudioBitResolution = AudioBitResolution; } /// \brief Sets (override) Underrun Mode virtual void setUnderRunMode(underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; } /// \brief Sets port numbers for the local and peer machine. /// Receive port is port virtual void setAllPorts(int port) { mReceiverBindPort = port; mSenderPeerPort = port; mSenderBindPort = port; mReceiverPeerPort = port; } /// \brief Sets port numbers to bind in RECEIVER and SENDER sockets. virtual void setBindPorts(int port) { mReceiverBindPort = port; mSenderBindPort = port; } /// \brief Sets port numbers for the peer (remote) machine. virtual void setPeerPorts(int port) { mSenderPeerPort = port; mReceiverPeerPort = port; } /// \brief Set Client Name to something different that the default (JackTrip) virtual void setClientName(const char* ClientName) { mJackClientName = ClientName; } /// \brief Set the number of audio channels virtual void setNumChannels(int num_chans) { mNumChans = num_chans; } virtual int getReceiverBindPort() const { return mReceiverBindPort; } virtual int getSenderPeerPort() const { return mSenderPeerPort; } virtual int getSenderBindPort() const { return mSenderBindPort; } virtual int getReceiverPeerPort() const { return mReceiverPeerPort; } virtual DataProtocol* getDataProtocolSender() const { return mDataProtocolSender; } virtual DataProtocol* getDataProtocolReceiver() const { return mDataProtocolReceiver; } virtual void setDataProtocolSender(DataProtocol* const DataProtocolSender) { mDataProtocolSender = DataProtocolSender; } virtual void setDataProtocolReceiver(DataProtocol* const DataProtocolReceiver) { mDataProtocolReceiver = DataProtocolReceiver; } virtual RingBuffer* getSendRingBuffer() const { return mSendRingBuffer; } virtual RingBuffer* getReceiveRingBuffer() const { return mReceiveRingBuffer; } virtual void setSendRingBuffer(RingBuffer* const SendRingBuffer) { mSendRingBuffer = SendRingBuffer; } virtual void setReceiveRingBuffer(RingBuffer* const ReceiveRingBuffer) { mReceiveRingBuffer = ReceiveRingBuffer; } virtual void setPacketHeader(PacketHeader* const PacketHeader) { mPacketHeader = PacketHeader; } virtual int getRingBuffersSlotSize() { return getTotalAudioPacketSizeInBytes(); } virtual void setAudiointerfaceMode(JackTrip::audiointerfaceModeT audiointerface_mode) { mAudiointerfaceMode = audiointerface_mode; } virtual void setAudioInterface(AudioInterface* const AudioInterface) { mAudioInterface = AudioInterface; } void setSampleRate(uint32_t sample_rate) { mSampleRate = sample_rate; } void setAudioBufferSizeInSamples(uint32_t buf_size) { mAudioBufferSize = buf_size; } JackTrip::connectionModeT getConnectionMode() const { return mConnectionMode; } void setConnectionMode(JackTrip::connectionModeT connection_mode) { mConnectionMode = connection_mode; } JackTrip::jacktripModeT getJackTripMode() const { return mJackTripMode; } QString getPeerAddress() const { return mPeerAddress; } bool receivedConnectionFromPeer() { return mReceivedConnection; } bool tcpConnectionError() { return mTcpConnectionError; } //@} //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ /// \name Mediator Functions //@{ /// \todo Document all these functions virtual void createHeader(const DataProtocol::packetHeaderTypeT headertype); void putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet); virtual int getPacketSizeInBytes(); void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet); virtual void sendNetworkPacket(const int8_t* ptrToSlot) { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); } virtual void receiveNetworkPacket(int8_t* ptrToReadSlot) { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); } virtual void readAudioBuffer(int8_t* ptrToReadSlot) { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); } virtual void writeAudioBuffer(const int8_t* ptrToSlot) { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); } uint32_t getBufferSizeInSamples() const { return mAudioBufferSize; /*return mAudioInterface->getBufferSizeInSamples();*/ } AudioInterface::samplingRateT getSampleRateType() const { return mAudioInterface->getSampleRateType(); } int getSampleRate() const { return mSampleRate; /*return mAudioInterface->getSampleRate();*/ } uint8_t getAudioBitResolution() const { return mAudioBitResolution*8; /*return mAudioInterface->getAudioBitResolution();*/ } unsigned int getNumInputChannels() const { return mNumChans; /*return mAudioInterface->getNumInputChannels();*/ } unsigned int getNumOutputChannels() const { return mNumChans; /*return mAudioInterface->getNumOutputChannels();*/ } unsigned int getNumChannels() const { if (getNumInputChannels() == getNumOutputChannels()) { return getNumInputChannels(); } else { return 0; } } virtual void checkPeerSettings(int8_t* full_packet); void increaseSequenceNumber() { mPacketHeader->increaseSequenceNumber(); } int getSequenceNumber() const { return mPacketHeader->getSequenceNumber(); } uint64_t getPeerTimeStamp(int8_t* full_packet) const { return mPacketHeader->getPeerTimeStamp(full_packet); } uint16_t getPeerSequenceNumber(int8_t* full_packet) const { return mPacketHeader->getPeerSequenceNumber(full_packet); } uint16_t getPeerBufferSize(int8_t* full_packet) const { return mPacketHeader->getPeerBufferSize(full_packet); } uint8_t getPeerSamplingRate(int8_t* full_packet) const { return mPacketHeader->getPeerSamplingRate(full_packet); } uint8_t getPeerBitResolution(int8_t* full_packet) const { return mPacketHeader->getPeerBitResolution(full_packet); } uint8_t getPeerNumChannels(int8_t* full_packet) const { return mPacketHeader->getPeerNumChannels(full_packet); } uint8_t getPeerConnectionMode(int8_t* full_packet) const { return mPacketHeader->getPeerConnectionMode(full_packet); } size_t getSizeInBytesPerChannel() const { return mAudioInterface->getSizeInBytesPerChannel(); } int getHeaderSizeInBytes() const { return mPacketHeader->getHeaderSizeInBytes(); } virtual int getTotalAudioPacketSizeInBytes() const { return mAudioInterface->getSizeInBytesPerChannel() * mNumChans; } //@} //------------------------------------------------------------------------------------ void printTextTest() {std::cout << "=== JackTrip PRINT ===" << std::endl;} void printTextTest2() {std::cout << "=== JackTrip PRINT2 ===" << std::endl;} public slots: /// \brief Slot to stop all the processes and threads virtual void slotStopProcesses() { std::cout << "Stopping JackTrip..." << std::endl; mStopped = true; this->stop(); } /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds * when UDP has waited for more than 30 seconds. * * It is used to remove the thread from the server. */ void slotUdpWatingTooLong(int wait_msec) { int wait_time = 30000; // msec if ( !(wait_msec%wait_time) ) { std::cerr << "UDP WAITED MORE THAN 30 seconds." << std::endl; emit signalNoUdpPacketsForSeconds(); } } void slotPrintTest() { std::cout << "=== TESTING ===" << std::endl; } void slotReceivedConnectionFromPeer() { mReceivedConnection = true; } signals: void signalUdpTimeOut(); /// \brief Signal emitted when all the processes and threads are stopped void signalProcessesStopped(); /// \brief Signal emitted when no UDP Packets have been received for a while void signalNoUdpPacketsForSeconds(); void signalTcpClientConnected(); public: /// \brief Set the AudioInteface object virtual void setupAudio(); /// \brief Close the JackAudioInteface and disconnects it from JACK void closeAudio(); /// \brief Set the DataProtocol objects virtual void setupDataProtocol(); /// \brief Set the RingBuffer objects void setupRingBuffers(); /// \brief Starts for the CLIENT mode void clientStart() throw(std::invalid_argument); /// \brief Starts for the SERVER mode /// \param timout Set the server to timeout after 2 seconds if no client connections are received. /// Usefull for the multithreaded server /// \return 0 on success, -1 on error int serverStart(bool timeout = false, int udpTimeout = gTimeOutMultiThreadedServer) throw(std::invalid_argument, std::runtime_error); /// \brief Stats for the Client to Ping Server /// \return -1 on error, 0 on success virtual int clientPingToServerStart() throw(std::invalid_argument); private: //void bindReceiveSocket(QUdpSocket& UdpSocket, int bind_port, // QHostAddress PeerHostAddress, int peer_port) //throw(std::runtime_error); jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT dataProtocolT mDataProtocol; ///< Data Protocol Tipe DataProtocol::packetHeaderTypeT mPacketHeaderType; ///< Packet Header Type JackTrip::audiointerfaceModeT mAudiointerfaceMode; int mNumChans; ///< Number of Channels (inputs = outputs) int mBufferQueueLength; ///< Audio Buffer from network queue length uint32_t mSampleRate; ///< Sample Rate uint32_t mAudioBufferSize; ///< Audio buffer size to process on each callback AudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode /// Pointer to Abstract Type DataProtocol that sends packets DataProtocol* mDataProtocolSender; /// Pointer to Abstract Type DataProtocol that receives packets DataProtocol* mDataProtocolReceiver; AudioInterface* mAudioInterface; ///< Interface to Jack Client PacketHeader* mPacketHeader; ///< Pointer to Packet Header underrunModeT mUnderRunMode; ///< underrunModeT Mode /// Pointer for the Send RingBuffer RingBuffer* mSendRingBuffer; /// Pointer for the Receive RingBuffer RingBuffer* mReceiveRingBuffer; int mReceiverBindPort; ///< Incoming (receiving) port for local machine int mSenderPeerPort; ///< Incoming (receiving) port for peer machine int mSenderBindPort; ///< Outgoing (sending) port for local machine int mReceiverPeerPort; ///< Outgoing (sending) port for peer machine int mTcpServerPort; unsigned int mRedundancy; ///< Redundancy factor in network data const char* mJackClientName; ///< JackAudio Client Name JackTrip::connectionModeT mConnectionMode; ///< Connection Mode QVector mProcessPlugins; ///< Vector of ProcesPlugins volatile bool mReceivedConnection; ///< Bool of received connection from peer volatile bool mTcpConnectionError; volatile bool mStopped; }; #endif jacktrip-1.1/src/jacktrip_main.cpp0000644000175000017500000000561612531357105017534 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file main.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #include #include #include "JackAudioInterface.h" #include "UdpDataProtocol.h" #include "RingBuffer.h" #include "JackTrip.h" #include "Settings.h" //#include "TestRingBuffer.h" #include "LoopBack.h" #include "PacketHeader.h" //#include "JackTripThread.h" #include "RtAudioInterface.h" #include "jacktrip_tests.cpp" #include "jacktrip_globals.h" using std::cout; using std::endl; int main(int argc, char** argv) { QCoreApplication app(argc, argv); bool testing = false; if ( argc > 1 ) { if ( !strcmp(argv[1], "test") ) { testing = true; } } if ( testing ) { cout << "=========TESTING=========" << endl; //main_tests(argc, argv); // test functions JackTrip jacktrip; RtAudioInterface rtaudio(&jacktrip); //rtaudio.setup(); rtaudio.listAllInterfaces(); //rtaudio.printDeviceInfo(0); //while (true) sleep(9999); } else { //--------------------------------------- // Get Settings from user // ---------------------- try { // Get Settings from user // ---------------------- Settings* settings = new Settings; settings->parseInput(argc, argv); settings->startJackTrip(); } catch ( const std::exception & e ) { std::cerr << "ERROR:" << endl; std::cerr << e.what() << endl; std::cerr << "Exiting JackTrip..." << endl; std::cerr << gPrintSeparator << endl; return -1; } } return app.exec(); } jacktrip-1.1/src/JackAudioInterface.cpp0000644000175000017500000004051412531357105020370 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackAudioInterface.cpp * \author Juan-Pablo Caceres * \date June 2008 */ #include "JackAudioInterface.h" #include "jacktrip_globals.h" #include "JackTrip.h" #include #include #include #include ///************PROTORYPE FOR CELT************************** //#include //#include //#include ///******************************************************** #include #include using std::cout; using std::endl; // sJackMutex definition QMutex JackAudioInterface::sJackMutex; //******************************************************************************* JackAudioInterface::JackAudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, AudioInterface::audioBitResolutionT AudioBitResolution, const char* ClienName) : AudioInterface(jacktrip, NumInChans, NumOutChans, AudioBitResolution), mNumInChans(NumInChans), mNumOutChans(NumOutChans), //mAudioBitResolution(AudioBitResolution*8), mBitResolutionMode(AudioBitResolution), mClient(NULL), mClientName(ClienName), mJackTrip(jacktrip) {} //******************************************************************************* JackAudioInterface::~JackAudioInterface() {} //******************************************************************************* void JackAudioInterface::setup() { setupClient(); AudioInterface::setup(); setProcessCallback(); } //******************************************************************************* void JackAudioInterface::setupClient() { const char* client_name = mClientName; const char* server_name = NULL; jack_options_t options = JackNoStartServer; jack_status_t status; // Try to connect to the server /// \todo Write better warning messages. This following line displays very /// verbose message, check how to desable them. { QMutexLocker locker(&sJackMutex); mClient = jack_client_open (client_name, options, &status, server_name); } if (mClient == NULL) { //fprintf (stderr, "jack_client_open() failed, " // "status = 0x%2.0x\n", status); if (status & JackServerFailed) { fprintf (stderr, "Unable to connect to JACK server\n"); //std::cerr << "ERROR: Maybe the JACK server is not running?" << std::endl; //std::cerr << gPrintSeparator << std::endl; } //std::exit(1); throw std::runtime_error("Maybe the JACK server is not running?"); } if (status & JackServerStarted) { fprintf (stderr, "JACK server started\n"); } if (status & JackNameNotUnique) { client_name = jack_get_client_name(mClient); fprintf (stderr, "unique name `%s' assigned\n", client_name); } // Set function to call if Jack shuts down jack_on_shutdown (mClient, this->jackShutdown, 0); // Create input and output channels createChannels(); // Buffer size member mNumFrames = getBufferSizeInSamples(); // Initialize Buffer array to read and write audio mInBuffer.resize(mNumInChans); mOutBuffer.resize(mNumOutChans); } //******************************************************************************* void JackAudioInterface::createChannels() { //Create Input Ports mInPorts.resize(mNumInChans); for (int i = 0; i < mNumInChans; i++) { QString inName; QTextStream (&inName) << "send_" << i+1; mInPorts[i] = jack_port_register (mClient, inName.toLatin1(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); } //Create Output Ports mOutPorts.resize(mNumOutChans); for (int i = 0; i < mNumInChans; i++) { QString outName; QTextStream (&outName) << "receive_" << i+1; mOutPorts[i] = jack_port_register (mClient, outName.toLatin1(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } } //******************************************************************************* uint32_t JackAudioInterface::getSampleRate() const { return jack_get_sample_rate(mClient); } //******************************************************************************* uint32_t JackAudioInterface::getBufferSizeInSamples() const { return jack_get_buffer_size(mClient); } //******************************************************************************* size_t JackAudioInterface::getSizeInBytesPerChannel() const { return (getBufferSizeInSamples() * getAudioBitResolution()/8); } //******************************************************************************* void JackAudioInterface::setProcessCallback() { std::cout << "Setting JACK Process Callback..." << std::endl; if ( int code = jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, this) ) { //std::cerr << "Could not set the process callback" << std::endl; //return(code); (void) code; // to avoid compiler warnings throw std::runtime_error("Could not set the Jack process callback"); //std::exit(1); } std::cout << "SUCCESS" << std::endl; std::cout << gPrintSeparator << std::endl; //return(0); } //******************************************************************************* int JackAudioInterface::startProcess() const { //Tell the JACK server that we are ready to roll. Our //process() callback will start running now. if ( int code = (jack_activate(mClient)) ) { std::cerr << "Cannot activate client" << std::endl; return(code); } return(0); } //******************************************************************************* int JackAudioInterface::stopProcess() const { QMutexLocker locker(&sJackMutex); int code = (jack_client_close(mClient)); if ( code != 0 ) { std::cerr << "Cannot disconnect client" << std::endl; return(code); } return(0); } //******************************************************************************* void JackAudioInterface::jackShutdown (void*) { //std::cout << "The Jack Server was shut down!" << std::endl; throw std::runtime_error("The Jack Server was shut down!"); //std::cout << "Exiting program..." << std::endl; //std::exit(1); } //******************************************************************************* int JackAudioInterface::processCallback(jack_nframes_t nframes) { // Get input and output buffers from JACK //------------------------------------------------------------------- for (int i = 0; i < mNumInChans; i++) { // Input Ports are READ ONLY mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes); } for (int i = 0; i < mNumOutChans; i++) { // Output Ports are WRITABLE mOutBuffer[i] = (sample_t*) jack_port_get_buffer(mOutPorts[i], nframes); } //------------------------------------------------------------------- // TEST: Loopback // To test, uncomment and send audio to client input. The same audio // should come out as output in the first channel //memcpy (mOutBuffer[0], mInBuffer[0], sizeof(sample_t) * nframes); //memcpy (mOutBuffer[1], mInBuffer[1], sizeof(sample_t) * nframes); //------------------------------------------------------------------- AudioInterface::callback(mInBuffer, mOutBuffer, nframes); return 0; } //******************************************************************************* int JackAudioInterface::wrapperProcessCallback(jack_nframes_t nframes, void *arg) { return static_cast(arg)->processCallback(nframes); } //******************************************************************************* void JackAudioInterface::connectDefaultPorts() { const char** ports; // Get physical output (capture) ports if ( (ports = jack_get_ports (mClient, NULL, NULL, JackPortIsPhysical | JackPortIsOutput)) == NULL) { cout << "WARING: Cannot find any physical capture ports" << endl; } else { // Connect capure ports to jacktrip send for (int i = 0; i < mNumInChans; i++) { // Check that we don't run out of capture ports if ( ports[i] != NULL ) { jack_connect(mClient, ports[i], jack_port_name(mInPorts[i])); } } std::free(ports); } // Get physical input (playback) ports if ( (ports = jack_get_ports (mClient, NULL, NULL, JackPortIsPhysical | JackPortIsInput)) == NULL) { cout << "WARING: Cannot find any physical playback ports" << endl; } else { // Connect playback ports to jacktrip receive for (int i = 0; i < mNumOutChans; i++) { // Check that we don't run out of capture ports if ( ports[i] != NULL ) { jack_connect(mClient, jack_port_name(mOutPorts[i]), ports[i]); } } std::free(ports); } } // OLD CODE // ============================================================================== //******************************************************************************* /* int JackAudioInterface::processCallback(jack_nframes_t nframes) { // Get input and output buffers from JACK //------------------------------------------------------------------- for (int i = 0; i < mNumInChans; i++) { // Input Ports are READ ONLY mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes); } for (int i = 0; i < mNumOutChans; i++) { // Output Ports are WRITABLE mOutBuffer[i] = (sample_t*) jack_port_get_buffer(mOutPorts[i], nframes); } //------------------------------------------------------------------- // TEST: Loopback // To test, uncomment and send audio to client input. The same audio // should come out as output in the first channel //memcpy (mOutBuffer[0], mInBuffer[0], sizeof(sample_t) * nframes); //memcpy (mOutBuffer[1], mInBuffer[1], sizeof(sample_t) * nframes); //------------------------------------------------------------------- // Allocate the Process Callback //------------------------------------------------------------------- // 1) First, process incoming packets // ---------------------------------- computeNetworkProcessFromNetwork(); // 2) Dynamically allocate ProcessPlugin processes // ----------------------------------------------- // The processing will be done in order of allocation ///\todo Implement for more than one process plugin, now it just works propertely with one. /// do it chaining outputs to inputs in the buffers. May need a tempo buffer for (int i = 0; i < mNumInChans; i++) { std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); std::memcpy(mInProcessBuffer[i], mOutBuffer[i], sizeof(sample_t) * nframes); } for (int i = 0; i < mNumOutChans; i++) { std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); } for (int i = 0; i < mProcessPlugins.size(); i++) { //mProcessPlugins[i]->compute(nframes, mOutBuffer.data(), mInBuffer.data()); mProcessPlugins[i]->compute(nframes, mInProcessBuffer.data(), mOutProcessBuffer.data()); } // 3) Finally, send packets to peer // -------------------------------- computeNetworkProcessToNetwork(); */ ///************PROTORYPE FOR CELT************************** ///******************************************************** /* CELTMode* mode; int* error; mode = celt_mode_create(48000, 2, 64, error); */ //celt_mode_create(48000, 2, 64, NULL); //unsigned char* compressed; //CELTEncoder* celtEncoder; //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); ///******************************************************** ///******************************************************** // return 0; //} //******************************************************************************* /* void JackAudioInterface::setRingBuffers (const std::tr1::shared_ptr InRingBuffer, const std::tr1::shared_ptr OutRingBuffer) { mInRingBuffer = InRingBuffer; mOutRingBuffer = OutRingBuffer; } */ //******************************************************************************* // Before sending and reading to Jack, we have to round to the sample resolution // that the program is using. Jack uses 32 bits (gJackBitResolution in globals.h) // by default /* void JackAudioInterface::computeNetworkProcessFromNetwork() { /// \todo cast *mInBuffer[i] to the bit resolution //cout << mNumFrames << endl; // Output Process (from NETWORK to JACK) // ---------------------------------------------------------------- // Read Audio buffer from RingBuffer (read from incoming packets) //mOutRingBuffer->readSlotNonBlocking( mOutputPacket ); mJackTrip->receiveNetworkPacket( mOutputPacket ); // Extract separate channels to send to Jack for (int i = 0; i < mNumOutChans; i++) { //-------- // This should be faster for 32 bits //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = mOutBuffer[i]; //sample buffer for channel i for (int j = 0; j < mNumFrames; j++) { //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); // Change the bit resolution on each sample //cout << tmp_sample[j] << endl; fromBitToSampleConversion(&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], &tmp_sample[j], mBitResolutionMode); } } } */ //******************************************************************************* /* void JackAudioInterface::computeNetworkProcessToNetwork() { // Input Process (from JACK to NETWORK) // ---------------------------------------------------------------- // Concatenate all the channels from jack to form packet for (int i = 0; i < mNumInChans; i++) { //-------- // This should be faster for 32 bits //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = mInBuffer[i]; //sample buffer for channel i sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process sample_t tmp_result; for (int j = 0; j < mNumFrames; j++) { //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); // Change the bit resolution on each sample // Add the input jack buffer to the buffer resulting from the output process tmp_result = tmp_sample[j] + tmp_process_sample[j]; fromSampleToBitConversion(&tmp_result, &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], mBitResolutionMode); } } // Send Audio buffer to RingBuffer (these goes out as outgoing packets) //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); mJackTrip->sendNetworkPacket( mInputPacket ); } */ //******************************************************************************* /* //void JackAudioInterface::appendProcessPlugin(const std::tr1::shared_ptr plugin) void JackAudioInterface::appendProcessPlugin(ProcessPlugin* plugin) { /// \todo check that channels in ProcessPlugins are less or same that jack channels if ( plugin->getNumInputs() ) {} mProcessPlugins.append(plugin); } */ jacktrip-1.1/src/JackTripThread.h0000644000175000017500000000420312531357105017214 0ustar zmoelnigzmoelnig//***************************************************************** /* JackTrip: A System for High-Quality Audio Network Performance over the Internet Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. SoundWIRE group at CCRMA, Stanford University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************** /** * \file JackTripThread.h * \author Juan-Pablo Caceres * \date September 2008 */ #ifndef __JACKTRIPTHREAD_H__ #define __JACKTRIPTHREAD_H__ #include #include "JackTrip.h" /** \brief Test class that runs JackTrip inside a thread */ class JackTripThread : public QThread { public: JackTripThread(JackTrip::jacktripModeT JacktripMode) : mJackTripMode(JacktripMode) {} virtual ~JackTripThread(){} void run(); void setPort(int port_num) { mPortNum = port_num; } void setPeerAddress(const char* PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } private: JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT int mPortNum; const char* mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode }; #endif //__JACKTRIPTHREAD_H__ jacktrip-1.1/jacktrip_doxygen0000644000175000017500000016406612531357105016722 0ustar zmoelnigzmoelnig# Doxyfile 1.5.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = JackTrip # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./WWW/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, # and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ./src ./documentation # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = ./documentation/img # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = ./documentation/html_footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hiererachy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NONE # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Sans # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is enabled by default, which results in a transparent # background. Warning: Depending on the platform used, enabling this option # may lead to badly anti-aliased labels on the edges of a graph (i.e. they # become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO jacktrip-1.1/TODO.txt0000644000175000017500000000104212531357105014721 0ustar zmoelnigzmoelnigHigh Priority TODOS: -------------------- - Add redundancy to UDP (DONE) - Finish header implementation, add run-time check Plug-ins: --------- - Extend Plugin structure to include more than 1 plug-in and add the mode for local effect (not loopback) - add the offset option to process starting from a different channel - Set the faust compiler to automatically generate plugins - Add low latency compression www.celt-codec.org Protocol: --------- - Add TCP clacc - Add SCTP class - Maybe add a layer of OSC communication for control messages jacktrip-1.1/INSTALL.txt0000644000175000017500000000703712531357105015274 0ustar zmoelnigzmoelnigJacktrip : Build Instructions for Linux and MacOS X JackTrip: A System for High-Quality Audio Network Performance over the Internet. --- MacOS X (10.9 or higher) Installation: If you are installing on MacOS X, a binary is provided. You just need to have JackOSX installed on your machine http://www.jackosx.com/ To install (using Terminal): go to bin/ directory and type: sudo cp jacktrip /usr/bin/ (enter your password when prompted) sudo chmod 755 /usr/bin/jacktrip (now you can run jacktrip from any directory using Terminal) --- Dependencies: You need to have installed the libraries in your system: Qt 5.3 or higher jack-audio-connection-kit-devel If you are using yum (in Fedora 8 or later) you can just install them (as root) with: yum install jack-audio-connection-kit-devel and install qt from the qt site. If you want to build on MacOS X, you need JackOSX http://www.jackosx.com/ and Qt 5.3 or higher. It is also possible to build without jack, see below. --- Build: If you're on Mac OS X or Fedora Linux and have all the dependencies installed, you can build by simply going to the /src directory and typing the following: ./build If you want to build without Jack support (meaning that you won't need jack installed to use the app) type the following: ./build nojack If the previous script doesn't work on a different Linux flavor, try building the Makfiles yourself. You'd need qmake (e.g., on Fedora, this command is called qmake-qt4). Then you can build by: qmake jacktrip.pro make release Or without Jack support: qmake -config nojack jacktrip.pro make release If you want to install install (using Terminal): on the /src directory type: sudo cp jacktrip /usr/bin/ (enter your password when prompted) sudo chmod 755 /usr/bin/jacktrip (now you can run jacktrip from any directory using Terminal) ----------------------------- WINDOWS (XP and later) Dependencies: - ASIO4all audio driver is required even with Audio interfaces that support ASIO: www.asio4all.com Installation: Simply Run the setup file Jacktrip-1.1.exe, which will unpack Jacktrip in the directory of your choice, the default is Program Files/Jacktrip. The installer also adds the executable Jacktrip.exe in the System32 directory, so that it can be executed from a command prompt from any working directory. This executable (jacktrip.exe) can be found in the bin directory, along with the Dynamic Link Libraries (DLLs) it links to. Building: The easiest way to build is to download the free Qt Creator IDE from http://qt.nokia.com/products/ Make a copy of the src and externals directories into a new directory of your choice, open the jacktrip.pro file in src, and build the project. You can alternatively download the MinGW (Minimalist GNU for Windows), a Windows port of the GNU compiler from http://www.mingw.org/ and use mingw32-make from a command terminal to build the makefile. Building is not supported with Microsoft Visual Studio Compilers. Note: compiling with modifications in the .pro file (like adding a new source or header file) requires qmake which is only available in the Qt Creator package. --- BUILD WARNING Always keep the /src and /externals directories under the same directory. --- Post Configuration Detailed instructions at http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ --- Using JackTrip Detailed instructions at http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ jacktrip-1.1/faust-src/0000755000175000017500000000000012531357105015325 5ustar zmoelnigzmoelnigjacktrip-1.1/faust-src/Makefile.vstcompile0000644000175000017500000000222012531357105021145 0ustar zmoelnigzmoelnigdspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) appl := $(addprefix $(DEST), $(dspsrc:.dsp=$(EXT))) # Setup this variable to access the VST SDK files vst_sdk := "/Volumes/Document1/Developpement/ProjectsCVS/JackCVS/JackOSX/jackosx/jackplugins/JACK-ASinsert/VST/VSTSDK" # Setup this variable with the location for the compiled VST plug-ins install_plug_ins := "/Library/Audio/Plug-Ins/VST" all : $(appl) $(DEST)% : %.dsp install -d $@ cp -r $(vst_sdk) $@ cp -r /usr/local/lib/faust/VST/* $@ faust $(VEC) -a $(ARCH) $< -o $@/vst-output.cpp mv $@/vst-output.cpp $@/$(<:.dsp=.cpp) sed -e 's/vst-output.cpp/$(<:.dsp=.cpp)/' $@/VST.xcode/project.pbxproj > $@/VST.xcode/new_project.pbxproj && mv $@/VST.xcode/new_project.pbxproj $@/VST.xcode/project.pbxproj sed -e 's/XXXX/$(<:.dsp=)/' $@/Info.plist > $@/new_Info.plist && mv $@/new_Info.plist $@/Info.plist xcodebuild -project $@/VST.xcode clean xcodebuild -project $@/VST.xcode mv $@/build/FaustVST.vst $@/build/$(<:.dsp=.vst) rm -r $@/build/VST.build install -d $(install_plug_ins) cp -r $@/build/$(<:.dsp=.vst) $(install_plug_ins) clean : rm -f $(DEST) jacktrip-1.1/faust-src/minimal.cpp0000644000175000017500000000630012531357105017456 0ustar zmoelnigzmoelnig#ifdef __GNUC__ #define max(x,y) (((x)>(y)) ? (x) : (y)) #define min(x,y) (((x)<(y)) ? (x) : (y)) // abs is now predefined //template T abs (T a) { return (a> n); } /****************************************************************************** ******************************************************************************* VECTOR INTRINSICS ******************************************************************************* *******************************************************************************/ //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); } inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); } <> /****************************************************************************** ******************************************************************************* ABSTRACT USER INTERFACE ******************************************************************************* *******************************************************************************/ class UI { bool fStopped; public: UI() : fStopped(false) {} virtual ~UI() {} virtual void addButton(char* label, float* zone) = 0; virtual void addToggleButton(char* label, float* zone) = 0; virtual void addCheckButton(char* label, float* zone) = 0; virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) = 0; virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) = 0; virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) = 0; virtual void openFrameBox(char* label) = 0; virtual void openTabBox(char* label) = 0; virtual void openHorizontalBox(char* label) = 0; virtual void openVerticalBox(char* label) = 0; virtual void closeBox() = 0; virtual void run() = 0; void stop() { fStopped = true; } bool stopped() { return fStopped; } }; /****************************************************************************** ******************************************************************************* FAUST DSP ******************************************************************************* *******************************************************************************/ //---------------------------------------------------------------- // abstract definition of a signal processor //---------------------------------------------------------------- class dsp { protected: int fSamplingFreq; public: dsp() {} virtual ~dsp() {} virtual int getNumInputs() = 0; virtual int getNumOutputs() = 0; virtual void buildUserInterface(UI* interface) = 0; virtual void init(int samplingRate) = 0; virtual void compute(int len, float** inputs, float** outputs) = 0; }; //---------------------------------------------------------------------------- // FAUST generated signal processor //---------------------------------------------------------------------------- <> jacktrip-1.1/faust-src/Makefile.qtcompile0000644000175000017500000000230212531357105020756 0ustar zmoelnigzmoelnig###-------------------------------------------- ### DEST : directory where to put binaries ### ARCH : faust architecture file system := $(shell uname -s) dspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) ### check what type of applications to build (MacOSX Darwin or Linux) ifeq ($(system), Darwin) appls := $(addprefix $(DEST), $(dspsrc:.dsp=.app)) else appls := $(addprefix $(DEST), $(dspsrc:.dsp=)) endif TMP = /var/tmp/$(<:.dsp=) ###-------------------------------------------- all : $(appls) ### Darwin $(DEST)%.app : %.dsp rm -rf $(TMP) install -d $(TMP) faust -a $(ARCH) $< -o $(TMP)/$<.cpp cd $(TMP); qmake -project "INCLUDEPATH+=/usr/local/lib/faust/" "LIBS+=$(LIB)" "HEADERS+=/usr/local/lib/faust/faustqt.h" cd $(TMP); qmake cd $(TMP); xcodebuild -project $(<:.dsp=).xcodeproj mv $(TMP)/build/Default/$(<:.dsp=.app) $@ rm -rf $(TMP) ### Linux $(DEST)% : %.dsp rm -rf $(TMP) install -d $(TMP) faust -a $(ARCH) $< -o $(TMP)/$<.cpp cd $(TMP); qmake -project "INCLUDEPATH+=/usr/local/lib/faust/" "LIBS+=$(LIB)" "HEADERS+=/usr/local/lib/faust/faustqt.h" cd $(TMP); qmake make -C $(TMP) mv $(TMP)/$(<:.dsp=) $@ rm -rf $(TMP) clean: rm -rf $(DEST) jacktrip-1.1/faust-src/Makefile0000644000175000017500000000641412531357105016772 0ustar zmoelnigzmoelnigall : jackgtk test: jackgtk alsagtk jackqt alsaqt ladspa ossgtk bench plot sndfile jackconsole svg: $(MAKE) -f Makefile.svg puredata : install -d puredatadir $(MAKE) DEST='puredatadir/' ARCH='puredata.cpp' LIB='' -f Makefile.pdcompile alsagtk : install -d alsagtkdir $(MAKE) DEST='alsagtkdir/' ARCH='alsa-gtk.cpp' LIB='-lpthread -lasound `pkg-config --cflags --libs gtk+-2.0`' -f Makefile.compile jackgtk : install -d jackgtkdir $(MAKE) DEST='jackgtkdir/' ARCH='jack-gtk.cpp' LIB='`pkg-config --cflags --libs jack gtk+-2.0`' -f Makefile.compile jackqt : install -d jackqtdir $(MAKE) DEST='jackqtdir/' ARCH='jack-qt.cpp' LIB='-ljack' -f Makefile.qtcompile alsaqt : install -d alsaqtdir $(MAKE) DEST='alsaqtdir/' ARCH='alsa-qt.cpp' LIB='-lpthread -lasound' -f Makefile.qtcompile ladspa : install -d ladspadir $(MAKE) DEST='ladspadir/' ARCH='ladspa.cpp' LIB='-fPIC -shared' EXT='.so' -f Makefile.ladspacompile jackwx : install -d jackwxdir $(MAKE) DEST='jackwxdir/' ARCH='jack-wx.cpp' LIB='`pkg-config jack --cflags --libs` `wx-config --cflags --libs`' -f Makefile.compile ossgtk : install -d ossgtkdir $(MAKE) DEST='ossgtkdir/' ARCH='oss-gtk.cpp' LIB='-lpthread `pkg-config gtk+-2.0 --cflags --libs`' -f Makefile.compile osswx : install -d osswxdir $(MAKE) DEST='osswxdir/' ARCH='oss-wx.cpp' LIB='-lpthread `wx-config --cflags --libs`' -f Makefile.compile pagtk : install -d pagtkdir $(MAKE) DEST='pagtkdir/' ARCH='pa-gtk.cpp' LIB='-lpthread -lportaudio `pkg-config gtk+-2.0 --cflags --libs`' -f Makefile.compile pawx : install -d pawxdir $(MAKE) DEST='pawxdir/' ARCH='pa-wx.cpp' LIB='-lpthread -lportaudio `wx-config --cflags --libs`' -f Makefile.compile module : install -d moduledir $(MAKE) DEST='moduledir/' ARCH='module.cpp' LIB='-fPIC -shared' EXT='.so' -f Makefile.compile bundle : install -d bundledir $(MAKE) DEST='bundledir/' ARCH='module.cpp' LIB='-fPIC -bundle' EXT='.so' -f Makefile.compile msp : install -d mspdir $(MAKE) DEST='mspdir/' ARCH='max-msp.cpp' LIB='' -f Makefile.mspcompile vst : install -d vstdir $(MAKE) DEST='vstdir/' ARCH='vst.cpp' LIB='' -f Makefile.vstcompile bench : install -d benchdir $(MAKE) DEST='benchdir/' ARCH='bench.cpp' LIB='' -f Makefile.compile sndfile : install -d sndfiledir $(MAKE) DEST='sndfiledir/' ARCH='sndfile.cpp' LIB='-lsndfile' -f Makefile.compile plot : install -d plotdir $(MAKE) DEST='plotdir/' ARCH='plot.cpp' LIB='' -f Makefile.compile matlabplot : install -d matlabplotdir $(MAKE) DEST='matlabplotdir/' ARCH='matlabplot.cpp' LIB='' -f Makefile.compile q : install -d qdir $(MAKE) DEST='qdir/' ARCH='q.cpp' LIB='' -f Makefile.qcompile supercollider : install -d supercolliderdir $(MAKE) DEST='supercolliderdir/' ARCH='../architecture/supercollider.cpp' CXXFLAGS='`pkg-config --cflags libscsynth`' LIB='-fPIC -shared' EXT='.so' -f Makefile.sccompile jackconsole : install -d jackconsoledir $(MAKE) DEST='jackconsoledir/' ARCH='jack-console.cpp' LIB='`pkg-config --cflags --libs jack `' -f Makefile.compile clean : rm -rf alsagtkdir jackgtkdir alsaqtdir jackqtdir vecalsagtkdir vecjackgtkdir ladspadir jackwxdir ossgtkdir osswxdir pagtkdir pawxdir moduledir bundledir mspdir vstdir benchdir sndfiledir plotdir benchdir supercolliderdir puredatadir qdir plotdir jackconsoledir matlabplotdir *-ps *-svg jacktrip-1.1/faust-src/Makefile.ladspacompile0000644000175000017500000000103412531357105021577 0ustar zmoelnigzmoelnigDEST := ladspadir/ dspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) modules := $(addprefix $(DEST), $(dspsrc:%.dsp=%.so)) os := $(shell uname) ###allcpp: $(cppsrc) allmodules: $(modules) $(DEST)%.so: $(DEST)%.cpp ifeq ($(os), Darwin) $(CXX) -fPIC -bundle -O3 $(CXXFLAGS) -Dmydsp=$(patsubst %.so,%,$(notdir $@)) $< -o $@ else $(CXX) -fPIC -shared -O3 $(CXXFLAGS) -Dmydsp=$(patsubst %.so,%,$(notdir $@)) $< -o $@ endif $(DEST)%.cpp: %.dsp faust $(VEC) -a ladspa.cpp $< -o $@ clean: rm -rf $(DEST) jacktrip-1.1/faust-src/Makefile.compile0000644000175000017500000000043612531357105020417 0ustar zmoelnigzmoelnigdspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) appl := $(addprefix $(DEST), $(dspsrc:.dsp=$(EXT))) all : $(appl) $(DEST)%$(EXT) : %.dsp faust $(VEC) -a $(ARCH) $< -o $@.cpp $(CXX) -O3 $(CXXFLAGS) $(LIB) $@.cpp -o $@ clean : rm -f $(DEST) jacktrip-1.1/faust-src/Makefile.qcompile0000644000175000017500000000057712531357105020606 0ustar zmoelnigzmoelnigDEST := qdir/ dspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) modules := $(addprefix $(DEST), $(dspsrc:%.dsp=%.so)) ###allcpp: $(cppsrc) allmodules: $(modules) $(DEST)%.so: $(DEST)%.cpp $(CXX) -shared -O3 $(CXXFLAGS) -Dmydsp=$(patsubst %.so,%,$(notdir $@)) $< -o $@ $(DEST)%.cpp: %.dsp faust $(VEC) -a q.cpp $< -o $@ clean: rm -rf $(DEST) jacktrip-1.1/faust-src/Makefile.mspcompile0000644000175000017500000000453712531357105021145 0ustar zmoelnigzmoelnigdspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) appl := $(addprefix $(DEST), $(dspsrc:.dsp=~.mxo)) processor := $(shell uname -p) INC := -I/usr/local/include/c74support/max-includes -I/usr/local/include/c74support/msp-includes all : $(appl) $(DEST)%~.mxo : %.dsp Info.plist.template install -d $@/Contents/MacOS faust $(VEC) -a $(ARCH) $< -o $@/$(<:.dsp=.cpp) ifeq ($(processor), i386) g++ -arch i386 -fpascal-strings -fasm-blocks -g -O3 $(INC) -c $@/$(<:.dsp=.cpp) -o $@/$(<:.dsp=.i386.o) g++ -framework MaxAPI -framework Carbon -framework MaxAudioAPI -arch i386 -Wl,-Y,1455 -bundle $@/$(<:.dsp=.i386.o) -o $@/$(<:.dsp=.i386~) g++ -arch ppc -fpascal-strings -fasm-blocks -g -O3 $(INC) -c $@/$(<:.dsp=.cpp) -o $@/$(<:.dsp=.ppc.o) g++ -framework Carbon -framework MaxAPI -framework MaxAudioAPI -arch ppc -Wl,-Y,1455 -bundle $@/$(<:.dsp=.ppc.o) -o $@/$(<:.dsp=.ppc~) sed s/FOO/$(<:.dsp=~)/ $@/Contents/Info.plist lipo -create $@/$(<:.dsp=.i386~) $@/$(<:.dsp=.ppc~) -output $@/Contents/MacOS/$(<:.dsp=~) rm -f $@/$(<:.dsp=.ppc~) $@/$(<:.dsp=.ppc.o) $@/$(<:.dsp=.i386.o) $@/$(<:.dsp=.i386~) else g++ -arch ppc -fpascal-strings -fasm-blocks -g -O3 $(INC) -c $@/$(<:.dsp=.cpp) -o $@/$(<:.dsp=.ppc.o) g++ -framework Carbon -framework MaxAPI -framework MaxAudioAPI -arch ppc -Wl,-Y,1455 -bundle $@/$(<:.dsp=.ppc.o) -o $@/$(<:.dsp=.ppc~) sed s/FOO/$(<:.dsp=~)/ $@/Contents/Info.plist lipo -create $@/$(<:.dsp=.ppc~) -output $@/Contents/MacOS/$(<:.dsp=~) rm -f $@/$(<:.dsp=.ppc~) $@/$(<:.dsp=.ppc.o) endif Info.plist.template : echo '' > Info.plist.template echo '' >> Info.plist.template echo '' >> Info.plist.template echo '' >> Info.plist.template echo ' CFBundleExecutable' >> Info.plist.template echo ' FOO' >> Info.plist.template echo ' CFBundleName' >> Info.plist.template echo ' FOO' >> Info.plist.template echo ' CFBundlePackageType' >> Info.plist.template echo ' iLaX' >> Info.plist.template echo '' >> Info.plist.template echo '' >> Info.plist.template clean : rm -f $(DEST) jacktrip-1.1/faust-src/Makefile.sccompile0000644000175000017500000000152712531357105020747 0ustar zmoelnigzmoelnig# Makefile to produce supercollider plugins with Faust # 'foo.dsp' -> 'foo.so' and 'foo.sc' # dspsrc := $(wildcard *.dsp) scfiles := $(addprefix $(DEST), $(dspsrc:.dsp=.sc)) sofiles := $(addprefix $(DEST), $(dspsrc:.dsp=.so)) CXXFLAGS := `pkg-config --cflags libscsynth` $(CXXFLAGS) LIB := -shared ###-------------------------------------------- ### Will use faust2sc to create the class file ### only if it is installed helper:=$(shell whereis faust2sc) ifeq ($(helper),faust2sc:) todo:=$(sofiles) else todo:=$(sofiles) $(scfiles) endif ###-------------------------------------------- all : $(todo) $(DEST)%.cpp: %.dsp faust -a $(ARCH) $< -o $@ $(DEST)%.so: $(DEST)%.cpp $(CXX) $(CXXFLAGS) $(OPTFLAGS) $(LIB) $< -o $@ $(DEST)%.sc : %.dsp.xml faust2sc --prefix=Faust $< --output=$@ %.dsp.xml: %.dsp faust --xml -o /dev/null $< jacktrip-1.1/faust-src/Makefile.svg0000644000175000017500000000023112531357105017557 0ustar zmoelnigzmoelnigsrc := $(wildcard *.dsp) target := $(src:.dsp=.dsp-svg) all : $(target) %.dsp-svg : %.dsp faust -svg $< > /dev/null clean : rm -rf $(target) jacktrip-1.1/faust-src/net-ks.dsp0000644000175000017500000000135712531357105017244 0ustar zmoelnigzmoelnigdeclare name "net-ks"; declare version "1.0"; declare author "Juan-Pablo Caceres"; declare license "MIT"; declare copyright "(c) Juan-Pablo Caceres 2008"; //----------------------------------------------------- // Network-karplus-strong, // Based on 'karplus' from Faust Examples //----------------------------------------------------- import("music.lib"); // Excitation //----------- upfront(x) = (x-x') > 0.0; decay(n,x) = x - (x>0.0)/n; release(n) = + ~ decay(n); trigger(n) = upfront : release(n) : >(0.0); // Filters //-------- // Average LowPass Filter av_lowpass(x) = (x+x')/2; process = _ , ( noise * 0.9 : vgroup("excitator", *(button("play") : trigger(300))) ) :> av_lowpass; jacktrip-1.1/faust-src/Makefile.pdcompile0000644000175000017500000000204312531357105020737 0ustar zmoelnigzmoelnigDEST := pddir/ dspsrc := $(wildcard *.dsp) cppsrc := $(addprefix $(DEST), $(dspsrc:.dsp=.cpp)) modules := $(addprefix $(DEST), $(dspsrc:.dsp=~.pd_linux)) patches := $(addprefix $(DEST), $(dspsrc:.dsp=.pd)) FAUST2PD := faust2pd F2PDFLAGS := -r 10 -s LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -fPIC \ -Wall -W -Wshadow -Wno-unused -Wno-parentheses -Wno-switch $(CFLAGS) LINUXINCLUDE = ###-------------------------------------------- ### Will use faust2pd to create the GUI patches ### only if it is installed helper:=$(shell whereis faust2pd) ifeq ($(helper),faust2pd:) todo:=$(modules) else todo:=$(modules) $(patches) endif ###-------------------------------------------- allmodules: $(todo) $(DEST)%~.pd_linux: $(DEST)%.cpp $(CXX) $(LINUXCFLAGS) $(LINUXINCLUDE) -shared -Dmydsp=$(patsubst %~.pd_linux,%,$(notdir $@)) $< -o $@ $(DEST)%.cpp: %.dsp faust -a $(ARCH) $< -o $@ $(DEST)%.pd: %.dsp faust -xml $< -o /dev/null $(FAUST2PD) $(F2PDFLAGS) $<.xml mv $(<:.dsp=.pd) $(DEST) rm -f $<.xml clean: rm -rf $(DEST)