首页 > 其他分享 >ALSA学习笔记

ALSA学习笔记

时间:2024-03-23 19:01:33浏览次数:34  
标签:snd rc PCM SND 笔记 学习 params pcm ALSA

        ALSA框架介绍:ALSA-LINUX音频框架学习笔记-CSDN博客

        代码参考(博客园):Alsa音频编程【精华】

        对原博客代码进行了修改并添加了注释(测试通过,可直接运行),代码包含三个测试用例:1、显示了一些ALSA使用的PCM数据类型和参数;2、添加声音回放;3、添加录音。

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#if 0
typedef enum _snd_pcm_stream {
        /** Playback stream */
        SND_PCM_STREAM_PLAYBACK = 0,
        /** Capture stream */
        SND_PCM_STREAM_CAPTURE,
        SND_PCM_STREAM_LAST = SND_PCM_STREAM_CAPTURE
} snd_pcm_stream_t;
typedef enum _snd_pcm_access {
        /** mmap access with simple interleaved channels */
        SND_PCM_ACCESS_MMAP_INTERLEAVED = 0,     /* 使用简单交错通道的 mmap 访问 */
        /** mmap access with simple non-interleaved channels */
        SND_PCM_ACCESS_MMAP_NONINTERLEAVED,      /* 使用简单非交错通道的 mmap 访问 */
        /** mmap access with complex placement */
        SND_PCM_ACCESS_MMAP_COMPLEX,             /* 使用复杂布局的 mmap 访问 */
        /** snd_pcm_readi/snd_pcm_writei access */
        SND_PCM_ACCESS_RW_INTERLEAVED,           /* 使用 snd_pcm_readi/snd_pcm_writei 访问 */
        /** snd_pcm_readn/snd_pcm_writen access */
        SND_PCM_ACCESS_RW_NONINTERLEAVED,        /* 使用 snd_pcm_readn/snd_pcm_writen 访问 */
        SND_PCM_ACCESS_LAST = SND_PCM_ACCESS_RW_NONINTERLEAVED
} snd_pcm_access_t;
typedef enum _snd_pcm_format {
        /** Unknown */
        SND_PCM_FORMAT_UNKNOWN = -1,
        /** Signed 8 bit */
        SND_PCM_FORMAT_S8 = 0,
        /** Unsigned 8 bit */
        SND_PCM_FORMAT_U8,
        /** Signed 16 bit Little Endian */
        SND_PCM_FORMAT_S16_LE,
        ... 
} snd_pcm_format_t;
typedef enum _snd_pcm_subformat {
        /** Unknown */
        SND_PCM_SUBFORMAT_UNKNOWN = -1,
        /** Standard */
        SND_PCM_SUBFORMAT_STD = 0,
        /** Maximum bits based on PCM format */
        SND_PCM_SUBFORMAT_MSBITS_MAX = 1,
        /** 20 most significant bits */
        SND_PCM_SUBFORMAT_MSBITS_20 = 2,
        /** 24 most significant bits */
        SND_PCM_SUBFORMAT_MSBITS_24 = 3,
        SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_MSBITS_24
} snd_pcm_subformat_t;
typedef enum _snd_pcm_state {
        /** Open */
        SND_PCM_STATE_OPEN = 0,
        /** Setup installed */
        SND_PCM_STATE_SETUP,
        /** Ready to start */
        SND_PCM_STATE_PREPARED,
        /** Running */
        SND_PCM_STATE_RUNNING,
        /** Stopped: underrun (playback) or overrun (capture) detected */
        SND_PCM_STATE_XRUN,
        /** Draining: running (playback) or stopped (capture) */
        SND_PCM_STATE_DRAINING,
        /** Paused */
        SND_PCM_STATE_PAUSED,
        /** Hardware is suspended */
        SND_PCM_STATE_SUSPENDED,
        /** Hardware is disconnected */
        SND_PCM_STATE_DISCONNECTED,
        SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED,
        /** Private - used internally in the library - do not use*/
        SND_PCM_STATE_PRIVATE1 = 1024
} snd_pcm_state_t;
#endif
// 1、显示了一些ALSA使用的PCM数据类型和参数。
int main1()
{
    int val;
    printf("ALSA library version: %s\n", SND_LIB_VERSION_STR);

    printf("\nPCM stream types:\n");
    for (val = 0; val <= SND_PCM_STREAM_LAST; val++)
        printf(" %s\n", snd_pcm_stream_name((snd_pcm_stream_t)val));

    printf("\nPCM access types:\n");
    for (val = 0; val <= SND_PCM_ACCESS_LAST; val++) {
        printf(" %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
    }

    printf("\nPCM formats:\n");
    for (val = 0; val <= SND_PCM_FORMAT_LAST; val++) {
        if (snd_pcm_format_name((snd_pcm_format_t)val) != NULL) {
            printf(" %s (%s)\n", snd_pcm_format_name((snd_pcm_format_t)val), snd_pcm_format_description((snd_pcm_format_t)val));
        }
    }
    printf("\nPCM subformats:\n");
    for (val = 0; val <= SND_PCM_SUBFORMAT_LAST; val++) {
        printf(" %s (%s)\n", snd_pcm_subformat_name((snd_pcm_subformat_t)val), snd_pcm_subformat_description((snd_pcm_subformat_t)val));
    }
    printf("\nPCM states:\n");
    for (val = 0; val <= SND_PCM_STATE_LAST; val++)
        printf(" %s\n", snd_pcm_state_name((snd_pcm_state_t)val));

    return 0;
}
// 2、添加声音回放
// ./example  </dev/urandom
int main2()
{
    long loops;
    int rc;
    int size;
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    unsigned int val;
    int dir;
    snd_pcm_uframes_t frames;
    char *buffer;

    /* Open PCM device for playback. */
    rc = snd_pcm_open(&handle, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) {
        fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
        exit(1);
    }

    /* Allocate a hardware parameters object. */
    snd_pcm_hw_params_malloc(&params);

    /* Fill it in with default values. */
    snd_pcm_hw_params_any(handle, params); // 设置默认值

    /* Set the desired hardware parameters. */

    /* Interleaved mode */
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); // 交错模式并使用 snd_pcm_readi/snd_pcm_writei 访问

    /* Signed 16-bit little-endian format */
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); // 一个采样点16bits 小端

    /* Two channels (stereo) */
    snd_pcm_hw_params_set_channels(handle, params, 2); // 通道数

    /* 44100 bits/second sampling rate (CD quality) */
    val = 44100;
    /*
      snd_pcm_hw_params_set_rate_near:设置最接近所需采样率的合适值。它会尝试在给定的范围内找到最接近所需采样率的值,并将其设置为参数 params 的采样率。
      snd_pcm_hw_params_set_rate_first:设置为第一个可用的采样率值。它将采样率设置为所需范围内的最小值。
      snd_pcm_hw_params_set_rate_last:设置为最后一个可用的采样率值。它将采样率设置为所需范围内的最大值。
      这些函数的最后一个参数 int *dir 是一个指针,用于获取设置的方向。方向可以是以下值之一:
      0 表示没有方向的限制。
      1 表示设置的值是一个最小值。
      -1 表示设置的值是一个最大值。
      调整音频设备的采样率时,如果用户更希望选择一个低于或等于所需采样率的最大可能值,则可以将采样率方向设置为 -1。相反,如果用户更希望选择一个高于或等于所需采样率的最小可能值,则可以将采样率方向设置为 1。如果不关心方向,则可以将方向参数设置为 0,这样函数将在所需范围内选择最合适的值
    */
    dir = 0;
    snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); // 采样率

    frames = 1024;
    snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); // 一个周期1024个采样点,alsa中一帧就是一个采样点(这个概念真坑)

    // 设置周期数量
    snd_pcm_hw_params_set_periods(handle, params, 5, 0);

    /* Write the parameters to the driver */
    rc = snd_pcm_hw_params(handle, params); // 参数设置到硬件驱动
    if (rc < 0) {
        fprintf(stderr,
                "unable to set hw parameters: %s\n",
                snd_strerror(rc));
        exit(1);
    }

    /* Use a buffer large enough to hold one period */
    snd_pcm_hw_params_get_period_size(params, &frames, &dir); // 获取一个周期大小
    size = frames * 4;                                        /* 2 bytes/sample, 2 channels */
    buffer = (char *)malloc(size);                            // 分配一个周期大小的buffer

    /* We want to loop for 5 seconds */
    snd_pcm_hw_params_get_period_time(params, &val, &dir); // 获取一个周期的时间 微妙
    /* 5 seconds in microseconds divided by period time */
    loops = 5000000 / val; // 录制5秒需要的循环次数

    while (loops > 0) // 循环录音 5 s
    {
        loops--;
        printf("loops:%lld\n", loops);
        rc = read(0, buffer, size); // 从标准输入中读取一个周期的数据(1024个采样点,在alsa中就是1024帧)
        if (rc == 0)                // 没有读取到数据
        {
            fprintf(stderr, "end of file on input\n");
            break;
        } else if (rc != size) // 实际读取 的数据 小于 要读取的数据
        {
            fprintf(stderr, "short read: read %d bytes\n", rc);
        }

        rc = snd_pcm_writei(handle, buffer, frames); // 写入声卡  (放音) 最后一个参数是一个周期帧数量,不是一个周期的数据总大小
        // 返回值为EPIPE表明发生了under run,使得PCM音频流进入到XRUN状态并停止处理数据。从该状态中恢复过来的标准方法是调用snd_pcm_prepare()函数,把PCM流置于PREPARED状态,这样下次我们向该PCM流中数据时,它就能重新开始处理数据
        // 应用程序写入数据到环形缓冲区 buffer 中的速度不够快,缓存区将会“饿死”(缓冲区中无数据可播放)
        if (rc == -EPIPE) {
            /* EPIPE means underrun */
            fprintf(stderr, "underrun occurred\n");
            snd_pcm_prepare(handle);
        } else if (rc < 0) {
            fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
        } else if (rc != (int)frames) {
            fprintf(stderr, "short write, write %d frames\n", rc);
        }
    }
    snd_pcm_hw_params_free(params);
    // snd_pcm_drain用于等待当前音频播放完毕并清空音频缓冲区。它的作用是在停止音频播放之前等待音频缓冲区中的所有数据被完全播放,以避免意外丢失音频数据或造成不完整的播放。
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);

    return 0;
}
// 3、添加录音
// ffplay -f s16le -ar 44100 -ac 2 out.pcm
int main()
{
    long loops;
    int rc;
    int size;
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    unsigned int val;
    int dir;
    snd_pcm_uframes_t frames;
    char *buffer;
    FILE *out;
    out = fopen("out.pcm", "w");
    /* Open PCM device for recording (capture). */
    rc = snd_pcm_open(&handle, "hw:0,0", SND_PCM_STREAM_CAPTURE, 0);
    if (rc < 0) {
        fprintf(stderr,
                "unable to open pcm device: %s\n",
                snd_strerror(rc));
        exit(1);
    }

    /* Allocate a hardware parameters object. */
    snd_pcm_hw_params_malloc(&params);

    /* Fill it in with default values. */
    snd_pcm_hw_params_any(handle, params);

    /* Set the desired hardware parameters. */

    /* Interleaved mode */
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

    /* Signed 16-bit little-endian format */
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

    /* Two channels (stereo) */
    snd_pcm_hw_params_set_channels(handle, params, 2);

    /* 44100 bits/second sampling rate (CD quality) */
    val = 44100;
    snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

    frames = 1024;
    snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

    // 设置周期数量
    snd_pcm_hw_params_set_periods(handle, params, 5, 0);

    /* Write the parameters to the driver */
    rc = snd_pcm_hw_params(handle, params);
    if (rc < 0) {
        fprintf(stderr,
                "unable to set hw parameters: %s\n",
                snd_strerror(rc));
        exit(1);
    }

    /* Use a buffer large enough to hold one period */
    snd_pcm_hw_params_get_period_size(params, &frames, &dir);
    printf("frames:%lld\n", frames);
    size = frames * 4; /* 2 bytes/sample, 2 channels */
    buffer = (char *)malloc(size);

    /* We want to loop for 5 seconds */
    snd_pcm_hw_params_get_period_time(params, &val, &dir);
    loops = 5000000 / val;
    printf("loops:%lld\n", loops);
    while (loops > 0) {
        loops--;
        printf("loops:%lld\n", loops);
        rc = snd_pcm_readi(handle, buffer, frames);
        if (rc == -EPIPE) {
            /* EPIPE means overrun */
            fprintf(stderr, "overrun occurred\n");
            snd_pcm_prepare(handle);
        } else if (rc < 0) {
            fprintf(stderr,
                    "error from read: %s\n",
                    snd_strerror(rc));
        } else if (rc != (int)frames) {
            fprintf(stderr, "short read, read %d frames\n", rc);
        }
        rc = fwrite(buffer, 1, size, out);
        if (rc != size)
            fprintf(stderr,
                    "short write: wrote %d bytes\n", rc);
    }
    snd_pcm_hw_params_free(params);
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    fclose(out);
    free(buffer);

    return 0;
}

 

 

 

标签:snd,rc,PCM,SND,笔记,学习,params,pcm,ALSA
From: https://blog.csdn.net/weixin_43147845/article/details/136948414

相关文章

  • 五、Spring源码学习之postProcessBeanFactory方法
    简介在应用程序上下文完成其标准初始化后,修改其内部的BeanFactory。此时,所有的bean定义都已经加载完成,但还没有任何bean被实例化。这允许在某些ApplicationContext实现中注册特殊的BeanPostProcessor等。在应用程序上下文的初始化过程中,会经历多个阶段。其中,一个关键阶段......
  • Javascript学习笔记
    Javascript基础   js是什么?         定义       是一种运行在客户端(浏览器)的编程语言,实现人机交互效果      html和css只是标记语言,并没有涉及编程的部分    作用      网页特效(监听用户的一些行为让网页做......
  • nodejs学习
    什么是nodejs就是一个基于chormeV8引擎的JavaScript运行环境,是一个用于后端的运行环境nodejs中的运行环境分为两部分,分别是V8引擎和内置Api,前者用于解析js,后者用于被js调用终端的概念 虽然用的很多,但一讨论他的概念,我倒是有点说不上来:终端,是专门为开发人员设计的,用于实现人......
  • python小白学习笔记Mac版本
    和win系统的不同之处python的cmd验证在win系统中,只需要输入python就可以得到相关python的版本信息但是在mac系统中,需要输入python3.12(3.12是具体版本的号码)只输入python和pip也显示找不到相关文件(已经成功的安装前提下)只有输入第三行代码python3.12才会显示pyth......
  • 网络学习:DHCPV6
    目录背景:一、DHCPV6概述DHCPv6Client:DHCPv6Relay:DHCPv6Server:二、DHCPV6工作原理DHCPV6无状态自动分配三、DHCP基础配置服务端四、DHCPV6地址更新时间(DHCPV4租期)五、DHCPV6一些常用参数基本概念DHCPV6组播UDP端口号DHCP唯一标识符(DUID)身份联盟(IA)六、DHCPV6......
  • 机器学习:智能时代的核心引擎
    目录一、什么是机器学习二、监督学习三、无监督学习四、半监督学习五、强化学习一、什么是机器学习机器学习是人工智能的一个分支,它主要基于计算机科学,旨在使计算机系统能够自动地从经验和数据中进行学习并改进,而无需进行明确的编程。机器学习算法通过构建模型来处......
  • Python机器学习笔记:CART算法实战
    完整代码及其数据,请移步小编的GitHub传送门:请点击我如果点击有误:https://github.com/LeBron-Jian/MachineLearningNote前言在python机器学习笔记:深入学习决策树算法原理一文中我们提到了决策树里的ID3算法,C4.5算法,并且大概的了解了CART算法。对于ID3算法的实战可......
  • 【Python学习】——函数进阶
    零、函数基础在之前的文章里:【Python学习】——基础语法一、多返回值deftest_return():    return1,2x,y=test_return()print(x) #结果1print(y) #结果2按照返回值的顺序,写对应顺序的多个变量接受即可变量之间用逗号隔开支持不同类型的数据return......
  • [ROS 系列学习教程] rqt可视化工具箱 - 日志工具
    ROS系列学习教程(总目录)本文目录零、rqt可视化工具箱一、rqt_console二、rqt_logger_level零、rqt可视化工具箱rqt是ROS的一个软件框架,以插件的形式实现各种GUI工具。可以在rqt中将所有现有的GUI工具作为子窗口运行,也可以以独立方法运行,但rqt可以更轻松地同......
  • 最近的学习笔记YBTOJ
    写在前面:洛谷月赛太烂了,或者说,效率太低了,所以来写总结你好!开学了,平凡的我回到了平凡的世界不得不承认,在学校还是很好的不仅有生活,还有OI最近的OI学习总是围绕着数据结构这个我最烂的板块来讲不知道是不是对我不努力的报复有两位巨佬停课了,实名表示羡慕语文作业是真的不想......