1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
为什么RK3328 Android 7.1录音会出现偶现语音无法识别的情况呢?
怎样去解决RK3328 Android 7.1录音出现偶现语音无法识别的问题?
|
|
相关推荐
1个回答
|
|
RK3328 Android 7.1 录音左右声道分离的情况下,有时候会出现,右声道的声音和左声道一样的问题
问题现象: 产品有语音识别功能,需要回音消除,所以立体声录音需要左右声道分离,左声道为主MIC,右声道为回音消除MIC,产品偶现语音无法识别问题,查看出问题时候的PCM数据,左右声道数据一样,并且都是左声道数据,导致回音消除之后,软件以为没有说话! RK3328 Android 7.1平台,默认左右声道是没有分离的,左右声道叠加在一起,需要注释掉宏“#define SPEEX_DENOISE_ENABLE”,在文件“hardware/rockchip/audio/tinyalsa_hal/audio_hw.h”里,来使能左右声道分离的功能! 为了排除APK和上层系统的原因,当问题出现的时候,使用“tinycap”命令在shell下抓取PCM数据,发现确实是左右声道一样,那么肯定和APK或者framework没有关系。 rk3328_box:/storage # tinycap ./test.wav -D 0 -d 0 Cc 2 Cr 44100 Cb 16 Cp 1024 Cn 3 Capturing sample: 2 ch, 44100 hz, 16 bit Captured 483328 frames 为了排除codec传入的数据问题,当问题出现的时候,直接测量codec的I2S输出信号,发现左右声道数据是不一样的,所以,也不是codec硬件或者驱动的问题。 因为使用“tinycap”命令抓取数据也有问题,并且“tinycap”命令已经是非常底层的操作了,所以从“tinycap”命令入手分析,“tinycap”命令源码目录:external/tinyalsa/tinycap.c 主要调用关系如下:main->capture_sample->pcm_read int main(int argc, char **argv) { ...... /* install signal handler and begin capturing */ signal(SIGINT, sigint_handler); frames = capture_sample(file, card, device, header.num_channels, header.sample_rate, format, period_size, period_count); printf("Captured %d framesn", frames); ...... return 0; } unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, unsigned int rate, enum pcm_format format, unsigned int period_size, unsigned int period_count) { ...... pcm = pcm_open(card, device, PCM_IN, &config); ...... printf("Capturing sample: %u ch, %u hz, %u bitn", channels, rate, pcm_format_to_bits(format)); while (capturing && !pcm_read(pcm, buffer, size)) { ...... } ...... } 使用“pcm_open”函数打开驱动/dev/snd/下面的录音节点,然后用“pcm_read”函数录到数据,再写进文件里面。 “pcm_open”函数和“pcm_read”函数源码目录:external/tinyalsa/pcm.c 其中“pcm_read”函数如下: int pcm_read(struct pcm *pcm, void *data, unsigned int count) { struct snd_xferi x; if (!(pcm->flags & PCM_IN)) return -EINVAL; //数据buffer的指针 x.buf = data; //上面buffer的大小能放的帧数,对于2通道,16bit数据,帧数为 count / 4 x.frames = count / (pcm->config.channels * pcm_format_to_bits(pcm->config.format) / 8); for (;;) { if (!pcm->running) { if (pcm_start(pcm) < 0) { fprintf(stderr, "start error"); return -errno; } } //这里从驱动拿到硬件数据,存放在 x.buf 里面 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { pcm->prepared = 0; pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart */ pcm->underruns++; continue; } return oops(pcm, errno, "cannot read stream data"); } //这个if判断里面的操作,就有点迷惑,问题就出在这里,下面分析 //========================================================= //因为我们录音是2通道,这个条件会一直成立 if(!(pcm->config.channels == 1)) { //channalFlags变量初始值为 -1 ,所以,第一次调用,这里会执行一次 if(channalFlags == -1 ) { //startCheckCount变量初始值为0,SAMPLECOUNT=441*5*2*2,所以这里,第一次调用也会执行 if(startCheckCount < SAMPLECOUNT) { startCheckCount += count; } //上面执行一两次之后,startCheckCount不满足上面条件,就会执行这里 else { //在这里改变了channalFlags的值,之后 if(channalFlags == -1 ) 就不会再执行了 channalFlags = channel_check(data,count/2); } } //这里每次都会执行 channel_fixed(data,count/2, channalFlags); } //========================================================= return 0; } } 当看到“pcm_read”函数时,发现了一段代码比较迷惑,就是上面等号分割的部分,几个变量初始值如下: #define SAMPLECOUNT 441*5*2*2 int channalFlags = -1; int startCheckCount = 0; 再看上面用到的两个函数“channel_check”和“channel_fixed”: int channel_check(void * data, unsigned len) { short * pcmLeftChannel = (short *)data; //数据以左声道开始 short * pcmRightChannel = pcmLeftChannel+1;//右声道和左声道交错放置,相差16bit unsigned index = 0; int leftValid = 0x0; int rightValid = 0x0; short checkValue = 0; //拿到左声道第一个数据 checkValue = *pcmLeftChannel; //循环对比左声道其他数据 for(index = 0; index < len; index += 2) { if((pcmLeftChannel[index] >= checkValue+50)||(pcmLeftChannel[index] <= checkValue-50)) { leftValid++;// = 0x01; } } //如果有效数据的个数大于20个,则左声道有效 if(leftValid >20) leftValid = 0x01; else leftValid = 0; //右声道判断和上面左声道一样 checkValue = *pcmRightChannel; for(index = 0; index < len; index += 2) { if((pcmRightChannel[index] >= checkValue+50)||(pcmRightChannel[index] <= checkValue-50)) { rightValid++;//= 0x02; } } if(rightValid >20) rightValid = 0x02; else rightValid = 0; //返回值有4个,0,1,2,3,用来设置变量 channalFlags //0:左右声道都无效;1:左声道有效;2:右声道有效;3:左右都有效 return leftValid|rightValid; } void channel_fixed(void * data, unsigned len, int chFlag) { //这里判断的就是变量 channalFlags,只有单个声道有效的话,函数才起作用 if(chFlag <= 0 || chFlag > 2 ) return; //下面的操作就是,把有效声道的数据,复制到无效声道去 short * pcmValid = (short *)data; short * pcmInvalid = pcmValid; if(chFlag == 1) pcmInvalid += 1; else if (chFlag == 2) pcmValid += 1; unsigned index ; for(index = 0; index < len; index += 2) { pcmInvalid[index] = pcmValid[index]; } return; } 分析到这里就知道了,那段比较迷惑的代码,是在开始录音的时候,判断每个通道的声音是否有效,判断完成之后,某一个声道无效的话,用另一个有效声道的数据覆盖! 结合实际现象,我们测试的时候,右声道并没有接硬件设备,并且录出来的错误声音数据,都是左声道有接设备的数据,所以,推测就是这里导致的这个问题的产生,为了确定是这里问题,加log打印,先打印一下从驱动读上来的buffer的左右声道前3个数据,再打印一下,“tinycap”命令写入文件时候的每次buffer的左右声道前3个数据,log如下: pcm_read--> pcmLeftChannel=[-29],[-21],[23]; pcmRightChannel=[2],[-1],[-24]; capture_sample--> pcmLeftChannel=[-29],[-21],[23]; pcmRightChannel=[-29],[-21],[23]; 可以看到,确实是右声道的数据被左声道覆盖了,然后屏蔽掉那段比较迷惑的代码,再打印log如下: pcm_read--> pcmLeftChannel=[149],[87],[79]; pcmRightChannel=[-7],[-2],[-14]; capture_sample--> pcmLeftChannel=[149],[87],[79]; pcmRightChannel=[-7],[-2],[-14]; 这下右声道就不会被覆盖了! 实际软件功能验证也OK,没有出现语音无法识别的问题了! |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
基于米尔瑞芯微RK3576核心板/开发板的人脸疲劳检测应用方案
487 浏览 0 评论
759 浏览 1 评论
654 浏览 1 评论
1884 浏览 1 评论
3131 浏览 1 评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 12:40 , Processed in 0.531274 second(s), Total 42, Slave 34 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号