code wiki / (root) / nx_voice_clone_synth.nx

nx_voice_clone_synth.nx source

↩ module page · 78 lines · 2967 B

1// nx_voice_clone_synth.nx -- VOICE-CLONE-001 rung 2: CREATE new audio in a 2// captured voice. Given a voiceprint (the speaker's vocal-tract filter, as 3// averaged PARCOR) this drives that filter with a glottal source at ANY 4// requested pitch -> voiced speech in that speaker's voice, at that pitch. 5// 6// This is the "create voices" half of duplicate-and-create. Because pitch is 7// the SOURCE and identity is the FILTER, the same voiceprint can read at a low 8// pitch, a high pitch, or sing a melody (the singing-teacher tie-in) while 9// staying recognizably the same speaker. 10// 11// Pipeline (no reinvention): PARCOR k (Q30, from nx_voiceprint) --step-up 12// recursion--> LPC predictor a (Q30) ; glottal impulse train at fs/F0 as the 13// residual --> nx_lpc_synth(residual, a) --> i16 PCM. 14// 15// license_tier: ORIGINAL 16// module: nishi-core.audio.voice_clone_synth 17// depends: nishi-core.voice.lpc_synth, nishi-core.audio.voiceprint 18// capability: AUDIO_VOICE_SYNTH 19import "nx_syscalls_x86_64.nx" 20import "nx_lpc_synth.nx" 21import "nx_voiceprint.nx" 22 23const CS_AMP: i64 = 6000 // glottal impulse amplitude (excitation) 24 25// step-up recursion: PARCOR k[1..p] (in vp[2..]) -> LPC predictor a[1..p]. 26// Both Q30. Writes a_out as p i64-LE words. Stable because |k|<1 is 27// preserved by averaging convex combinations of stable frames. 28func _cs_stepup(vp: *i64, p: i64, a_out: *u8) -> i64 { 29 let acur: *i64 = sys_mmap(p * 8) as *i64 30 let anew: *i64 = sys_mmap(p * 8) as *i64 31 acur[0] = vp[2] // a_1 = k_1 32 var i: i64 = 2 33 while i <= p { 34 let ki: i64 = vp[2 + (i - 1)] // k_i 35 var j: i64 = 1 36 while j <= i - 1 { 37 anew[j - 1] = acur[j - 1] + ((ki * acur[(i - j) - 1]) >> 30) 38 j = j + 1 39 } 40 anew[i - 1] = ki 41 var c: i64 = 0 42 while c < i { acur[c] = anew[c]; c = c + 1 } 43 i = i + 1 44 } 45 var w: i64 = 0 46 while w < p { 47 let v: i64 = acur[w] 48 var b: i64 = 0 49 while b < 8 { 50 a_out[w * 8 + b] = ((v >> (b * 8)) & 0xff) as u8 51 b = b + 1 52 } 53 w = w + 1 54 } 55 return 0 56} 57 58// nx_voice_clone_synth -- synthesize n_samples of voiced speech in the 59// voiceprint's vocal tract at f0_hz. out_pcm: i16-LE (caller mmaps, zeroed). 60func nx_voice_clone_synth(vp: *i64, f0_hz: i64, fs: i64, 61 n_samples: i64, out_pcm: *u8) -> i64 { 62 let a_buf: *u8 = sys_mmap(VP_ORDER * 8) 63 _cs_stepup(vp, VP_ORDER, a_buf) 64 65 // glottal impulse train residual at period = fs / f0 66 let e_pcm: *u8 = sys_mmap(n_samples * 2) // mmap zero-filled 67 var period: i64 = fs / f0_hz 68 if period < 1 { period = 1 } 69 var i: i64 = 0 70 while i < n_samples { 71 let x: i64 = CS_AMP 72 e_pcm[i * 2] = (x & 0xff) as u8 73 e_pcm[i * 2 + 1] = ((x >> 8) & 0xff) as u8 74 i = i + period 75 } 76 nx_lpc_synth(e_pcm, n_samples, a_buf, VP_ORDER, out_pcm) 77 return 0 78}