項(xiàng)目中遇到一個(gè)問題拐迁,集成的語(yǔ)音識(shí)別模塊胸私,由于對(duì)采樣率有要求挫掏,所以識(shí)別結(jié)束后保存的PCM音頻文件是用的16K的采樣率,而原先的項(xiàng)目中特纤,包括安卓端的語(yǔ)音播報(bào)也都是使用8K的采樣率军俊,這樣就出現(xiàn)個(gè)問題:是把16K的PCM轉(zhuǎn)換成16K的AMR還8K的AMR?
方案一:16K PCM 轉(zhuǎn)換成 16K AMR
由于對(duì)音頻轉(zhuǎn)換相關(guān)方面知識(shí)的欠缺,只好上谷歌捧存、百度粪躬,也成功的找到一篇關(guān)于轉(zhuǎn)換的文章:使用opencore_amr實(shí)現(xiàn)WAV 轉(zhuǎn) AMR (8Khz,16Khz)(更新版)
解決方案也很簡(jiǎn)單,先將PCM轉(zhuǎn)換成WAV昔穴,其實(shí)PCM就是缺少信息頭的WAV镰官,所以轉(zhuǎn)換WAV就是給PCM添加頭信息。
這里有現(xiàn)成解決方案傻咖,請(qǐng)參考別人實(shí)現(xiàn)的代碼,git地址如下:https://github.com/codemonkeybulucck/opencore-amrDemo-iOS
方案二:16K PCM轉(zhuǎn)換成8K PCM岖研,再轉(zhuǎn)換成8K AMR
通過方案一我已經(jīng)成功轉(zhuǎn)換成16K的AMR卿操,上傳TFS服務(wù)器上后警检,發(fā)現(xiàn)H5無(wú)法正常播報(bào),安卓端還沒有轉(zhuǎn)換成功害淤,推薦給安卓方案二扇雕,結(jié)果他們找到了一個(gè)網(wǎng)上的現(xiàn)成解決方案:JSSRC,并且順利完成把16K PCM轉(zhuǎn)換成了8K AMR窥摄,也解決后續(xù)H5播放的問題镶奉。
通過代碼了解到,JSSRC是基于SSRC的C語(yǔ)言版本翻譯成JAVA崭放,于是在github上找到了C語(yǔ)言版的源碼:https://github.com/ronalde/ssrc-packaging
通過研究JAVA版和C語(yǔ)言版哨苛,最終在ssrc.c中新增了一個(gè)轉(zhuǎn)換方法:
int tansfer(FILE *fpi,FILE *fpo,int sfrq,int dfrq,int bps, int dbps, int nch,unsigned int length, double att, int dither, int quiet_) {
int twopass = 0;
int pdf = 0;
double noiseamp, peak;
noiseamp = 0.18;
if (dither < 0 || dither > 4) {
fprintf(stderr,"unrecognized dither type : %d\n", dither);
return -1;
}
quiet = quiet_;
if (!quiet) printf("Shibatch sampling rate converter version " VERSION "\n\n");
if (bps != 1 && bps != 2 && bps != 3 && bps != 4) {
fprintf(stderr,"Error: Only 8bit, 16bit and 24bit PCM are supported.\n");
return -1;
}
if (dbps == -1) {
if (bps != 1) {
dbps = bps;
} else {
dbps = 2;
}
if (dbps == 4) {
dbps = 3;
}
}
if (dfrq == -1) {
dfrq = sfrq;
}
if (dither == -1) {
if (dbps < bps) {
if (dbps == 1) {
dither = 4;
} else {
dither = 3;
}
} else {
dither = 1;
}
}
if (!quiet) {
const char *dtype[] = {
"none","no noise shaping","triangular spectral shape","ATH based noise shaping","ATH based noise shaping(less amplitude)"
};
const char *ptype[] = {
"rectangular","triangular","gaussian"
};
printf("frequency : %d -> %d\n",sfrq,dfrq);
printf("attenuation : %gdB\n",att);
printf("bits per sample : %d -> %d\n",bps*8,dbps*8);
printf("nchannels : %d\n",nch);
printf("length : %d bytes, %g secs\n",length,(double)length/bps/nch/sfrq);
if (dither == 0) {
printf("dither type : none\n");
} else {
printf("dither type : %s, %s p.d.f, amp = %g\n",dtype[dither],ptype[pdf],noiseamp);
}
printf("\n");
}
if (sfrq < dfrq) {
peak = upsample(fpi,fpo,nch,bps,dbps,sfrq,dfrq,1,length/bps/nch,twopass,dither);
}
else if (sfrq > dfrq) {
peak = downsample(fpi,fpo,nch,bps,dbps,sfrq,dfrq,1,length/bps/nch,twopass,dither);
}
else {
peak = no_src(fpi,fpo,nch,bps,dbps,1,length/bps/nch,twopass,dither);
}
if (!quiet) {
printf("\n");
}
if (dither != 0) {
quit_shaper(nch);
}
if (!twopass && peak > 1) {
if (!quiet) printf("clipping detected : %gdB\n",20*log10(peak));
}
return 0;
}
通過下述的使用方式,即可完成16kPCM到8kPCM的轉(zhuǎn)換币砂,至于8kPCM到8kAMR就通過大家常用的opencore-amr庫(kù)就可以實(shí)現(xiàn)了建峭。
char *pcm16k = (char *)[[[NSBundle mainBundle] pathForResource:@"uscvoice16k" ofType:@"pcm"] cStringUsingEncoding:NSUTF8StringEncoding];
FILE *fpi = fopen(pcm16k, "rb");
if (fpi == NULL)
{
return NO;
}
NSString *documentDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
char *pcm8k = (char *)[[documentDir stringByAppendingPathComponent:@"pcm8k.pcm"] cStringUsingEncoding:NSUTF8StringEncoding];
// 創(chuàng)建并初始化amr文件
FILE *fpo = fopen(pcm8k, "wb");
if (fpo == NULL)
{
fclose(fpi);
return NO;
}
tansfer(fpi, fpo, 16000, 8000, 2, 2, 1, INT_MAX, 0,0, true);