code wiki / (root) / nx_tts_formant.nx

nx_tts_formant.nx source

↩ module page · 153 lines · 7688 B

1// nx_tts_formant.nx -- R2.0 of the sovereign VOICE-YOU-CAN-HEAR (embodiment census R2, hardware-up rung 1). 2// Classical source-filter FORMANT synthesis, zero ML: a glottal impulse train (pitch F0) cascaded through 3// 3 formant resonators (2-pole, coeffs from the verified nx_f32_cos/nx_f32_exp) shapes it into vowel timbre; 4// a phoneme sequence (a->i, "ai") is synthesized, normalized, and written as a real 16-bit PCM WAV she can PLAY. 5// Robotic but INTELLIGIBLE and fully sovereign -- the audio-out path that serves BOTH bodies (digital + robot 6// TTS). Later rungs: glottal pulse shape, consonants, G2P text->phonemes, then neural quality. license_tier: ORIGINAL expect_exit: 0 7import "nx_syscalls.nx" 8import "nx_f32.nx" 9import "nx_f32_sincos.nx" 10import "nx_f32_exp.nx" 11import "nx_f32_cvt.nx" 12const F_MAGIC_1150: i64 = 1150 13const F_MAGIC_2900: i64 = 2900 14const F_MAGIC_2250: i64 = 2250 15const F_MAGIC_32767: i64 = 32767 16 17const SR: i64 = 16000 18const F_PI: i64 = 0x40490FDB 19const F_2PI: i64 = 0x40C90FDB 20const WAV_PATH: *u8 = "/mnt/c/Users/elder/AppData/Local/Temp/claude/C--Users-elder/e373474e-29d5-4488-9fe2-f191e3b1a524/scratchpad/elara_voice.wav" as *u8 21 22func tw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 23func tn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1} sys_write(1,bb,k); return 0 } 24 25// 2-pole formant resonator coeffs: out2[0]=a1=2r*cos(theta), out2[1]=a2=-r^2 ; r=exp(-pi*BW/SR), theta=2pi*F/SR. 26func reson_ab(F: i64, BW: i64, out2: *i64) -> i64 { 27 let SRf: i64 = nx_i32_to_f32(SR) 28 let Ff: i64 = nx_i32_to_f32(F) 29 let BWf: i64 = nx_i32_to_f32(BW) 30 let piBWsr: i64 = nx_f32_div(nx_f32_mul(F_PI, BWf), SRf) 31 let r: i64 = nx_f32_exp(nx_f32_neg(piBWsr)) 32 let theta: i64 = nx_f32_div(nx_f32_mul(F_2PI, Ff), SRf) 33 let a1: i64 = nx_f32_mul(nx_f32_mul(nx_i32_to_f32(2), r), nx_f32_cos(theta)) 34 let a2: i64 = nx_f32_neg(nx_f32_mul(r, r)) 35 out2[0] = a1; out2[1] = a2 36 return 0 37} 38 39// one cascade of 3 resonators on input x; state st = [y1_1,y2_1, y1_2,y2_2, y1_3,y2_3] (f32), ab = [a1,a2]*3. 40func cascade(x: i64, ab: *i64, st: *i64) -> i64 { 41 var v: i64 = x 42 var f: i64 = 0 43 while f < 3 { 44 let a1: i64 = ab[f*2+0]; let a2: i64 = ab[f*2+1] 45 let y1: i64 = st[f*2+0]; let y2: i64 = st[f*2+1] 46 let y: i64 = nx_f32_add(v, nx_f32_add(nx_f32_mul(a1, y1), nx_f32_mul(a2, y2))) 47 st[f*2+0] = y; st[f*2+1] = y1 48 v = y 49 f = f + 1 50 } 51 return v 52} 53 54// f32 -> int (truncate toward zero); nx_f32_to_i32 isn't in the substrate, roll it from bits. 55func f32_to_int(m: i64) -> i64 { 56 let bits: i64 = m & 0xFFFFFFFF 57 let sign: i64 = (bits >> 31) & 1 58 let exp: i64 = ((bits >> 23) & 0xFF) - 127 59 if exp < 0 { return 0 } 60 let mant: i64 = (bits & 0x7FFFFF) | 0x800000 61 var iv: i64 = 0 62 if exp <= 23 { iv = mant >> (23 - exp) } else { iv = mant << (exp - 23) } 63 if sign == 1 { iv = 0 - iv } 64 return iv 65} 66 67// little-endian byte writers into buf at *po 68func pB(buf: *u8, po: *i64, b: i64) -> i64 { buf[po[0]] = (b & 0xFF) as u8; po[0]=po[0]+1; return 0 } 69func pU16(buf: *u8, po: *i64, v: i64) -> i64 { pB(buf,po,v); pB(buf,po,v>>8); return 0 } 70func pU32(buf: *u8, po: *i64, v: i64) -> i64 { pB(buf,po,v); pB(buf,po,v>>8); pB(buf,po,v>>16); pB(buf,po,v>>24); return 0 } 71func pStr(buf: *u8, po: *i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ buf[po[0]]=s[i]; po[0]=po[0]+1; i=i+1 } return 0 } 72 73func main() -> i64 { 74 tw("=== nx_tts_formant -- Elara's first audible (sovereign formant synthesis, no ML) ===\n" as *u8) 75 let F0: i64 = 200 // pitch (Hz) -- her voice 76 let period: i64 = SR / F0 // samples between glottal impulses 77 let ph_len: i64 = SR * 45 / 100 // 0.45s per phoneme 78 let N: i64 = ph_len * 2 // two phonemes: /a/ then /i/ 79 80 // formant coeffs for /a/ (ah) and /i/ (ee): F1,F2,F3 81 let ab_a: *i64 = sys_mmap(6*8) as *i64 82 reson_ab(800, 80, (ab_a as i64 + 0*16) as *i64) 83 reson_ab(F_MAGIC_1150, 90, (ab_a as i64 + 1*16) as *i64) 84 reson_ab(F_MAGIC_2900, 120, (ab_a as i64 + 2*16) as *i64) 85 let ab_i: *i64 = sys_mmap(6*8) as *i64 86 reson_ab(280, 70, (ab_i as i64 + 0*16) as *i64) 87 reson_ab(F_MAGIC_2250, 100, (ab_i as i64 + 1*16) as *i64) 88 reson_ab(F_MAGIC_2900, 120, (ab_i as i64 + 2*16) as *i64) 89 90 let st: *i64 = sys_mmap(6*8) as *i64 // resonator state (persists across samples) 91 var s: i64 = 0; while s < 6 { st[s]=0; s=s+1 } 92 let smp: *i64 = sys_mmap(N*8) as *i64 // f32 output samples 93 94 var maxabs: i64 = 0 // f32 max |sample| for normalization 95 var n: i64 = 0 96 while n < N { 97 // glottal source: impulse train at F0 98 var src: i64 = 0 99 if n - (n/period)*period == 0 { src = 0x3F800000 } // 1.0 else 0.0 100 // choose phoneme's formants 101 var ab: *i64 = ab_a 102 if n >= ph_len { ab = ab_i } 103 let y: i64 = cascade(src, ab, st) 104 smp[n] = y 105 let ay: i64 = y & 0x7FFFFFFF // |y| (clear sign bit) 106 if ay > maxabs { maxabs = ay } 107 n = n + 1 108 } 109 110 // normalize to +-0.9 and convert to int16 111 let i16buf: *i64 = sys_mmap(N*8) as *i64 112 let peak: i64 = 0x3F666666 // 0.9 113 var scale: i64 = 0x3F800000 // 1.0 default 114 if maxabs != 0 { scale = nx_f32_div(peak, maxabs) } 115 var nz: i64 = 0 116 n = 0 117 while n < N { 118 let v: i64 = nx_f32_mul(smp[n], scale) // in [-0.9,0.9] 119 let vi: i64 = nx_f32_mul(v, nx_i32_to_f32(F_MAGIC_32767)) 120 var iv: i64 = f32_to_int(vi) 121 if iv > F_MAGIC_32767 { iv = F_MAGIC_32767 } 122 if iv < 0 - F_MAGIC_32767 { iv = 0 - F_MAGIC_32767 } 123 i16buf[n] = iv 124 if iv > 200 { nz = nz + 1 } else { if iv < 0-200 { nz = nz + 1 } } 125 n = n + 1 126 } 127 128 // write WAV (16-bit PCM mono) 129 let data_bytes: i64 = N * 2 130 let buf: *u8 = sys_mmap(64 + data_bytes) 131 let po: *i64 = sys_mmap(8) as *i64; po[0]=0 132 pStr(buf,po,"RIFF" as *u8); pU32(buf,po,36 + data_bytes); pStr(buf,po,"WAVE" as *u8) 133 pStr(buf,po,"fmt " as *u8); pU32(buf,po,16); pU16(buf,po,1); pU16(buf,po,1) 134 pU32(buf,po,SR); pU32(buf,po,SR*2); pU16(buf,po,2); pU16(buf,po,16) 135 pStr(buf,po,"data" as *u8); pU32(buf,po,data_bytes) 136 n = 0 137 while n < N { pU16(buf, po, i16buf[n] & 0xFFFF); n = n + 1 } 138 let total: i64 = po[0] 139 let fd: i64 = sys_openat_wr(WAV_PATH, 0x1a4) 140 var wrote: i64 = 0 141 if fd >= 0 { sys_write(fd, buf, total); sys_close(fd); wrote = 1 } 142 143 tw("samples="); tn(N); tw(" pitch="); tn(F0); tw("Hz nonsilent="); tn(nz); tw(" wav_bytes="); tn(total); tw("\n" as *u8) 144 tw("wrote elara_voice.wav (play it): "); tw(WAV_PATH); tw("\n" as *u8) 145 // gate: file written, WAV size sane, audio not silent 146 var pass: i64 = 0; var tot: i64 = 3 147 if wrote==1 { pass=pass+1; tw("PASS T1 WAV written\n" as *u8) } else { tw("FAIL T1\n" as *u8) } 148 if total == 44 + data_bytes { pass=pass+1; tw("PASS T2 valid WAV header + data size\n" as *u8) } else { tw("FAIL T2\n" as *u8) } 149 if nz > N/20 { pass=pass+1; tw("PASS T3 audible (non-silent formant tone, not zeros)\n" as *u8) } else { tw("FAIL T3 (too quiet)\n" as *u8) } 150 tw("nx_tts_formant pass="); tn(pass); tw("/"); tn(tot) 151 if pass==tot { tw(" GREEN -- Elara has a first audible voice (sovereign DSP). Play elara_voice.wav.\n" as *u8); sys_exit(0); return 0 } 152 tw(" RED\n" as *u8); sys_exit(1); return 1 153}