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}