AlsaLib基本使用(基于1.2.4版本)

本文详细介绍了如何使用AlsaLib在不同平台上设置硬件参数,配置buffer和period,实现声音的捕获与播放(普通模式和中断模式),并指导如何根据平台选择合适的mixer elem名称。同时,展示了如何通过Mixer控制播放音量的示例和编译测试过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于AlsaLib 捕获声音

capture.c 捕获声音

捕获声音格式如下:
SND_PCM_ACCESS_RW_INTERLEAVED
SND_PCM_FORMAT_S16_LE
44100 Hz
2 chanels
buffer_frames:128 frames
period_frames:32 frames
捕获时长5s

基本流程如下:

  1. 按照要求设置硬件参数(访问数据方式,数据格式,channels数,采样率)
  2. 配置捕获buffer_size(驱动能够容纳的总帧数)
    period_frames (驱动每 period_frames 产生一个中断通知应用层有period_frames已经准备好了)
  3. 循环捕获数据直到退出

1. 硬件参数的设置

/* 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);

2.buffer及period设置

/* Set buffer size to 128 frames. */
buffer_frames = 128;
snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames);
/* Set period size to buffer_frames/4 frames. */
period_frames = buffer_frames / 4;
snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, &dir);

3. 数据捕获

循环捕获数据直到退出
/*Read data from codec and write to one file*/
	/*每次读period_frames帧数据到buffer中,直到退出*/
	while (loops > 0) {
		
		rc = snd_pcm_readi(handle, buffer, period_frames);
		if (rc == -EPIPE) { 
			/* EPIPE means overrun */
			fprintf(stderr, "overrun occurred/n");
			snd_pcm_prepare(handle); //overrun occurred 驱动没用新数据可以读取了,让驱动继续准备好新数据
		}
		else if (rc < 0) {
			fprintf(stderr,
				"error from read: %s\n",
				snd_strerror(rc));
		}
		else if (rc != (int)period_frames) {
			fprintf(stderr, "short read, read %d frames\n", rc);
		}
		rc = fwrite(buffer, 1, buff_size, fp); //将读取到的数据保存到文件中
		if (rc != buff_size) {
			fprintf(stderr, "short write: wrote %d bytes/n", rc);
		}

		loops--;
		++i;
	}

基于AlsaLib 播放声音(普通模式)

playback.c 播放声音(普通模式)
主要流程如下:

  1. 按照要求设置硬件参数(访问数据方式,数据格式,channels数,采样率)
  2. 配置捕获buffer_size(驱动能够容纳的总帧数)
    period_frames (驱动每 period_frames 产生一个中断通知应用层有period_frames已经准备好了)
  3. 从文件读取数据,送到codec,直到读取数据结束

1,2设置参数的过程同capture类似,不同的是打开pcm设备的方式
playback时候的打开方式:
snd_pcm_open(&playback_handle, snd_card_name, SND_PCM_STREAM_PLAYBACK, 0);
capture时候的打开方式:
snd_pcm_open(&handle, snd_card_name, SND_PCM_STREAM_CAPTURE, 0);

步骤3如下:

/*Read data from file and send to codec*/
while ((rc = fread(period_buff, FRAME_SIZE, period_frames, fp)) != 0) {
	send_frames = rc;
	rc = snd_pcm_writei(handle, period_buff, send_frames);
	if (rc == -EPIPE) {
		/* EPIPE means overrun */
		fprintf(stderr, "underrun occurred\n");
		snd_pcm_prepare(handle);
	} else if (rc < 0) {
		fprintf(stderr,
			"error from write: %s\n",
			snd_strerror(rc));
	}
	else if (rc != (int)send_frames) {
		fprintf(stderr, "short write, read %d frames\n", rc);
	}
}

基于AlsaLib 播放声音(中断模式)

playback_interrupt.c 播放声音(中断方式)
主要流程如下:

  1. 按照要求设置硬件参数(访问数据方式,数据格式,channels数,采样率)
  2. 配置捕获buffer_size(驱动能够容纳的总帧数)
    period_frames (驱动每 period_frames 产生一个中断通知应用层有period_frames已经准备好了)
  3. 设置sw参数,配置产生中断的参数
  4. 从文件读取数据直到读取数据结束

1,2的设置同capture.c中的设置
3 sw设置如下:

当buffer有PERIOD_SIZE 帧数据可用时产生一个中断:
snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, PERIOD_SIZE)
当buufer有数据时,就开始frame的计数,到了PERIOD_SIZE就产生一个中断
snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U) //0为开始计数帧的值
这个例子中的sw的参数的作用就是用于start_frame的计数值及产生中断的frames的周期间隔

4 基于中断方式的播放流程如下

/*Read data from codec and write to one file*/
	while ((rc = fread(period_buff, FRAME_SIZE, period_frames, fp)) != 0) {
		send_frames = rc;
		/* wait till the interface is ready for data, or 1 second
		   has elapsed.
		   等到有需要的数据或者超时都会退出来
		*/
		if ((rc = snd_pcm_wait (playback_handle, 1000)) < 0) {
		        fprintf (stderr, "poll failed (%s)\n", strerror (errno));
		        break;
		}	           
		/* find out how much space is available for playback data
		看下有多少帧数据准备好了
	 */
		if ((frames_to_deliver = snd_pcm_avail_update (playback_handle)) < 0) {
			if (frames_to_deliver == -EPIPE) {
				fprintf (stderr, "an xrun occured\n");
				break;
			} else {
				fprintf (stderr, "unknown ALSA avail update return value (%d)\n", 
					 (int)frames_to_deliver);
				break;
			}
		}
		/*
			决定最终发送多少frames数据到codec,发送的实际帧数为
			frames_to_deliver ,PERIOD_SIZE, send_frames(period_buff对应帧数) 最小值
		*/
		frames_to_deliver = frames_to_deliver > PERIOD_SIZE ? PERIOD_SIZE : frames_to_deliver;
		frames_to_deliver = frames_to_deliver > send_frames ? send_frames : frames_to_deliver;
		/* deliver the data 
		数据送到解码器播放
	*/
		if (playback_callback ((short *)period_buff, frames_to_deliver) != frames_to_deliver) {
		        fprintf (stderr, "playback callback failed\n");
			break;
		}	
		++i;
	}

基于AlsaMixer 控制播放声音的大小

通过mixer 接口设置不同mixer elem的1种方式是:
通过SimpleConrtl 的名称获取mixer_elem,然后通过mixer_elem设置对应elem的值

通过mixer控制播放声音的主要代码如下:

int set_playbak_volume(long volume)
{
   long min, max;
   snd_mixer_t *handle;
   snd_mixer_selem_id_t *sid;
   const char *card = "default"; //sound card名称
   const char *selem_name = "amba-playback"; //控制Playback声音的SimpleControl 对应的elem名称 不同平台这个名字可能不一样
   	/*
   下面流程对于设置不同的参数基本流程一致
   */
   snd_mixer_open(&handle, 0);
   snd_mixer_attach(handle, card);
   snd_mixer_selem_register(handle, NULL, NULL); 
   snd_mixer_load(handle);
   snd_mixer_selem_id_alloca(&sid);
   snd_mixer_selem_id_set_index(sid, 0);
   snd_mixer_selem_id_set_name(sid, selem_name); //mixer_elem 同一个sid关联起来
    snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);  //通过mixer_elem关联的sid查找到对应的mixer_elem
    /*
    	获取声音的min,max,因为函数输入参数volume为(0-100)要映射到(min,max)
    */
    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
	snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);
    snd_mixer_close(handle);
}

问题:如何确定使用平台的selem_name 对应名字?

以控制Playback音量的例子为例说明下:
执行如下命令, 获取所有SimpleControl的内容
amixer scontents

Simple mixer control 'amba-playback',0
  Capabilities: volume
  Playback channels: Front Left - Front Right
  Capture channels: Front Left - Front Right
  Limits: 0 - 255
  Front Left: 127 [50%]
  Front Right: 127 [50%]

找到类似输出的内容, amba-playback对应字段就是要找的selem_name。Limits对应参数的范围。要通过mixer设置其它参数方法类似

代码的编译及测试

代码使用的Makefile这里简单的讲解下(这个仅针对初学者,大佬可以忽略这个Makefile解析章节)
Makefile 很简单就几行而已。

srcs = $(wildcard *.c)      #获取到当前目录所有.c文件列表
targets = $(srcs:%.c=%) #每个.c 对应的前缀部分部分作为最终targets

CC = /usr/local/linaro-aarch64-2018.08-gcc8.2/bin/aarch64-linux-gnu-gcc #编译器绝路径
INC_PATH += -I /cvdump/lwua/SDK/cv_master_local/ambarella/prebuild/oss/armv8-a/alsa-lib/include #改成自己alsalib 头文件目录
CFLAGS   += -Wall
LD_FLAGS += -L /cvdump/lwua/SDK/cv_master_local/ambarella/prebuild/oss/armv8-a/alsa-lib/usr/lib \ #改成自己的AlasLib路径
		   -lasound 

%:%.c #target中每个元素,依赖对应.c文件对应,例如a <->a.c, b<->b.c
	$(CC) $(CFLAGS) $(LD_FLAGS) $(INC_PATH) $< -o  $@

all:$(targets)

.PHONY:clean
clean:
	rm $(targets)
这种格式Makefile的好处是每次文件夹新建一个xxx.c文件,xxx.c 对应的target xxx会自动编译,不用每次都手动修改Makefile

依次再命令行执行如下测试命令,可以测试所有的例子。

# ./capture default  test.raw
[86360.321414] simple-card: Ambarella fix DAI clock at 12288000.
----->System configure info
Alsa lib version 1.2.4 //使用的AlsaLib代码版本号
channels:    2
rate:        44100 bps
period time: 725 us
period size: 32 frames
buffer time: 2902 us
buffer size: 128 frames
----->Alreay capture 100% //显示进度
Capure finished, capture 220672 frames, save data in test.raw

# ./test_amixer_playback_vol 50
set_playbak_volume vol_val:50 ok //音量设置成功打印ok

# ./playback_interrupt default  test.raw
[86382.135874] simple-card: Ambarella fix DAI clock at 12288000.
----->System configure info
Alsa lib version 1.2.4
----->hw_params:
channels:         2
rate:             44100 bps
period time: 2902 us
period size: 128 frames
buffer time: 11609 us
buffer size: 512 frames
----->Alreay Playback 100%
Playback finished, playback 220672 frames, data from test.raw

# ./playback default  test.raw
[86392.744021] simple-card: Ambarella fix DAI clock at 12288000.
----->System configure info
Alsa lib version 1.2.4
channels:         2
rate:             44100 bps
period time: 725 us
period size: 32 frames
buffer time: 2902 us
buffer size: 128 frames
----->Alreay Playback 100%
Playback finished, playback 220672 frames, data from test.raw
#

源代码要就拿去用用,不需要就直接略过^_ ^

https://gitee.com/wllw7176/open-source-third-part/tree/master/test_alsa_src
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值