# c.AlsaAudioUtils **Repository Path**: zinface/c.alsaaudioutils ## Basic Information - **Project Name**: c.AlsaAudioUtils - **Description**: C 语言的 AlsaAudio 封装 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-02-26 - **Last Updated**: 2022-06-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # c.AlsaAudioUtils > C 语言的 Alsa 音频数据读写开发基本库 > 以下部分来源于 CSDN 博客 * 引言\ 在Linux平台,涉及到音频的开发,必然离不开 libasound,无论是混音器的参数设置,还是录音,播放音频等,libasound都提供了支持。\ 这编文章介绍时是播放音频部分,即PCM部分 * 音频相关知识 1. 采样率(每秒采样次数)/帧率\ 表示每一秒对声音的波形模拟量取样的次数,频率越高,音质越好 2. 字节率(每秒采样字节数)\ 与采样率、通道数和单位采样字节数综合相关,实际上等于这三个数据的乘积除以8(8位代表1字节) 3. 通道数(1: 单通道,2:立体声)\ 就是我们平常听音乐时使用的耳机,如果是单通道数据,我们会听到两个耳机内声音时一模一样,如是是双通道,一些音质比较高的音乐或者影视甚至游戏,在两个耳机里面听到的声音会不一样,能给人一种有距离的感觉,所以叫立体声,一些音质更高的播放设备和音乐文件中也会包含双通道以外的音频信息,使播放出来的音频效果更逼真 4. 单位采样数据位数(8位无符号或16位有符号)\ 指的是每次对声音小型的模拟量采样记录时,用来记录数据的数据位数,数据位数越高,数据表示的模拟量越精细,音质越好 * 帧数与字节数据转换相关的API 1. snd_pcm_bytes_to_frames(bytes) 2. snd_pcm_frames_to_bytes(frames) * 往音频设备读写数据 * snd_pcm_readi(pcm, buffer, frames)\ frame 不是 buffer 数据的字节数,而是帧数(采样数),返回值也是帧数 * snd_pcm_writei(pcm, buffer, frames)\ frame 不是 buffer 数据的字节数,而是帧数(采样数),返回值也是帧数\ 帧数与字节数之间需要通过 snd_pcm_frames_to_bytes 和 snd_pcm_bytes_to_frames 做转换\ 每次调用完 snd_pcm_writei 后,需要做适当延时,延时时间为,理论上已经写入帧数所需时间与实际调用该函数所花时间的差值\ 每次调用 snd_pcm_writei 返回后,下次 buffer 的起始地址需要向前进 (frame * 通道数 * 采样位数 / 8) 字节,而剩余的帧数只需要在上一次总帧数基础上减去已写入的帧数\ 建议每次准备1秒的数据到缓冲区,太大会浪费缓冲区,大小又需要频繁请求数据 > 以上是摘录于 CSDN 博客,但经过部分修改\ > 以下是对本库涉及到的alsa部分进行说明 * 快速了解音频概念 1. 音频量化\ 从模拟数据经过采样、量化、编码得到最终的数字信号 2. 通道\ 常见的**通道**概念为单声道,双声道(也可以叫立体声)\ 变量名 channels,表示通道,在alsa中某处作为参数使用,1表示单声道,2表示双声道 3. 样本数据\ 样本属于音频数据中约最小单位,一个样本记录了一个以"位"为单位的数据量\ 最常用的有8位、16位来记录一个样本,这种量已经符合日常使用要求 4. 采样率\ 采样率(每秒采样次数)表示每一秒对声音的波形模拟量取样的次数,频率越高音质越好(如上CSDN述)\ 常用率有14400(14.4k)、16000(16k)、32000(32k)、44800(44.8k)\ 如果设定值超出上限或下限,程序将可能无法工作,建议使用常用速率值 5. 帧数据\ 帧数据由一个或多个样本数据组成,帧数据大小的由样本数据位/8转字节再乘通道数\ 扩展: * 计算单帧数据大小可用 `frame_size = 16(样本位) / 8(转字节) * channels` * 在计算单通道16位单帧数据大小可用 `16/8*1=2` byte(字节) * 在计算双通道16位单帧数据大小可用 `16/8*2=4` byte(字节) 6. 周期\ 一个周期由多个帧数据组成,周期帧数是描述在某一时间内的数据量,周期常用单位是ns(纳秒),常用值是 `20ms(20000ns)`\ 扩展: * 在计算 1s 内有多少个周期可用 `1000000(1s) / 20000(20ms) = 50` 个周期, * 在计算一个周期内有多少帧可用 `frames = 14400(1秒采样率) / 50(1秒周期数) = 288` 帧 7. 数据量\ 在使用程序进行计算处理时,alsa 提供了 snd_pcm_readi, snd_pcm_writei 对设备进行音频流的读写,在上CSDN述中,明确指出3个参数为 `pcm,buffer,frames` 。\ 如果使用编程的方式读取流信息: * 需要先计算一秒的周期数量\ `1000000(1s) / 20000(20ms) = 50` 个周期, * 再由一秒的采样率/一秒的周期数量计算出一个周期的帧数\ `14400(1秒采样率) / 50(1秒周期数) = 288` 帧 * 再将帧数与单帧大小相乘得出周期的帧数据大小, 以此为 buffer 的大小,frames 为周期的帧数\ `288 * 4 = 1152` byte(字节) 一个周期的帧数据大小 * 调用 snd_pcm_readi 来读取一个周期帧的数据量 * 或调用 snd_pcm_writei 来写入一个周期帧的数据量。 其它参考\ ``` channel: 通道, 1 单通道, 2 双通道 rate: 采样速率, rate = 14400 = 14.4k frame: 帧, period: 周期 设置参数: sample = 16 bit channels = 2 rate = 14400 period = 20ms frames = 得: 一个样本的长度 = 16, 采样通道数量 = 2 采样率 = 14400 帧大小(frame) = 样本位*通道 转字节 = 16*2/8 = 4byte 周期 = 20ms , 每秒周期数量 = 1000/20 = 50 周期帧数(frames) = 14400/50 = 288 周期帧数据量(buffer) = 288*4 = 1152 byte frames = 32 // 32帧/周期 保存一个周期 = frames * 4 (2 bytes/样本, 2 通道) ``` ---- 以下是本函数库提供的功能 1. 基本结构体与函数 ```c // AlsaAudioPcm 表示 snd_pcm_t 的封装 typedef struct AlsaAudioPcm AlsaAudioPcm; struct AlsaAudioPcm { snd_pcm_t *pcm; char *device; snd_pcm_stream_t stream; int mode; }; // 打开一个PCM设备 int openAlsaAudioPcm(AlsaAudioPcm *pcm, char *name, snd_pcm_stream_t stream, int mode); // 关闭一个PCM设备 void closeAlsaAudioPcm(AlsaAudioPcm *pcm); // snd_pcm_set_params(snd_pcm_t *pcm, // snd_pcm_format_t format, // 所需的PCM格式 // snd_pcm_access_t access, // 所需的PCM访问权限 // unsigned int channels, // 所需的PCM通道 // unsigned int rate, // 所需的采样速率(单位Hz) // int soft_resample, // 0=不允许alsa-lib重采样流,1=允许重采样 // unsigned int latency) // latency 要求 us 的总延迟 // snd_pcm_set_params 的封装 int setAlsaAudioPcmParam(AlsaAudioPcm *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency); ``` 2. 配置容器 ```c // PCM 软件配置容器 - 未封装 typedef struct AlsaAudioPcmSwParams AlsaAudioPcmSwParams; struct AlsaAudioPcmSwParams { }; // ---------------------------------------------------------------- // PCM 硬件配置容器 typedef struct AlsaAudioPcmHwParams AlsaAudioPcmHwParams; struct AlsaAudioPcmHwParams { int channels; // 通道 int rate; // 采样率 snd_pcm_format_t format; // 数据格式 snd_pcm_access_t access; // 访问模式 // int // -- stream int stream_rate; // // -- buffer int buffer_time; // // other AlsaAudioPcm *pcm; snd_pcm_hw_params_t *params; }; // 初始化 AlsaAudioPcmHwParams 容器 // 使用 setAlsaAudioPcmHwParamsMalloc(建议使用) 或者 setAlsaAudioPcmHwParamsAlloca void setAlsaAudioPcmHwParamsAlloca(AlsaAudioPcmHwParams *params); void setAlsaAudioPcmHwParamsMalloc(AlsaAudioPcmHwParams *params); // 使用默认值来填充它 // 使用 AlsaAudioPcm 设备初始化 AlsaAudioPcmHwParams int setAlsaAudioPcmHwParamsAny(AlsaAudioPcmHwParams *params, AlsaAudioPcm *pcm); // set AlsaAudioPcmHwParams - 通过一系列 set 来设置硬件参数中的基本参数 void setAlsaAudioPcmHwParamsChannels(AlsaAudioPcmHwParams *params, int channels); void setAlsaAudioPcmHwParamsFormat(AlsaAudioPcmHwParams *params, snd_pcm_format_t format); void setAlsaAudioPcmHwParamsAccess(AlsaAudioPcmHwParams *params, snd_pcm_access_t access); void setAlsaAudioPcmHwParamsRate(AlsaAudioPcmHwParams *params, int rate, int dir); int setAlsaAudioPcmHwParamsRateNear(AlsaAudioPcmHwParams *params, unsigned int *val, int *dir); int setAlsaAudioPcmHwParamsPeriodSizeNear(AlsaAudioPcmHwParams *params, snd_pcm_uframes_t *val, int *dir); int setAlsaAudioPcmHwParamsPeriodTime(AlsaAudioPcmHwParams *params, snd_pcm_uframes_t val, int dir); // get AlsaAudioPcmHwParams - 通过一系列 get 来获取硬件参数中的基本参数 int getAlsaAudioPcmHwParamsChannels(AlsaAudioPcmHwParams *params, unsigned int *channels); int getAlsaAudioPcmHwParamsPeriodSize(AlsaAudioPcmHwParams *params, snd_pcm_uframes_t *frames, int *dir); int getAlsaAudioPcmHwParamsPeriodTime(AlsaAudioPcmHwParams *params, unsigned int *val, int *dir); // apply AlsaAudioPcmHwParams - 将硬件配置应用到PCM设备 int applyAlsaAudioPcmHwParams(AlsaAudioPcmHwParams *params, AlsaAudioPcm *pcm); // 释放不再使用的 AlsaAudioPcmHwParams void freeAlsaAudioPcmHwParams(AlsaAudioPcmHwParams *params); ```