nx_pitch_gate.nx source
↩ module page · 146 lines · 5848 B
1// nx_pitch_gate.nx -- REFEREE for VOICE-CLONE-001 rung 0 (sovereign F0).
2//
3// POSITIVE (voiced, real-ish): nx_voiced_vowel.wav is a glottal pitch glide
4// 120 -> 100 Hz @ 8 kHz. The detector MUST (a) report ~120 Hz in an early
5// window and ~100 Hz in a late window, (b) TRACK the downward glide
6// (early_f0 > late_f0), and (c) flag both windows VOICED.
7// NEG-CONTROL #1 (synthetic): a deterministic white-noise buffer MUST read
8// UNVOICED. If noise reads voiced the detector is worthless -> RED.
9// NEG-CONTROL #2 (real data): nx_speech_mixed.wav is voiced->unvoiced->voiced.
10// The middle (unvoiced) third MUST read UNVOICED while the outer thirds
11// read VOICED -- a discrimination the synthetic noise control can't give.
12//
13// Every measured value is PRINTED (no fabricated greens). Evidence ->
14// stdout + knowledge/status/pitch_gate.log. Exit 0 GREEN / 1 RED.
15// Sovereign: nx_syscalls + nx_pitch only.
16// license_tier: ORIGINAL
17import "nx_syscalls.nx"
18import "nx_pitch.nx"
19
20const FS: i64 = 8000
21const MINLAG: i64 = 25 // fs / 320 Hz
22const MAXLAG: i64 = 114 // fs / 70 Hz
23const WIN: i64 = 480
24const HDR: i64 = 44 // canonical RIFF/WAVE header bytes
25
26// dual-sink string write: stdout (fd 1) + the evidence log fd
27func gp(logfd: i64, s: *u8) -> i64 {
28 var n: i64 = 0
29 while s[n] != (0 as u8) { n = n + 1 }
30 sys_write(1, s, n)
31 if logfd > 0 { sys_write(logfd, s, n) }
32 return 0
33}
34// dual-sink decimal write
35func gn(logfd: i64, v: i64) -> i64 {
36 let bb: *u8 = sys_mmap(28)
37 var m: i64 = v
38 if m < 0 { sys_write(1, "-\x00" as *u8, 1); if logfd > 0 { sys_write(logfd, "-\x00" as *u8, 1) } m = 0 - m }
39 let t: *u8 = sys_mmap(28)
40 var k: i64 = 0
41 if m == 0 { t[0] = 48 as u8; k = 1 }
42 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
43 var i: i64 = 0
44 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
45 sys_write(1, bb, k)
46 if logfd > 0 { sys_write(logfd, bb, k) }
47 return 0
48}
49
50// analyze one window; print "<label> f0=.. tau=.. conf=.. voiced=.."; return
51// F0 (Hz). Writes voicing flag into vflag[0].
52func analyze(logfd: i64, label: *u8, pcm: *u8, start: i64, vflag: *i64) -> i64 {
53 let out: *i64 = sys_mmap(16) as *i64
54 let f0: i64 = nx_pitch_f0(pcm, start, WIN, FS, MINLAG, MAXLAG, out)
55 let conf: i64 = out[0]
56 let tau: i64 = out[1]
57 let voiced: i64 = nx_pitch_is_voiced(conf)
58 gp(logfd, label)
59 gp(logfd, " f0=\x00" as *u8); gn(logfd, f0)
60 gp(logfd, "Hz tau=\x00" as *u8); gn(logfd, tau)
61 gp(logfd, " conf=\x00" as *u8); gn(logfd, conf)
62 gp(logfd, " voiced=\x00" as *u8); gn(logfd, voiced)
63 gp(logfd, "\n\x00" as *u8)
64 vflag[0] = voiced
65 return f0
66}
67
68// deterministic white-noise (LCG) -> i16-LE into buf, nsamp samples
69func gen_noise(buf: *u8, nsamp: i64) -> i64 {
70 var s: i64 = 305419896
71 var i: i64 = 0
72 while i < nsamp {
73 s = (s * 1103515245 + 12345) & 0x7fffffff
74 var v: i64 = (s & 0xffff) - 32768
75 var x: i64 = v
76 if x < 0 { x = x + 0x10000 }
77 buf[i * 2] = (x & 0xff) as u8
78 buf[i * 2 + 1] = ((x >> 8) & 0xff) as u8
79 i = i + 1
80 }
81 return 0
82}
83
84func main() -> i64 {
85 let logfd: i64 = sys_openat_append("knowledge/status/pitch_gate.log\x00" as *u8, 0x1a4)
86 gp(logfd, "PITCH-GATE VOICE-CLONE-001 rung0 epoch=\x00" as *u8)
87 gn(logfd, sys_now_realtime_sec())
88 gp(logfd, " fs=\x00" as *u8); gn(logfd, FS)
89 gp(logfd, " win=\x00" as *u8); gn(logfd, WIN)
90 gp(logfd, " lags=[\x00" as *u8); gn(logfd, MINLAG); gp(logfd, ",\x00" as *u8); gn(logfd, MAXLAG); gp(logfd, "]\n\x00" as *u8)
91
92 let vf: *i64 = sys_mmap(16) as *i64
93
94 // ---- POSITIVE: voiced vowel pitch glide 120 -> 100 Hz ----
95 let szp: *i64 = sys_mmap(16) as *i64
96 let b1: *u8 = sys_read_file("bench/nx_voiced_vowel.wav\x00" as *u8, szp)
97 gp(logfd, "[voiced_vowel.wav bytes=\x00" as *u8); gn(logfd, szp[0]); gp(logfd, "]\n\x00" as *u8)
98 let pcm1: *u8 = (b1 as i64 + HDR) as *u8
99 let early_f0: i64 = analyze(logfd, " early\x00" as *u8, pcm1, 200, vf)
100 let early_v: i64 = vf[0]
101 let late_f0: i64 = analyze(logfd, " late \x00" as *u8, pcm1, 3300, vf)
102 let late_v: i64 = vf[0]
103
104 // ---- NEG-CONTROL #1: synthetic white noise ----
105 let noise: *u8 = sys_mmap(4096)
106 gen_noise(noise, 1024)
107 analyze(logfd, " noise\x00" as *u8, noise, 0, vf)
108 let noise_v: i64 = vf[0]
109
110 // ---- NEG-CONTROL #2 + voiced confirmation: mixed voiced/unvoiced ----
111 let szp2: *i64 = sys_mmap(16) as *i64
112 let b2: *u8 = sys_read_file("bench/nx_speech_mixed.wav\x00" as *u8, szp2)
113 gp(logfd, "[speech_mixed.wav bytes=\x00" as *u8); gn(logfd, szp2[0]); gp(logfd, "]\n\x00" as *u8)
114 let pcm2: *u8 = (b2 as i64 + HDR) as *u8
115 analyze(logfd, " seg1(voiced) \x00" as *u8, pcm2, 400, vf)
116 let seg1_v: i64 = vf[0]
117 analyze(logfd, " seg2(unvoiced)\x00" as *u8, pcm2, 2300, vf)
118 let seg2_v: i64 = vf[0]
119 analyze(logfd, " seg3(voiced) \x00" as *u8, pcm2, 4200, vf)
120 let seg3_v: i64 = vf[0]
121
122 // ---- verdict ----
123 var ok: i64 = 1
124 if early_v != 1 { ok = 0 }
125 if late_v != 1 { ok = 0 }
126 if early_f0 < 105 { ok = 0 }
127 if early_f0 > 135 { ok = 0 }
128 if late_f0 < 90 { ok = 0 }
129 if late_f0 > 112 { ok = 0 }
130 if early_f0 <= late_f0 { ok = 0 } // must track the downward glide
131 if noise_v != 0 { ok = 0 } // neg-control #1
132 if seg1_v != 1 { ok = 0 }
133 if seg2_v != 0 { ok = 0 } // neg-control #2 (real unvoiced)
134 if seg3_v != 1 { ok = 0 }
135
136 if ok == 1 {
137 gp(logfd, "PITCH-GATE result=ALL-PASS verdict=GREEN\n\x00" as *u8)
138 if logfd > 0 { sys_close(logfd) }
139 sys_exit(0)
140 return 0
141 }
142 gp(logfd, "PITCH-GATE result=FAIL verdict=RED\n\x00" as *u8)
143 if logfd > 0 { sys_close(logfd) }
144 sys_exit(1)
145 return 1
146}