code wiki / _hdl_build / nx_asr_frontend.nx

nx_asr_frontend.nx source

↩ module page · 133 lines · 5074 B

1// nx_asr_frontend.nx -- THE DEEP, rung 1: the ASR acoustic FRONT-END (PCM frame -> spectral features), the 2// bottom layer every speech recognizer stands on. Self-contained radix-2 forward FFT (integer Q14, sibling of 3// nx_fft but written in the harness-safe idioms + no inverse) -> power spectrum -> a contiguous filterbank of 4// band energies + the dominant bin/band. NO float. HONESTLY just the front-end -- NOT recognition (acoustic 5// model HMM/CTC, language model, decoding = next rungs, grounded by knowledge/fetched/asr_*.raw). Linear 6// filterbank R1; mel-warp = R2. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8const K_MAGIC_16384: i64 = 16384 9const K_MAGIC_16069: i64 = 16069 10const K_MAGIC_3196: i64 = 3196 11const K_MAGIC_15137: i64 = 15137 12const K_MAGIC_6270: i64 = 6270 13const K_MAGIC_13623: i64 = 13623 14const K_MAGIC_9102: i64 = 9102 15const K_MAGIC_11585: i64 = 11585 16 17// N=32 twiddle table: re=cos(2pi k/32), im=-sin(2pi k/32), Q14. negatives as 0-N (harness-safe). 18func af_tw(tr: *i64, ti: *i64) -> i64 { 19 tr[0]=K_MAGIC_16384; ti[0]=0 20 tr[1]=K_MAGIC_16069; ti[1]=0-K_MAGIC_3196 21 tr[2]=K_MAGIC_15137; ti[2]=0-K_MAGIC_6270 22 tr[3]=K_MAGIC_13623; ti[3]=0-K_MAGIC_9102 23 tr[4]=K_MAGIC_11585; ti[4]=0-K_MAGIC_11585 24 tr[5]=K_MAGIC_9102; ti[5]=0-K_MAGIC_13623 25 tr[6]=K_MAGIC_6270; ti[6]=0-K_MAGIC_15137 26 tr[7]=K_MAGIC_3196; ti[7]=0-K_MAGIC_16069 27 tr[8]=0; ti[8]=0-K_MAGIC_16384 28 tr[9]=0-K_MAGIC_3196; ti[9]=0-K_MAGIC_16069 29 tr[10]=0-K_MAGIC_6270; ti[10]=0-K_MAGIC_15137 30 tr[11]=0-K_MAGIC_9102; ti[11]=0-K_MAGIC_13623 31 tr[12]=0-K_MAGIC_11585; ti[12]=0-K_MAGIC_11585 32 tr[13]=0-K_MAGIC_13623; ti[13]=0-K_MAGIC_9102 33 tr[14]=0-K_MAGIC_15137; ti[14]=0-K_MAGIC_6270 34 tr[15]=0-K_MAGIC_16069; ti[15]=0-K_MAGIC_3196 35 return 0 36} 37func af_log2(n: i64) -> i64 { var l: i64 = 0; var v: i64 = n; while v > 1 { v = v / 2; l = l + 1 } return l } 38func af_bitrev(idx: i64, log2n: i64) -> i64 { 39 var r: i64 = 0 40 var v: i64 = idx 41 var b: i64 = 0 42 while b < log2n { r = r * 2; r = r + (v - (v / 2) * 2); v = v / 2; b = b + 1 } 43 return r 44} 45// in-place forward radix-2 FFT, n in {2,4,8,16,32} 46func af_fwd(re: *i64, im: *i64, n: i64) -> i64 { 47 let log2n: i64 = af_log2(n) 48 let tr: *i64 = sys_mmap(16 * 8) as *i64 49 let ti: *i64 = sys_mmap(16 * 8) as *i64 50 af_tw(tr, ti) 51 var i: i64 = 0 52 while i < n { 53 let j: i64 = af_bitrev(i, log2n) 54 if i < j { 55 let xr: i64 = re[i]; let xi: i64 = im[i] 56 re[i]=re[j]; im[i]=im[j]; re[j]=xr; im[j]=xi 57 } 58 i = i + 1 59 } 60 var m: i64 = 1 61 while m < n { 62 let m2: i64 = m * 2 63 let stride: i64 = 16 / m 64 var k: i64 = 0 65 while k < n { 66 var j: i64 = 0 67 while j < m { 68 let tw: i64 = j * stride 69 let wr: i64 = tr[tw]; let wi: i64 = ti[tw] 70 let xr: i64 = re[k + j + m]; let xi: i64 = im[k + j + m] 71 let t_r: i64 = (wr * xr - wi * xi) / K_MAGIC_16384 72 let t_i: i64 = (wr * xi + wi * xr) / K_MAGIC_16384 73 let u_r: i64 = re[k + j]; let u_i: i64 = im[k + j] 74 re[k + j] = u_r + t_r 75 im[k + j] = u_i + t_i 76 re[k + j + m] = u_r - t_r 77 im[k + j + m] = u_i - t_i 78 j = j + 1 79 } 80 k = k + m2 81 } 82 m = m2 83 } 84 return 0 85} 86 87// power spectrum of an N-sample real frame -> power[0..N) 88func nx_asr_power(pcm: *i64, n: i64, power: *i64) -> i64 { 89 let re: *i64 = sys_mmap(n * 8) as *i64 90 let im: *i64 = sys_mmap(n * 8) as *i64 91 var i: i64 = 0 92 while i < n { re[i] = pcm[i]; im[i] = 0; i = i + 1 } 93 af_fwd(re, im, n) 94 var k: i64 = 0 95 while k < n { power[k] = re[k] * re[k] + im[k] * im[k]; k = k + 1 } 96 return 0 97} 98// index of the max power bin in [1, N/2] (skip DC) 99func nx_asr_peak_bin(power: *i64, n: i64) -> i64 { 100 var best: i64 = 0 - 1 101 var bi: i64 = 0 102 let half: i64 = n / 2 103 var k: i64 = 1 104 while k <= half { if power[k] > best { best = power[k]; bi = k } k = k + 1 } 105 return bi 106} 107// linear filterbank: sum power bins [1..N/2] into `nfilt` contiguous bands -> energies (mel-warp = R2) 108func nx_asr_filterbank(power: *i64, n: i64, nfilt: i64, energies: *i64) -> i64 { 109 let half: i64 = n / 2 110 var f: i64 = 0 111 while f < nfilt { energies[f] = 0; f = f + 1 } 112 var k: i64 = 1 113 while k <= half { 114 var band: i64 = ((k - 1) * nfilt) / half 115 if band >= nfilt { band = nfilt - 1 } 116 energies[band] = energies[band] + power[k] 117 k = k + 1 118 } 119 return 0 120} 121func nx_asr_dom_band(energies: *i64, nfilt: i64) -> i64 { 122 var best: i64 = 0 - 1 123 var bi: i64 = 0 124 var f: i64 = 0 125 while f < nfilt { if energies[f] > best { best = energies[f]; bi = f } f = f + 1 } 126 return bi 127} 128func nx_asr_total_energy(energies: *i64, nfilt: i64) -> i64 { 129 var s: i64 = 0 130 var f: i64 = 0 131 while f < nfilt { s = s + energies[f]; f = f + 1 } 132 return s 133}