linux从alsa驱动框架中读取pcm数据源代码

ALSA编程细节分析
在alsa驱动这一层,目前为止,抽象出了4层设备:

一是hw:0,0;(对于硬件参数软件必须设置一致)不知道设备号的可以用arecord -l和cat /proc/asound/devices命令获取
cat /proc/asound/devices返回

  1:        : sequencer
  2: [ 0- 3]: digital audio playback
  3: [ 0- 7]: digital audio playback
  4: [ 0- 8]: digital audio playback
  5: [ 0- 9]: digital audio playback
  6: [ 0- 0]: hardware dependent
  7: [ 0]   : control
  8: [ 1- 6]: digital audio capture
  9: [ 1- 7]: digital audio capture
 10: [ 1- 0]: digital audio playback
 11: [ 1- 0]: digital audio capture
 12: [ 1- 1]: digital audio playback
 13: [ 1- 1]: digital audio capture
 14: [ 1- 3]: digital audio playback
 15: [ 1- 4]: digital audio playback
 16: [ 1- 5]: digital audio playback
 17: [ 1- 2]: hardware dependent
 18: [ 1- 0]: hardware dependent
 19: [ 1]   : control
 33:        : timer


其中第8,9,11,13行,就是可用的录音设备,我们用hw:1,6来设设置
二是plughw:0,0;(设备自适应接口,编程者不必关心硬件,如果软件层设置的参数与硬件支持参数不一致则ALSA会自动转换数据区匹配;0:0表示设备号和次设备号subdevice)

三是default:0;

四是default。

具体的步骤,我在下面的代码的注释写的很清楚了.

/***************************************************
** Author:         xuhong                          *
** E-mail:         zaitianya999@163.com            *
** Date:           2023-06-25                      *
** Description:                              *
** Copyright 2023 by lianruixin.com Rights Reserved  *
****************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <alsa/asoundlib.h>

//采样深度16位 即每帧占两个字节
#define PCM_FORMAT SND_PCM_FORMAT_S16_LE
//通道数,1为单声道,2为立体声
#define CHANNEL 2
//采样率单位HZ,每秒多少帧
#define SAMPLE_RATE 16000

int main(int argc, char *argv[])
{
    /* Handle for the PCM device */
    snd_pcm_t *handle;
    /* Playback stream */
    snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
    snd_pcm_hw_params_t *hwparams;
    //要打开的声卡名称,1.(default)默认2.hw:0,0
    //对应的硬件接口,3.plughw:0,0自适应(0,0代表设备号和次设备号)
    char *pcm_name = "default";
    int err;
    int dir = 0;
    snd_pcm_format_t format = PCM_FORMAT;
    unsigned int nChannels = CHANNEL;
    unsigned int rate = SAMPLE_RATE;
    //每一次readi(硬中断)有多少帧音频,和采样率有关,和采样深度和通道无关
    snd_pcm_uframes_t psize_frames = 64;
    // alsa采集时缓存多少帧,一般是psize_frames的倍数,2或4倍
    snd_pcm_uframes_t bsize_frames = 256;

    char *filename = "record.pcm";
    int fd;
    char *buffer = NULL;
    int count, loop;
    //一次硬中断的时间
    unsigned int period_time;

    fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 666);
    if (fd < 0) {
        perror("open file failed!\n");
        return -1;
    }
    chmod(filename, 0777);

    //打开声卡设备
    if (snd_pcm_open(&handle, pcm_name, stream, 0) < 0) {
        printf("Error opening PCM device %s\n", pcm_name);
        return -1;
    }

    //分配一个配置结构体
    err = snd_pcm_hw_params_malloc(&hwparams);
    if (err < 0) {
        perror("malloc space for snd_pcm_hw_params_t structure failed !\n");
        return -1;
    }

    //初始化配置结构体
    err = snd_pcm_hw_params_any(handle, hwparams);
    if (err < 0) {
        printf("Broken configuration for this PCM\n");
        return err;
    }

    //设置音频数据格式为交互式,即左右左右,非交互式为左左右右
    err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) {
        printf("Access type not available\n");
        return err;
    }

    //设置采样深度为16位,2个字节
    err = snd_pcm_hw_params_set_format(handle, hwparams, format);
    if (err < 0) {
        printf("Sample format non available\n");
        return err;
    }

    //设置通道
    err = snd_pcm_hw_params_set_channels(handle, hwparams, nChannels);
    if (err < 0) {
        printf("Channels count non available\n");
        return err;
    }

    //采样率
    err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, &dir);
    if (err < 0) {
        printf("Set rate failed\n");
        return err;
    }

    //设置缓存大小,单位(帧)
    err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &bsize_frames);
    if (err < 0) {
        printf("Set period size failed\n");
        return err;
    }

    //设置硬中断的包大小,单位(帧)
    err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &psize_frames, 0);
    if (err < 0) {
        printf("Set buffer size failed\n");
        return err;
    }

    //将配置结构体配置到ALSA驱动中
    err = snd_pcm_hw_params(handle, hwparams);
    if (err < 0) {
        printf("failed to set hardware parameters:%s\n", snd_strerror(err));
        return err;
    }

    //获取一次中断的音频数
    err = snd_pcm_hw_params_get_period_size(hwparams, &psize_frames, &dir);
    if (err < 0) {
        printf("get period size fail\n");
        return err;
    } else {
        printf("period size is %d frames.\n", psize_frames);
    }
    //获取一次中断的时长,单位:us,返回的period_time应该是4000us
    snd_pcm_hw_params_get_period_time(hwparams, &period_time, &dir);
    printf("period_time is %d\n", period_time);
    loop = 10000000 / period_time; //录音10秒,循环250次

    //一次硬件中断所要的空间
    buffer = (char *)malloc(psize_frames * nChannels * 2); // 2 channel,sizeof(frame) = 2*2byte;
    while (loop--) {
        //去alsa驱动中取数据
        err = snd_pcm_readi(handle, buffer, psize_frames);

        if (err == -EAGAIN || (err >= 0 && (size_t)err < psize_frames)) {
            //取出的数据有误
            snd_pcm_wait(handle, 1000);
        } else if (err == -EPIPE) {
            //数据没有准备好
            snd_pcm_prepare(handle);
            printf("<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>/n");
        } else if (err == -ESTRPIPE) {
            //音频流是暂停的
            snd_pcm_resume(handle);
            printf("<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>/n");
        } else if (err < 0) {
            printf("Error snd_pcm_writei: [%s]", snd_strerror(err));
            return -1;
        }

        // 将音频数据写入文件
        count = write(fd, buffer, psize_frames * 2);
        if (count != psize_frames * 2) {
            printf("short write: wrote %d bytes\n", count);
        }
    }
    //等alsa中的数据走完再关闭,snd_pcm_drop立即停止
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    close(fd);

    return 0;
}

:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

q472599451

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值