// funasr-paraformer: Paraformer (non-autoregressive ASR) on ggml. // WAV → kaldi fbank → CMVN → SANM encoder (ggml) → CIF predictor (host) → // SANM decoder w/ cross-attn (ggml) → argmax → token ids (stdout). // Encoder/FSMN/attention primitives are shared with the Fun-ASR-Nano runtime. #include "ggml.h" #include "ggml-cpu.h" #include "ggml-alloc.h" #include "ggml-backend.h" #include "gguf.h" #include #include #include #include #include #include #include static const float LN_EPS = 1e-5f; // ===== audio loader: any wav/mp3/flac, any rate/channels -> 16k mono (miniaudio) ===== #define FUNASR_AUDIO_IMPLEMENTATION #include "funasr_audio.h" #include "funasr_vad.h" // built-in FSMN-VAD front end (--vad segmentation) #include static const int FS=16000,WINLEN=400,SHIFT=160,NFFT=512,NMEL=80,LFR_M=7,LFR_N=6; static const float PREEMPH=0.97f,LOWF=20.0f,HIGHF=8000.0f; static inline float melf(float f){return 1127.0f*logf(1.0f+f/700.0f);} static void fftc(std::vector&re,std::vector&im,int n){for(int i=1,j=0;i>1;for(;j&b;b>>=1)j^=b;j^=b;if(i compute_fbank(std::vector wav,int&T_out){ for(auto&v:wav)v*=32768.0f;std::vectorwin(WINLEN);for(int i=0;i>fb(NMEL,std::vector(NB,0.0f)); for(int m=0;mL&&mf>feat(T,std::vector(NMEL)); std::vectorre(NFFT),im(NFFT),fr(WINLEN);const float fl=1.1920929e-07f; for(int t=0;t0;i--)fr[i]-=PREEMPH*fr[i-1];fr[0]-=PREEMPH*fr[0]; for(int i=0;i0)e+=fb[m][k]*(re[k]*re[k]+im[k]*im[k]);feat[t][m]=logf(e>fl?e:fl);}} const int pad=(LFR_M-1)/2;int Tl=(T+LFR_N-1)/LFR_N;std::vector>pd;pd.reserve(T+pad+LFR_M); for(int i=0;iout((size_t)Tl*D);for(int i=0;it; ggml_tensor*g(const std::string&n){auto it=t.find(n);if(it==t.end()){fprintf(stderr,"missing %s\n",n.c_str());exit(1);}return it->second;} bool has(const std::string&n){return t.count(n);} }; static ggml_tensor* lin(ggml_context*c,ggml_tensor*w,ggml_tensor*b,ggml_tensor*x){auto y=ggml_mul_mat(c,w,x);return b?ggml_add(c,y,b):y;} static ggml_tensor* lnorm(ggml_context*c,ggml_tensor*x,ggml_tensor*g,ggml_tensor*b){return ggml_add(c,ggml_mul(c,ggml_norm(c,x,LN_EPS),g),b);} // FSMN depthwise (kernel stored [K,D]): out = sum_j w[:,j]*pad(v)[:, t+j] (+ residual v) static ggml_tensor* fsmn(ggml_context*c,ggml_tensor*v,ggml_tensor*fk,int D,int T,int K){ const int pad=(K-1)/2;ggml_tensor*vp=ggml_pad_ext(c,v,0,0,pad,pad,0,0,0,0);ggml_tensor*acc=v; for(int j=0;jnb[1],(size_t)j*vp->nb[1]);auto wj=ggml_view_1d(c,fk,D,(size_t)j*fk->nb[1]); acc=ggml_add(c,acc,ggml_mul(c,ggml_cont(c,sl),wj));}return acc;} // SAN-M encoder self-attn (fused qkv + fsmn) static ggml_tensor* enc_attn(ggml_context*c,model&m,const std::string&p,ggml_tensor*x,int T){ const int D=m.c.d_model,H=m.c.enc_head,dk=D/H,K=m.c.enc_kernel; ggml_tensor*qkv=lin(c,m.g(p+"linear_q_k_v.weight"),m.g(p+"linear_q_k_v.bias"),x);size_t nb1=qkv->nb[1]; ggml_tensor*q=ggml_cont(c,ggml_view_2d(c,qkv,D,T,nb1,0)); ggml_tensor*k=ggml_cont(c,ggml_view_2d(c,qkv,D,T,nb1,(size_t)D*sizeof(float))); ggml_tensor*v=ggml_cont(c,ggml_view_2d(c,qkv,D,T,nb1,(size_t)2*D*sizeof(float))); ggml_tensor*fm=fsmn(c,v,m.g(p+"fsmn_block.weight"),D,T,K); q=ggml_permute(c,ggml_reshape_3d(c,q,dk,H,T),0,2,1,3);k=ggml_permute(c,ggml_reshape_3d(c,k,dk,H,T),0,2,1,3); ggml_tensor*vh=ggml_cont(c,ggml_permute(c,ggml_reshape_3d(c,v,dk,H,T),1,2,0,3)); ggml_tensor*kq=ggml_soft_max(c,ggml_scale(c,ggml_mul_mat(c,k,q),1.0f/sqrtf((float)dk))); ggml_tensor*o=ggml_cont_2d(c,ggml_permute(c,ggml_mul_mat(c,vh,kq),0,2,1,3),D,T); return ggml_add(c,lin(c,m.g(p+"linear_out.weight"),m.g(p+"linear_out.bias"),o),fm);} static ggml_tensor* enc_layer(ggml_context*c,model&m,const std::string&p,ggml_tensor*x,int T,bool res){ auto r=x;auto h=lnorm(c,x,m.g(p+"norm1.weight"),m.g(p+"norm1.bias"));auto sa=enc_attn(c,m,p+"self_attn.",h,T); x=res?ggml_add(c,r,sa):sa;r=x;h=lnorm(c,x,m.g(p+"norm2.weight"),m.g(p+"norm2.bias")); h=lin(c,m.g(p+"feed_forward.w_1.weight"),m.g(p+"feed_forward.w_1.bias"),h);h=ggml_relu(c,h); h=lin(c,m.g(p+"feed_forward.w_2.weight"),m.g(p+"feed_forward.w_2.bias"),h);return ggml_add(c,r,h);} // decoder FFN-SANM: w_2(LayerNorm(relu(w_1(x)))) (w_2 has no bias) static ggml_tensor* dec_ffn(ggml_context*c,model&m,const std::string&p,ggml_tensor*x){ auto h=lin(c,m.g(p+"w_1.weight"),m.g(p+"w_1.bias"),x);h=ggml_relu(c,h); h=lnorm(c,h,m.g(p+"norm.weight"),m.g(p+"norm.bias"));return ggml_mul_mat(c,m.g(p+"w_2.weight"),h);} // cross attn: q=linear_q(tgt)[D,N], kv=linear_k_v(mem)[2D,T] static ggml_tensor* cross_attn(ggml_context*c,model&m,const std::string&p,ggml_tensor*tgt,ggml_tensor*mem,int N,int T){ const int D=m.c.d_model,H=m.c.dec_head,dk=D/H; ggml_tensor*q=lin(c,m.g(p+"linear_q.weight"),m.g(p+"linear_q.bias"),tgt); // [D,N] ggml_tensor*kv=lin(c,m.g(p+"linear_k_v.weight"),m.g(p+"linear_k_v.bias"),mem);// [2D,T] size_t nb1=kv->nb[1]; ggml_tensor*k=ggml_cont(c,ggml_view_2d(c,kv,D,T,nb1,0)); ggml_tensor*v=ggml_cont(c,ggml_view_2d(c,kv,D,T,nb1,(size_t)D*sizeof(float))); q=ggml_permute(c,ggml_reshape_3d(c,q,dk,H,N),0,2,1,3); // [dk,N,H] k=ggml_permute(c,ggml_reshape_3d(c,k,dk,H,T),0,2,1,3); // [dk,T,H] ggml_tensor*vh=ggml_cont(c,ggml_permute(c,ggml_reshape_3d(c,v,dk,H,T),1,2,0,3)); // [T,dk,H] ggml_tensor*kq=ggml_soft_max(c,ggml_scale(c,ggml_mul_mat(c,k,q),1.0f/sqrtf((float)dk))); // [T,N,H] ggml_tensor*o=ggml_cont_2d(c,ggml_permute(c,ggml_mul_mat(c,vh,kq),0,2,1,3),D,N); return lin(c,m.g(p+"linear_out.weight"),m.g(p+"linear_out.bias"),o);} static ggml_tensor* dec_layer(ggml_context*c,model&m,const std::string&p,ggml_tensor*tgt,ggml_tensor*mem,int N,int T){ const int D=m.c.d_model,K=m.c.dec_kernel; auto residual=tgt;auto h=lnorm(c,tgt,m.g(p+"norm1.weight"),m.g(p+"norm1.bias")); h=dec_ffn(c,m,p+"feed_forward.",h); // FFN first auto y=lnorm(c,h,m.g(p+"norm2.weight"),m.g(p+"norm2.bias")); auto sa=fsmn(c,y,m.g(p+"self_attn.fsmn_block.weight"),D,N,K); // FSMN self-attn (+residual y inside) auto x=ggml_add(c,residual,sa); residual=x;auto z=lnorm(c,x,m.g(p+"norm3.weight"),m.g(p+"norm3.bias")); auto ca=cross_attn(c,m,p+"src_attn.",z,mem,N,T); return ggml_add(c,residual,ca);} static ggml_tensor* dec3_layer(ggml_context*c,model&m,const std::string&p,ggml_tensor*tgt){ auto h=lnorm(c,tgt,m.g(p+"norm1.weight"),m.g(p+"norm1.bias"));return dec_ffn(c,m,p+"feed_forward.",h);} static void add_posenc(std::vector&x,int T,int depth){double inc=log(10000.0)/(depth/2.0-1.0); for(int t=0;t run_graph(ggml_context*c,ggml_tensor*out,ggml_tensor*in1,const float*d1,ggml_tensor*in2,const float*d2){ ggml_backend_t be=ggml_backend_cpu_init();ggml_cgraph*gf=ggml_new_graph_custom(c,32768,false);ggml_build_forward_expand(gf,out); ggml_gallocr_t ga=ggml_gallocr_new(ggml_backend_cpu_buffer_type());ggml_gallocr_alloc_graph(ga,gf); ggml_backend_tensor_set(in1,d1,0,ggml_nbytes(in1));if(in2)ggml_backend_tensor_set(in2,d2,0,ggml_nbytes(in2)); ggml_backend_cpu_set_n_threads(be,8);ggml_backend_graph_compute(be,gf); int D=out->ne[0],N=out->ne[1];std::vectorr((size_t)D*N);ggml_backend_tensor_get(out,r.data(),0,ggml_nbytes(out)); ggml_gallocr_free(ga);ggml_backend_free(be);return r;} // Paraformer detok: tokens.json is BPE; join, drop "@@" continuations, "▁"(U+2581)->space. static std::string pf_trim(const std::string&s){size_t a=s.find_first_not_of(' ');if(a==std::string::npos)return "";size_t b=s.find_last_not_of(' ');return s.substr(a,b-a+1);} static std::string detok_pf(const std::vector&ids,const std::vector&vocab){ std::string s; for(int id:ids){ if(id==1||id==2)continue; if(id>=0&&id<(int)vocab.size())s+=vocab[id]; } size_t p; while((p=s.find("@@"))!=std::string::npos)s.erase(p,2); const std::string lb="\xe2\x96\x81"; while((p=s.find(lb))!=std::string::npos)s.replace(p,3," "); return pf_trim(s); } int main(int argc,char**argv){ std::string gguf_path,wav_path,vad_path; int vad_maxseg=30000; bool ids_mode=false; for(int i=1;i vocab; {int ki=gguf_find_key(gg,"pf.vocab"); if(ki>=0){int nv=gguf_get_arr_n(gg,ki); vocab.resize(nv); for(int i=0;idata,*scale=(float*)m.g("cmvn.scale")->data; float*cw=(float*)m.g("predictor.cif_conv1d.weight")->data; // [512,512,3] = [o][i][j] float*cb=(float*)m.g("predictor.cif_conv1d.bias")->data; // [512] float*ow=(float*)m.g("predictor.cif_output.weight")->data; // [1,512] float ob=((float*)m.g("predictor.cif_output.bias")->data)[0]; // Full pipeline (fbank -> CMVN -> encoder -> CIF -> decoder) on one wav window; prints token IDs. auto run_seg=[&](const std::vector& wav){ int T=0;auto fb=compute_fbank(wav,T); if(T<1)return; for(int t=0;tenc; {ggml_init_params cp={(size_t)1024*1024*1024,nullptr,true};ggml_context*c=ggml_init(cp); ggml_tensor*x=ggml_new_tensor_2d(c,GGML_TYPE_F32,F,T);ggml_set_input(x); ggml_tensor*h=enc_layer(c,m,"encoder.encoders0.0.",x,T,false); for(int i=0;i cif_output -> sigmoid -> alpha ===== std::vector outp((size_t)T*D); std::vector alphas(T); for(int t=0;t=T)continue; const float*ev=&enc[(size_t)tt*D]; const float*wo=&cw[(size_t)o*D*3]; for(int i=0;i0?s:0; } std::vector hid=enc; hid.resize((size_t)(T+1)*D,0.0f); std::vector al=alphas; al.push_back(m.c.tail); int L=T+1; std::vector acoustic; acoustic.reserve(64*D); float integrate=0; std::vector frame(D,0.0f); for(int t=0;t=m.c.thresh; float cur=fire?dc:alpha; float rem=alpha-cur; for(int d=0;d logits; {ggml_init_params cp={(size_t)2048*1024*1024,nullptr,true};ggml_context*c=ggml_init(cp); ggml_tensor*tgt=ggml_new_tensor_2d(c,GGML_TYPE_F32,D,N);ggml_set_input(tgt); ggml_tensor*mem=ggml_new_tensor_2d(c,GGML_TYPE_F32,D,T);ggml_set_input(mem); ggml_tensor*x=tgt; for(int i=0;i seg_ids; seg_ids.reserve(N); for(int n=0;nbest){best=col[v];am=v;}seg_ids.push_back(am);} if(emit_ids){ for(int id:seg_ids) printf("%d ",id); } else { std::string t=detok_pf(seg_ids,vocab); printf("%s",t.c_str()); } }; int64_t t0=ggml_time_us(); std::vectorwav;if(!funasr_load_audio_16k_mono(wav_path.c_str(),wav)){fprintf(stderr,"read audio failed\n");return 1;} if(!vad_path.empty()){ std::vector> segs; if(!funasr_vad_segments(vad_path,wav,vad_maxseg,segs)){fprintf(stderr,"vad failed\n");return 1;} for(auto&s:segs){ int off=(int)((int64_t)s.first*16000/1000), end=(int)((int64_t)s.second*16000/1000); if(end>(int)wav.size())end=wav.size(); if(end-off seg(wav.begin()+off,wav.begin()+end); run_seg(seg); } fprintf(stderr,"[paraformer] %zu vad segments\n",segs.size()); } else { run_seg(wav); } printf("\n"); fprintf(stderr,"[paraformer] done %.2fs\n",(ggml_time_us()-t0)/1e6); if(m.ctx_w) ggml_free(m.ctx_w); return 0; }