数字MIC(es7202 PDM协议)MIC录音声音较小

问题:我司有个项目,android 11 rk3566 的项目,该项目带audio 模块,项目MIC 使用的es7202(ADC),该芯片是一个编码芯片,没有解码功能,该模块的录音的增益已经调到最大,但录入的MIC音量还是很小,硬件也没有解决的办法,该数字MIC 利用的是PDM 协议,而PDM数字信号较为复杂。PDM信号在我的其它文章中有介绍。

rk3566 android11 配置声卡(es7202 ADC)_android不会飞的博客-CSDN博客_android 声卡

分析:MIC给到CPU为ES7202输出数字MIC信号,请确认CPU内对数字MIC信号的放大处理是否有?理论上是有的,CPU PDM 数据信号默认为可接受低幅值输入,而PDM 信号在CPU 中的处理是相对复杂的。基于PDM接口的应用降低了发送设备的复杂性,由于作为接收设备的CODEC内部集成抽取滤波器,因此系统整体复杂度大大降低。而PDM则使用远高于PCM采样率的时钟采样调制模拟分量,只有1位输出,要么为0,要么为1。因此通过PDM方式表示的数字音频也被称为Oversampled 1-bit Audio。相比PDM一连串的0和1,PCM的量化结果更为直观简单。以PDM方式作为模数转换的接收端,需要用到抽取滤波器(Decimation Filter),将密密麻麻的0和1代表的密度分量转换为幅值分量,而PCM方式得到的已经是幅值分量了。

解决方向:由于我们的es7202 MIC 增益已经调增到最大,但还是没有好的效果,无法实现增大MIC录音的效果。直接去处理PDM数据信号,由于 DATA MIC录入的数据,而PDM的数据复杂度较高我们不好去处理相关的数据,所以我们采用第三种,就是在tinyalsa 中处理,上层android应用在调用底层驱动时,是同过HAL层来调用底层驱动,通过驱动硬件工作,而在HAL层中的处理会调用,由于我们使用的是audio ALSA 架构,上层调用驱动会经过相关的pcm处理函数,相信使用过tinycap ,tinyplay 可能比较了解,如果不太了解的话,可以去看一下相关的源码,在我们调试过程中时,用的比较多相关命令,继续分析...... 而我们音频采样,PCM流是什么样的呢,如果是单声道,那每个采样的数据,这里我们就把数据看成一个个整型数据,它是一连串的,每个整数占据2个字节(16-bit),9个采样也就是18字节的数据。每个采样的整数大小最小为 -32768,最大为 32768(注意:这里的数据类型是short 类型的) 。根据采样数据的位置和值画一个图的话,就会得到像播放器上那样的波浪形图。而立体声,其实就是双通道采样的,其数据也看成一串数据的话,它其实就是左右声道的数据交叉存放,每一个frame是一个16-bit的采样点。所以我们可以在alsa调用底层驱动时,获取到的PCM原始数据进行处理操作,而我们所要做的操作就是将它放大。而这就解决了我们的问题,其原理就是将MIC 录入的PCM原始数据看成一个正弦波,而我们要做的就是将该正弦波的振幅放大,这就相当于增加了PCM数据的音量了,只需要将每一个采样的数据乘以一个系数就行了。

首先我们先来看一下HAL层的调用函数代码:

static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
                           struct resampler_buffer* buffer)
{
    struct stream_in *in;
    size_t i,size;

    if (buffer_provider == NULL || buffer == NULL)
        return -EINVAL;

    in = (struct stream_in *)((char *)buffer_provider -
                              offsetof(struct stream_in, buf_provider));

    if (in->pcm == NULL) {
        buffer->raw = NULL;
        buffer->frame_count = 0;
        in->read_status = -ENODEV;
        return -ENODEV;
    }

    if (in->frames_in == 0) {
        size = pcm_frames_to_bytes(in->pcm,pcm_get_buffer_size(in->pcm)
        in->read_status = pcm_read(in->pcm,//这里就调用了pcm_read,来起一个过度作用,其实是使用了tinyalsa,来获取原始pcm数据,(void*)in->buffer,中装入了PCM 原始数据。
                                   (void*)in->buffer,pcm_frames_to_bytes(in->pcm, in->config->period_size));
        if (in->read_status != 0) {
            ALOGE("get_next_buffer() pcm_read error %d", in->read_status);
            buffer->raw = NULL;
            buffer->frame_count = 0;
            return in->read_status;
        }

        //fwrite(in->buffer,pcm_frames_to_bytes(in->pcm,pcm_get_buffer_size(in->pcm)),1,in_debug);
        in->frames_in = in->config->period_size;

        /* Do stereo to mono conversion in place by discarding right channel */
        if ((in->channel_mask == AUDIO_CHANNEL_IN_MONO)
                &&(in->config->channels == 2)) {
            //ALOGE("channel_mask = AUDIO_CHANNEL_IN_MONO");
            for (i = 0; i < in->frames_in; i++)
                in->buffer[i] = in->buffer[i * 2];
        }
    }

    //ALOGV("pcm_frames_to_bytes(in->pcm,pcm_get_buffer_size(in->pcm)):%d",size);
    buffer->frame_count = (buffer->frame_count > in->frames_in) ?
                          in->frames_in : buffer->frame_count;
    buffer->i16 = in->buffer +
                  (in->config->period_size - in->frames_in) *
                  audio_channel_count_from_in_mask(in->channel_mask);

    return in->read_status;

}

再来看一下tinyalsa,中的代码,我这里主要给的是输入流的相关代码,即录音时,而播放流与其相反。

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)) {//用户层通过ioctl的方式来调用kernel,//读出入初始化数据,即录音。
            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 (pcm->config.channels == 2) {
            if (channalFlags == -1 ) {
                if (startCheckCount < SAMPLECOUNT) {
                    startCheckCount += count;
                } else {
                    channalFlags = channel_check(data,count / 2);
                }
            }

            channel_fixed(data,count / 2, channalFlags);
        }

        return 0;
    }
}

kernel层的实现,在内核中发起系统调用,执行本应用户空间发起的fops函数集。

kernel 内核版本,4.19.172

PCM逻辑设备文件操作函数集:snd_pcm_f_ops[]
PCM逻辑设备文件操作函数集对于Playback和Capture是分开定义的,该操作函数集如下:

const struct file_operations snd_pcm_f_ops[2] = {
        {
                .owner =                THIS_MODULE,
                .write =                snd_pcm_write,//用于单通道音频信号写
                .write_iter =           snd_pcm_writev, 
                .open =                 snd_pcm_playback_open,
                .release =              snd_pcm_release,
                .llseek =               no_llseek,
                .poll =                 snd_pcm_poll,
                .unlocked_ioctl =       snd_pcm_ioctl,//用于多通道音频信号写
                .compat_ioctl =         snd_pcm_ioctl_compat,
                .mmap =                 snd_pcm_mmap,
                .fasync =               snd_pcm_fasync,
                .get_unmapped_area =    snd_pcm_get_unmapped_area,
        },
        {
                .owner =                THIS_MODULE,
                .read =                 snd_pcm_read,//用于单通道音频信号读
                .read_iter =            snd_pcm_readv,
                .open =                 snd_pcm_capture_open,
                .release =              snd_pcm_release,
                .llseek =               no_llseek,
                .poll =                 snd_pcm_poll,
                .unlocked_ioctl =       snd_pcm_ioctl,//用于多通道音频信号读
                .compat_ioctl =         snd_pcm_ioctl_compat,
                .mmap =                 snd_pcm_mmap,
                .fasync =               snd_pcm_fasync,
                .get_unmapped_area =    snd_pcm_get_unmapped_area,
        }
};

        先来看一下我们es7202 的硬件电路图,以及框架图与相关的调节增益的相关寄存器。

es7202 内部框架图:

es7202外围电路,相对较少:

 

es7202内部调制MIC 增益的寄存器: 

 

        所以从整体来说,使用数字MIC来讲,整体的优势还是比较明显的,外围电路比较少,录入的MIC数据同CLK 的采集混入PDM DATA 通过PDM数据接口直接送入CPU内部,进行处理。 

下面给到修改的patch,目录在external/tinyalsa/pcm.c

diff --git a/pcm.c b/pcm.c
index 0b97bbc..01d777e 100644
--- a/pcm.c
+++ b/pcm.c
@@ -635,13 +635,36 @@ void channel_fixed(void *data, unsigned len, int chFlag)
     return;
 }
 
+static short out_vol = 4.0;//扩大音量倍数
+static void volume_process(const void *buffer, size_t length, short volume) {
+
+short * buffer_end = (short*)buffer + length/2;
+       short * pcmData = (short *)buffer;
+          int i = 0;
+       int pcmval;
+
+       while (pcmData < buffer_end) {
+               pcmval = (short)*pcmData * volume;
+                if (pcmval < 32768 && pcmval > -32768) {
+                        *pcmData = pcmval;
+                } else if (pcmval > 32767) {
+                        *pcmData = 32767; //限制最大幅度
+                } else if (pcmval < -32767) {
+                        *pcmData = -32767;//限制最小幅度
+                }
+                
+            ++pcmData;
+           i++;
+       }
+}
+
 int pcm_read(struct pcm *pcm, void *data, unsigned int count)
 {
     struct snd_xferi x;
 
     if (!(pcm->flags & PCM_IN))
         return -EINVAL;
-
+       memset(data, 0, count);
     x.buf = data;
     x.frames = count / (pcm->config.channels *
                         pcm_format_to_bits(pcm->config.format) / 8);
@@ -676,6 +699,7 @@ int pcm_read(struct pcm *pcm, void *data, unsigned int count)
             channel_fixed(data,count / 2, channalFlags);
         }
 
+       volume_process(x.buf, count , out_vol);
         return 0;
     }
 }

 理论上来说,该patch也可解决其它codec引起的输入MIC录入音量较小的问题。

猜你喜欢

转载自blog.csdn.net/qq_48709036/article/details/124991843