ALSA音频编程常用参数详细说明

ALSA音频编程常用参数详细说明

    • ALSA简介
    • 音频采样回放过程
      • 音频采样过程
      • 音频回放过程
    • PCM及其相关参数
      • PCM简介
      • 参数说明
      • 实例分析
    • PCM接口编程
    • WAV音频格式
      • WAV简介
      • WAV格式
    • opus简介及libopus库安装使用
      • 在PC下的配置编译安装
      • 在交叉编译环境下的配置编译安装
      • opus编码的使用约束
      • libopus-1.3.1使用

最近在做ALSA音频采集、编码相关的东西,也是一点点了解这方面的知识,查了挺多资料,当然很多都是大同小异,无非介绍一些ALSA的由来、相关参数以及ALSA编程采样或者回放音频,虽然直接把代码拷贝编译后,可以正常执行音频的采集与回放,但总感觉有的细节没有摸索清楚。

在完成项目的同时,通过网上的资料和自己的理解把ALSA的一些东西梳理一下,一方面给有需要的朋友提供有用的帮助,另一方面,也是自己对ALSA的总结。


ALSA简介

ALSA(Advanced Linux Sound Architecture)高级Linux音频架构,为 Linux 系统提供了统一的音频和MIDI功能和驱动。它由许多声卡的声卡驱动程序组成,同时它也提供一个称为libasound的API库,应用程序开发者应该使用libasound而不是内核中的 ALSA接口,因为libasound提供最高级并且编程方便的编程接口。ALSA提供一系列基于命令行的工具集,比如混音器(mixer),音频文件播放器(aplay),以及控制特定卡特定属性的工具。

ALSA官网链接

ALSA提供的API库可以分解成以下几个主要的接口:

  1. 控制接口:提供管理声卡注册和请求可用设备的通用功能。
  2. PCM接口:管理数字音频回放(playback)和录音(capture)的接口。本文后续总结重点放在这个接口上,因为它是开发数字音频程序最常用到的接口。
  3. Raw MIDI接口:支持MIDI(Musical Instrument Digital Interface),标准的电子乐器。这些API提供对声卡上MIDI总线的访问。这个原始接口基于MIDI事件工作,由程序员负责管理协议以及时间处理。
  4. 定时器(Timer)接口:为同步音频事件提供对声卡上时间处理硬件的访问。
  5. 时序器(Sequencer)接口
  6. 混音器(Mixer)接口

音频采样回放过程

音频采样过程

音频采样过程是一个将麦克风的模拟信号(电压)按一定的周期进行采样(即采样率),并将采样的数据转换为数字信号(0,1)的过程,在数字电路中称之为ADC(Analog-Digital Conversion,模数转换),具体实现是通过逐次逼近电路将模拟信号进行量化,量化时有一个精度,这个精度就是采样长度,量化后得到对应的数字量以便于在数字设备中传输;

音频回放过程

音频回放过程是一个将声卡中的数字信号还原为模拟信号的过程,在数字电路中称之为DAC(Digital -Analog Conversion,数模转换),具体实现是通过数模转换器电路将数字信号还原为模拟信号(电压),最终将转换后的模拟信号传送到喇叭,喇叭根据电信号的特征播放出声音。


PCM及其相关参数

PCM简介

PCM(Pulse Code Modulation----脉冲编码调制)即将音频采样量化过程后变成符号化的脉冲序列,并予以记录。PCM是由数字信号(0,1)未经过任何编码和压缩处理的数字量。与模拟信号比,它不易受传送系统的杂波及失真的影响。动态范围宽,可得到音质相当好的影响效果。PCM数据是最原始的音频数据完全无损。

参数说明

ALSA的API库的PCM接口中有一些参数是需要知道的,这些参数也是音频相关的基本知识:

  1. 采样长度(sample_length):将音频进行采样量化的精度,有8bit/16bit
  2. 采样通道(sample_channels):音频采样的通路数,比如立体声就有2个采样通路数分别进行左右声道的采样,单声道就有1个通路数进行单声道采样;
  3. 采样帧大小(sample_frame):以字节Byte为单位,是采样长度与采样通道的乘积,由于采样长度单位是位,所以这个乘积的单位是位,要想转换为字节,需要将这个乘积再除以8,因此采样帧字节大小公式即sample_frame=sample_length*sample_channels/8;举个栗子,采样长度位16bit,采样通道为立体声,那么帧大小(字节)=16*2/8=4Byte;
  4. 采样周期(sample_period):指多久完成一次采样;
  5. 采样率(sample_rate):指每秒采样的帧数,注意这里是每秒采样的帧数,不是每秒采样的位数/字节数。
  6. 交错模式(interleaved):是一种音频数据的记录方式,在交错模式下,数据以连续桢的形式存放,即首先记录完桢1的左声道样本和右声道样本(假设为立体声格式),再开始桢2的记录。而在非交错模式下,首先记录的是一个周期内所有桢的左声道样本,再记录右声道样本,数据是以连续通道的方式存储。不过多数情况下,我们只需要使用交错模式就可以了。

实例分析

设定采样长度为16bit,采样通道为立体声,采样率为16K,采样周期为20ms,那么采样帧大小是多少?每秒有多少个采样周期?每个采样周期采样到的帧数是多少?需要多少字节的缓存区存放每个周期的数据量?
·
首先 sample_length=16 sample_channels=2 sample_rate=16000 sample_period=20ms
a. 采样帧大小:sample_frame=sample_length*sample_channels/8=4
b. 因为采样周期知道(sample_period=20ms),1秒的采样周期个数=1000/20=50
c. 每个采样周期采样到的帧数:因为每秒钟的采样帧数已知sample_rate=16000,根据b又知道每秒有50个采样周期,所以每个周期的采样帧数=16000/50=320;(当然也可以通过sample_rate*sample_period求得)
d. 由c知道每个采样周期的帧数为320,而由a知道sample_frame=4,所以每帧的数据量为320*4=1280Byte,因此需要1280字节存放每个周期的数据量。

在这里目的不是为了求解,目的在于理解,因为这些东西理解清楚了,在进行音频采集和回放时采用多大的缓冲区就明白了。在利用PCM接口进行音频的采集和回放时,每次读取和写入的数据量是每个采样周期的帧数;在使用PCM接口进行读数据时,读取的数据量是每个采样周期采样到的帧数,读出的数据缓存区大小PCM底层会自动填充至每个采样周期的数据量大小。比如在上述例子中,通过PCM接口读取和写入的数据量大小应该为每个采样周期采样到的帧数即320,而读出的数据buffer会被填充为采样周期的数据量即1280B;


PCM接口编程

参考链接Alsa音频编程
可先参考上面的链接,后续会发布PCM的音视频采集和回放的源码


WAV音频格式

WAV简介

Waveform Audio File Format(WAVE,又或者是因为扩展名而被大众所知的WAV),是微软与IBM公司所开发在个人电脑存储音频流的编码格式,此格式属于资源交换文件格式(RIFF)的应用之一,通常会将采用PCM的音频资存储在区块中。由于此音频格式未经过压缩,所以在音质方面不会出现失真的情况,但文件的体积因而在众多音频格式中较为大。

WAV格式

WAV文件遵守RIFF(Resource Interchange File Format 资源交换文件格式)规则,在文件的前44(或46)字节放置标头(header),使播放器或编辑器能够简单掌握文件的基本信息。标头的前3个区块记录文件格式及长度;接着第一个子区块包含8个区块,记录声道数量、采样率等信息;接着第二个子区块才是真正的音频数据,长度则视音频长度而定。内容如下表所示。须注意的是,每个区块的端序不尽相同,而音频数据本身则是采用小端序1

起始地址(字节) 区块名称 区块大小 端序 区块内容
0 区块编号 4 “RIFF”
4 总区块大小 4 =N+36
8 档案格式 4 “WAVE”
12 子区块标签 4 “fmt”
16 子区块大小 4 16
18 音频格式 2 1(PCM)
20 声道数量 2 1(单声道) 2(立体声)
22 采样频率 4 每秒的采样帧数
26 位元(组)率 4 =采样频率*位元深度/8
30 区块对齐 2 4
32 位元深度 2 指采样长度
36 子区块2标签 4 “data”
40 子区块2大小 4 N(=位元(组)率秒数声道数量)
44 音频数据 =N PCM音频数据

opus简介及libopus库安装使用

opus是有损音频编码的一种适用于网络上的实时声音传输的编码格式。在使用中可以使用opus的开源库,在opus官网可以阅读其相关内容。

在PC下的配置编译安装

  1. 下载libopus 1.3.1
    wget https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
  2. 解压配置
    tar -zxvf opus-1.3.1.tar.gz
    a.安装路径配置为默认路径./configure
    b.安装路径配置为指定路径./configure --perfix=${path} ${path}替换为指定的安装目录
  3. 编译安装
    a. 编译make
    b. 安装make install

在交叉编译环境下的配置编译安装

  1. 下载libopus 1.3.1
    wget https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
  2. 解压配置
    tar -zxvf opus-1.3.1.tar.gz
    a.安装路径配置为默认路径./configure --host=交叉工具链名称 CC=gcc CXX=g++
    b.安装路径配置为指定路径./configure --perfix=${path} --host=交叉工具链名称 CC=gcc CXX=g++ ${path}替换为指定的安装目录
  3. 编译安装
    a. 编译make
    b. 安装make install

opus编码的使用约束

  1. 采样率约束:输入信号的采样率(Hz),必须是8000、12000、16000、24000、或48000。
  2. 帧长约束:opus为了对一个帧进行编码,必须正确地用音频数据的帧(2.5, 5, 10, 20, 40 or 60 ms)来调用opus_encode()函数。比如,在16kHz的采样率下,opus_encode()参数中的合法的frame_size(单通道的帧大小)值只有:40, 80, 160, 320, 640, 960m,即:frame_size = 采样率 * 帧时间。
  3. 兼容opus的容器格式:有ogg,ts,mkv。但ts无法播放,mkv只能foobar播放,ogg能用foobar,vlc播放。

libopus-1.3.1使用

opus编码

// 创建编码器
OpusEncoder *opus_encoder_create(
	opus_int32 Fs, 		// 采样率,可以设置的大小为8000, 12000, 16000, 24000, 48000,opus_int32(int)
	int channels,		// 声道数,网络中实时音频一般为单声道
	int application, 	// 应用场景 OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO
	int *error			// 是否创建失败,返回0表示创建成功
)						// 成功返回OpusEncoder首地址,失败返回NULL

// 销毁编码器
void opus_encoder_destroy(
	OpusEncoder *st		// 创建编码器的返回值
)

// 编码器参数设置
int opus_encoder_ctl(
	OpusEncoder *st,	// 创建编码器的返回值
	int request, 		// 参数设置值
	...					// 其他参数
)						// 成功返回0,失败返回-1

// 编码
opus_int32 opus_encode(
	OpusEncoder *st,	 		// 创建编码器的返回值
	const opus_int16 *pcm,		// pcm数据,传入参数,opus_int16(short)
	int frame_size, 			// pcm每个周期的采样帧数
	unsigned char *data,	 	// 编码后的数据,传出参数
	opus_int32 max_data_bytes	// 最大的编码缓存区大小,opus_int32(int)
)								// 成功返回编码后的数据量大小,失败返回-1

实例:

OpusEncoder *tOpusEncoder;
int OpusEncoderInit(void)
{
	int opus_err;
	tOpusEncoder=opus_encoder_create(16000,2,OPUS_APPLICATION_VOIP,&opus_err);
	if (!m_tOpusEncoder || opus_err < 0)
	{
	        if (!m_tOpusEncoder) {
	            opus_encoder_destroy(tOpusEncoder);
	        }
	        return -1;
	 }	
	opus_encoder_ctl(tOpusEncoder, OPUS_SET_VBR(0)); //关闭可变码率
	opus_encoder_ctl(tOpusEncoder, OPUS_SET_BITRATE(128000));//设置固定码率128k
	return 0;
}

voidOpusEncoderDeinit(void)
{
	opus_encoder_destroy(tOpusEncoder);
}

opus_int32 OpusEncode(const opus_int16 pcm,int frame_size,unsigned char *opus_data,opus_int32 max_data_bytes)
{
	return opus_encode(tOpusEncoder,pcm,frame_size,opus_data,opus_data_max_size);
}

opus解码

// 创建解码器
OpusDecoder *opus_decoder_create(
	opus_int32 Fs,				// 采样率,可以设置的大小为8000, 12000, 16000, 24000, 48000.
	int channels,				// 声道数,网络中实时音频一般为单声道
	int *error					// 是否创建失败,返回0表示创建成功
);								// 成功返回OpusDecoder首地址,失败返回NULL

// 销毁解码器
void opus_decoder_destroy(
	OpusDecoder *st		// 创建解码器的返回值
)

// 编码器参数设置
int opus_decoder_ctl(
	OpusDecoder *st,	// 创建解码器的返回值
	int request, 		// 参数设置值
	...					// 其他参数
)						// 成功返回0,失败返回-1

// opus解码
int opus_decode(
	OpusDecoder *st, 			// 创建解码器的返回值
	const unsigned char *data, 	// 要解码的opus数据
	opus_int32 len, 			// opus数据长度
	opus_int16 *pcm, 			// 解码后的数据,注意是一个以16位长度为基本单位的数组
	int frame_size, 			// pcm每个周期的采样帧数
	int decode_fec				// 是否需要fec,设置0为不需要,1为需要
)			

编程实例见参考链接4


[参考链接]

  1. Alsa音频编程
  2. WAV和PCM的关系和区别
  3. 维基百科
  4. Opus介绍及编译
  5. Opus音频编码的使用约束
  6. 常见的15种音频格式
  7. alsa-lib及alsa-utils成功移植

  1. 大小端序,指数据的高低字节在内存中的存放方式;大端序,也称大端模式,是指数据的高字节存储到内存的低地址,数据的低字节存储到内存的高地址;小端序,也称小端模式,是指数据的高字节存储到内存的高地址,数据的低字节存储到内存的低地址。 ↩︎

你可能感兴趣的:(音视频编程,linux,编程语言)