code wiki / _hdl_build / nx_gamemusic_gate.nx
nx_gamemusic_gate.nx source
↩ module page · 213 lines · 9372 B
1// nx_gamemusic_gate.nx -- CERTIFICATION of nx_gamemusic, completing `audio-sfx-music`.
2// The SFX half is already proven by nx_game_audio_gate (3/3, real WAV). This gate proves the MUSIC half so
3// the capability can honestly be called HAVE rather than inferred from SFX alone.
4// T1 EXACT equal-temperament octaves: A4=440 -> A5=880 -> A3=220, no drift
5// T2 DETERMINISTIC render: same song rendered twice -> byte-identical samples
6// T3 DATA-DRIVEN: a different song array produces different audio (melody is data, not code)
7// T4 SATURATING MIX: layering loud tracks clamps instead of wrapping (no integer-overflow crackle)
8// T5 PER-NOTE ENVELOPE: a note attacks then decays (it does not click on at full amplitude)
9// T6 ★ANTI-VACUITY: output is non-silent AND pitch is real -- a high note has strictly more
10// zero-crossings than a low note. Silence or a constant would satisfy T2/T4 vacuously.
11// T7 REAL ARTIFACT: a playable WAV is written to disk via the existing nx_audio_wav writer
12// T8 composes with nx_gamesave: the song DATA round-trips through save/load
13// license_tier: ORIGINAL expect_exit: 0
14import "nx_syscalls.nx"
15import "nx_gamemusic.nx"
16import "nx_audio_wav.nx"
17import "nx_gamesave.nx"
18
19func mw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
20func mn(v: i64) -> i64 {
21 if v==0 { sys_write(1,"0" as *u8,1); return 0 }
22 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
23 let t: *u8=sys_mmap(32); var k: i64=0
24 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
25 let o: *u8=sys_mmap(32); var q: i64=k-1; var i: i64=0
26 while q>=0 { o[i]=t[q]; i=i+1; q=q-1 }
27 sys_write(1,o,i); return 0
28}
29func zerocross(b: *i64, n: i64) -> i64 {
30 var c: i64 = 0
31 var i: i64 = 1
32 while i < n {
33 if b[i-1] < 0 { if b[i] >= 0 { c = c + 1 } }
34 if b[i-1] >= 0 { if b[i] < 0 { c = c + 1 } }
35 i = i + 1
36 }
37 return c
38}
39func clearbuf(b: *i64, n: i64) -> i64 { var i: i64=0; while i<n { b[i]=0; i=i+1 } return 0 }
40
41func main() -> i64 {
42 mw("=== nx_gamemusic_gate: is the MUSIC half of audio-sfx-music real? ===\n\n")
43 var pass: i64 = 0
44 var checks: i64 = 0
45 let RATE: i64 = 22050
46 let TICK: i64 = 2756 // ~1/8 s per tick
47
48 // ---------- T1 exact equal temperament ----------
49 checks = checks + 1
50 let a4: i64 = mus_note_freq(9,4)
51 let a5: i64 = mus_note_freq(9,5)
52 let a3: i64 = mus_note_freq(9,3)
53 let c4: i64 = mus_note_freq(0,4)
54 var t1: i64 = 0
55 if a4==440 { if a5==880 { if a3==220 { if c4==262 { t1=1 } } } }
56 if t1==1 {
57 mw("T1 GREEN exact octaves: A3="); mn(a3); mw(" A4="); mn(a4); mw(" A5="); mn(a5)
58 mw(" C4="); mn(c4); mw(" (integer doubling, zero drift)\n"); pass=pass+1
59 } else { mw("T1 RED pitch table wrong: A3/A4/A5="); mn(a3); mw("/"); mn(a4); mw("/"); mn(a5); mw("\n") }
60
61 // ---------- song data (DATA, not code): a simple C-major phrase + a bass line ----------
62 let NN: i64 = 8
63 let song: *i64 = sys_mmap(NN*3*8) as *i64
64 // (semi, octave, ticks)
65 song[0]=0; song[1]=4; song[2]=2 // C4
66 song[3]=2; song[4]=4; song[5]=2 // D4
67 song[6]=4; song[7]=4; song[8]=2 // E4
68 song[9]=5; song[10]=4; song[11]=2 // F4
69 song[12]=7; song[13]=4; song[14]=2 // G4
70 song[15]=MUS_REST; song[16]=4; song[17]=1
71 song[18]=9; song[19]=4; song[20]=2 // A4
72 song[21]=11; song[22]=4; song[23]=3 // B4
73 let NS: i64 = mus_song_samples(song, NN, TICK)
74
75 // ---------- T2 deterministic render ----------
76 checks = checks + 1
77 let b1: *i64 = sys_mmap(NS*8+64) as *i64
78 let b2: *i64 = sys_mmap(NS*8+64) as *i64
79 clearbuf(b1, NS); clearbuf(b2, NS)
80 mus_render(b1, NS, song, NN, RATE, 9000, TICK)
81 mus_render(b2, NS, song, NN, RATE, 9000, TICK)
82 var det: i64 = 1
83 var i: i64 = 0
84 while i < NS { if b1[i]!=b2[i] { det=0 } i=i+1 }
85 if det==1 {
86 mw("T2 GREEN deterministic: identical song -> byte-identical "); mn(NS); mw(" samples\n"); pass=pass+1
87 } else { mw("T2 RED render diverged between runs\n") }
88
89 // ---------- T3 data-driven ----------
90 checks = checks + 1
91 let song2: *i64 = sys_mmap(NN*3*8) as *i64
92 i = 0
93 while i < NN*3 { song2[i]=song[i]; i=i+1 }
94 song2[0]=11; song2[1]=5 // change only the first note
95 let b3: *i64 = sys_mmap(NS*8+64) as *i64
96 clearbuf(b3, NS)
97 mus_render(b3, NS, song2, NN, RATE, 9000, TICK)
98 var diff: i64 = 0
99 i = 0
100 while i < NS { if b3[i]!=b1[i] { diff = diff + 1 } i=i+1 }
101 if diff > 100 {
102 mw("T3 GREEN data-driven: changing ONE note changed "); mn(diff)
103 mw(" samples -- melody is data, not code\n"); pass=pass+1
104 } else { mw("T3 RED changing the song changed only "); mn(diff); mw(" samples\n") }
105
106 // ---------- T4 saturating mix ----------
107 checks = checks + 1
108 let b4: *i64 = sys_mmap(NS*8+64) as *i64
109 clearbuf(b4, NS)
110 // layer the same loud track four times; without saturation this wraps negative
111 mus_render(b4, NS, song, NN, RATE, 30000, TICK)
112 mus_render(b4, NS, song, NN, RATE, 30000, TICK)
113 mus_render(b4, NS, song, NN, RATE, 30000, TICK)
114 mus_render(b4, NS, song, NN, RATE, 30000, TICK)
115 var over: i64 = 0
116 var peak: i64 = 0
117 i = 0
118 while i < NS {
119 var v: i64 = b4[i]
120 if v < 0 { v = 0 - v }
121 if v > peak { peak = v }
122 if b4[i] > MUS_SAMPMAX { over = over + 1 }
123 if b4[i] < 0 - MUS_SAMPMAX { over = over + 1 }
124 i = i + 1
125 }
126 if over==0 { if peak > 0 {
127 mw("T4 GREEN saturating mix: 4 layers at amp 30000 -> peak "); mn(peak)
128 mw(", 0 samples past the ceiling (no overflow crackle)\n"); pass=pass+1
129 } }
130 if over>0 { mw("T4 RED "); mn(over); mw(" samples exceeded the sample ceiling\n") }
131 if over==0 { if peak==0 { mw("T4 RED mix produced silence\n") } }
132
133 // ---------- T5 per-note envelope ----------
134 checks = checks + 1
135 let e0: i64 = mus_env(0, 1000, 125)
136 let emid: i64 = mus_env(125, 1000, 125)
137 let eend: i64 = mus_env(999, 1000, 125)
138 var t5: i64 = 0
139 if e0 < emid { if eend < emid { if e0 >= 0 { t5=1 } } }
140 if t5==1 {
141 mw("T5 GREEN envelope attacks then decays: env(0)="); mn(e0); mw(" env(peak)="); mn(emid)
142 mw(" env(end)="); mn(eend); mw("\n"); pass=pass+1
143 } else { mw("T5 RED envelope not attack-decay shaped\n") }
144
145 // ---------- T6 anti-vacuity: non-silent + real pitch ----------
146 checks = checks + 1
147 var nonzero: i64 = 0
148 i = 0
149 while i < NS { if b1[i]!=0 { nonzero = nonzero + 1 } i=i+1 }
150 // a low note must cross zero far less often than a high note over the same span
151 let SPAN: i64 = 4000
152 let lo: *i64 = sys_mmap(SPAN*8+64) as *i64
153 let hi: *i64 = sys_mmap(SPAN*8+64) as *i64
154 clearbuf(lo, SPAN); clearbuf(hi, SPAN)
155 let slo: *i64 = sys_mmap(3*8) as *i64
156 let shi: *i64 = sys_mmap(3*8) as *i64
157 slo[0]=9; slo[1]=2; slo[2]=1 // A2 = 110Hz
158 shi[0]=9; shi[1]=6; shi[2]=1 // A6 = 1760Hz
159 mus_render(lo, SPAN, slo, 1, RATE, 9000, SPAN)
160 mus_render(hi, SPAN, shi, 1, RATE, 9000, SPAN)
161 let zlo: i64 = zerocross(lo, SPAN)
162 let zhi: i64 = zerocross(hi, SPAN)
163 var t6: i64 = 0
164 if nonzero > NS/4 { if zhi > zlo*4 { t6=1 } }
165 if t6==1 {
166 mw("T6 GREEN anti-vacuity: "); mn(nonzero); mw("/"); mn(NS)
167 mw(" non-silent; A2 crossings="); mn(zlo); mw(" vs A6="); mn(zhi)
168 mw(" -- real pitch, not a constant\n"); pass=pass+1
169 } else {
170 mw("T6 RED vacuous audio: nonzero="); mn(nonzero); mw(" zlo="); mn(zlo); mw(" zhi="); mn(zhi); mw("\n")
171 }
172
173 // ---------- T7 real playable artifact ----------
174 checks = checks + 1
175 let MP: *u8 = "knowledge/nx_gamemusic.wav" as *u8
176 sys_unlinkat(MP)
177 let wbuf: *u8 = sys_mmap(NS*4 + 4096)
178 let wlen: i64 = wav_render_stereo(b1, b1, NS, RATE, wbuf)
179 wav_save(MP, wbuf, wlen)
180 let lp: *i64 = sys_mmap(8) as *i64
181 lp[0]=0
182 let chk: *u8 = sys_read_file(MP, lp)
183 var t7: i64 = 0
184 if (chk as i64)!=0 { if lp[0] > 1000 {
185 if chk[0]==(82 as u8) { if chk[1]==(73 as u8) { if chk[2]==(70 as u8) { if chk[3]==(70 as u8) { t7=1 } } } }
186 } }
187 if t7==1 {
188 mw("T7 GREEN real artifact: knowledge/nx_gamemusic.wav written, RIFF header verified, ")
189 mn(lp[0]); mw(" bytes playable\n"); pass=pass+1
190 } else { mw("T7 RED WAV not written or not RIFF, bytes="); mn(lp[0]); mw("\n") }
191
192 // ---------- T8 composes with nx_gamesave ----------
193 checks = checks + 1
194 let SP: *u8 = "knowledge/nx_gamemusic_song.sav" as *u8
195 sys_unlinkat(SP)
196 let wrote: i64 = gs_save(SP, 5150, song, NN*3, 1784579000)
197 let rd: *i64 = sys_mmap(NN*3*8) as *i64
198 let got: i64 = gs_load(SP, rd, NN*3, 0 as *i64)
199 var t8: i64 = 1
200 if got != NN*3 { t8=0 }
201 if wrote <= 0 { t8=0 }
202 i = 0
203 while i < NN*3 { if rd[i]!=song[i] { t8=0 } i=i+1 }
204 if t8==1 {
205 mw("T8 GREEN composes with nx_gamesave: "); mn(NN); mw("-note song round-tripped ("); mn(wrote)
206 mw("B) -- soundtracks persist with the save\n"); pass=pass+1
207 } else { mw("T8 RED song data did not survive save/load, rc="); mn(got); mw("\n") }
208
209 mw("\n=== nx_gamemusic_gate "); mn(pass); mw("/"); mn(checks)
210 if pass == checks { mw(" GREEN ===\n"); return 0 }
211 mw(" RED ===\n")
212 return 1
213}