nx_pitch_grade_gate.nx source
↩ module page · 131 lines · 4952 B
1// nx_pitch_grade_gate.nx -- REFEREE for the singing-tutor grader (nx_pitch_grade).
2//
3// Generates pure impulse-train test "notes" at exact frequencies (fs/period) and
4// grades them against a target note (125 Hz):
5// ON : sung at 125 Hz (period 64) -> |cents| <= 25, verdict ON
6// SHARP : sung at 129 Hz (period 62) -> cents > 25, verdict SHARP
7// FLAT : sung at 121 Hz (period 66) -> cents < -25, verdict FLAT
8// MELODY: three on-pitch notes 100/125/160 Hz all grade ON (a sung phrase)
9// NEG-CONTROL: white noise -> NO_PITCH (never a false on-pitch grade)
10//
11// Every value PRINTED. Evidence -> stdout + knowledge/status/pitch_grade_gate.log.
12// Exit 0 GREEN / 1 RED. Sovereign x86_64 throughout.
13// license_tier: ORIGINAL
14import "nx_syscalls_x86_64.nx"
15import "nx_pitch_grade.nx"
16
17const FS: i64 = 8000
18const MINLAG: i64 = 25
19const MAXLAG: i64 = 114
20const WIN: i64 = 480
21const NS: i64 = 1024
22
23func gp(logfd: i64, s: *u8) -> i64 {
24 var n: i64 = 0
25 while s[n] != (0 as u8) { n = n + 1 }
26 sys_write(1, s, n)
27 if logfd > 0 { sys_write(logfd, s, n) }
28 return 0
29}
30func gn(logfd: i64, v: i64) -> i64 {
31 let bb: *u8 = sys_mmap(28)
32 var m: i64 = v
33 if m < 0 { sys_write(1, "-\x00" as *u8, 1); if logfd > 0 { sys_write(logfd, "-\x00" as *u8, 1) } m = 0 - m }
34 let t: *u8 = sys_mmap(28)
35 var k: i64 = 0
36 if m == 0 { t[0] = 48 as u8; k = 1 }
37 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
38 var i: i64 = 0
39 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
40 sys_write(1, bb, k)
41 if logfd > 0 { sys_write(logfd, bb, k) }
42 return 0
43}
44
45// pure impulse train at `period` -> exactly periodic -> F0 = fs/period exactly
46func gen_tone(out_pcm: *u8, nsamp: i64, period: i64) -> i64 {
47 var i: i64 = 0
48 while i < nsamp { out_pcm[i * 2] = 0 as u8; out_pcm[i * 2 + 1] = 0 as u8; i = i + 1 }
49 i = 0
50 while i < nsamp {
51 let x: i64 = 8000
52 out_pcm[i * 2] = (x & 0xff) as u8
53 out_pcm[i * 2 + 1] = ((x >> 8) & 0xff) as u8
54 i = i + period
55 }
56 return 0
57}
58func gen_noise(out_pcm: *u8, nsamp: i64) -> i64 {
59 var s: i64 = 305419896
60 var i: i64 = 0
61 while i < nsamp {
62 s = (s * 1103515245 + 12345) & 0x7fffffff
63 var v: i64 = (s & 0xffff) - 32768
64 var x: i64 = v
65 if x < 0 { x = x + 0x10000 }
66 out_pcm[i * 2] = (x & 0xff) as u8
67 out_pcm[i * 2 + 1] = ((x >> 8) & 0xff) as u8
68 i = i + 1
69 }
70 return 0
71}
72
73// grade one tone; print "<label> f0=.. cents=.. verdict=.."; returns verdict.
74func grade1(logfd: i64, label: *u8, pcm: *u8, target: i64, out: *i64) -> i64 {
75 let cents: i64 = nx_pitch_grade(pcm, 200, WIN, FS, MINLAG, MAXLAG, target, out)
76 gp(logfd, label)
77 gp(logfd, " tgt=\x00" as *u8); gn(logfd, target)
78 gp(logfd, " f0=\x00" as *u8); gn(logfd, out[1])
79 gp(logfd, " cents=\x00" as *u8); gn(logfd, cents)
80 gp(logfd, " verdict=\x00" as *u8); gn(logfd, out[0])
81 gp(logfd, "\n\x00" as *u8)
82 return out[0]
83}
84
85func main() -> i64 {
86 let logfd: i64 = sys_openat_append("knowledge/status/pitch_grade_gate.log\x00" as *u8, 0x1a4)
87 gp(logfd, "PITCH-GRADE-GATE singing-tutor tol=\x00" as *u8); gn(logfd, PG_TOL_CENTS); gp(logfd, "cents\n\x00" as *u8)
88
89 let out: *i64 = sys_mmap(16) as *i64
90 let buf: *u8 = sys_mmap(NS * 2)
91
92 // target note 125 Hz (period 64)
93 gen_tone(buf, NS, 64)
94 let v_on: i64 = grade1(logfd, " ON \x00" as *u8, buf, 125, out)
95 gen_tone(buf, NS, 62) // 129 Hz
96 let v_sharp: i64 = grade1(logfd, " SHARP\x00" as *u8, buf, 125, out)
97 gen_tone(buf, NS, 66) // 121 Hz
98 let v_flat: i64 = grade1(logfd, " FLAT \x00" as *u8, buf, 125, out)
99
100 // melody: three on-pitch notes
101 gen_tone(buf, NS, 80) // 100 Hz
102 let v_m1: i64 = grade1(logfd, " MEL1 \x00" as *u8, buf, 100, out)
103 gen_tone(buf, NS, 64) // 125 Hz
104 let v_m2: i64 = grade1(logfd, " MEL2 \x00" as *u8, buf, 125, out)
105 gen_tone(buf, NS, 50) // 160 Hz
106 let v_m3: i64 = grade1(logfd, " MEL3 \x00" as *u8, buf, 160, out)
107
108 // neg-control: noise must NOT grade as a pitch
109 gen_noise(buf, NS)
110 let v_noise: i64 = grade1(logfd, " NOISE\x00" as *u8, buf, 125, out)
111
112 var ok: i64 = 1
113 if v_on != PG_ON { ok = 0 }
114 if v_sharp != PG_SHARP { ok = 0 }
115 if v_flat != PG_FLAT { ok = 0 }
116 if v_m1 != PG_ON { ok = 0 }
117 if v_m2 != PG_ON { ok = 0 }
118 if v_m3 != PG_ON { ok = 0 }
119 if v_noise != PG_NOPITCH { ok = 0 }
120
121 if ok == 1 {
122 gp(logfd, "PITCH-GRADE-GATE result=ALL-PASS verdict=GREEN\n\x00" as *u8)
123 if logfd > 0 { sys_close(logfd) }
124 sys_exit(0)
125 return 0
126 }
127 gp(logfd, "PITCH-GRADE-GATE result=FAIL verdict=RED\n\x00" as *u8)
128 if logfd > 0 { sys_close(logfd) }
129 sys_exit(1)
130 return 1
131}