目录
最近需要做一个语言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