我是靠谱客的博主 平淡火,最近开发中收集的这篇文章主要介绍rk音频驱动分析之tinycap录音,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一.Tinycap分析

使用命令:tinycap /sdcard/test.wav -D card0 -d device0 -c 2 -r 48000 -b 16 -p 1024 -n 16

Tinycap.c (externaltinyalsa)

录音流程:

    #define ID_RIFF 0x46464952 //这个值是RIFF的ASCII值

    #define ID_WAVE 0x45564157 //这个值是WAVE的ASCII值

    #define ID_FMT 0x20746d66

    #define ID_DATA 0x61746164 //这个值是DATA的ASCII值

 

main函数

    header.riff_id = ID_RIFF;  //这里是RIFF,标志符

    header.riff_sz = 0;

    header.riff_fmt = ID_WAVE; // 格式类型WAVE

    header.fmt_id = ID_FMT; //波形文件标志:FMT(最后一位空格符)

    header.fmt_sz = 16; //数值是16或者18,18是有附带了信息

    header.audio_format = FORMAT_PCM; //编码方式,一般是1

    header.num_channels = channels;  //声道数值,1是单声道,2是双声道

    header.sample_rate = rate; //采样频率

    format = PCM_FORMAT_S24_LE; //每个采样需要的bytes

    header.bits_per_sample = pcm_format_to_bits(format); //转化为bit数

    header.byte_rate = (header.bits_per_sample / 8) * channels * rate; //每秒传输的byte数

    header.block_align = channels * (header.bits_per_sample / 8);    //每个采样的byte数

    header.data_id = ID_DATA; //Data Chunk,数据块

    

    /* leave enough room for header */

    fseek(file, sizeof(struct wav_header), SEEK_SET); //给header足够的空间

    /* install signal handler and begin capturing */

    signal(SIGINT, sigint_handler); //SIGINT   :来自键盘的中断信号 ( ctrl + c ) .

    //开始录制,单独分析1

    frames = capture_sample(file, card, device, header.num_channels,  header.sample_rate, format,  period_size, period_count);

 

 

1.单独分析1

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);

        pcm->config = *config;

        

        snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, flags & PCM_IN ? 'c' : 'p');

        pcm->fd = open(fn, O_RDWR); //打开/dev/snd/pcmC0D0c,与rk音频驱动分析之tinyplay播放类似

        //获取pcm的信息,包括card,流,dma各种信息,与rk音频驱动分析之tinyplay播放类似

        ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)

        param_init(&params); //限制参数初始化

        .............一些限制参数设置..............

         param_set_flag(&params, config->flag);

        //根据底层的限制,重新给params赋值,设置cpu dai和codec dai的类型和时钟等

        ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params) //与rk音频驱动分析之tinyplay播放类似

        /* get our refined hw_params */

        config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); //得到period大小

        config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS); //数量

        pcm->buffer_size = config->period_count * config->period_size; //总的buf大小

        if (flags & PCM_MMAP) //如果是映射,

            //mmap将一个文件或者其它对象映射进内存,调用底层的mmap函数,与rk音频驱动分析之tinyplay播放类似

             pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),

                                PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);

        //包括一些period_step ,silence_threshold 等参数,与rk音频驱动分析之tinyplay播放类似

        ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)  //主要是把上层的参数赋值给snd_pcm_runtime

        //与rk音频驱动分析之tinyplay播放类似

        rc = pcm_hw_mmap_status(pcm); //分配SNDRV_PCM_MMAP_OFFSET_CONTROL的内存映射,

        #ifdef SNDRV_PCM_IOCTL_TTSTAMP

        int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;

        rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg); //设置为绝对时间

    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); //得到ring buf的大小

    buffer = malloc(size); //分配

    while (capturing && !pcm_read(pcm, buffer, size)) //从PCM里面读取数据到buffer里面,单独分析2

        if (fwrite(buffer, 1, size, file) != size) //把buf的数据写入到file

        bytes_read += size; //+size

 

单独分析2

int pcm_read(struct pcm *pcm, void *data, unsigned int count)

    x.buf = data; //buf的地址

    x.frames = count / (pcm->config.channels * pcm_format_to_bits(pcm->config.format) / 8); //表示有多少帧

    for (;;)

        if (!pcm->running)

            if (pcm_start(pcm) < 0) //调用驱动预备pcm可以触发,这里就是搜索path,widgets相关,

                ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) //与rk音频驱动分析之tinyplay播放类似

            ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x) //这里调用驱动进行读取操作,驱动分析一

 

 

二.驱动分析一

调用snd_pcm_capture_ioctl的snd_pcm_capture_ioctl1函数

static int snd_pcm_capture_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg)

    switch (cmd)

    case SNDRV_PCM_IOCTL_READI_FRAMES:

        struct snd_xferi __user *_xferi = arg; //获取应用传来的参数

        copy_from_user(&xferi, _xferi, sizeof(xferi)) //把_xferi参数结构体从用户空间拷贝过来

        result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); //读取数据

            err = pcm_sanity_check(substream);  //检查有不有读写函数

            //用snd_pcm_lib_read_transfer函数读取数据,单独分析1

            snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);

 

 

单独分析1

static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, unsigned long data,

        snd_pcm_uframes_t size, int nonblock,  transfer_f transfer)

    switch (runtime->status->state)

    case SNDRV_PCM_STATE_PREPARED:

        //开始传输,与rk音频驱动分析之tinyplay播放相似,我们这里是录音,通路方向不一样

        //主要是启动DMA开始读取数据,然后启动I2S开始读取codec的数据

        err = snd_pcm_start(substream); //start all linked streams

    ........

    if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)

        snd_pcm_update_hw_ptr(substream); //更新ring buf相关的位置

    avail = snd_pcm_capture_avail(runtime);  //获取DMA已经写入的ring缓冲区大小,就是可读数据的大小

        snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr; 

    while (size > 0)

        if (!avail) //没有可读的数据

            if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) //暂时不知道

                snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); //停止

            if (nonblock) //如果是非阻塞,直接返回

                err = -EAGAIN;

            err = wait_for_avail(substream, &avail); //等待

        frames = size > avail ? avail : size; //读取的大小

        //这里还没有搞懂

        cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;

        if (frames > cont)

            frames = cont;

        appl_ptr = runtime->control->appl_ptr; //读指针的值

        appl_ofs = appl_ptr % runtime->buffer_size; //读指针在buf里面的偏移

        err = transfer(substream, appl_ofs, data, offset, frames); //单独分析2,这里是把数据拷贝给user

        appl_ptr += frames; //读地址加刚才拷贝的数据大小

        if (appl_ptr >= runtime->boundary) //循环

            appl_ptr -= runtime->boundary;

        runtime->control->appl_ptr = appl_ptr;

        if (substream->ops->ack) //这里没有

           substream->ops->ack(substream);

        offset += frames; //偏移增加

        size -= frames; //size减少

        xfer += frames; //传输xfer增加

        avail -= frames; //可读的减少

 

 

2.单独分析2

static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, unsigned int hwoff,

         unsigned long data, unsigned int off, snd_pcm_uframes_t frames)

    //获取buf的地址,这里off是偏移地址

    char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);

    if (substream->ops->copy) //这里没有

        err = substream->ops->copy(substream, -1, hwoff, buf, frames)

    else

        char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); //获取ring buf读的地址

        copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)) //把dma的从hwbuf开始的数据拷贝到user,大小为frames

 

分类: Linux音频相关

最后

以上就是平淡火为你收集整理的rk音频驱动分析之tinycap录音的全部内容,希望文章能够帮你解决rk音频驱动分析之tinycap录音所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部