aboutsummaryrefslogtreecommitdiffstats
path: root/www/fenggui.jpg
blob: c55a8c1c691d17a6a5086671b1a645e4f40ec762 (plain)
ofshex dumpascii
0000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 00 48 00 00 ff db 00 43 00 02 01 01 02 01 01 02 ......JFIF.....H.H.....C........
0020 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0b 09 08 ................................
0040 08 0a 08 07 07 0a 0d 0a 0a 0b 0c 0c 0c 0c 07 09 0e 0f 0d 0c 0e 0b 0c 0c 0c ff db 00 43 01 02 02 ............................C...
0060 02 03 03 03 06 03 03 06 0c 08 07 08 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c ................................
0080 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c ff c0 ................................
00a0 00 11 08 00 7f 00 a0 03 01 22 00 02 11 01 03 11 01 ff c4 00 1d 00 01 00 01 05 01 01 01 00 00 00 ........."......................
00c0 00 00 00 00 00 00 00 07 03 04 05 06 08 02 01 09 ff c4 00 4c 10 00 01 03 03 02 04 02 05 06 08 0a ...................L............
00e0 0a 03 00 00 00 01 02 03 04 00 05 11 06 12 07 08 13 21 22 31 09 14 41 55 d1 16 17 32 51 93 94 23 .................!"1..AU...2Q..#
0100 56 61 71 81 95 a1 d3 15 18 24 38 52 53 62 74 91 b4 33 36 37 42 43 45 72 75 96 d2 a3 b1 b3 ff c4 Vaq......$8RSbt..367BCEru.......
0120 00 1c 01 00 01 04 03 01 00 00 00 00 00 00 00 00 00 00 00 00 04 05 06 07 01 02 08 03 ff c4 00 3f ...............................?
0140 11 00 01 02 03 04 03 0d 05 07 05 01 00 00 00 00 00 01 00 02 03 04 11 05 12 21 51 06 31 41 07 13 .........................!Q.1A..
0160 14 15 22 52 53 61 71 92 a1 b1 d1 17 34 81 91 b2 23 35 42 72 73 d2 e1 24 32 33 62 a2 c3 ff da 00 .."RSaq.....4...#5Brs..$23b.....
0180 0c 03 01 00 02 11 03 11 00 3f 00 ea 9b c5 f2 fc 99 13 1e b5 6a 6b bc 5f 5b 94 64 f4 1f 08 7d b6 .........?..........jk._[.d...}.
01a0 c2 9c 51 28 49 58 2a 4a 42 0a 52 12 30 01 41 20 0d dd b2 1a 27 5a df 34 e5 fb d6 6e 7a 8a e1 7d ..Q(IX*JB.R.0.A.....'Z.4...nz..}
01c0 85 ea c5 9f 54 7a 23 0c 8e a6 e0 a0 ee f4 24 2b 70 19 4e 3c b0 7c b2 33 58 aa 57 47 fb 3d b0 ba ....Tz#.......$+p.N<.|.3X.WG.=..
01e0 13 de 77 aa 88 71 b4 cf 3b c0 2b 37 1b be da 19 50 b4 6b 1d 4a 31 20 be 84 5c 1e 12 ce 0a 56 8d ..w..q..;.+7....P.k.J1...\....V.
0200 85 6a ef b4 05 82 32 08 dc 90 71 9e e3 0b a9 39 9c 97 c2 64 4b d3 d3 e5 6a dd 55 72 7a 1b 77 14 .j....2...q....9...dK...j.Urz.w.
0220 cc 6a 33 40 c3 6d 2e ab 72 94 1a 28 3b 49 49 0a c1 4a 42 76 81 8a d9 ab 21 c2 6e 4d ec bc cc f1 [email protected]..(;II..JBv....!.nM....
0240 32 ed 75 b8 6a 8d 43 a6 26 d8 20 c2 6d 87 6d 8a 68 75 02 9e 90 be fb d2 4e 42 9b 07 b1 fd 14 9e 2.u.j.C.&...m.m.hu......NB......
0260 6b 44 f4 4e cd 84 67 2d 38 6e 10 9b ae 8e 79 35 38 0d 47 35 ab a7 ad 28 d4 87 28 41 79 cc 0f 8a kD.N..g-8n....y58.G5...(..(Ay...
0280 86 5b e7 8a e4 f3 ea 51 99 ad b3 29 d3 ea e1 16 e8 85 2a ca d7 84 a5 38 cf 6c a5 18 c9 27 a7 9c .[.....Q...)......*....8.l...'..
02a0 e5 58 17 ba b7 9d d8 f3 2d 96 86 d1 17 5f 5a ae 10 20 b6 13 2f 20 7a d7 89 95 89 0b 6b a8 1b 5e .X......-...._Z...../.z.....k..^
02c0 e4 b6 a1 e2 4a 81 0f 2b f4 f5 23 7e 8a ed 34 db 6a 40 e2 96 b6 da b5 a9 c5 02 cc 32 14 a5 12 a2 ....J..+..#[email protected]....
02e0 4e 5b ef dc 93 51 94 ce 4f 78 55 6d 45 c6 d2 ef 15 38 9a c4 88 91 9d 8e a8 4d c0 67 74 86 1a 8e N[...Q..OxUmE....8.......M.gt...
0300 a2 53 b9 0d 94 04 f4 da 52 70 e2 92 41 48 4a 82 49 02 99 db 3d b9 69 38 b2 27 ce 27 a2 c1 97 d2 .S......Rp..AHJ.I...=.i8.'.'....
0320 81 cd f9 37 d5 45 3a 2b 9f 87 74 9d e5 d5 5e 57 ac ef ad b0 d2 e3 b9 16 44 08 71 f6 3a 54 9c 2d ...7.E:+..t...^W........D.q.:T.-
0340 45 18 50 52 42 54 9d bd 87 8d 5d bc b1 81 d3 5c ee bb a2 67 d9 3a f7 3d 71 70 8b 11 d5 b9 25 b9 E.PRBT....]....\...g.:.=qp....%.
0360 c8 60 ae 5b 27 77 83 2a 51 27 f0 80 78 8e 48 01 40 11 e4 26 cb 7f 2d bc 1a 55 b4 a7 e7 6b 88 f0 .`.['w.*Q'..x.H.@..&..-..U...k..
0380 5a e9 fa db c0 d8 d2 8f 57 5a 80 2a 42 82 18 23 79 c8 51 c6 41 f3 0a 39 24 ee b3 3d 10 3c 3c e2 Z.......WZ.*B..#y.Q.A..9$..=.<<.
03a0 1b 31 2e 8f f1 1f 59 4f 4c 88 e8 53 0f 29 98 c9 2a 6d 5e 24 f6 e9 8f e9 67 b8 cf 7a f4 87 3b b9 .1....YOL..S.)..*m^$....g..z..;.
03c0 55 ef b4 6c 40 3a 8c 4f e1 6a e8 1a 53 4e 4d df f9 51 dd 93 d2 0f 6e bd 37 6d 91 f2 33 55 31 6d U..l@:.O.j..SNM..Q....n.7m..3U1m
03e0 ba cc 44 16 67 b8 da 04 62 ea d4 52 13 bf 38 27 29 57 61 df c2 7e aa 93 3e 7b 98 f7 7b bf 6a 3e ..D.g...b..R..8')Wa..~..>{..{.j>
0400 15 83 e3 17 a3 93 4d 70 47 82 c8 97 03 5d 6a bb ac 3d 39 71 8d 3e 25 b2 42 23 26 3f 59 52 10 92 ......MpG....]j..=9q.>%.B#&?YR..
0420 b3 b5 bd d9 f1 f7 20 82 a0 94 82 70 94 e3 58 a5 76 76 8e e8 7d ae c7 46 b2 e1 92 c6 b8 8c 4b c7 ...........p..X.vv..}..F......K.
0440 58 d6 75 d0 e3 b1 7a f0 ab 56 5c dc 9c 20 38 80 75 0f 8e a5 21 fc f7 31 ee f7 7e d4 7c 29 f3 dc X.u...z..V\...8.u...!..1..~.|)..
0460 c7 bb dd fb 51 f0 a8 f2 94 e3 ec f6 c2 e8 4f 79 de a8 e3 69 9e 77 80 52 1f cf 73 1e ef 77 ed 47 ....Q.........Oy...i.w.R..s..w.G
0480 c2 9f 3d cc 7b bd df b5 1f 0a 8f 29 47 b3 db 0b a1 3d e7 7a a3 8d a6 79 de 01 48 7f 3d cc 7b bd ..=.{......)G....=.z...y..H.=.{.
04a0 df b5 1f 0a 7c f7 31 ee f7 7e d4 7c 2a 3c a5 1e cf 6c 2e 84 f7 9d ea 8e 36 99 e7 78 05 21 fc f7 ....|.1..~.|*<...l......6..x.!..
04c0 31 ee f7 7e d4 7c 29 f3 dc c7 bb dd fb 51 f0 a8 f2 94 7b 3d b0 ba 13 de 77 aa 38 da 67 9d e0 14 1..~.|)......Q....{=....w.8.g...
04e0 87 f3 dc c7 bb dd fb 51 f0 a7 cf 73 1e ef 77 ed 47 c2 a3 ca 51 ec f6 c2 e8 4f 79 de a8 e3 69 9e .......Q...s..w.G...Q....Oy...i.
0500 77 80 4a 52 95 35 4d c9 53 77 24 b6 88 b7 3b a6 ac 32 23 b2 f9 43 50 42 4a d0 15 8e f2 6a 11 a9 w.JR.5M.Sw$...;..2#..CPBJ....j..
0520 d3 91 d7 7a 37 0d 5a 42 54 b2 a4 5b d2 12 08 04 92 a9 20 79 90 3c cf b4 d4 03 74 e1 5d 1d 8d da ...z7.ZBT..[.......y.<....t.]...
0540 cf ac 27 4b 17 df 1b f1 f2 2a 68 d5 56 68 56 7d 3f 2a 54 7b 5c 57 de 61 3b d0 d0 08 6f aa 41 1e ..'K.....*h.VhV}?*T{\W.a;...o.A.
0560 1d ca 04 27 3e 59 23 03 39 a8 e2 27 14 17 25 d9 31 c6 8b 75 2e 44 23 f9 4b af c7 6d 99 68 24 00 ...'>Y#.9..'..%.1..u.D#.K..m.h$.
0580 b6 d2 10 a5 03 85 25 5b 14 41 1b 56 09 04 0c cb 0b d4 71 9a 95 35 85 3b 11 2f db 76 7a db 66 74 ......%[.A.V......q..5.;./.vz.ft
05a0 60 b8 bb c6 51 d4 1d 4c a7 70 ee 33 8c fb 2b c6 a3 d5 31 34 7c 14 ca bb bf 0a d7 19 6b e9 25 d9 `...Q..L.p.3..+...14|.......k.%.
05c0 97 08 ac 21 4b c1 3b 42 94 e0 19 c0 27 1f 90 d7 2c 06 11 a9 be 0a 76 1d 9a 86 60 f1 cd c6 db e8 ...!K.;B....'...,.....v...`.....
05e0 c9 e1 cd d5 e9 4d 43 69 f5 ae 3b b1 90 cb 8e 2d b0 4a 13 bd 03 18 5e e4 11 92 46 dc 91 82 33 be .....MCi..;....-.J....^...F...3.
0600 f0 aa 7c 7d 7b 02 44 a9 7a 7c 5a 8a 55 84 c5 90 1a 71 c6 c6 f7 02 4a 94 d8 db b8 a0 20 90 09 00 ..|}{.D.z|Z.U....q....J.........
0620 e7 04 8e f5 b1 dc 75 dd ae cf 09 72 65 cf b6 45 8e db 8b 65 6e bd 73 88 84 21 68 69 4f 2d 04 97 ......u....re..E...en.s..!hiO-..
0640 70 14 96 90 b7 08 f3 08 4a 94 7b 02 6b 2a 7d 61 2b 4a 4c 55 05 2c e1 23 d6 18 c9 3e 7d bf 09 41 p.......J.{.k*}a+JLU.,.#...>}..A
0660 61 3f 87 c1 17 f2 51 97 34 9a 6e df 13 80 da 81 c6 e1 46 43 88 43 25 2a 0d 80 52 7a ed f9 57 29 a?....Q.4.n.......FC.C%*..Rz..W)
0680 d7 50 f3 c7 0a e5 79 e5 57 5e db 2d cf 1b 55 ea 45 ac 22 1c 87 14 14 98 ee b8 e0 4b 6e 65 04 fd .P....y.W^.-..U.E."........Kne..
06a0 15 8c f6 ef da bf 36 f8 3b cd bd cf 4d 6a d6 74 27 17 60 a3 4d 6a df a3 12 e0 70 20 5e 52 0e 03 ......6.;...Mj.t'.`.Mj....p.^R..
06c0 8d b8 30 9c 9e de 58 00 9c 10 83 e1 ab e3 72 6b 46 14 ac 84 48 71 81 68 74 4c 1d f8 6b 75 b8 13 ..0...X.......rkF...Hq.htL..ku..
06e0 b0 9d 95 c0 ea ad 54 5e db 96 7c 68 a0 b0 d4 81 ab 6d 2a 71 19 f9 ae 83 a5 29 57 72 8c 25 29 4a ......T^..|h.....m*q.....)Wr.%)J
0700 10 94 a5 28 42 52 94 a1 09 4a 52 84 25 29 4a 10 95 38 f2 4d 0f d7 e4 6a f6 ba 8e 35 96 e0 10 b4 ...(BR...JR.%)J..8.M...j...5....
0720 63 72 48 54 92 08 c8 23 cc 7d 55 07 57 47 7a 36 6c 91 6f ba bf 5c a2 63 29 7d 0d 43 b7 29 09 51 crHT...#.}U.WGz6l.o..\.c)}.C.).Q
0740 38 49 2b 97 93 fb 05 40 f7 4a 86 5f a3 f1 9a 36 96 7d 41 39 d8 ee bb 36 d3 db e4 56 d1 ab 79 59 [email protected]._...6.}A9...6...V..yY
0760 d2 3a ef 51 3d 76 bb c6 7e 7d c5 f2 92 e3 ce 2d 39 56 d4 6c 03 01 38 c6 dc 76 c6 09 00 9e e0 1a .:.Q=v..~}.....-9V.l..8..v......
0780 f9 6b e5 53 47 59 e3 38 cb 10 57 d1 7a 5b 53 d6 db 81 0e a0 be d0 50 6d cc 29 24 65 21 4a c7 e7 .k.SGY.8..W.z[S.......Pm.)$e!J..
07a0 ae 89 4e 83 b2 2c a8 26 04 62 52 70 a0 33 d8 fd 47 bd 7c 77 43 d8 98 23 7c 28 a8 dd e5 b8 e3 3f ..N..,.&.bRp.3..G.|wC..#|(.....?
07c0 b6 b9 8b 80 3f 9d e6 a6 dc 20 ae 6f 67 94 1d 12 c5 9d cb 7a 61 c9 10 dd 7d 12 54 d1 7b 20 ba 84 ....?......og......za...}.T.{...
07e0 b8 94 af cb 21 40 3a be e3 da ac f9 e0 d5 93 3c 91 e8 38 f7 53 2d 10 e6 25 5f 82 21 01 e0 10 95 ....!@:........<..8.S-..%_.!....
0800 36 ac a1 63 c3 9c 8e c0 77 c0 09 4f 6e c2 ba 77 e4 2d 8f 70 4f a8 c5 dc a2 40 19 ee 7f 6d 7d 4e [email protected]}N
0820 83 b2 2c e0 40 8c 4f e4 cf c6 8e 00 fe 77 9a 38 42 e6 4e 6d 4c 7d 19 cb 76 b3 bb 4e 9b 29 d6 23 ..,[email protected]}..v..N.).#
0840 43 69 e9 0e 3a 90 b2 db 4d 3c 16 48 08 48 27 b1 51 c0 04 9a fc f5 e3 1e bd e1 4f 1b 74 2a 2d b7 Ci..:...M<.H.H'.Q.........O.t*-.
0860 87 23 6a 6b 74 b4 29 e6 d7 08 75 1e 82 a4 8f f4 81 40 85 32 e0 ef f5 1c 67 23 07 bf ea 17 3f ba .#jkt.)[email protected]#....?.
0880 36 d7 6f e5 4f 52 2d 98 4c 21 4b 72 1b 4a ed 90 a4 2e 5b 29 52 48 3d 88 29 24 10 7e ba fc fb e0 6.o.OR-.L!Kr.J....[)RH=.)$.~....
08a0 0f a3 c2 d1 c4 1e 31 6a 49 da 66 f6 74 8b ad c2 8e f3 cd 2a de 99 f1 dc 53 8b 7d 3e 14 29 69 09 ......1jI.f.t......*....S.}>.)i.
08c0 1e 1c 90 77 24 f9 60 0c 83 76 6e 61 1a 04 8d 9f 1a 25 a8 e6 89 40 e3 be 1a 38 91 56 80 da 00 d7 ...w$.`..vna.....%[email protected]....
08e0 54 17 50 1e 49 a5 6a a3 56 f5 62 1f e9 ea 63 e1 77 55 0e 38 d4 d4 50 81 52 31 5c d9 c1 5e 3d 5f T.P.I.j.V.b...c.wU.8..P.R1\..^=_
0900 b8 0d aa 91 a7 24 cc 99 ae 38 7e 7b 41 b9 c8 28 66 e5 6c 4f 97 49 c4 95 00 e0 48 c7 d1 f6 79 04 .....$...8~{A..(f.lO.I....H...y.
0920 fd 13 34 c3 e6 f7 4b 3c da bd 61 9b b4 27 50 b5 20 b4 f3 28 2b 18 24 67 c2 b2 3b e3 38 ce 7b f7 ..4...K<..a..'P....(+.$g..;.8.{.
0940 c1 a9 43 8b 9c 84 ab 87 b2 e2 da ee ba de 73 d0 2e cc 3e 7d 6e 26 93 61 c4 46 3d 34 a0 a5 5f ca ..C...........s...>}n&.a.F=4.._.
0960 ba a1 4a 1b 40 50 49 04 f9 ab 00 e2 9d bf 91 fb 54 d9 3e ae c7 11 26 47 72 6b cd 39 d5 f9 10 ac [email protected].>...&Grk.9....
0980 2b ae f0 69 1b 89 78 f8 54 e3 fb b2 bf 20 92 7b 27 ce ca 92 d3 5d 02 96 87 72 1c f3 cb 76 02 22 +..i..x.T......{'....]...r...v."
09a0 10 06 40 ef 15 a0 d8 0d 69 d9 45 19 8f 29 a4 11 5d 79 d0 1b 5c f9 38 f6 fd a5 3e 2a 38 fe 36 fa [email protected]..)..]y..\.8...>*8.6.
09c0 43 fa d9 ff 00 60 3e 34 fe 36 fa 43 fa d9 ff 00 60 3e 35 99 b9 f2 0d a7 f8 95 ac 21 07 f5 e6 a5 C....`>4.6.C....`>5........!....
09e0 13 ae eb 8e d3 7b 74 69 6e 3b 6b 75 c2 36 29 62 47 4d 1b 32 09 ef 82 14 36 6e 39 03 1b 3b d1 c3 .....{tin;ku.6)bGM.2....6n9..;..
0a00 a4 2d f6 8b 6c f5 71 07 53 b9 0a e2 eb ec 75 59 d2 0a 74 b0 b6 92 95 10 a4 26 41 59 dc 54 90 0a .-..l.q.S.....uY..t......&AY.T..
0a20 52 a1 93 dc 80 09 0e ec d3 ed cf 4b 45 e9 f7 83 fa 71 0f fe 49 19 b3 f4 8a b8 40 6f cd bf bd 50 [email protected]
0a40 fe 36 fa 43 fa d9 ff 00 60 3e 35 ed 8e 6b b4 ac b5 ed 64 5d 9f 56 33 b5 a8 85 64 77 03 c8 13 ed .6.C....`>5..k....d].V3...dw....
0a60 20 7e 9a b9 d4 5e 8d 6d 29 a7 de ba b2 9e 20 6a 69 d2 6c e5 d4 be d4 7d 24 72 4b 6e 86 94 12 a5 .~...^.m)......ji.l....}$rKn....
0a80 c9 4a 4f 75 02 0e 70 a1 9c 64 a5 41 33 2f 08 7d 13 b2 78 74 b7 6e da 77 8a 4a 48 bc c0 31 d6 a7 .JOu..p..d.A3/.}..xt.n.w.JH..1..
0aa0 f4 d0 57 52 3b 9b 54 53 b5 52 01 4e e0 06 7c 88 fc 86 bc 66 f4 ff 00 40 5b 08 ba 5e 79 c5 fb 01 ..WR;.TS.R.N..|....f...@[..^y...
0ac0 64 40 0f c7 79 3e 4b 78 56 76 90 17 81 12 00 03 a8 b7 f7 ad 2f 49 6a a8 7a db 4e c5 ba 5b d6 a7 [email protected]>KxVv........../Ij.z.N..[..
0ae0 22 4c 49 53 6a 52 4a 4f 62 41 c8 fc e0 8a c8 d6 2f 48 70 f0 70 92 d6 fe 97 4c b3 3d 3a 7e 74 c8 "LISjRJObA....../Hp.p....L.=:~t.
0b00 02 49 6b a5 d7 e9 c9 75 3b b6 e4 ed ce 33 8c 9c 7d 75 94 a7 11 12 14 41 be 40 35 61 c4 1c c1 d4 .Ik....u;....3..}u.....A.@5a....
0b20 76 6c ea 4a 5a 1c 00 0f 14 3b 7b 52 94 a5 65 6c 94 a5 28 42 57 4c 7a 30 ff 00 d7 4d 7b fd ca db vl.JZ....;{R..el..(BWLz0...M{...
0b40 ff 00 e9 32 b9 9e a6 5e 4a e6 c2 8b a8 f5 67 af 49 72 2a 4c 68 3d 32 87 12 d9 59 dd 2b 23 24 1c ...2...^J.....g.Ir*Lh=2...Y.+#$.
0b60 e3 b7 f8 d4 13 74 98 97 2c 08 ae 39 b7 ea 09 ce c7 69 74 db 40 eb f2 2b aa 35 67 2a 3a 5b 5a 6a .....t..,..9.....it.@..+.5g*:[Zj
0b80 6b d5 d6 72 ee ca 91 7f 75 b7 a6 21 12 b6 b2 e1 43 6d b6 91 b3 18 c0 0d 20 fe 7c 9a a4 f7 28 9a k..r....u..!....Cm........|...(.
0ba0 42 7d 9a 2c 29 e6 f5 73 4c 0b 8f f0 9c 47 65 dc 5c 71 d8 ae ec 42 0a 50 ac f6 68 f4 d0 4b 5f 40 B}.,)..sL....Ge.\q...B.P..h..K_@
0bc0 91 9d b5 1a 71 3f 89 0d e8 e8 b6 c7 6c d1 ae 3a 81 72 65 f4 64 b2 dc d4 a1 4c b5 d2 71 5d 4d db ....q?......l..:.re.d....L..q]M.
0be0 48 ec a4 a5 38 3e 7b fb 56 bd 67 e6 02 4c bb aa e3 cb d2 1a 9a 33 27 ac a6 df 6e e6 cb 89 c2 1b H...8>{.V.g..L.......3'...n.....
0c00 52 d0 16 0a 13 b5 4b 52 42 00 1b 80 2a 19 20 77 ae 66 e1 ed cb c5 4d b8 33 94 d6 c7 28 ba 56 39 R.....KRB...*..w.f....M.3...(.V9
0c20 79 48 7e fa db ce 4a 76 6b 6f 37 38 b6 e4 57 9c 6d 96 d4 b6 94 90 0a 09 4b 0d 8e de 7e 22 72 54 yH~...Jvko78..W.m.......K...~"rT
0c40 4d 62 a5 72 43 a6 d3 6c 7d 88 17 cd 5d 6c 75 e2 e2 fa cc dc 89 5a 94 b4 a9 2a 0b ca 7f 08 82 14 Mb.rC..l}...]lu......Z...*......
0c60 46 c5 12 9c 13 d8 54 47 27 8f d7 36 ee 8d a1 8d 23 7c 7e 3b f1 83 a9 2a ba 21 b5 32 b2 fb c8 d8 F.....TG'..6....#|~;...*.!.2....
0c80 e1 2d 91 9e 92 1b 70 ed ce 37 ed 1b 8e dd d9 ce 1a 71 8d 9d 6d 39 d8 f7 4b 26 a2 d3 6a 69 80 ef .-....p..7.......q..m9..K&..ji..
0ca0 52 4c e6 5c 69 4a 3b 72 d8 50 00 95 02 4e 7b 63 c2 48 24 60 93 87 b7 21 f3 58 e0 ce 59 9f 4b 35 RL.\iJ;r.P...N{c.H$`...!.X..Y.K5
0cc0 da 4e 9d f4 79 f1 2a 74 37 54 c4 b8 36 f6 df 61 d4 e3 2d ad 0f 36 a4 a8 7b 32 08 06 b8 9b d1 a9 .N..y.*t7T..6..a..-..6..{2......
0ce0 ce 23 d7 4b b5 da 65 ff 00 45 6a f8 92 26 5b ad ad bb 22 24 0e bc 5c 2d 52 16 97 c1 c8 5f 4d 49 .#.K..e..Ej..&[..."$..\-R...._MI
0d00 ca 86 12 ae c0 8e e7 19 e8 1f 49 4d d2 d6 f7 22 9c 4d 4c 39 af 4a 90 6c ae ed 6d c7 92 e0 23 23 ..........IM...".ML9.J.l..m...##
0d20 71 c0 19 ec 32 73 ec c5 69 9e 8b 94 84 40 75 29 00 24 69 6b 38 00 79 0e cf 54 c2 cb 8d 16 2e 8f q...2s..i....@u).$ik8.y..T......
0d40 cf 16 3e 8c 17 6a dd 60 f2 99 4c 76 11 fc 24 d1 e1 c3 87 12 18 7b 6a e2 70 39 60 7e 75 fe 57 4d ..>..j.`..Lv..$......{j.p9`~u.WM
0d60 f0 df 89 d6 be 2b 59 a4 5c 2d 1e ba 62 47 92 a8 bb e4 c5 72 31 71 49 4a 49 29 4a c0 56 df 16 32 .....+Y.\-..bG.....r1qIJI)J.V..2
0d80 40 ce 0f b3 bd 6c 35 61 32 f3 16 dd 70 52 26 4a 5c 64 16 d2 a6 c2 54 84 ee 39 56 ef a4 0f d4 9a @....l5a2...pR&J\d....T..9V.....
0da0 d2 78 8d c6 b7 34 6e a1 84 cd ba d7 71 bf 40 7d b2 5f 76 33 ed 85 b0 ac 2f 19 ca 31 8c 84 e7 19 .x...4n.....q.@}._v3..../..1....
0dc0 38 2a 38 c8 4a 57 5e 61 9a 5e 18 e2 2a a4 5a 54 3f 7f e6 4a e7 63 85 6e 96 ad 21 7c 2d 3c a9 5e 8*8.JW^a.^..*.ZT?..J.c.n..!|-<.^
0de0 b9 1d b9 91 e4 3f 19 2d 74 fa 6a 1d 34 29 27 7e e5 e0 03 9f 06 7c ab ec 8e 64 e7 db df 7d 6b d2 .....?.-t.j.4)'~.....|...d...}k.
0e00 77 e9 d0 d6 ea 04 45 c3 90 cf 51 4d 98 c1 d2 a5 a1 c4 24 a7 f0 99 68 76 ce ec 12 12 9e f5 9b a3 w.....E...QM......$...hv........
0e20 30 8b 8e c9 4b f5 a2 f1 07 98 8d 3d c2 fd 5a ab 45 e5 37 36 16 21 b7 31 2f b3 0d 52 1a 5e f5 3a 0...K......=..Z.E.76.!.1/..R.^.:
0e40 94 b6 03 7b 97 bf 2c ab cd 21 3e 24 8d d9 50 15 92 d1 5c 4b b7 ea ab 12 66 4c 45 d2 c8 ea 96 a4 ...{..,..!>$..P...\K....fLE.....
0e60 88 d2 de 8e 5c da 0e 02 bc 20 e0 11 df 07 07 f2 56 6e cb 72 45 cd 52 d4 cb aa 7e 3a 1e d8 d2 ce ....\...........Vn.rE.R...~:....
0e80 d2 48 d8 82 7b 80 01 f1 15 56 30 cd 05 8e 02 a5 70 ad d2 f4 d6 a4 d4 97 bb 8b 08 79 b6 67 dd e7 .H..{....V0.....p..........y.g..
0ea0 c9 6d 2e a3 63 89 4a e5 3a a0 14 3d 87 07 b8 f6 1a a5 59 1d 69 fe d0 b5 4f fd fe e5 fe 71 ea c7 .m..c.J.:..=......Y.i...O....q..
0ec0 57 6b d9 1e e3 07 f2 37 c8 2a d6 37 f9 1d da 52 94 a5 38 2f 34 a5 2b 49 e3 c4 2b 95 d7 49 44 87 Wk.....7.*.7...R..8/4.+I..+..ID.
0ee0 6a 6e f0 f4 89 77 06 50 b6 ed 68 5a e5 2d a1 b9 4e 6c 4a 01 51 c2 52 55 d8 1f a3 9c 76 af 48 4c jn...w.P..hZ.-..NlJ.Q.RU....v.HL
0f00 0f 78 69 34 04 eb 3a 87 59 5e 71 5f 71 85 f4 ad 16 ed 56 b3 6c 90 ae 4e 85 c9 89 16 42 c0 da 14 .xi4..:.Y^q_q.....V.l..N....B...
0f20 e3 49 59 03 ea c9 15 cf 97 0e 10 6b 05 5b e4 3d 11 9e 2c 25 f0 54 a6 63 3d 64 9a 49 1e 69 4a 96 .IY........k.[.=..,%.T.c=d.I.iJ.
0f40 06 33 ec 24 0f d1 55 5d e0 ce ad 40 52 c2 78 ae 52 56 a0 94 22 c9 35 45 29 dc e6 3b 90 92 7c 09 .3.$..U][email protected]..".5E)..;..|.
0f60 68 e7 68 ca 9c 23 00 24 9a 72 36 7c 9d 31 9a 62 6c e3 38 9a b7 97 29 e7 e4 a5 af dd b6 ff 00 bb h.h..#.$.r6|.1.bl.8...).........
0f80 a3 e1 4f 92 96 bf 76 db fe ee 8f 85 73 36 a7 e1 77 14 20 5e de 66 dd 65 e2 64 98 8d ed 4a 5d 76 ..O...v.....s6..w..^.f.e.d...J]v
0fa0 db 35 0a 59 da 37 1d a5 20 81 bb 38 07 d9 8a c7 fc dd 71 77 f1 7f 88 ff 00 70 9b ff 00 ad 29 65 .5.Y.7.....8......qw.....p....)e
0fc0 85 2a e6 87 70 b8 58 ff 00 b0 5e 26 d9 70 34 de 1d f2 5d 53 f2 52 d7 ee db 7f dd d1 f0 a8 f3 9a .*..p.X...^&.p4...]S.R..........
0fe0 88 b0 b4 bf 01 75 24 b8 d1 22 46 7d 30 9d 42 1d 6d 90 95 20 a9 05 20 82 06 41 c9 18 3e c3 50 cf .....u$.."F}0.B.m........A..>.P.
1000 cd d7 17 7f 17 f8 8f f7 09 bf fa d5 0b b7 07 b8 a7 7e b6 b9 1e 76 93 d7 f3 61 bb e1 5b 4f da e6 .................~...v...a..[O..
1020 38 da ff 00 21 05 24 1a 6c b7 34 42 14 f4 84 49 48 33 d0 58 e7 8a 56 f0 c3 11 91 c9 3c 68 fe 95 8...!.$.l.4B...IH3.X..V.....<h..
1040 b2 42 d0 87 39 1a 55 ef 6b 2a 69 4d 66 84 0d 63 32 0a c1 5f 63 16 bd 1d f6 fb 83 ee 17 27 5c 65 .B..9.U.k*iMf..c2.._c........'\e
1060 b8 a5 3e b5 95 3a b4 fa de cd bb 8f 7c 61 07 b7 d5 9a c7 71 f3 99 76 75 27 05 b4 ce 9e 55 b2 ff ..>..:......|a.....q..vu'....U..
1080 00 a6 6f f0 61 b4 84 09 8c 29 81 2d a4 a5 ac 38 ca fb 15 a0 84 93 90 31 df cc d6 c2 f7 00 78 87 ..o.a....).-...8.......1......x.
10a0 22 ca c5 b5 cd 0d ac 97 6e 8c 42 99 8a ab 2c 92 cb 44 1c 82 94 6c da 3b f7 ec 2b 66 b1 72 e1 76 ".......n.B...,..D...l.;..+f.r.v
10c0 d7 96 9b a9 d7 7a 47 88 b3 25 db e2 2c d8 52 6c 92 1f 43 72 0a 55 9d db da 56 d4 1d a8 07 6e 09 .....zG..%..,.Rl..Cr.U...V....n.
10e0 f0 8c 60 95 26 05 39 b9 8b e0 40 8e e1 69 c3 37 db 09 b4 69 04 f2 03 05 7f ba b8 91 5c 36 13 55 ..`.&[email protected]........\6.U
1100 2c 6e e9 10 22 3a 59 ad 91 70 de f7 ca 93 a8 df 74 43 95 30 6b e8 2b b4 0a 2b ce 25 6a 4b 64 8e ,n..":Y..p......tC.0k.+..+.%jKd.
1120 38 70 a5 ab 73 f0 df b7 cd 72 38 7b d5 54 95 b4 f9 50 71 38 56 de ca ef 8c e6 b6 4e 56 6d 2c ce 8p..s....r8{.T...Pq8V......NVm,.
1140 d7 9c 49 62 63 2d 4a 44 4b a3 08 65 2f 20 38 19 49 68 92 12 0f 90 fc d5 af f0 df 92 7b 1d bf 88 ..Ibc-JDK..e/.8.Ih..........{...
1160 1a 2b 53 c4 94 bb 0b d0 60 c7 99 2a ce 0a b7 bf 29 b3 95 39 b1 67 f0 63 2a 4a 54 12 00 1b 71 80 .+S.....`..*....)..9.g.c*JT...q.
1180 49 35 b7 f2 f2 53 03 98 1e 2c c1 4f 85 1d 7b 74 94 27 1e 7b a3 a8 29 59 ff 00 a9 3f b2 a3 d2 72 I5...S...,.O..{t.'.{..)Y...?...r
11a0 d3 72 d6 fc 26 cd 52 8f 7c 6a 50 d4 1a 31 be 18 61 50 a4 93 51 65 a6 b4 7a 2c 58 23 16 36 00 c4 .r..&.R.|jP..1..aP..Qe..z,X#.6..
11c0 63 89 89 d5 d9 55 2c 7c 94 b5 fb b6 df f7 74 7c 29 f2 52 d7 ee db 7f dd d1 f0 ab fa 55 a1 75 b9 c....U,|......t|).R.........U.u.
11e0 2a ce 8a c3 e4 a5 af dd b6 ff 00 bb a3 e1 4f 92 96 bf 76 db fe ee 8f 85 5f d2 8b ad c9 14 5e 23 *.............O...v....._.....^#
1200 46 6e 1b 09 69 96 d0 d3 68 ec 94 21 21 29 4f e6 02 bd d2 95 b2 ca 52 94 a1 09 59 8e 17 5a e4 5e Fn..i...h..!!)O.......R...Y..Z.^
1220 b8 c9 a4 a2 c4 90 b8 92 5f 97 25 2d 3e 87 0b 6b 65 5e a1 2b 0b 4a 87 70 a1 e6 08 f6 81 58 7a da ........_.%->..ke^.+.J.p.....Xz.
1240 78 05 fc e1 f4 3f f7 f9 1f e4 25 d3 26 93 7d cf 37 fa 51 3e 82 94 4a 7b c4 3f cc df 30 a7 57 34 x....?....%.&.}.7.Q>..J{.?..0.W4
1260 3f 1a 55 7a 92 23 71 29 ab 75 ad 6e 3e e3 2d 22 23 32 5c 6c ad 2d 94 a7 73 cc a9 5b 12 e7 5c 80 ?.Uz.#q).u.n>.-"#2\l.-..s..[..\.
1280 56 a3 b4 b6 9c f8 6b 33 27 47 71 16 56 a3 8b 28 eb 47 1a b7 c3 9c b9 88 82 d1 21 2e a5 4f b8 e1 V.....k3'Gq.V..(.G........!..O..
12a0 6d c5 a8 29 4b 4e c7 0b 58 ec 80 94 a0 a5 09 52 41 a9 16 95 c6 85 ef cd 58 d7 ba 96 87 ae f4 be m..)KN..X......RA.......X.......
12c0 b6 ba 68 38 76 dd 3d 7b 6a c5 3e d8 e7 5a 3c 96 a4 b8 12 ea 92 95 ed 42 d2 13 82 82 b2 92 42 82 ..h8v.={j.>..Z<........B......B.
12e0 c1 09 20 a4 e6 b1 76 0d 23 c5 a4 b9 30 de 78 82 9b 8b 2f b6 b6 9b 8c dc 66 a3 a1 b2 1f 2a 43 9d ......v.#...0.x.../.....f....*C.
1300 46 9a 43 b9 2d e1 0a 4a 54 3c ce d5 02 37 19 42 95 80 f7 01 40 51 7b a9 45 d0 74 37 16 61 5d ad F.C.-..JT<...7.B....@Q{.E.t7.a].
1320 ee fc e6 be e4 28 52 02 d7 19 70 63 a8 c9 63 09 05 95 ac b4 54 7e 89 21 7f 4c 15 1c a8 f7 ce c5 .....(R...pc..c.....T~.!.L......
1340 ae f4 fe a8 d5 b2 53 26 25 c9 36 99 48 6f a5 d4 8f 2d d4 29 48 da e0 20 94 a4 1f f8 9b 86 72 02 ......S&%.6.Ho...-.)H.........r.
1360 90 92 42 b1 83 b7 d2 82 e7 66 8b dd 4a 22 b0 e8 7e 33 bf 6e 92 d5 f3 89 6d c8 32 62 39 18 a2 2c ..B......f..J"..~3.n....m.2b9..,
1380 36 59 09 52 e2 a9 be a8 71 0c a1 c4 a9 2f 2b a8 36 a8 7d 04 8c f7 26 ab 59 f4 27 16 22 6a 58 ee 6Y.R....q..../+.6.}...&.Y.'."jX.
13a0 4b e2 00 95 67 84 96 90 88 c5 b4 ef 92 80 e2 56 e2 5c 56 cc 92 a0 84 a3 7e 54 70 a5 63 6e 55 ba K...g..........V.\V.....~Tp.cnU.
13c0 57 a5 66 fb f3 45 ee a5 f9 31 cc 76 a3 93 c1 ef 91 ba 9d 1b 9c 8d 66 9c 63 cf 08 ff 00 7d 95 02 W.f..E...1.v..........f.c....}..
13e0 85 e3 eb f0 17 14 33 ed 4a 7e aa b4 e1 55 cd 98 bc e7 6a e6 1b 71 2b 6e f5 63 66 73 4b 4a b2 97 ......3.J~...U....j..q+n.cfsKJ..
1400 43 6e ec 04 7b 0f 65 03 db d9 fa 6b 4e e6 73 85 1c 52 b8 70 fb 5b 5c 65 6a b8 52 ec 11 a5 c8 97 Cn..{.e....kN.s..R.p.[\ej.R.....
1420 06 cc dc 34 38 e2 a3 97 d5 b5 2a 74 a1 2a 49 4b 6a 27 b1 57 96 33 ed a8 f7 97 be 27 b9 a6 f8 81 ...48.....*t.*IKj'.W.3.....'....
1440 c3 6b ed c5 6b 4b 4f 46 91 60 92 ea 8f d3 40 ca 58 1d fd 81 41 03 bf d4 6a fa 9a 9f 74 ad b5 2f .k..kKOF.`[email protected]../
1460 12 2b 0c 36 17 35 dc aa 61 56 ef 6e 38 13 81 a3 4e 34 3a f0 4b ec e8 22 6e c1 8f 01 8e 0f 7b 59 .+.6.5..aV.n8...N4:.K.."n.....{Y
1480 77 0a e3 75 f7 da 31 03 10 1c e1 85 70 a6 2b ba 69 51 7f 27 d1 04 5e 09 30 72 54 e3 97 19 ea 70 w..u..1.....p.+.iQ.'..^.0rT....p
14a0 e7 b6 f1 29 d4 9c 7e 4c a6 a5 0a b7 ec e9 c1 37 29 0e 68 0a 5f 68 75 35 d2 a2 aa ad 9f 94 32 b3 ...)..~L.......7).h._hu5......2.
14c0 31 25 9c 6a 58 e2 df 91 a2 52 94 a5 a9 22 52 94 a1 09 4a 52 84 25 6d 3c 02 fe 70 fa 1f fb fc 8f 1%.jX....R..."R...JR.%m<..p.....
14e0 f2 12 eb 56 ac b7 0d 2f ab d3 3c 5f d2 97 06 d8 72 52 e1 ca 92 e8 65 b4 15 ad ec 40 95 e1 48 1d ...V.../..<[email protected].
1500 ca 8f 90 03 3d c8 ec 69 93 49 be e7 9b fd 28 9f 41 4a 25 3d e2 1f e6 6f 98 5d cd 4a 8d af 1c c8 ....=..i.I....(.AJ%=...o.].J....
1520 40 b4 ba fa 53 60 d7 b3 51 1f e9 3b 17 4e 38 a4 2f c4 84 e5 1b 94 95 28 65 c0 7e 8e 70 95 1c 61 @...S`..Q..;.N8./......(e.~.p..a
1540 24 8a b7 1e 3d 26 0d da 34 26 ec 3a a2 53 ef 4c 72 3b aa 6a d4 be 8c 66 d0 b7 13 d5 53 8a c2 0e $...=&..4&.:.S.Lr;.j...f....S...
1560 42 02 80 49 23 0b 4f 7a e3 4b 85 58 d7 1d 92 91 29 56 f6 89 6a b8 5a 62 c8 5e d0 a7 d9 43 87 6f B..I#.Oz.K.X....)V..j.Zb.^...C.o
1580 96 4a 41 ed 57 15 aa d5 29 4a d7 75 f6 bd 4e 85 72 dc a7 62 cf 94 c4 b5 ba 87 3d 4e 23 92 5d 6f .JA.W...)J.u..N.r..b......=N#.]o
15a0 6b 4a 70 1d a8 07 b1 29 08 ef 81 97 13 dc 79 10 05 90 2b a9 6c 54 a8 cd 5c c7 b0 fb 13 04 7d 33 kJp....)......y...+.lT..\.....}3
15c0 ae 4c 88 ed 49 5b 49 91 63 53 2d be a6 99 5b a0 6e dc 48 de 53 b1 3d 89 2a 50 18 ab 6b 3f 34 50 .L..I[I.cS-...[.n.H.S.=.*P..k?4P
15e0 af 3a 96 3d a1 16 3d 58 89 ab 4b 42 4e 6d 2a d9 01 4e b8 94 27 a8 4a 86 d4 01 bd 65 47 03 6b 67 .:.=..=X..KBNm*..N..'.J....eG.kg
1600 6e e3 d8 6f 70 ac dc 76 4b 92 ae 96 86 75 05 8e 6c 19 03 2c 4c 4b ac ac 7f 65 45 40 ff 00 f7 5c n..op..vK....u..l..,LK...eE@...\
1620 c9 cd c7 2d 51 b8 7d cb a4 27 6c 45 e7 0e 99 98 e4 b7 9c 50 01 c2 97 9d 2a 2b ed fd 05 28 01 8f ...-Q.}..'lE.......P....*+...(..
1640 24 92 4f 95 74 fd 96 51 9b 6c 69 e5 00 95 3c 0a c8 1e 40 92 4d 7b b9 db 23 de ad b2 21 cc 61 a9 $.O.t..Q.li...<[email protected]{..#...!.a.
1660 31 25 b6 a6 5e 65 d4 85 21 d4 28 61 49 50 3d 88 20 90 45 76 15 a3 60 4a da 52 77 22 b7 96 59 74 1%..^e..!.(aIP=...Ev..`J.Rw"..Yt
1680 3b 68 d4 47 88 07 c3 6a 85 59 76 dc cc 84 56 98 4e e4 87 54 8d 87 02 0f 81 23 c7 62 89 79 11 bd ;h.G...j.Yv...V.N..T.....#.b.y..
16a0 1b ef 2d b6 a7 9c 5f 52 47 ad 4c 2f 9f ed aa 4b 8b 3f b1 60 fe 9a 98 6b 52 e0 d7 05 ec bc 08 d2 ..-..._RG.L/...K.?.`...kR.......
16c0 af 59 6c 02 62 6d ee cb 72 5a 51 21 f2 f1 68 ac 24 6c 49 3d f6 80 90 06 72 7e b2 4f 7a db 69 c6 .Yl.bm..rZQ!..h.$lI=....r~.Oz.i.
16e0 c7 95 89 2d 23 06 5a 2d 2f 31 a1 a6 9a b0 14 48 ed 29 96 cc 4d c4 98 6d 68 f7 13 8e bc 4d 52 94 ...-#.Z-/1.....H.)..M..mh....MR.
1700 a5 39 24 49 4a 52 84 25 29 4a 10 95 e1 e6 3a aa 42 83 8f b2 e3 44 a9 b7 58 79 6c ba d9 20 a4 94 .9$IJR.%)J....:.B....D..Xyl.....
1720 ad 04 29 39 49 20 e0 f7 04 8f 22 6b dd 2b 57 34 38 16 b8 54 14 2a 7d 39 3e f7 d4 7f ae a5 fe f2 ..)9I....."k.+W48..T.*}9>.......
1740 9d 39 3e f7 d4 7f ae a5 fe f2 aa 52 92 71 6c a7 44 de e8 f4 5b ef af cc aa e9 be de 50 90 06 a5 .9>........R.ql.D...[.......P...
1760 d5 c0 0e c0 0d 41 37 b7 ff 00 2d 7d fe 1e bc fe 33 6a ff 00 fc 82 6f ef 6a de 95 9e 2e 94 e8 9b .....A7...-}....3j....o.j.......
1780 dd 1e 88 df 1d 9a b8 fe 1e bc fe 33 6a ff 00 fc 82 6f ef 6a 8c b9 d7 29 e1 21 fb fe a9 7c 27 cb ...........3j....o.j...).!...|'.
17a0 a9 7d 98 ac 7f 8b b5 e6 95 8e 2d 94 e8 9b dd 1e 88 df 1f 9a a7 d3 93 ef 7d 47 fa ea 5f ef 29 d3 .}........-.............}G.._.).
17c0 92 3f e6 fa 8f f5 d4 bf de 55 4a 51 c5 b2 9d 13 7b a3 d1 1b eb f3 2b cb 0c a6 3b 29 42 33 b5 23 .?.......UJQ....{.....+...;)B3.#
17e0 03 24 93 fa 49 ee 4f e5 35 ea 94 a5 a0 2d 12 94 a5 08 4a 52 94 21 29 4a 50 84 a5 29 42 17 ff d9 .$..I.O.5....-....JR.!)JP..)B...
href='#n1231'>1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
/**
 * Ambisonic reverb engine for the OpenAL cross platform audio library
 * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
 * This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the
 *  Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * Or go to http://www.gnu.org/copyleft/lgpl.html
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "alMain.h"
#include "alu.h"
#include "alAuxEffectSlot.h"
#include "alListener.h"
#include "alError.h"
#include "filters/defs.h"

/* This is a user config option for modifying the overall output of the reverb
 * effect.
 */
ALfloat ReverbBoost = 1.0f;

/* This is the maximum number of samples processed for each inner loop
 * iteration. */
#define MAX_UPDATE_SAMPLES  256

/* The number of samples used for cross-faded delay lines.  This can be used
 * to balance the compensation for abrupt line changes and attenuation due to
 * minimally lengthed recursive lines.  Try to keep this below the device
 * update size.
 */
#define FADE_SAMPLES  128

/* The number of spatialized lines or channels to process. Four channels allows
 * for a 3D A-Format response. NOTE: This can't be changed without taking care
 * of the conversion matrices, and a few places where the length arrays are
 * assumed to have 4 elements.
 */
#define NUM_LINES 4


/* The B-Format to A-Format conversion matrix. The arrangement of rows is
 * deliberately chosen to align the resulting lines to their spatial opposites
 * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
 * back left). It's not quite opposite, since the A-Format results in a
 * tetrahedron, but it's close enough. Should the model be extended to 8-lines
 * in the future, true opposites can be used.
 */
static const aluMatrixf B2A = {{
    { 0.288675134595f,  0.288675134595f,  0.288675134595f,  0.288675134595f },
    { 0.288675134595f, -0.288675134595f, -0.288675134595f,  0.288675134595f },
    { 0.288675134595f,  0.288675134595f, -0.288675134595f, -0.288675134595f },
    { 0.288675134595f, -0.288675134595f,  0.288675134595f, -0.288675134595f }
}};

/* Converts A-Format to B-Format. */
static const aluMatrixf A2B = {{
    { 0.866025403785f,  0.866025403785f,  0.866025403785f,  0.866025403785f },
    { 0.866025403785f, -0.866025403785f,  0.866025403785f, -0.866025403785f },
    { 0.866025403785f, -0.866025403785f, -0.866025403785f,  0.866025403785f },
    { 0.866025403785f,  0.866025403785f, -0.866025403785f, -0.866025403785f }
}};

static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;

/* The all-pass and delay lines have a variable length dependent on the
 * effect's density parameter, which helps alter the perceived environment
 * size. The size-to-density conversion is a cubed scale:
 *
 * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
 *
 * The line lengths scale linearly with room size, so the inverse density
 * conversion is needed, taking the cube root of the re-scaled density to
 * calculate the line length multiplier:
 *
 *     length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
 *
 * The density scale below will result in a max line multiplier of 50, for an
 * effective size range of 5m to 50m.
 */
static const ALfloat DENSITY_SCALE = 125000.0f;

/* All delay line lengths are specified in seconds.
 *
 * To approximate early reflections, we break them up into primary (those
 * arriving from the same direction as the source) and secondary (those
 * arriving from the opposite direction).
 *
 * The early taps decorrelate the 4-channel signal to approximate an average
 * room response for the primary reflections after the initial early delay.
 *
 * Given an average room dimension (d_a) and the speed of sound (c) we can
 * calculate the average reflection delay (r_a) regardless of listener and
 * source positions as:
 *
 *     r_a = d_a / c
 *     c   = 343.3
 *
 * This can extended to finding the average difference (r_d) between the
 * maximum (r_1) and minimum (r_0) reflection delays:
 *
 *     r_0 = 2 / 3 r_a
 *         = r_a - r_d / 2
 *         = r_d
 *     r_1 = 4 / 3 r_a
 *         = r_a + r_d / 2
 *         = 2 r_d
 *     r_d = 2 / 3 r_a
 *         = r_1 - r_0
 *
 * As can be determined by integrating the 1D model with a source (s) and
 * listener (l) positioned across the dimension of length (d_a):
 *
 *     r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
 *
 * The initial taps (T_(i=0)^N) are then specified by taking a power series
 * that ranges between r_0 and half of r_1 less r_0:
 *
 *     R_i = 2^(i / (2 N - 1)) r_d
 *         = r_0 + (2^(i / (2 N - 1)) - 1) r_d
 *         = r_0 + T_i
 *     T_i = R_i - r_0
 *         = (2^(i / (2 N - 1)) - 1) r_d
 *
 * Assuming an average of 1m, we get the following taps:
 */
static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
{
    0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
};

/* The early all-pass filter lengths are based on the early tap lengths:
 *
 *     A_i = R_i / a
 *
 * Where a is the approximate maximum all-pass cycle limit (20).
 */
static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
{
    9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
};

/* The early delay lines are used to transform the primary reflections into
 * the secondary reflections.  The A-format is arranged in such a way that
 * the channels/lines are spatially opposite:
 *
 *     C_i is opposite C_(N-i-1)
 *
 * The delays of the two opposing reflections (R_i and O_i) from a source
 * anywhere along a particular dimension always sum to twice its full delay:
 *
 *     2 r_a = R_i + O_i
 *
 * With that in mind we can determine the delay between the two reflections
 * and thus specify our early line lengths (L_(i=0)^N) using:
 *
 *     O_i = 2 r_a - R_(N-i-1)
 *     L_i = O_i - R_(N-i-1)
 *         = 2 (r_a - R_(N-i-1))
 *         = 2 (r_a - T_(N-i-1) - r_0)
 *         = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
 *
 * Using an average dimension of 1m, we get:
 */
static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
{
    5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
};

/* The late all-pass filter lengths are based on the late line lengths:
 *
 *     A_i = (5 / 3) L_i / r_1
 */
static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
{
    1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
};

/* The late lines are used to approximate the decaying cycle of recursive
 * late reflections.
 *
 * Splitting the lines in half, we start with the shortest reflection paths
 * (L_(i=0)^(N/2)):
 *
 *     L_i = 2^(i / (N - 1)) r_d
 *
 * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
 *
 *     L_i = 2 r_a - L_(i-N/2)
 *         = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
 *
 * For our 1m average room, we get:
 */
static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
{
    1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
};


typedef struct DelayLineI {
    /* The delay lines use interleaved samples, with the lengths being powers
     * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
     */
    ALsizei  Mask;
    ALfloat (*Line)[NUM_LINES];
} DelayLineI;

typedef struct VecAllpass {
    DelayLineI Delay;
    ALsizei Offset[NUM_LINES][2];
} VecAllpass;

typedef struct T60Filter {
    /* Two filters are used to adjust the signal. One to control the low
     * frequencies, and one to control the high frequencies. The HF filter also
     * adjusts the overall output gain, affecting the remaining mid-band.
     */
    ALfloat HFCoeffs[3];
    ALfloat LFCoeffs[3];

    /* The HF and LF filters each keep a state of the last input and last
     * output sample.
     */
    ALfloat HFState[2];
    ALfloat LFState[2];
} T60Filter;

typedef struct EarlyReflections {
    /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
     * The spread from this filter also helps smooth out the reverb tail.
     */
    VecAllpass VecAp;

    /* An echo line is used to complete the second half of the early
     * reflections.
     */
    DelayLineI Delay;
    ALsizei    Offset[NUM_LINES][2];
    ALfloat    Coeff[NUM_LINES];

    /* The gain for each output channel based on 3D panning. */
    ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
    ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
} EarlyReflections;

typedef struct LateReverb {
    /* Attenuation to compensate for the modal density and decay rate of the
     * late lines.
     */
    ALfloat DensityGain;

    /* A recursive delay line is used fill in the reverb tail. */
    DelayLineI Delay;
    ALsizei    Offset[NUM_LINES][2];

    /* T60 decay filters are used to simulate absorption. */
    T60Filter T60[NUM_LINES];

    /* A Gerzon vector all-pass filter is used to simulate diffusion. */
    VecAllpass VecAp;

    /* The gain for each output channel based on 3D panning. */
    ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
    ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
} LateReverb;

typedef struct ALreverbState {
    DERIVE_FROM_TYPE(ALeffectState);

    /* All delay lines are allocated as a single buffer to reduce memory
     * fragmentation and management code.
     */
    ALfloat *SampleBuffer;
    ALuint   TotalSamples;

    /* Master effect filters */
    struct {
        ALfilterState Lp;
        ALfilterState Hp;
    } Filter[NUM_LINES];

    /* Core delay line (early reflections and late reverb tap from this). */
    DelayLineI Delay;

    /* Tap points for early reflection delay. */
    ALsizei EarlyDelayTap[NUM_LINES][2];
    ALfloat EarlyDelayCoeff[NUM_LINES];

    /* Tap points for late reverb feed and delay. */
    ALsizei LateFeedTap;
    ALsizei LateDelayTap[NUM_LINES][2];

    /* The feed-back and feed-forward all-pass coefficient. */
    ALfloat ApFeedCoeff;

    /* Coefficients for the all-pass and line scattering matrices. */
    ALfloat MixX;
    ALfloat MixY;

    EarlyReflections Early;

    LateReverb Late;

    /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
    ALsizei FadeCount;

    /* The current write offset for all delay lines. */
    ALsizei Offset;

    /* Temporary storage used when processing. */
    alignas(16) ALfloat AFormatSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
    alignas(16) ALfloat ReverbSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
    alignas(16) ALfloat EarlySamples[NUM_LINES][MAX_UPDATE_SAMPLES];
} ALreverbState;

static ALvoid ALreverbState_Destruct(ALreverbState *State);
static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALreverbState)

DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);

static void ALreverbState_Construct(ALreverbState *state)
{
    ALsizei i, j;

    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
    SET_VTABLE2(ALreverbState, ALeffectState, state);

    state->TotalSamples = 0;
    state->SampleBuffer = NULL;

    for(i = 0;i < NUM_LINES;i++)
    {
        ALfilterState_clear(&state->Filter[i].Lp);
        ALfilterState_clear(&state->Filter[i].Hp);
    }

    state->Delay.Mask = 0;
    state->Delay.Line = NULL;

    for(i = 0;i < NUM_LINES;i++)
    {
        state->EarlyDelayTap[i][0] = 0;
        state->EarlyDelayTap[i][1] = 0;
        state->EarlyDelayCoeff[i] = 0.0f;
    }

    state->LateFeedTap = 0;

    for(i = 0;i < NUM_LINES;i++)
    {
        state->LateDelayTap[i][0] = 0;
        state->LateDelayTap[i][1] = 0;
    }

    state->ApFeedCoeff = 0.0f;
    state->MixX = 0.0f;
    state->MixY = 0.0f;

    state->Early.VecAp.Delay.Mask = 0;
    state->Early.VecAp.Delay.Line = NULL;
    state->Early.Delay.Mask = 0;
    state->Early.Delay.Line = NULL;
    for(i = 0;i < NUM_LINES;i++)
    {
        state->Early.VecAp.Offset[i][0] = 0;
        state->Early.VecAp.Offset[i][1] = 0;
        state->Early.Offset[i][0] = 0;
        state->Early.Offset[i][1] = 0;
        state->Early.Coeff[i] = 0.0f;
    }

    state->Late.DensityGain = 0.0f;

    state->Late.Delay.Mask = 0;
    state->Late.Delay.Line = NULL;
    state->Late.VecAp.Delay.Mask = 0;
    state->Late.VecAp.Delay.Line = NULL;
    for(i = 0;i < NUM_LINES;i++)
    {
        state->Late.Offset[i][0] = 0;
        state->Late.Offset[i][1] = 0;

        state->Late.VecAp.Offset[i][0] = 0;
        state->Late.VecAp.Offset[i][1] = 0;

        for(j = 0;j < 3;j++)
        {
            state->Late.T60[i].HFCoeffs[j] = 0.0f;
            state->Late.T60[i].LFCoeffs[j] = 0.0f;
        }
        state->Late.T60[i].HFState[0] = 0.0f;
        state->Late.T60[i].HFState[1] = 0.0f;
        state->Late.T60[i].LFState[0] = 0.0f;
        state->Late.T60[i].LFState[1] = 0.0f;
    }

    for(i = 0;i < NUM_LINES;i++)
    {
        for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
        {
            state->Early.CurrentGain[i][j] = 0.0f;
            state->Early.PanGain[i][j] = 0.0f;
            state->Late.CurrentGain[i][j] = 0.0f;
            state->Late.PanGain[i][j] = 0.0f;
        }
    }

    state->FadeCount = 0;
    state->Offset = 0;
}

static ALvoid ALreverbState_Destruct(ALreverbState *State)
{
    al_free(State->SampleBuffer);
    State->SampleBuffer = NULL;

    ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
}

/**************************************
 *  Device Update                     *
 **************************************/

static inline ALfloat CalcDelayLengthMult(ALfloat density)
{
    return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
}

/* Given the allocated sample buffer, this function updates each delay line
 * offset.
 */
static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
{
    union {
        ALfloat *f;
        ALfloat (*f4)[NUM_LINES];
    } u;
    u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
    Delay->Line = u.f4;
}

/* Calculate the length of a delay line and store its mask and offset. */
static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
                             const ALuint extra, DelayLineI *Delay)
{
    ALuint samples;

    /* All line lengths are powers of 2, calculated from their lengths in
     * seconds, rounded up.
     */
    samples = fastf2i(ceilf(length*frequency));
    samples = NextPowerOf2(samples + extra);

    /* All lines share a single sample buffer. */
    Delay->Mask = samples - 1;
    Delay->Line = (ALfloat(*)[NUM_LINES])offset;

    /* Return the sample count for accumulation. */
    return samples;
}

/* Calculates the delay line metrics and allocates the shared sample buffer
 * for all lines given the sample rate (frequency).  If an allocation failure
 * occurs, it returns AL_FALSE.
 */
static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
{
    ALuint totalSamples, i;
    ALfloat multiplier, length;

    /* All delay line lengths are calculated to accomodate the full range of
     * lengths given their respective paramters.
     */
    totalSamples = 0;

    /* Multiplier for the maximum density value, i.e. density=1, which is
     * actually the least density...
     */
    multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);

    /* The main delay length includes the maximum early reflection delay, the
     * largest early tap width, the maximum late reverb delay, and the
     * largest late tap width.  Finally, it must also be extended by the
     * update size (MAX_UPDATE_SAMPLES) for block processing.
     */
    length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
             AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
             (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
    totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
                                   &State->Delay);

    /* The early vector all-pass line. */
    length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
    totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
                                   &State->Early.VecAp.Delay);

    /* The early reflection line. */
    length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
    totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
                                   &State->Early.Delay);

    /* The late vector all-pass line. */
    length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
    totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
                                   &State->Late.VecAp.Delay);

    /* The late delay lines are calculated from the larger of the maximum
     * density line length or the maximum echo time.
     */
    length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[NUM_LINES-1]*multiplier);
    totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
                                   &State->Late.Delay);

    if(totalSamples != State->TotalSamples)
    {
        ALfloat *newBuffer;

        TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
        newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
        if(!newBuffer) return AL_FALSE;

        al_free(State->SampleBuffer);
        State->SampleBuffer = newBuffer;
        State->TotalSamples = totalSamples;
    }

    /* Update all delays to reflect the new sample buffer. */
    RealizeLineOffset(State->SampleBuffer, &State->Delay);
    RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
    RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
    RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
    RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);

    /* Clear the sample buffer. */
    for(i = 0;i < State->TotalSamples;i++)
        State->SampleBuffer[i] = 0.0f;

    return AL_TRUE;
}

static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
{
    ALuint frequency = Device->Frequency;
    ALfloat multiplier;

    /* Allocate the delay lines. */
    if(!AllocLines(frequency, State))
        return AL_FALSE;

    multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);

    /* The late feed taps are set a fixed position past the latest delay tap. */
    State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
                                  EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
                                 frequency);

    return AL_TRUE;
}

/**************************************
 *  Effect Update                     *
 **************************************/

/* Calculate a decay coefficient given the length of each cycle and the time
 * until the decay reaches -60 dB.
 */
static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
{
    return powf(REVERB_DECAY_GAIN, length/decayTime);
}

/* Calculate a decay length from a coefficient and the time until the decay
 * reaches -60 dB.
 */
static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
{
    return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
}

/* Calculate an attenuation to be applied to the input of any echo models to
 * compensate for modal density and decay time.
 */
static inline ALfloat CalcDensityGain(const ALfloat a)
{
    /* The energy of a signal can be obtained by finding the area under the
     * squared signal.  This takes the form of Sum(x_n^2), where x is the
     * amplitude for the sample n.
     *
     * Decaying feedback matches exponential decay of the form Sum(a^n),
     * where a is the attenuation coefficient, and n is the sample.  The area
     * under this decay curve can be calculated as:  1 / (1 - a).
     *
     * Modifying the above equation to find the area under the squared curve
     * (for energy) yields:  1 / (1 - a^2).  Input attenuation can then be
     * calculated by inverting the square root of this approximation,
     * yielding:  1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
     */
    return sqrtf(1.0f - a*a);
}

/* Calculate the scattering matrix coefficients given a diffusion factor. */
static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
{
    ALfloat n, t;

    /* The matrix is of order 4, so n is sqrt(4 - 1). */
    n = sqrtf(3.0f);
    t = diffusion * atanf(n);

    /* Calculate the first mixing matrix coefficient. */
    *x = cosf(t);
    /* Calculate the second mixing matrix coefficient. */
    *y = sinf(t) / n;
}

/* Calculate the limited HF ratio for use with the late reverb low-pass
 * filters.
 */
static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
                                  const ALfloat decayTime, const ALfloat SpeedOfSound)
{
    ALfloat limitRatio;

    /* Find the attenuation due to air absorption in dB (converting delay
     * time to meters using the speed of sound).  Then reversing the decay
     * equation, solve for HF ratio.  The delay length is cancelled out of
     * the equation, so it can be calculated once for all lines.
     */
    limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);

    /* Using the limit calculated above, apply the upper bound to the HF ratio.
     */
    return minf(limitRatio, hfRatio);
}

/* Calculates the first-order high-pass coefficients following the I3DL2
 * reference model.  This is the transfer function:
 *
 *                1 - z^-1
 *     H(z) = p ------------
 *               1 - p z^-1
 *
 * And this is the I3DL2 coefficient calculation given gain (g) and reference
 * angular frequency (w):
 *
 *                                    g
 *      p = ------------------------------------------------------
 *          g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2))
 *
 * The coefficient is applied to the partial differential filter equation as:
 *
 *     c_0 = p
 *     c_1 = -p
 *     c_2 = p
 *     y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
 *
 */
static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
{
    ALfloat g, g2, cw, p;

    if(gain >= 1.0f)
    {
        coeffs[0] = 1.0f;
        coeffs[1] = 0.0f;
        coeffs[2] = 0.0f;
        return;
    }

    g = maxf(0.001f, gain);
    g2 = g * g;
    cw = cosf(w);
    p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f)));

    coeffs[0] = p;
    coeffs[1] = -p;
    coeffs[2] = p;
}

/* Calculates the first-order low-pass coefficients following the I3DL2
 * reference model.  This is the transfer function:
 *
 *              (1 - a) z^0
 *     H(z) = ----------------
 *             1 z^0 - a z^-1
 *
 * And this is the I3DL2 coefficient calculation given gain (g) and reference
 * angular frequency (w):
 *
 *          1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2))
 *     a = ----------------------------------------------------------------
 *                                    1 - g^2
 *
 * The coefficient is applied to the partial differential filter equation as:
 *
 *     c_0 = 1 - a
 *     c_1 = 0
 *     c_2 = a
 *     y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
 *
 */
static inline void CalcLowpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
{
    ALfloat g, g2, cw, a;

    if(gain >= 1.0f)
    {
        coeffs[0] = 1.0f;
        coeffs[1] = 0.0f;
        coeffs[2] = 0.0f;
        return;
    }

    /* Be careful with gains < 0.001, as that causes the coefficient
     * to head towards 1, which will flatten the signal. */
    g = maxf(0.001f, gain);
    g2 = g * g;
    cw = cosf(w);
    a = (1.0f - g2*cw - sqrtf((2.0f*g2*(1.0f - cw)) - g2*g2*(1.0f - cw*cw))) /
        (1.0f - g2);

    coeffs[0] = 1.0f - a;
    coeffs[1] = 0.0f;
    coeffs[2] = a;
}

/* Calculates the first-order low-shelf coefficients.  The shelf filters are
 * used in place of low/high-pass filters to preserve the mid-band.  This is
 * the transfer function:
 *
 *             a_0 + a_1 z^-1
 *     H(z) = ----------------
 *              1 + b_1 z^-1
 *
 * And these are the coefficient calculations given cut gain (g) and a center
 * angular frequency (w):
 *
 *          sin(0.5 (pi - w) - 0.25 pi)
 *     p = -----------------------------
 *          sin(0.5 (pi - w) + 0.25 pi)
 *
 *          g + 1           g + 1
 *     a = ------- + sqrt((-------)^2 - 1)
 *          g - 1           g - 1
 *
 *            1 + g + (1 - g) a
 *     b_0 = -------------------
 *                    2
 *
 *            1 - g + (1 + g) a
 *     b_1 = -------------------
 *                    2
 *
 * The coefficients are applied to the partial differential filter equation
 * as:
 *
 *            b_0 + p b_1
 *     c_0 = -------------
 *              1 + p a
 *
 *            -(b_1 + p b_0)
 *     c_1 = ----------------
 *               1 + p a
 *
 *             p + a
 *     c_2 = ---------
 *            1 + p a
 *
 *     y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
 *
 */
static inline void CalcLowShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
{
    ALfloat g, rw, p, n;
    ALfloat alpha, beta0, beta1;

    if(gain >= 1.0f)
    {
        coeffs[0] = 1.0f;
        coeffs[1] = 0.0f;
        coeffs[2] = 0.0f;
        return;
    }

    g = maxf(0.001f, gain);
    rw = F_PI - w;
    p = sinf(0.5f*rw - 0.25f*F_PI) / sinf(0.5f*rw + 0.25f*F_PI);
    n = (g + 1.0f) / (g - 1.0f);
    alpha = n + sqrtf(n*n - 1.0f);
    beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
    beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;

    coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
    coeffs[1] = -(beta1 + p*beta0) / (1.0f + p*alpha);
    coeffs[2] = (p + alpha) / (1.0f + p*alpha);
}

/* Calculates the first-order high-shelf coefficients.  The shelf filters are
 * used in place of low/high-pass filters to preserve the mid-band.  This is
 * the transfer function:
 *
 *             a_0 + a_1 z^-1
 *     H(z) = ----------------
 *              1 + b_1 z^-1
 *
 * And these are the coefficient calculations given cut gain (g) and a center
 * angular frequency (w):
 *
 *          sin(0.5 w - 0.25 pi)
 *     p = ----------------------
 *          sin(0.5 w + 0.25 pi)
 *
 *          g + 1           g + 1
 *     a = ------- + sqrt((-------)^2 - 1)
 *          g - 1           g - 1
 *
 *            1 + g + (1 - g) a
 *     b_0 = -------------------
 *                    2
 *
 *            1 - g + (1 + g) a
 *     b_1 = -------------------
 *                    2
 *
 * The coefficients are applied to the partial differential filter equation
 * as:
 *
 *            b_0 + p b_1
 *     c_0 = -------------
 *              1 + p a
 *
 *            b_1 + p b_0
 *     c_1 = -------------
 *              1 + p a
 *
 *            -(p + a)
 *     c_2 = ----------
 *            1 + p a
 *
 *     y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1)
 *
 */
static inline void CalcHighShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3])
{
    ALfloat g, p, n;
    ALfloat alpha, beta0, beta1;

    if(gain >= 1.0f)
    {
        coeffs[0] = 1.0f;
        coeffs[1] = 0.0f;
        coeffs[2] = 0.0f;
        return;
    }

    g = maxf(0.001f, gain);
    p = sinf(0.5f*w - 0.25f*F_PI) / sinf(0.5f*w + 0.25f*F_PI);
    n = (g + 1.0f) / (g - 1.0f);
    alpha = n + sqrtf(n*n - 1.0f);
    beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f;
    beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f;

    coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha);
    coeffs[1] = (beta1 + p*beta0) / (1.0f + p*alpha);
    coeffs[2] = -(p + alpha) / (1.0f + p*alpha);
}

/* Calculates the 3-band T60 damping coefficients for a particular delay line
 * of specified length using a combination of two low/high-pass/shelf or
 * pass-through filter sections (producing 3 coefficients each) given decay
 * times for each band split at two (LF/HF) reference frequencies (w).
 */
static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
                                 const ALfloat mfDecayTime, const ALfloat hfDecayTime,
                                 const ALfloat lfW, const ALfloat hfW, ALfloat lfcoeffs[3],
                                 ALfloat hfcoeffs[3])
{
    ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
    ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
    ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);

    if(lfGain <= mfGain)
    {
        CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs);
        if(mfGain >= hfGain)
        {
            CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
            hfcoeffs[0] *= mfGain; hfcoeffs[1] *= mfGain;
        }
        else
        {
            CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs);
            hfcoeffs[0] *= hfGain; hfcoeffs[1] *= hfGain;
        }
    }
    else
    {
        CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs);
        if(mfGain >= hfGain)
        {
            CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs);
            hfcoeffs[0] *= lfGain; hfcoeffs[1] *= lfGain;
        }
        else
        {
            ALfloat hg = mfGain / lfGain;
            ALfloat lg = mfGain / hfGain;
            ALfloat mg = maxf(lfGain, hfGain) / maxf(hg, lg);

            CalcLowShelfCoeffs(lg, hfW, hfcoeffs);
            hfcoeffs[0] *= mg; hfcoeffs[1] *= mg;
        }
    }
}

/* Update the offsets for the main effect delay line. */
static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
{
    ALfloat multiplier, length;
    ALuint i;

    multiplier = CalcDelayLengthMult(density);

    /* Early reflection taps are decorrelated by means of an average room
     * reflection approximation described above the definition of the taps.
     * This approximation is linear and so the above density multiplier can
     * be applied to adjust the width of the taps.  A single-band decay
     * coefficient is applied to simulate initial attenuation and absorption.
     *
     * Late reverb taps are based on the late line lengths to allow a zero-
     * delay path and offsets that would continue the propagation naturally
     * into the late lines.
     */
    for(i = 0;i < NUM_LINES;i++)
    {
        length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
        State->EarlyDelayTap[i][1] = fastf2i(length * frequency);

        length = EARLY_TAP_LENGTHS[i]*multiplier;
        State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);

        length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
        State->LateDelayTap[i][1] = State->LateFeedTap + fastf2i(length * frequency);
    }
}

/* Update the early reflection line lengths and gain coefficients. */
static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
{
    ALfloat multiplier, length;
    ALsizei i;

    multiplier = CalcDelayLengthMult(density);

    for(i = 0;i < NUM_LINES;i++)
    {
        /* Calculate the length (in seconds) of each all-pass line. */
        length = EARLY_ALLPASS_LENGTHS[i] * multiplier;

        /* Calculate the delay offset for each all-pass line. */
        Early->VecAp.Offset[i][1] = fastf2i(length * frequency);

        /* Calculate the length (in seconds) of each delay line. */
        length = EARLY_LINE_LENGTHS[i] * multiplier;

        /* Calculate the delay offset for each delay line. */
        Early->Offset[i][1] = fastf2i(length * frequency);

        /* Calculate the gain (coefficient) for each line. */
        Early->Coeff[i] = CalcDecayCoeff(length, decayTime);
    }
}

/* Update the late reverb line lengths and T60 coefficients. */
static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lfW, const ALfloat hfW, const ALfloat echoTime, const ALfloat echoDepth, const ALuint frequency, LateReverb *Late)
{
    ALfloat multiplier, length, bandWeights[3];
    ALsizei i;

    /* To compensate for changes in modal density and decay time of the late
     * reverb signal, the input is attenuated based on the maximal energy of
     * the outgoing signal.  This approximation is used to keep the apparent
     * energy of the signal equal for all ranges of density and decay time.
     *
     * The average length of the delay lines is used to calculate the
     * attenuation coefficient.
     */
    multiplier = CalcDelayLengthMult(density);
    length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
              LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
    /* Include the echo transformation (see below). */
    length = lerp(length, echoTime, echoDepth);
    length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
               LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
    /* The density gain calculation uses an average decay time weighted by
     * approximate bandwidth.  This attempts to compensate for losses of
     * energy that reduce decay time due to scattering into highly attenuated
     * bands.
     */
    bandWeights[0] = lfW;
    bandWeights[1] = hfW - lfW;
    bandWeights[2] = F_TAU - hfW;
    Late->DensityGain = CalcDensityGain(
        CalcDecayCoeff(length, (bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime +
                                bandWeights[2]*hfDecayTime) / F_TAU)
    );

    for(i = 0;i < NUM_LINES;i++)
    {
        /* Calculate the length (in seconds) of each all-pass line. */
        length = LATE_ALLPASS_LENGTHS[i] * multiplier;

        /* Calculate the delay offset for each all-pass line. */
        Late->VecAp.Offset[i][1] = fastf2i(length * frequency);

        /* Calculate the length (in seconds) of each delay line.  This also
         * applies the echo transformation.  As the EAX echo depth approaches
         * 1, the line lengths approach a length equal to the echoTime.  This
         * helps to produce distinct echoes along the tail.
         */
        length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);

        /* Calculate the delay offset for each delay line. */
        Late->Offset[i][1] = fastf2i(length*frequency + 0.5f);

        /* Approximate the absorption that the vector all-pass would exhibit
         * given the current diffusion so we don't have to process a full T60
         * filter for each of its four lines.
         */
        length += lerp(LATE_ALLPASS_LENGTHS[i],
                       (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
                        LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
                       diffusion) * multiplier;

        /* Calculate the T60 damping coefficients for each line. */
        CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
                             lfW, hfW, Late->T60[i].LFCoeffs,
                             Late->T60[i].HFCoeffs);
    }
}

/* Creates a transform matrix given a reverb vector. The vector pans the reverb
 * reflections toward the given direction, using its magnitude (up to 1) as a
 * focal strength. This function results in a B-Format transformation matrix
 * that spatially focuses the signal in the desired direction.
 */
static aluMatrixf GetTransformFromVector(const ALfloat *vec)
{
    const ALfloat sqrt_3 = 1.732050808f;
    aluMatrixf focus;
    ALfloat norm[3];
    ALfloat mag;

    /* Normalize the panning vector according to the N3D scale, which has an
     * extra sqrt(3) term on the directional components. Converting from OpenAL
     * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
     * that the reverb panning vectors use right-handed coordinates, unlike the
     * rest of OpenAL which use left-handed. This is fixed by negating Z, which
     * cancels out with the B-Format Z negation.
     */
    mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
    if(mag > 1.0f)
    {
        norm[0] = vec[0] / mag * -sqrt_3;
        norm[1] = vec[1] / mag * sqrt_3;
        norm[2] = vec[2] / mag * sqrt_3;
        mag = 1.0f;
    }
    else
    {
        /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
         * term. There's no need to renormalize the magnitude since it would
         * just be reapplied in the matrix.
         */
        norm[0] = vec[0] * -sqrt_3;
        norm[1] = vec[1] * sqrt_3;
        norm[2] = vec[2] * sqrt_3;
    }

    aluMatrixfSet(&focus,
        1.0f,   0.0f,    0.0f,   0.0f,
        norm[0], 1.0f-mag, 0.0f, 0.0f,
        norm[1], 0.0f, 1.0f-mag, 0.0f,
        norm[2], 0.0f, 0.0f, 1.0f-mag
    );

    return focus;
}

/* Update the early and late 3D panning gains. */
static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat gain, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
{
    aluMatrixf transform, rot;
    ALsizei i;

    STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
    STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;

    /* Note: _res is transposed. */
#define MATRIX_MULT(_res, _m1, _m2) do {                                                   \
    int row, col;                                                                          \
    for(col = 0;col < 4;col++)                                                             \
    {                                                                                      \
        for(row = 0;row < 4;row++)                                                         \
            _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
                               _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col];  \
    }                                                                                      \
} while(0)
    /* Create a matrix that first converts A-Format to B-Format, then
     * transforms the B-Format signal according to the panning vector.
     */
    rot = GetTransformFromVector(ReflectionsPan);
    MATRIX_MULT(transform, rot, A2B);
    memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
        ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*earlyGain,
                               State->Early.PanGain[i]);

    rot = GetTransformFromVector(LateReverbPan);
    MATRIX_MULT(transform, rot, A2B);
    memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
        ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*lateGain,
                               State->Late.PanGain[i]);
#undef MATRIX_MULT
}

static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
{
    const ALCdevice *Device = Context->Device;
    const ALlistener *Listener = Context->Listener;
    ALuint frequency = Device->Frequency;
    ALfloat lf0norm, hf0norm, hfRatio;
    ALfloat lfDecayTime, hfDecayTime;
    ALfloat gain, gainlf, gainhf;
    ALsizei i;

    /* Calculate the master filters */
    hf0norm = props->Reverb.HFReference / frequency;
    /* Restrict the filter gains from going below -60dB to keep the filter from
     * killing most of the signal.
     */
    gainhf = maxf(props->Reverb.GainHF, 0.001f);
    ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf,
                            gainhf, hf0norm, calc_rcpQ_from_slope(gainhf, 1.0f));
    lf0norm = props->Reverb.LFReference / frequency;
    gainlf = maxf(props->Reverb.GainLF, 0.001f);
    ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf,
                            gainlf, lf0norm, calc_rcpQ_from_slope(gainlf, 1.0f));
    for(i = 1;i < NUM_LINES;i++)
    {
        ALfilterState_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
        ALfilterState_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
    }

    /* Update the main effect delay and associated taps. */
    UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
                    props->Reverb.Density, props->Reverb.DecayTime, frequency,
                    State);

    /* Calculate the all-pass feed-back/forward coefficient. */
    State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f);

    /* Update the early lines. */
    UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime,
                     frequency, &State->Early);

    /* Get the mixing matrix coefficients. */
    CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);

    /* If the HF limit parameter is flagged, calculate an appropriate limit
     * based on the air absorption parameter.
     */
    hfRatio = props->Reverb.DecayHFRatio;
    if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
        hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
            props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
        );

    /* Calculate the LF/HF decay times. */
    lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
                         AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
    hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
                         AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);

    /* Update the late lines. */
    UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
                    lfDecayTime, props->Reverb.DecayTime, hfDecayTime,
                    F_TAU * lf0norm, F_TAU * hf0norm,
                    props->Reverb.EchoTime, props->Reverb.EchoDepth,
                    frequency, &State->Late);

    /* Update early and late 3D panning. */
    gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
    Update3DPanning(Device, props->Reverb.ReflectionsPan,
                    props->Reverb.LateReverbPan, gain,
                    props->Reverb.ReflectionsGain,
                    props->Reverb.LateReverbGain, State);

    /* Determine if delay-line cross-fading is required. */
    for(i = 0;i < NUM_LINES;i++)
    {
        if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
           State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
           State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
           State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
           State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
           State->Late.Offset[i][1] != State->Late.Offset[i][0])
        {
            State->FadeCount = 0;
            break;
        }
    }
}


/**************************************
 *  Effect Processing                 *
 **************************************/

/* Basic delay line input/output routines. */
static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
{
    return Delay->Line[offset&Delay->Mask][c];
}

/* Cross-faded delay line output routine.  Instead of interpolating the
 * offsets, this interpolates (cross-fades) the outputs at each offset.
 */
static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
                                        const ALsizei off1, const ALsizei c, const ALfloat mu)
{
    return Delay->Line[off0&Delay->Mask][c]*(1.0f-mu) +
           Delay->Line[off1&Delay->Mask][c]*(     mu);
}
#define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c)

static inline ALvoid DelayLineIn(DelayLineI *Delay, ALsizei offset, const ALsizei c,
                                 const ALfloat *restrict in, ALsizei count)
{
    ALsizei i;
    for(i = 0;i < count;i++)
        Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
}

static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
{
    ALsizei i;
    offset &= Delay->Mask;
    for(i = 0;i < NUM_LINES;i++)
        Delay->Line[offset][i] = in[i];
}

static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
{
    ALsizei i;
    offset &= Delay->Mask;
    for(i = 0;i < NUM_LINES;i++)
        Delay->Line[offset][i] = in[NUM_LINES-1-i];
}

/* Applies a scattering matrix to the 4-line (vector) input.  This is used
 * for both the below vector all-pass model and to perform modal feed-back
 * delay network (FDN) mixing.
 *
 * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
 * matrix with a single unitary rotational parameter:
 *
 *     [  d,  a,  b,  c ]          1 = a^2 + b^2 + c^2 + d^2
 *     [ -a,  d,  c, -b ]
 *     [ -b, -c,  d,  a ]
 *     [ -c,  b, -a,  d ]
 *
 * The rotation is constructed from the effect's diffusion parameter,
 * yielding:
 *
 *     1 = x^2 + 3 y^2
 *
 * Where a, b, and c are the coefficient y with differing signs, and d is the
 * coefficient x.  The final matrix is thus:
 *
 *     [  x,  y, -y,  y ]          n = sqrt(matrix_order - 1)
 *     [ -y,  x,  y,  y ]          t = diffusion_parameter * atan(n)
 *     [  y, -y,  x,  y ]          x = cos(t)
 *     [ -y, -y, -y,  x ]          y = sin(t) / n
 *
 * Any square orthogonal matrix with an order that is a power of two will
 * work (where ^T is transpose, ^-1 is inverse):
 *
 *     M^T = M^-1
 *
 * Using that knowledge, finding an appropriate matrix can be accomplished
 * naively by searching all combinations of:
 *
 *     M = D + S - S^T
 *
 * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
 * whose combination of signs are being iterated.
 */
static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
                                        const ALfloat xCoeff, const ALfloat yCoeff)
{
    out[0] = xCoeff*in[0] + yCoeff*(          in[1] + -in[2] + in[3]);
    out[1] = xCoeff*in[1] + yCoeff*(-in[0]          +  in[2] + in[3]);
    out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1]          + in[3]);
    out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2]        );
}

/* Same as above, but reverses the input. */
static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in,
                                           const ALfloat xCoeff, const ALfloat yCoeff)
{
    out[0] = xCoeff*in[3] + yCoeff*(in[0] + -in[1] +  in[2]         );
    out[1] = xCoeff*in[2] + yCoeff*(in[0] +  in[1]          + -in[3]);
    out[2] = xCoeff*in[1] + yCoeff*(in[0]          + -in[2] +  in[3]);
    out[3] = xCoeff*in[0] + yCoeff*(        -in[1] + -in[2] + -in[3]);
}

/* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
 * filter to the 4-line input.
 *
 * It works by vectorizing a regular all-pass filter and replacing the delay
 * element with a scattering matrix (like the one above) and a diagonal
 * matrix of delay elements.
 *
 * Two static specializations are used for transitional (cross-faded) delay
 * line processing and non-transitional processing.
 */
#define DECL_TEMPLATE(T)                                                      \
static void VectorAllpass_##T(ALfloat *restrict out,                          \
                              const ALfloat *restrict in,                     \
                              const ALsizei offset, const ALfloat feedCoeff,  \
                              const ALfloat xCoeff, const ALfloat yCoeff,     \
                              const ALfloat mu, VecAllpass *Vap)              \
{                                                                             \
    ALfloat f[NUM_LINES], fs[NUM_LINES];                                      \
    ALfloat input;                                                            \
    ALsizei i;                                                                \
                                                                              \
    (void)mu; /* Ignore for Unfaded. */                                       \
                                                                              \
    for(i = 0;i < NUM_LINES;i++)                                              \
    {                                                                         \
        input = in[i];                                                        \
        out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0],       \
                                 offset-Vap->Offset[i][1], i, mu) -           \
                 feedCoeff*input;                                             \
        f[i] = input + feedCoeff*out[i];                                      \
    }                                                                         \
    VectorPartialScatter(fs, f, xCoeff, yCoeff);                              \
                                                                              \
    DelayLineIn4(&Vap->Delay, offset, fs);                                    \
}
DECL_TEMPLATE(Unfaded)
DECL_TEMPLATE(Faded)
#undef DECL_TEMPLATE

/* This generates early reflections.
 *
 * This is done by obtaining the primary reflections (those arriving from the
 * same direction as the source) from the main delay line.  These are
 * attenuated and all-pass filtered (based on the diffusion parameter).
 *
 * The early lines are then fed in reverse (according to the approximately
 * opposite spatial location of the A-Format lines) to create the secondary
 * reflections (those arriving from the opposite direction as the source).
 *
 * The early response is then completed by combining the primary reflections
 * with the delayed and attenuated output from the early lines.
 *
 * Finally, the early response is reversed, scattered (based on diffusion),
 * and fed into the late reverb section of the main delay line.
 *
 * Two static specializations are used for transitional (cross-faded) delay
 * line processing and non-transitional processing.
 */
#define DECL_TEMPLATE(T)                                                      \
static void EarlyReflection_##T(ALreverbState *State, const ALsizei todo,     \
                                ALfloat fade,                                 \
                                ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])  \
{                                                                             \
    ALsizei offset = State->Offset;                                           \
    const ALfloat apFeedCoeff = State->ApFeedCoeff;                           \
    const ALfloat mixX = State->MixX;                                         \
    const ALfloat mixY = State->MixY;                                         \
    ALfloat f[NUM_LINES], fr[NUM_LINES];                                      \
    ALsizei i, j;                                                             \
                                                                              \
    for(i = 0;i < todo;i++)                                                   \
    {                                                                         \
        for(j = 0;j < NUM_LINES;j++)                                          \
            fr[j] = T##DelayLineOut(&State->Delay,                            \
                offset-State->EarlyDelayTap[j][0],                            \
                offset-State->EarlyDelayTap[j][1], j, fade                    \
            ) * State->EarlyDelayCoeff[j];                                    \
                                                                              \
        VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade,       \
                          &State->Early.VecAp);                               \
                                                                              \
        DelayLineIn4Rev(&State->Early.Delay, offset, f);                      \
                                                                              \
        for(j = 0;j < NUM_LINES;j++)                                          \
            f[j] += T##DelayLineOut(&State->Early.Delay,                      \
                offset-State->Early.Offset[j][0],                             \
                offset-State->Early.Offset[j][1], j, fade                     \
            ) * State->Early.Coeff[j];                                        \
                                                                              \
        for(j = 0;j < NUM_LINES;j++)                                          \
            out[j][i] = f[j];                                                 \
                                                                              \
        VectorPartialScatterRev(fr, f, mixX, mixY);                           \
                                                                              \
        DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr);           \
                                                                              \
        offset++;                                                             \
        fade += FadeStep;                                                     \
    }                                                                         \
}
DECL_TEMPLATE(Unfaded)
DECL_TEMPLATE(Faded)
#undef DECL_TEMPLATE

/* Applies a first order filter section. */
static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat *restrict coeffs,
                                       ALfloat *restrict state)
{
    ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1];
    state[0] = in;
    state[1] = out;
    return out;
}

/* Applies the two T60 damping filter sections. */
static inline void LateT60Filter(ALfloat *restrict out, const ALfloat *restrict in,
                                 T60Filter *filter)
{
    ALsizei i;
    for(i = 0;i < NUM_LINES;i++)
        out[i] = FirstOrderFilter(
            FirstOrderFilter(in[i], filter[i].HFCoeffs, filter[i].HFState),
            filter[i].LFCoeffs, filter[i].LFState
        );
}

/* This generates the reverb tail using a modified feed-back delay network
 * (FDN).
 *
 * Results from the early reflections are attenuated by the density gain and
 * mixed with the output from the late delay lines.
 *
 * The late response is then completed by T60 and all-pass filtering the mix.
 *
 * Finally, the lines are reversed (so they feed their opposite directions)
 * and scattered with the FDN matrix before re-feeding the delay lines.
 *
 * Two variations are made, one for for transitional (cross-faded) delay line
 * processing and one for non-transitional processing.
 */
#define DECL_TEMPLATE(T)                                                      \
static void LateReverb_##T(ALreverbState *State, const ALsizei todo,          \
                           ALfloat fade,                                      \
                           ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])       \
{                                                                             \
    const ALfloat apFeedCoeff = State->ApFeedCoeff;                           \
    const ALfloat mixX = State->MixX;                                         \
    const ALfloat mixY = State->MixY;                                         \
    ALsizei offset;                                                           \
    ALsizei i, j;                                                             \
                                                                              \
    offset = State->Offset;                                                   \
    for(i = 0;i < todo;i++)                                                   \
    {                                                                         \
        ALfloat f[NUM_LINES], fr[NUM_LINES];                                  \
                                                                              \
        for(j = 0;j < NUM_LINES;j++)                                          \
            f[j] = T##DelayLineOut(&State->Delay,                             \
                offset - State->LateDelayTap[j][0],                           \
                offset - State->LateDelayTap[j][1], j, fade                   \
            ) * State->Late.DensityGain;                                      \
                                                                              \
        for(j = 0;j < NUM_LINES;j++)                                          \
            f[j] += T##DelayLineOut(&State->Late.Delay,                       \
                offset - State->Late.Offset[j][0],                            \
                offset - State->Late.Offset[j][1], j, fade                    \
            );                                                                \
                                                                              \
        LateT60Filter(fr, f, State->Late.T60);                                \
        VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade,       \
                          &State->Late.VecAp);                                \
                                                                              \
        for(j = 0;j < NUM_LINES;j++)                                          \
            out[j][i] = f[j];                                                 \
                                                                              \
        VectorPartialScatterRev(fr, f, mixX, mixY);                           \
                                                                              \
        DelayLineIn4(&State->Late.Delay, offset, fr);                         \
                                                                              \
        offset++;                                                             \
        fade += FadeStep;                                                     \
    }                                                                         \
}
DECL_TEMPLATE(Unfaded)
DECL_TEMPLATE(Faded)
#undef DECL_TEMPLATE

static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
    ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
    ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
    ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
    ALsizei fadeCount = State->FadeCount;
    ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
    ALsizei base, c;

    /* Process reverb for these samples. */
    for(base = 0;base < SamplesToDo;)
    {
        ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES);
        /* If cross-fading, don't do more samples than there are to fade. */
        if(FADE_SAMPLES-fadeCount > 0)
            todo = mini(todo, FADE_SAMPLES-fadeCount);

        /* Convert B-Format to A-Format for processing. */
        memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
        for(c = 0;c < NUM_LINES;c++)
            MixRowSamples(afmt[c], B2A.m[c],
                SamplesIn, MAX_EFFECT_CHANNELS, base, todo
            );

        /* Process the samples for reverb. */
        for(c = 0;c < NUM_LINES;c++)
        {
            /* Band-pass the incoming samples. Use the early output lines for
             * temp storage.
             */
            ALfilterState_process(&State->Filter[c].Lp, early[0], afmt[c], todo);
            ALfilterState_process(&State->Filter[c].Hp, early[1], early[0], todo);

            /* Feed the initial delay line. */
            DelayLineIn(&State->Delay, State->Offset, c, early[1], todo);
        }

        if(UNLIKELY(fadeCount < FADE_SAMPLES))
        {
            /* Generate early reflections. */
            EarlyReflection_Faded(State, todo, fade, early);

            /* Generate late reverb. */
            LateReverb_Faded(State, todo, fade, late);
            fade = minf(1.0f, fade + todo*FadeStep);
        }
        else
        {
            /* Generate early reflections. */
            EarlyReflection_Unfaded(State, todo, fade, early);

            /* Generate late reverb. */
            LateReverb_Unfaded(State, todo, fade, late);
        }

        /* Step all delays forward. */
        State->Offset += todo;

        if(UNLIKELY(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES)
        {
            /* Update the cross-fading delay line taps. */
            fadeCount = FADE_SAMPLES;
            fade = 1.0f;
            for(c = 0;c < NUM_LINES;c++)
            {
                State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
                State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
                State->Early.Offset[c][0] = State->Early.Offset[c][1];
                State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
                State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
                State->Late.Offset[c][0] = State->Late.Offset[c][1];
            }
        }

        /* Mix the A-Format results to output, implicitly converting back to
         * B-Format.
         */
        for(c = 0;c < NUM_LINES;c++)
            MixSamples(early[c], NumChannels, SamplesOut,
                State->Early.CurrentGain[c], State->Early.PanGain[c],
                SamplesToDo-base, base, todo
            );
        for(c = 0;c < NUM_LINES;c++)
            MixSamples(late[c], NumChannels, SamplesOut,
                State->Late.CurrentGain[c], State->Late.PanGain[c],
                SamplesToDo-base, base, todo
            );

        base += todo;
    }
    State->FadeCount = fadeCount;
}


typedef struct ReverbStateFactory {
    DERIVE_FROM_TYPE(EffectStateFactory);
} ReverbStateFactory;

static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
{
    ALreverbState *state;

    NEW_OBJ0(state, ALreverbState)();
    if(!state) return NULL;

    return STATIC_CAST(ALeffectState, state);
}

DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);

EffectStateFactory *ReverbStateFactory_getFactory(void)
{
    static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };

    return STATIC_CAST(EffectStateFactory, &ReverbFactory);
}


void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
    ALeffectProps *props = &effect->Props;
    switch(param)
    {
        case AL_EAXREVERB_DECAY_HFLIMIT:
            if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
                SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
            props->Reverb.DecayHFLimit = val;
            break;