1. 环境测试

alsa_test.c

#include <alsa/asoundlib.h>
#include <stdio.h> // 官方测试代码, 运行后只要有一堆信息打印出来,即说明安装成功了。 int main()
{
int val; printf("ALSA library version: %s\n",
SND_LIB_VERSION_STR); printf("\nPCM stream types:\n");
for (val = 0; val <= SND_PCM_STREAM_LAST; val++)
printf(" %s\n",
snd_pcm_stream_name((snd_pcm_stream_t)val)); printf("\nPCM access types:\n");
for (val = 0; val <= SND_PCM_ACCESS_LAST; val++)
{
printf(" %s\n",
snd_pcm_access_name((snd_pcm_access_t)val));
} printf("\nPCM formats:\n");
for (val = 0; val <= SND_PCM_FORMAT_LAST; val++)
{
if (snd_pcm_format_name((snd_pcm_format_t)val)!= NULL)
{
printf(" %s (%s)\n",
snd_pcm_format_name((snd_pcm_format_t)val),
snd_pcm_format_description(
(snd_pcm_format_t)val));
}
} printf("\nPCM subformats:\n");
for (val = 0; val <= SND_PCM_SUBFORMAT_LAST;val++)
{
printf(" %s (%s)\n",
snd_pcm_subformat_name((
snd_pcm_subformat_t)val),
snd_pcm_subformat_description((
snd_pcm_subformat_t)val));
} printf("\nPCM states:\n");
for (val = 0; val <= SND_PCM_STATE_LAST; val++)
printf(" %s\n",
snd_pcm_state_name((snd_pcm_state_t)val)); return 0;
}

makefile:

.PHONY : rebuild clean

CC:= mips-linux-gnu-gcc
TARGET:= alsa_test.out OBJS:= alsa_test.o INCLUDE += -I/usr/local/open_lib/include LIBS += -lpthread -L/usr/local/open_lib/lib -lasound $(TARGET) :$(OBJS)
$(CC) $(LIBS) $^ -o $@ $(OBJS):%.o:%.c
$(CC) $(INCLUDE) -c $^ -o $@
echo $(OBJS) clean:
$(RM) $(OBJS)
$(RM) $(TARGET)
@echo "clean" rebuild : clean $(TARGET)
@echo "rebuild"
官方测试代码, 运行后只要有一堆信息打印出来,即说明安装成功了。

2. 音频合成+语音切换 功能使用,单线程,不考虑多线程场景。

alsa_test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/asoundlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
static char * name;
struct wav_header
{
char rld[4]; //riff 标志符号
int rLen;
char wld[4]; //格式类型(wave)
char fld[4]; //"fmt" int fLen; //sizeof(wave format matex) short wFormatTag; //编码格式
short wChannels; //声道数
int nSamplesPersec ; //采样频率
int nAvgBitsPerSample;//WAVE文件采样大小
short wBlockAlign; //块对齐
short wBitsPerSample; //WAVE文件采样大小 char dld[4]; //”data“
int wSampleLength; //音频数据的大小 } wav_header, wav_header1, wav_header2, wav_header3;
static pthread_mutex_t mutex; // 等待停止
static pthread_mutex_t mutex_play; // 开启播放
snd_pcm_t* handle; //PCI设备句柄
static char * path1;
static char * path2;
static pthread_t id;
static char g_stop=0; static int lock = 0; int set_pcm_play(const char* path1, const char* path2, const char* path3);
int wait_stop_play(void); /**** 立即关闭播放的方法 ****
if(handle!=NULL)
{
g_stop=1;
snd_pcm_drop(handle);
}
**** ****/ #if 0
int main(void)
{
system("stty -icanon"); handle = NULL; set_pcm_play("/etc/door_sound/201.wav", "/etc/door_sound/203.wav", "/etc/door_sound/201.wav");
printf("-----OVER11111111111111----\n\n"); set_pcm_play("/etc/door_sound/201.wav", NULL, NULL);
set_pcm_play("/etc/door_sound/203.wav", NULL, NULL);
set_pcm_play("/etc/door_sound/201.wav", NULL, NULL);
printf("-----OVER222222222222-------\n\n"); sleep(3); #if 0 // 存在的问题:set_pcm_play两句话, 前面一句话叫得很快 , 后面正常
set_pcm_play("/etc/door_sound/201.wav", "/etc/door_sound/203.wav", NULL);
set_pcm_play("/etc/door_sound/201.wav", "/etc/door_sound/203.wav", NULL); set_pcm_play(NULL, NULL, "/etc/door_sound/203.wav"); // 这句话没有播放
printf("-----OVER333333333333-------\n\n");
#else
set_pcm_play("/etc/door_sound/201.wav", "/etc/door_sound/203.wav", NULL);
set_pcm_play("/etc/door_sound/201.wav", "/etc/door_sound/203.wav", NULL); set_pcm_play("/etc/door_sound/203.wav", NULL, NULL);
printf("-----OVER333333333333-------\n\n");
#endif //set_pcm_play("/TG/sound/333.wav", "/TG/sound/333.wav", "/TG/sound/333.wav");
// set_pcm_play("/TG/sound/1.wav", NULL, NULL); return 0;
} #else pthread_t Handle_stop_sound_thread; void *stop_sound_thread(void *arg)
{ sleep(2); if(handle!=NULL)
{
printf("即将关闭当前语音流A \n");
g_stop=1;
// snd_pcm_drop(handle); 不要立即关停。 因为这会导致当前正在执行snd_pcm_writei()写入出错。置位一个标志g_stop已经足够。
} return NULL;
} /* ALSA使用教程
** Demo演示
** LMW
** 2020 - 07 -xx
**使用了下alsa实现各语音提取合并(语音流A最多由三个wav文件合成) 、以及切换语音播报(关闭当前语音流A转而播报语音流B这样)、
**
**/ int main(void)
{
system("stty -icanon"); handle = NULL; //这里新建个线程,延时2秒后关闭当前的语音播放 if(pthread_create(&Handle_stop_sound_thread, NULL, stop_sound_thread, NULL))
{
perror("Create stop_sound_thread Err ");
}
else
{
pthread_detach(Handle_stop_sound_thread);
} //这里搞个远大于2秒的语音播报
printf("-准备播报语音流A..\n");
set_pcm_play("/etc/door_sound/254.wav", "/etc/door_sound/203.wav", "/etc/door_sound/201.wav");
printf("-已关闭(中止)当前语音流A!\n\n"); //sleep(3);
printf("-准备播报语音流B..\n");
set_pcm_play("/etc/door_sound/254.wav", "/etc/door_sound/203.wav", "/etc/door_sound/201.wav");
printf("-语音流B顺利执行完毕!\n\n"); //sleep(3);
printf("-准备播报语音流C..\n");
set_pcm_play("/etc/door_sound/254.wav", "/etc/door_sound/254.wav", "/etc/door_sound/254.wav");
printf("-语音流C顺利执行完毕!\n\n"); return 0;
} #endif int para_common_set(int channels, int frequency, \
int bit, int datablock, snd_pcm_uframes_t *pframes)
{
int size = 0, dir=0;;
snd_pcm_hw_params_t* params; //硬件信息和PCM流配置
unsigned int val;
int ret;
// 如果要做多线程处理,这里应该维护一个计数器cnt,使用mutex来保护该计数器
// 打开音频设备时,只有第一次打开才执行snd_pcm_open,以后则只是cnt++
// 同理,关闭设备时,执行计数器--操作,直到计数器减为0,才执行snd_pcm_close真正将设备关闭 //因为我的程序并不涉及多线程操作音频设备,所以不做上述处理
//单线程处理,这样够了:使用lock,确保该函数多次调用,只打开一次设备。
if(!lock)
{ lock = 1; //snd_pcm_close()内会清零该lock,以便下次再打开设备 ret= snd_pcm_open(&handle, "default", \
SND_PCM_STREAM_PLAYBACK, 0);
if(ret<0)
{
printf("open PCM device failed\n"); } } snd_pcm_hw_params_alloca(&params); //分配params结构体 ret= snd_pcm_hw_params_any(handle, params);//初始化params
if(ret<0)
{
printf("snd_pcm_hw_params_any err\n"); } ret= snd_pcm_hw_params_set_access(handle, params, \
SND_PCM_ACCESS_RW_INTERLEAVED); //初始化访问权限
if(ret<0)
{
printf("sed_pcm_hw_set_access err\n"); } //采样位数
switch(bit/8)
{
case 1:snd_pcm_hw_params_set_format(handle, params,\
SND_PCM_FORMAT_U8);
break ;
case 2:snd_pcm_hw_params_set_format(handle, params,\
SND_PCM_FORMAT_S16_LE);
break ;
case 3:snd_pcm_hw_params_set_format(handle, params,\
SND_PCM_FORMAT_S24_LE);
break ; default:
printf("default \n");
break; } ret= snd_pcm_hw_params_set_channels(handle, params, \
channels); //设置声道,1表示单声>道,2表示立体声
if(ret<0)
{
printf("snd_pcm_hw_params_set_channels err\n"); } val = frequency; ret= snd_pcm_hw_params_set_rate_near(handle, params, \
&val, &dir); //设置>频率
if(ret<0)
{
printf("snd_pcm_hw_params_set_rate_near err\n"); } ret = snd_pcm_hw_params(handle, params);
if(ret<0)
{
printf("snd_pcm_hw_params err \n"); } ret=snd_pcm_hw_params_get_period_size(params, pframes, \
&dir); /*获取周期长度*/
if(ret<0)
{
printf("snd_pcm_hw_params_get_period_size err\n"); } // 一个数据块,一个数据块,依次读取。
size = (*pframes) * datablock; /*代表数据块长度*/ printf("--malloc(size), size = %d \n",size); return size;
} #define max_num 5 struct wav_file_info { FILE* fp; int size; snd_pcm_uframes_t frames; short channels;
int frequency;
short bit;
short datablock; }wavinfo[max_num]; /*
path1 = malloc(50);
path2 = malloc(50); sprintf(path1,"/TG/sound/%d.wav", 1);
sprintf(path2,"/TG/sound/%d.wav", 2); */ //void Get_fp_and_wavheader(const char* path, FILE **pfp, struct wav_header* phead_info)
FILE * Get_fp_and_wavheader(const char* path, struct wav_header* phead_info)
{
if(NULL != path) { printf("path = %s \n", path); FILE *fp=fopen(path,"rb"); if(NULL==fp)
{
printf("open wav failed:\n");
return NULL;
} printf("fp = %p \n", fp);
fread(phead_info, 1, sizeof(struct wav_header),fp); return fp;
/*
printf("文件大小rLen: %d\n", phead_info->rLen);
printf("声道数: %d\n", phead_info->wChannels);
printf("采样频率: %d\n", phead_info->nSamplesPersec);
printf("采样的位数: %d\n", phead_info->wBitsPerSample);
printf("wSampleLength=%d\n", phead_info->wSampleLength);
*/ }
} // 对fopen的理解:
// 即使3次fopen同一个文件,也不要紧,会返回3个不同的fp,但是指向同一个文件.
int set_pcm_play(const char* path1, const char* path2, const char* path3)
{
int ret;
unsigned int size = 0;
unsigned int size1 = 0, size2 = 0, size3 = 0;
int num=0; int dir=0;
snd_pcm_uframes_t frames, frames1, frames2, frames3;
char *buffer; int channels;
int frequency;
int bit;
int datablock; FILE *fp1 = NULL, *fp2 = NULL, *fp3 = NULL;
FILE* fpCur[max_num] = {0}; const char* file_paths[3] = {0}; file_paths[0] = path1;
file_paths[1] = path2;
file_paths[2] = path3; if(NULL != path3) { fp3 = Get_fp_and_wavheader(path3, &wav_header3); if(fp3 != NULL) { channels = wav_header3.wChannels;
frequency= wav_header3.nSamplesPersec;
bit = wav_header3.wBitsPerSample;
datablock= wav_header3.wBlockAlign; #if 0
printf("fp3 datablock: %d\n", datablock);
printf("声道数: %d\n", channels);
printf("采样频率: %d\n", frequency);
printf("采样的位数: %d\n", bit);
printf("wSampleLength=%d\n\n\n", wav_header3.wSampleLength);
#endif wavinfo[2].channels = channels;
wavinfo[2].frequency = frequency;
wavinfo[2].bit = bit;
wavinfo[2].datablock = datablock; size3 = para_common_set(channels, frequency, bit, datablock, &frames3);
//获取各自的rames。
wavinfo[2].size = size3;
wavinfo[2].frames = frames3;
}
} if(NULL != path2) { fp2 = Get_fp_and_wavheader(path2, &wav_header2); if(fp2 != NULL) { channels = wav_header2.wChannels;
frequency= wav_header2.nSamplesPersec;
bit = wav_header2.wBitsPerSample;
datablock= wav_header2.wBlockAlign; #if 0
printf("fp2 datablock: %d\n", datablock);
printf("声道数: %d\n", channels);
printf("采样频率: %d\n", frequency);
printf("采样的位数: %d\n", bit);
printf("wSampleLength=%d\n\n\n", wav_header2.wSampleLength);
#endif wavinfo[1].channels = channels;
wavinfo[1].frequency = frequency;
wavinfo[1].bit = bit;
wavinfo[1].datablock = datablock; size2 = para_common_set(channels, frequency, bit, datablock, &frames2);
//获取各自的rames。
wavinfo[1].size = size2;
wavinfo[1].frames = frames2;
}
} if(NULL != path1) { #if 1
// 对fopen的理解:
// 即使3次fopen同一个文件,也不要紧,会返回3个不同的fp,但是指向同一个文件.
fp1 = Get_fp_and_wavheader(path1, &wav_header1);
#else
char *path111 = malloc(50);
sprintf(path111,"/TG/sound/%d.wav", 1);
fp1=fopen(path111,"rb");
if(fp1==NULL)
{
printf("open wav1 failed:\n");
return -1;
} fread(&wav_header1,1,sizeof(wav_header1),fp1);
#endif // 实际上给入的每个音频文件的采样率啥的都是一样的,但是每个音频文件的大小可能不一样,
// 所以需要单独针对每个音频文件设置这里,获取各自的frames。
if(fp1 != NULL) { printf("fp1 = %p \n", fp1);
channels = wav_header1.wChannels;
frequency= wav_header1.nSamplesPersec;
bit = wav_header1.wBitsPerSample;
datablock= wav_header1.wBlockAlign; #if 0
printf("fp1 datablock: %d\n", datablock);
printf("声道数: %d\n", channels);
printf("采样频率: %d\n", frequency);
printf("采样的位数: %d\n", bit);
printf("wSampleLength=%d\n\n\n", wav_header1.wSampleLength);
#endif wavinfo[0].channels = channels;
wavinfo[0].frequency = frequency;
wavinfo[0].bit = bit;
wavinfo[0].datablock = datablock; size1 = para_common_set(channels, frequency, bit, datablock, &frames1);
//获取各自的rames。
wavinfo[0].size = size1;
wavinfo[0].frames = frames1;
// printf("size1 = %d \n",size1);
// printf("wavinfo[0].size = %d \n", wavinfo[0].size); } } wavinfo[0].fp = fp1;
wavinfo[1].fp = fp2;
wavinfo[2].fp = fp3; #if 0
printf("wavinfo[0].fp = %d \n", wavinfo[0].fp);
printf("fp1 = %d \n", fp1); #endif if(size1 > size2) {
size = size1;
}
else {
size = size2;
} if(size > size3) { }
else {
size = size3;
} printf("size = %d \n", size); buffer = (char*)malloc(size); #if 1
// fseek(fp1, 58, SEEK_SET); //printf("wavinfo[0].fp = %d \n", wavinfo[0].fp);
//printf("fp1 = %d \n", fp1); if(wavinfo[0].fp) {
fseek(wavinfo[0].fp, 44, SEEK_SET); //定位歌曲到数据区
}
if(wavinfo[1].fp) {
printf("--2 \n");
fseek(wavinfo[1].fp, 44, SEEK_SET); //定位歌曲到数据区
}
if(wavinfo[2].fp) {
printf("--3 \n");
fseek(wavinfo[2].fp, 44, SEEK_SET); //定位歌曲到数据区
}
#endif int index = 0;
// printf("-wavinfo[0].fp = %d \n", wavinfo[0].fp);
// printf("-wavinfo[1].fp = %d \n", wavinfo[1].fp);
// printf("-wavinfo[2].fp = %d \n", wavinfo[2].fp); while ((g_stop == 0) && (wavinfo[index].fp))
{
num++; memset(buffer, 0x00, size); if(wavinfo[index].fp) { // 一个数据块,一个数据块,依次读取。
ret = fread(buffer, 1, wavinfo[index].size, wavinfo[index].fp);
if(ret == 0) { printf("曲目读取 end\n"); fclose(wavinfo[index].fp);
wavinfo[index].fp = NULL; index++; continue;
//printf("--not reached here \n"); }
else if (ret != size) {
printf("left: read %d bytes\n", ret);
} //printf("-play %d\n", ret); // 写音频数据到PCM设备
//struct timeval time1,time2;
//gettimeofday(&time1,NULL); ret = snd_pcm_writei(handle, buffer, wavinfo[index].frames);
//snd_pcm_prepare(handle);
//gettimeofday(&time2,NULL);
//printf("time=%d\n", \
(time2.tv_sec-time1.tv_sec)*1000000+time2.tv_usec-time1.tv_usec);
if (ret == -EPIPE)
{
printf("underrun occurred\n");
//完成硬件参数设置,使设备准备好
snd_pcm_prepare(handle); // 继续
}
else if (ret < 0)
{
printf("error from writei: %s\n",snd_strerror(ret));
}
else if (ret != (int)wavinfo[index].frames)
{
printf("short write, write %d frames\n", ret);
} } } snd_pcm_drain(handle); // 清空pcm内部音频数据流缓存
snd_pcm_drop(handle); // 暂停 int i=0;
for(i=0; i<max_num; i++)
{ if(wavinfo[i].fp)
fclose(wavinfo[i].fp);
} lock = 0;
snd_pcm_close(handle);
free(buffer);
handle = NULL;
g_stop=0;
return 0; }

makefile:

.PHONY : rebuild clean

CC:= mips-linux-gnu-gcc
TARGET:= alsa_test.out OBJS:= alsa_test.o # -I -L -l 详解
#-I /home/hello/include 表示将/home/hello/include目录作为第一个寻找头文件的目录
#-L /home/hello/lib 表示将/home/hello/lib目录作为第一个寻找库文件的目录
#-lworld 表示在上面的lib的路径中寻找libworld.so动态库文件(如果gcc编译选项中加入了“-static”表示寻找libworld.a静态库文件) INCLUDE += -I /usr/local/open_lib/include LIBS += -lpthread -L/usr/local/open_lib/lib -lasound $(TARGET) :$(OBJS)
$(CC) $(LIBS) $^ -o $@ $(OBJS):%.o:%.c
$(CC) $(INCLUDE) -c $^ -o $@
echo $(OBJS) clean:
$(RM) $(OBJS)
$(RM) $(TARGET)
@echo "clean" rebuild : clean $(TARGET)
@echo "rebuild"

本例子在项目中,功能使用良好。

.

Linux ALSA音频库(二) 环境测试+音频合成+语音切换 项目代码分享的更多相关文章

  1. Linux ALSA 音频库 配置和使用

    ALSA应用库是核心功能,而alsa-utils是一些工具功能集合库.单纯地播放一个wav文件,使用alsa-utils即可,如果还需要合成音频.调试音频质量,那么就需要ALSA应用库. 欲安装使用A ...

  2. 基于Linux ALSA音频驱动的wav文件解析及播放程序 2012

    本设计思路:先打开一个普通wav音频文件,从定义的文件头前面的44个字节中,取出文件头的定义消息,置于一个文件头的结构体中.然后打开alsa音频驱动,从文件头结构体取出采样精度,声道数,采样频率三个重 ...

  3. 嵌入式驱动开发之---Linux ALSA音频驱动(一)

    本文的部分内容参考来自DroidPhone的博客(http://blog.csdn.net/droidphone/article/details/6271122),关于ALSA写得很不错的文章,只是少 ...

  4. 构建微服务开发环境7————使用Github管理项目代码的版本

    [内容指引] 1.注册GitHub帐号: 2.下载Github Desktop客户端: 3.macOS安装Github Desktop客户端: 4.windows安装Github Desktop客户端 ...

  5. linux alsa音频中采样率fs、比特率BCLK 、主时钟MCLK关系

    转:https://blog.csdn.net/lugandong/article/details/72468831 一.拿512fs说话: 看图知道采样的位深是32bit(位),左右声道各占了8*3 ...

  6. Linux ALSA音频PCM播放编程

    使用ALSA播放两个频率的单音,并使用GNU Radio中的Audio Source和FFT来观测声音的频谱. #include <alsa/asoundlib.h> #include & ...

  7. 基于Orangpi Zero和Linux ALSA实现WIFI无线音箱(三)

    作品已经完成,先上源码: https://files.cnblogs.com/files/qzrzq1/WIFISpeaker.zip 全文包含三篇,这是第三篇,主要讲述接收端程序的原理和过程. 第一 ...

  8. Postman 使用技巧之多环境测试及接口依赖关系处理

    一.前言 在日常开发中,除了正常的单元测试,某些情况我们还需要测试 HTTP 接口,团队中目前使用的是「 Postman 」这款 API调试 . HTTP 请求工具.通常我们将经常要测试的接口按照项目 ...

  9. Linux实战教学笔记41:企业级SVN版本管理与大型代码上线方案

    第1章 SVN服务实战应用指南 1.1 SVN介绍 1.1.1 什么是SVN(Subversion)? Svn(subversion)是近年来崛起的非常优秀的版本管理工具,与CVS管理工具一样,SVN ...

随机推荐

  1. Django 仿ajax传递数据(Django十)

    之前用form表单传递数据,没有遇到任何问题 具体见:https://blog.csdn.net/qq_38175040/article/details/104867747 然后现在我想用ajax传递 ...

  2. vue3 报错解决:找不到模块‘xxx.vue’或其相应的类型声明。(Vue 3 can not find module)

    最近在用 vue3 写一个小组件库,在 ts 文件中引入 .vue 文件时出现以下报错: 报错原因:typescript 只能理解 .ts 文件,无法理解 .vue文件 解决方法:在项目根目录或 sr ...

  3. [Binder深入学习一]Binder驱动——基础数据结构

    具体代码路径: kernel/drivers/staging/android/binder.c kernel/drivers/staging/android/binder.h /* * binder_ ...

  4. adb命令—monkey篇

    monkey 目录 monkey 1.Monkey介绍 2.Monkey是用来做什么的 3.Monkey程序介绍 下面就是一些Monkey命令了 1.Monkey介绍 顾名思义,Monkey就是猴子, ...

  5. JDK15真的来了,一起来看看它的新特性

    目录 简介 JDK15的新特性 JEP 385 Deprecate RMI Activation for Removal JEP 371 Hidden Classes JEP 339 Edwards- ...

  6. 【转】Locust-工具核心原理分析

    Locust工具在市场上不如Loadrunner / JMeter流行,使用的范围也没有那么广,但不可否认其是一款很不错的工具.我个人觉得Locust使用不是那么广泛,主要是因为一下方式: Locus ...

  7. 一篇带你熟悉ansible-playbook剧本

    #playbook介绍 #playbook简单介绍 playbook翻译过来就是剧本,以yml/yaml为后缀结尾的一个文本文件 #playbook组成:分为两部分play(定义主机的角色)和task ...

  8. JVM性能调优(2) —— 垃圾回收器和回收策略

    一.垃圾回收机制 1.为什么需要垃圾回收 Java 程序在虚拟机中运行,是会占用内存资源的,比如创建的对象.加载的类型数据等,而且内存资源都是有限的.当创建的对象不再被引用时,就需要被回收掉,释放内存 ...

  9. XXE漏洞介绍 & XXE漏洞攻击 & 修复建议

    介绍XXE漏洞 XML外部实体注入(XML External Entity)简称XXE漏洞,XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是-种允许用户对自己的标记语 ...

  10. 一键生成数据库文档,堪称数据库界的Swagger,有点厉害

    最近部门订单业务调整,收拢其他业务线的下单入口,做个统一大订单平台.需要梳理各业务线的数据表,但每个业务线库都有近百张和订单相关的表,挨个表一个一个字段的弄脑瓜子嗡嗡的. 为了不重复 CV 操作,抱着 ...