code wiki / _hdl_build / nx_jitterbuf_gate.nx

nx_jitterbuf_gate.nx source

↩ module page · 80 lines · 4386 B

1// nx_jitterbuf_gate.nx -- proves + MEASURES the sovereign adaptive playout-delay (nx_jitterbuf): 2// the interp buffer sizes itself to MEASURED jitter so a bad-mobile link stays smooth without 3// needless latency on a good link. 4// 1) LOW jitter (regular arrivals) -> jitter ~0, playout near min (no needless latency) -- measured 5// 2) HIGH jitter (irregular arrivals) -> jitter rises, playout GROWS to cover it -- measured 6// 3) GROW FAST / SHRINK SLOW: a spike lifts playout immediately, then it decays gradually (not instantly) 7// 4) clamp (neg): playout always within [min,max] 8// Built nx_cc_sovereign -> nxasm_x86 (no gcc, no .sh). license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_gate_emit_lib.nx" 11import "nx_jitterbuf.nx" 12 13func main() -> i64 { 14 g_puts("nx_jitterbuf gate (sovereign adaptive playout delay sized to measured jitter)\n" as *u8) 15 var pass: i64 = 0; var total: i64 = 0 16 17 let SI: i64 = 100; let MIN: i64 = 100; let MAX: i64 = 1000 18 let lo: *i64 = sys_mmap(8*8) as *i64 19 let hi: *i64 = sys_mmap(8*8) as *i64 20 21 // 1) LOW jitter: perfectly regular arrivals every 100ms 22 jb_init(lo, SI, MIN, MAX) 23 var t: i64 = 1000 24 var clamp_ok: i64 = 1 25 var n: i64 = 0 26 while n < 12 { 27 let pd: i64 = jb_on_recv(lo, t) 28 if pd < MIN { clamp_ok = 0 } 29 if pd > MAX { clamp_ok = 0 } 30 t = t + 100 31 n = n + 1 32 } 33 let lo_jit: i64 = jb_jitter(lo); let lo_pd: i64 = jb_playout(lo) 34 g_puts(" [measure] LOW jitter: jitter=" as *u8); g_pn(lo_jit); g_puts("ms playout=" as *u8); g_pn(lo_pd); g_puts("ms (near min " as *u8); g_pn(MIN); g_puts(")\n" as *u8) 35 pass = pass + g_check("low jitter -> playout near min (no needless latency)" as *u8, lo_pd <= MIN + 20); total=total+1 36 37 // 2) HIGH jitter: irregular arrivals (alternating early/late around the 100ms cadence) 38 jb_init(hi, SI, MIN, MAX) 39 let arr: *i64 = sys_mmap(16*8) as *i64 40 arr[0]=1000; arr[1]=1180; arr[2]=1200; arr[3]=1420; arr[4]=1440; arr[5]=1700; arr[6]=1720 41 arr[7]=2050; arr[8]=2070; arr[9]=2400; arr[10]=2420; arr[11]=2800 42 n = 0 43 while n < 12 { 44 let pd: i64 = jb_on_recv(hi, arr[n]) 45 if pd < MIN { clamp_ok = 0 } 46 if pd > MAX { clamp_ok = 0 } 47 n = n + 1 48 } 49 let hi_jit: i64 = jb_jitter(hi); let hi_pd: i64 = jb_playout(hi) 50 g_puts(" [measure] HIGH jitter: jitter=" as *u8); g_pn(hi_jit); g_puts("ms playout=" as *u8); g_pn(hi_pd); g_puts("ms\n" as *u8) 51 var r2: i64 = 1 52 if hi_jit <= lo_jit { r2 = 0 } 53 if hi_pd <= lo_pd { r2 = 0 } // sized UP to cover the jitter 54 pass = pass + g_check("high jitter -> larger jitter estimate AND larger playout than low" as *u8, r2); total=total+1 55 56 // 3) GROW FAST / SHRINK SLOW 57 let sp: *i64 = sys_mmap(8*8) as *i64 58 jb_init(sp, SI, MIN, MAX) 59 jb_on_recv(sp, 1000); jb_on_recv(sp, 1100); jb_on_recv(sp, 1200) // steady -> playout ~min 60 let before: i64 = jb_playout(sp) 61 let after_spike: i64 = jb_on_recv(sp, 1500) // a 300ms-late arrival (gap 300 vs 100) 62 // then steady again; watch it decay GRADUALLY 63 jb_on_recv(sp, 1600); jb_on_recv(sp, 1700); jb_on_recv(sp, 1800) 64 let after_3: i64 = jb_playout(sp) 65 jb_on_recv(sp, 1900); jb_on_recv(sp, 2000); jb_on_recv(sp, 2100); jb_on_recv(sp, 2200); jb_on_recv(sp, 2300) 66 let after_8: i64 = jb_playout(sp) 67 g_puts(" [measure] spike: before=" as *u8); g_pn(before); g_puts(" after-spike=" as *u8); g_pn(after_spike); g_puts(" +3=" as *u8); g_pn(after_3); g_puts(" +8=" as *u8); g_pn(after_8); g_puts("\n" as *u8) 68 var r3: i64 = 1 69 if after_spike <= before { r3 = 0 } // grew immediately on the spike 70 if after_3 <= before { r3 = 0 } // still elevated a few samples later (slow shrink) 71 if after_8 >= after_3 { r3 = 0 } // and trending back down 72 pass = pass + g_check("grow-fast on spike, shrink-slow as link calms" as *u8, r3); total=total+1 73 74 // 4) clamp 75 pass = pass + g_check("playout always within [min,max] (clamp neg-control)" as *u8, clamp_ok); total=total+1 76 77 g_puts("---- jitterbuf gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 78 if pass == total { g_puts("verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 79 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 80}