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
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)
{
......
signal(SIGINT, sigint_handler);
frames = capture_sample(file, card, device, header.num_channels,
header.sample_rate, format,
period_size, period_count);
printf("Captured %d frames\n", 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 bit\n", 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;
x.buf = data;
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;
}
}
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
pcm->prepared = 0;
pcm->running = 0;
if (errno == EPIPE) {
pcm->underruns++;
continue;
}
return oops(pcm, errno, "cannot read stream data");
}
if(!(pcm->config.channels == 1))
{
if(channalFlags == -1 )
{
if(startCheckCount < SAMPLECOUNT)
{
startCheckCount += count;
}
else
{
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;
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++;
}
}
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++;
}
}
if(rightValid >20)
rightValid = 0x02;
else
rightValid = 0;
return leftValid|rightValid;
}
void channel_fixed(void * data, unsigned len, int chFlag)
{
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,没有出现语音无法识别的问题了!
Original: https://blog.csdn.net/qq_21059825/article/details/112901386
Author: 杨涂涂
Title: RK3328 Android 7.1 录音左右声道分离的情况下,有时候会出现,右声道的声音和左声道一样的问题
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/515876/
转载文章受原作者版权保护。转载请注明原作者出处!