linux alsa应用


概念

交错与非交叉

例:双声道,16位

交错:一次记录一个完整帧(左、右声道)。
记录顺序:左(16 bit)、右(16 bit)、左(16 bit)、右(16 bit)

非交错:一次记录完一个周期内声道数据,再记录另一个声道。
记录顺序:左(32 bit)、右(32 bit)

基本都是采用交错模式

阻塞打开和非阻塞打开区别

阻塞状态打开,读取时候,若无数据则等待。写入时候,若缓冲区满了,则等待。
非阻塞打开:读写不会等待,而是会以错误形式立刻返回

帧、周期、缓冲区

一帧 = 一个完整的声音单元
如双声道16bit : 一帧 = 16bit * 2

周期 等于 写(读) 完整一次传输的帧数量

一个周期一个硬件中断

一般一个缓冲区内有多个周期,用于读取或者写入数据

内存大小: 缓冲区 ≥ 周期 > 帧


同步方式

static int pcm_init(snd_pcm_t **pcm_handle)
{
	snd_pcm_t *pcm_handle = NULL;
	snd_pcm_open(&pcm_handle,"hw:0,0",SND_PCM_STREAM_PLAYBACK, 0); 
	snd_pcm_hw_params_t *hwparams = NULL;
	snd_pcm_hw_params_malloc(&hwparams);
	snd_pcm_hw_params_any(pcm_handle, hwparams);
	snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
	snd_pcm_hw_params_set_format(pcm_handle, hwparams,SND_PCM_FORMAT_S16_LE);
	snd_pcm_hw_params_set_channels(pcm_handle, hwparams,2);
	snd_pcm_hw_params_set_rate(pcm_handle, hwparams,48000,0);
	snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, 1024, 0);
	snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, 4*1024);
	snd_pcm_hw_params(pcm_handle, hwparams);
	snd_pcm_hw_params_free(hwparams);
}

int main()
{
	snd_pcm_t *pcm_handle = NULL;
	pcm_init(&pcm_handle);
	
	SND_PCM_STREAM_PLAYBACK -> snd_pcm_writei(pcm_handle ,buf,1024);	
	SND_PCM_STREAM_CAPTURE  -> snd_pcm_readi(pcm_handle  ,buf,1024);
	
	snd_pcm_close(pcm_handle);
}

播放器示例代码

#include "alsa/asoundlib.h"
#include <stdio.h>

typedef struct 
{
	unsigned int id;
	unsigned int size;
	unsigned int format;
}RIFF_S;

typedef struct 
{
	unsigned int id;
	unsigned int size;
	unsigned short audioformat;
	unsigned short channels;
	unsigned int sampleRate;
	unsigned int ByteRate;
	unsigned short BlockAlign;
	unsigned short SampleBit;
}FMT_S;

typedef struct 
{
	unsigned int id;
	unsigned int size;
}Data_s;

snd_pcm_t *pcm_handle = NULL;

static int pcm_init(int channels,int rate)
{
	/*
	打开PCM设备
	SND_PCM_STREAM_PLAYBACK : 播放 传输数据 DA
	SND_PCM_STREAM_CAPTURE  : 采集   收集数据 AD
	最后一个参数代表打开模式,0代表阻塞打开。SND_PCM_NONBLOCK无阻塞打开
	*/
	snd_pcm_open(&pcm_handle,"hw:0,0",SND_PCM_STREAM_PLAYBACK, 0); 
	
	/*
	实例化snd_pcm_hw_params_t对象
	snd_pcm_hw_params_t 用于描述PCM设备硬件配置参数
	*/
	snd_pcm_hw_params_t *hwparams = NULL;
	snd_pcm_hw_params_malloc(&hwparams);

	/*初始化snd_pcm_hw_params_t对象*/
	snd_pcm_hw_params_any(pcm_handle, hwparams);

	//设置交叉访问模式
	snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);

	//设置音频格式
	snd_pcm_hw_params_set_format(pcm_handle, hwparams,SND_PCM_FORMAT_S16_LE);

	//设置声道数
	snd_pcm_hw_params_set_channels(pcm_handle, hwparams,channels);

	//设置采样率大小
	snd_pcm_hw_params_set_rate(pcm_handle, hwparams,rate,0);

	/*设置周期大小
	原型:
	int snd_pcm_hw_params_set_period_size(snd_pcm_t *pcm, 
	snd_pcm_hw_params_t *params, 
	snd_pcm_uframes_t val, 
	int dir )
	alsa-lib使用snd_pcm_uframes_t类型表示帧的数量
	val形参的单位是帧,代表多少帧,而不是代表多少字节。
	一帧= 位数 * 通道数
	*/
	snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, 1024, 0);

	/*设置buffer大小
	int snd_pcm_hw_params_set_buffer_size(snd_pcm_t *pcm, 
	snd_pcm_hw_params_t *params, 
	snd_pcm_uframes_t val )
	val的单位也是帧
	下面两个函数都是设置buffer缓冲区大小的
	snd_pcm_hw_params_set_periods的val值是以周期为单位
	*/
	snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, 4*1024);
	//snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 4, 0);
	
	//安装/加载硬件配置参数
	snd_pcm_hw_params(pcm_handle, hwparams);

	snd_pcm_hw_params_free(hwparams);

}

static int open_wav_file(char const *wav_name)
{
	int fd;
	RIFF_S riff;
	FMT_S fmt;
	fd = open(wav_name,O_RDONLY);
	read(fd,&riff,sizeof(riff));
	read(fd,&fmt,sizeof(fmt));
	close(fd);
	printf("ch:%d rate:%d\n",fmt.channels,fmt.sampleRate);
	pcm_init(fmt.channels,fmt.sampleRate);
}

int main(int argc, char const *argv[])
{
	int fd,ret;
	short *audio_data_buf = malloc(4*1024);
	open_wav_file(argv[1]);
	fd = open(argv[1],O_RDONLY);
	lseek(fd,SEEK_SET,44);
	while(1)
	{
		memset(audio_data_buf,0,4*1024);
		ret = read(fd,audio_data_buf,4*1024);
		if( ret <= 0 )
			break;
		ret = snd_pcm_writei(pcm_handle,audio_data_buf,1024);
		if( ret < 1024 )
			lseek(fd, (ret-1024) * 4, SEEK_CUR);		
	}
	free(audio_data_buf);
	close(fd);
	snd_pcm_close(pcm_handle);
	return 0;
}

异步方式

pcm_init用于初始化pcm这个参数

用于注册异步处理函数
int snd_async_add_pcm_handler ( snd_async_handler_t **handler, 
snd_pcm_t *pcm, 
snd_async_callback_t callback,  <-回调函数
void *private_data )  <-传给回调函数的实参

当缓冲区有空闲的周期时,音频设备驱动会向应用程序发送信号触发回调函数
录音情况下,用于获取当前可读取的帧数
播放情况下,用于获取当前可写入的帧数
snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm);
用于在回调函数中获取snd_pcm_t句柄
snd_async_handler_get_pcm(snd_async_handler_t *handler)

编译问题

没有alsa库

官网:https://www.alsa-project.org/main/index.php/Main_Page
下载网址:https://www.alsa-project.org/files/pub/lib/

1.1.6版本:

tar -jxf alsa-lib-1.1.6.tar.bz2
cd alsa-lib-1.1.6

//编译alsa-lib
./configure 
--host=arm-linux-gnueabihf(根据自己情况修改) 
--disable-python 
--prefix=xxx(根据自己情况修改)

make
make install

//交叉编译程序
arm-linux-gnueabihf-gcc 
pcm.c -o pcm 
-I ~/alsa-lib/include/  // <- 重要
-L ~/alsa-lib/lib/ // <- 重要
-lasound // <- 重要


底层关系

在这里插入图片描述

常用函数

暂停PCM流

int snd_pcm_drop(snd_pcm_t * pcm)
立刻暂停pcm,缓冲区样本丢弃

int snd_pcm_drain(snd_pcm_t * pcm)
不是立刻暂停pcm,会对缓冲区进行处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值