openwrt(linux)使用alsa-lib播放wav pcm音频文件

目录

1. 打开openwrt的相关packages

1.1. 使用cmd:

1-2. alsa-lib api 官网说明:

2. alsa-lib引用Makefile写法

2-1. 外层menu makefile 写法

2-2. 内层*c 文件编译makefile 

3. alsa api的使用流程解析

3-1. alsa api需要引用头文件

3-2. 自定义头文件

4. 具体的alsa api调用流程

5. 完整代码



最近需要做一个语言wifi信号播报的小程序;需要用到alsa的api播放语音文件;

1. 打开openwrt的相关packages

1.1. 使用cmd:

make menuconfig

打开如下:

Libraries->alsa-lib

sound->alsa-utils

1-2. alsa-lib api 官网说明:

https://www.alsa-project.org/alsa-doc/alsa-lib/

2. alsa-lib引用Makefile写法

2-1. 外层menu makefile 写法

define Package/sndssi
	SECTION:=utils
	CATEGORY:=Utilities
	TITLE:=Report signal strenght by alsa
	DEPENDS:=+libpthread +librt +alsa-lib
endef

主要要引用 :alsa-lib

2-2. 内层*c 文件编译makefile 

# Copyright (C) 2020  Mesh Beidou/GPS contributors
#
include $(TOPDIR)/rules.mk
BINARY_NAME = sndssi


obj-y += audio_alsa.o rbtree.o  sndssi.o 
CFLAGS += -Wall -fPIC -O2
LDLIBS += -lpthread -lasound -ldl -lm
INCLUDE_DIR += -I ./include


$(warning $(INCLUDE_DIR))
all: $(BINARY_NAME)

# standard build rules
.SUFFIXES: .o .c
.c.o:
	$(CC) -o $@ $(INCLUDE_DIR) $(CFLAGS)  -g -c $<

$(BINARY_NAME): $(obj-y)
	$(CC) $^ $(LDLIBS_DIR) $(LDLIBS) -o  $@

clean:
	$(RM) $(BINARY_NAME) $(obj-y)

.PHONY: all clean install

注意要引用: -lasound 这个是alsa-lib编译后的.so的名字

3. alsa api的使用流程解析

3-1. alsa api需要引用头文件

#include <alsa/asoundlib.h>

3-2. 自定义头文件

audio_alsa.h

#ifndef _AUDIO_ALSA_H_
#define _AUDIO_ALSA_H_


#include <alsa/asoundlib.h>

#define	PCM_PERIOD			(32)
#define PCM_BUFFER			(32*160)
#define PCM_THRESHOLD		(8*160)

#define AUDIO_BUFFER_FRAMES   1024
#define AUDIO_RATE_SET 44100
#define dev_name  "default"
#define SAFE_FREE(p) if(NULL != p) { free(p); p = NULL; }
#define SAFE_CHECK(p) if(NULL == p) { printf("Eemory Exhausted Exit!"); exit(1); }

typedef struct {
	uint32_t mode;
	uint32_t rate;
	uint32_t format;
	snd_pcm_t *handle[2];	// 1 : playback , 0 : capture
} AUD_OBJ;


int aud_get_avail(AUD_OBJ *p, int dir);
int aud_open(AUD_OBJ *p, const char *name, int mode, int rate);
int aud_close(AUD_OBJ *p);
int aud_prepare(AUD_OBJ *p);
int aud_get_stream(AUD_OBJ *p, char *buf, int len); 
int aud_put_stream(AUD_OBJ *p, char *buf, int len);
int xrun_recovery(AUD_OBJ *p, int err);
int aud_pcm_state(AUD_OBJ *p);
int aud_get_params(AUD_OBJ *p);
int aud_playback_sound(const char * wav_file);

#endif
  • AUD_OBJ:

            这个结构体是 定义播放或者录音 init时定义handle方向

  • dev_name

          这个是/etc/asound.conf 定义的 声卡interface名字;这个文件还可以指定了例如每个周期的大小及时间等参数;

asound.conf内容如下:

# /etc/asound.conf                                                                                                             
 
 pcm.card0
 {
     type hw
     card 0
 }
  
 pcm.!default
 {
     type plug
     slave.pcm "asymer"
 }
  
 pcm.asymer
 {
     type asym
     playback.pcm    "dmixer"
     capture.pcm "dsnooper"
 }
  
 pcm.dmixer
 {
     type dmix
     ipc_key 1025
     ipc_key_add_uid yes
  
     slave
     {
         pcm     "hw:0,0"
         period_time 0
         period_size 512
         buffer_size     262144
         rate        44100
     }
 pcm.dsnooper
 {
     type dsnoop
     ipc_key 1125
     ipc_key_add_uid yes
  
     slave
     {
         pcm     "hw:0,0"
         period_time 0
         period_size 512
         buffer_size 262144
         rate        44100
     }
 }        

4. 具体的alsa api调用流程

open device流程如下:

int aud_open_device(AUD_OBJ *p, const char *name, int mode, int rate)
{
	printf("into aud_open_device \n");

	snd_pcm_hw_params_t *hw_params;
	snd_pcm_sw_params_t *sw_params;
	snd_pcm_uframes_t buffer_size = PCM_BUFFER;
	snd_pcm_uframes_t period_size = PCM_THRESHOLD;

	const char *dirname = (mode == 1) ? "PLAYBACK" : "CAPTURE";
	snd_pcm_stream_t dir =  (mode == 1) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;

	int err;

	p->mode = mode;
	p->rate = rate;
	p->format = SND_PCM_FORMAT_S16_LE;
#if DEBUG
	printf("into snd_pcm_open, dirname = %s\n", dirname);
	printf("into snd_pcm_open, dir = %d\n", dir);
#endif
	/* SND_PCM_STREAM_PLAYBACK 0 ; SND_PCM_STREAM_CAPTURE 1 */

	if ((err = snd_pcm_open(&(p->handle[mode]), name, dir, 0)) < 0) {  //SND_PCM_NONBLOCK
		fprintf(stderr, "%s (%s): Unable to open audio device (%s)\n", 
				name, dirname, snd_strerror(err));
		return err;
	}

	printf("into snd_pcm_hw_params_malloc\n");
	if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
		fprintf(stderr, "%s (%s): Unable to allocate hardware parameter structure(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	printf("into snD_pcm_hw_params_any\n");
	if ((err = snd_pcm_hw_params_any(p->handle[mode], hw_params)) < 0) {
		fprintf(stderr, "%s (%s): Unable to initialize hardware parameter structure(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	if ((err = snd_pcm_hw_params_set_access(p->handle[mode], hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set access type(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	printf("into snd_pcm_hw_params_set_format\n");
	if ((err = snd_pcm_hw_params_set_format(p->handle[mode], hw_params, p->format)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set sample format(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	printf("into snd_pcm_hw_params_set_rate_near\n");
	if ((err = snd_pcm_hw_params_set_rate_near(p->handle[mode], hw_params, &rate, NULL)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set sample rate(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	printf("into snd_pcm_hw_params_set_buffer_size_near\n");
	if ((err = snd_pcm_hw_params_set_buffer_size_near(p->handle[mode], hw_params, &buffer_size)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set buffer size(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	printf("into snd_pcm_hw_params_set_channels\n");
	if ((err = snd_pcm_hw_params_set_channels(p->handle[mode], hw_params, 1)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set channel count(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	/* set the period time */
	err = snd_pcm_hw_params_set_period_time_near(p->handle[mode], hw_params, &period_time, &dirr);
	if (err < 0) {
		printf("Unable to set period time %u for playback: %s\n", period_time, snd_strerror(err));
		return err;
	}

	/* get the period size */
	err = snd_pcm_hw_params_get_period_size(hw_params, &g_period_size, &dirr);
	if (err < 0) {
		printf("Unable to get period size for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the buffer time */
	err = snd_pcm_hw_params_set_buffer_time_near(p->handle[mode], hw_params, &buffer_time, &dirr);
	if (err < 0) {
		printf("Unable to set buffer time %u for playback: %s\n", buffer_time, snd_strerror(err));
		return err;
	}

	/* get the buffer size */
	err = snd_pcm_hw_params_get_buffer_size(hw_params, &g_buffer_size);
	if (err < 0) {
		printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
		return err;
	}

	printf("into snd_pcm_hw_params\n");
	if ((err = snd_pcm_hw_params(p->handle[mode], hw_params)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set parameters(%s)\n",
				name, dirname, snd_strerror(err));
		return err;
	}

	/******************** hw_params set end **********************/

	if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
		fprintf(stderr, "%s (%s): Unable to allocate software parameters structure(%s)\n",
			name, dirname, snd_strerror(err));
		return err;
	}
	if ((err = snd_pcm_sw_params_current(p->handle[mode], sw_params)) < 0) {
		fprintf(stderr, "%s (%s): Unable to initialize software parameters structure(%s)\n",
			name, dirname, snd_strerror(err));
		return err;
	}

	if (dir == SND_PCM_STREAM_PLAYBACK) {
		printf(" snd_pcm_sw_params_set_start_threshold size ls %d, buffer_size = %d, period_size=%d\n",
				(g_buffer_size / g_period_size) * g_period_size, g_buffer_size, g_period_size);

		if ((err = snd_pcm_sw_params_set_start_threshold(p->handle[mode], sw_params, (g_buffer_size / g_period_size) * g_period_size)) < 0) {
			fprintf(stderr, "%s (%s): Unable to set threshold(%s)\n",
					name, dirname, snd_strerror(err));
			return err;
		}
	}

	if ((err = snd_pcm_sw_params(p->handle[mode], sw_params)) < 0) {
		fprintf(stderr, "%s (%s): Unable to set software parameters(%s)\n",
			name, dirname, snd_strerror(err));
		return err;
	}

	return 0;
}

snd_pcm_open()

官网说明:最后的参数可以指的阻塞和非阻塞方式;

                 默认0是阻塞方式即是,则此writei函数会阻塞直到写入或者读取成功。

5. 完整代码

https://download.csdn.net/download/yang_quan_yang/16620253

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值