nx_song_grade_gate.nx source
↩ module page · 129 lines · 4852 B
1// nx_song_grade_gate.nx -- REFEREE for the learn-game grader (LG-2, nx_song_grade).
2//
3// SONG = 4 target notes [A2=45, A3=57, E2=40, E3=52] at 1024-sample slots.
4// Three performances are synthesized (impulse-train tones in each slot) and graded:
5// GOOD : correct note in every slot -> score 1000, all HIT
6// BAD : a wrong note in every slot -> score low, all WRONG (neg-control)
7// PARTIAL : correct, SILENT, correct, wrong -> score 500, verdicts HIT/MISS/HIT/WRONG
8// The BAD case is the neg-control: a wrong performance MUST score low, or the
9// grader can't tell right from wrong.
10//
11// Evidence -> stdout + knowledge/status/song_grade_gate.log. Exit 0 GREEN / 1 RED.
12// Sovereign x86_64 throughout. license_tier: ORIGINAL
13import "nx_syscalls_x86_64.nx"
14import "nx_song_grade.nx"
15import "nx_note.nx"
16
17const SLOT: i64 = 1024
18const NSAMP: i64 = 4096
19
20func gp(logfd: i64, s: *u8) -> i64 {
21 var n: i64 = 0
22 while s[n] != (0 as u8) { n = n + 1 }
23 sys_write(1, s, n)
24 if logfd > 0 { sys_write(logfd, s, n) }
25 return 0
26}
27func gn(logfd: i64, v: i64) -> i64 {
28 let bb: *u8 = sys_mmap(28)
29 var m: i64 = v
30 if m < 0 { sys_write(1, "-\x00" as *u8, 1); if logfd > 0 { sys_write(logfd, "-\x00" as *u8, 1) } m = 0 - m }
31 let t: *u8 = sys_mmap(28)
32 var k: i64 = 0
33 if m == 0 { t[0] = 48 as u8; k = 1 }
34 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
35 var i: i64 = 0
36 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
37 sys_write(1, bb, k)
38 if logfd > 0 { sys_write(logfd, bb, k) }
39 return 0
40}
41func setnote(sc: *i64, i: i64, midi: i64, start: i64, dur: i64) -> i64 {
42 sc[i * 3 + 0] = midi
43 sc[i * 3 + 1] = start
44 sc[i * 3 + 2] = dur
45 return 0
46}
47// impulse-train tone of given period into player[start .. start+dur)
48func put_tone(player: *u8, start: i64, dur: i64, period: i64) -> i64 {
49 var i: i64 = start
50 while i < start + dur {
51 player[i * 2] = (8000 & 0xff) as u8
52 player[i * 2 + 1] = ((8000 >> 8) & 0xff) as u8
53 i = i + period
54 }
55 return 0
56}
57func report(logfd: i64, label: *u8, score: i64, verdicts: *i64, n: i64) -> i64 {
58 gp(logfd, label)
59 gp(logfd, " score=\x00" as *u8); gn(logfd, score)
60 gp(logfd, "/1000 verdicts=[\x00" as *u8)
61 var i: i64 = 0
62 while i < n { gn(logfd, verdicts[i]); gp(logfd, " \x00" as *u8); i = i + 1 }
63 gp(logfd, "] (0=HIT 1=WRONG 2=MISS)\n\x00" as *u8)
64 return 0
65}
66
67func main() -> i64 {
68 let logfd: i64 = sys_openat_append("knowledge/status/song_grade_gate.log\x00" as *u8, 0x1a4)
69 gp(logfd, "SONG-GRADE-GATE LG-2 (learn-game)\n\x00" as *u8)
70
71 let tbl: *i64 = sys_mmap(128 * 8) as *i64
72 nx_note_table(tbl)
73
74 // SONG: A2 A3 E2 E3
75 let sc: *i64 = sys_mmap(4 * 3 * 8) as *i64
76 setnote(sc, 0, 45, 0, SLOT)
77 setnote(sc, 1, 57, SLOT, SLOT)
78 setnote(sc, 2, 40, 2 * SLOT, SLOT)
79 setnote(sc, 3, 52, 3 * SLOT, SLOT)
80
81 let vG: *i64 = sys_mmap(4 * 8) as *i64
82 let vB: *i64 = sys_mmap(4 * 8) as *i64
83 let vP: *i64 = sys_mmap(4 * 8) as *i64
84
85 // GOOD: correct periods (73->A2, 36->A3, 97->E2, 49->E3)
86 let pG: *u8 = sys_mmap(NSAMP * 2)
87 put_tone(pG, 0, SLOT, 73); put_tone(pG, SLOT, SLOT, 36); put_tone(pG, 2 * SLOT, SLOT, 97); put_tone(pG, 3 * SLOT, SLOT, 49)
88 let sG: i64 = nx_song_grade(sc, 4, pG, 8000, tbl, vG)
89 report(logfd, " GOOD \x00" as *u8, sG, vG, 4)
90
91 // BAD: wrong periods everywhere
92 let pB: *u8 = sys_mmap(NSAMP * 2)
93 put_tone(pB, 0, SLOT, 60); put_tone(pB, SLOT, SLOT, 30); put_tone(pB, 2 * SLOT, SLOT, 80); put_tone(pB, 3 * SLOT, SLOT, 40)
94 let sB: i64 = nx_song_grade(sc, 4, pB, 8000, tbl, vB)
95 report(logfd, " BAD \x00" as *u8, sB, vB, 4)
96
97 // PARTIAL: correct, SILENT, correct, wrong
98 let pP: *u8 = sys_mmap(NSAMP * 2)
99 put_tone(pP, 0, SLOT, 73); put_tone(pP, 2 * SLOT, SLOT, 97); put_tone(pP, 3 * SLOT, SLOT, 40)
100 let sP: i64 = nx_song_grade(sc, 4, pP, 8000, tbl, vP)
101 report(logfd, " PARTIAL\x00" as *u8, sP, vP, 4)
102
103 var ok: i64 = 1
104 // GOOD: perfect
105 if sG != 1000 { ok = 0 }
106 if vG[0] != SG_HIT { ok = 0 }
107 if vG[1] != SG_HIT { ok = 0 }
108 if vG[2] != SG_HIT { ok = 0 }
109 if vG[3] != SG_HIT { ok = 0 }
110 // BAD: must score low (neg-control)
111 if sB > 250 { ok = 0 }
112 // PARTIAL: 2/4 = 500, with the expected per-note pattern
113 if sP != 500 { ok = 0 }
114 if vP[0] != SG_HIT { ok = 0 }
115 if vP[1] != SG_MISS { ok = 0 }
116 if vP[2] != SG_HIT { ok = 0 }
117 if vP[3] != SG_WRONG { ok = 0 }
118
119 if ok == 1 {
120 gp(logfd, "SONG-GRADE-GATE result=ALL-PASS verdict=GREEN\n\x00" as *u8)
121 if logfd > 0 { sys_close(logfd) }
122 sys_exit(0)
123 return 0
124 }
125 gp(logfd, "SONG-GRADE-GATE result=FAIL verdict=RED\n\x00" as *u8)
126 if logfd > 0 { sys_close(logfd) }
127 sys_exit(1)
128 return 1
129}