一、前言
目的是实现一种效果,就是在音频识别时,能够实时显示当前人员说话的声音幅度,通过波形曲线的形式显示出来。如下效果(非我实现)

还可以实现在实时采集的同时,将需要的数据保存成指定格式文件,如csv,wav,pcm等。
目前函数大部分时存储为pcm格式,如需转成其他格式需要使用不同的库函数或者按照协议格式重写
二、环境
全志A40i
linux3.10
alsa
三、正文
从硬件上首先要具备Audio音频部分,这里硬件支持的话具备硬件电路接口就不同多说什么了,其次就主要是软件层次了
首先要在linux上使用音频采集,就要使用标注你的alsa库,交叉安装alsa库的方式也有很多
有的平台环境上和硬件上都默认是已经包含的了,可以通过find -name libasound.so查看
这里我是用的是qt交叉编译程序,一开始总是没有找到libasound库,后来把lib去掉才能正常识别
在pro文件中加入
LIBS +=   -L/root/workspace/allwinner/A40i/bsp/lichee/out/sun8iw11p1/linux/common/buildroot/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/ -lasound
 
头文件中加入
#include <alsa/asoundlib.h>
 
至此,我终于能够在我的编译环境中正常不报错的编译程序了,但是才刚刚入坑,后面的问题一把又一把的出现,每次都是以为解决当前的问题后面就都不是问题了,结果,,,哎,都是泪
我是用的源码如下:
void menu::on_btn_Ingames_2_clicked()
{
    snd_pcm_t *handle;//PCM句柄
    snd_pcm_hw_params_t *params;//配置硬件参数结构体(PCM硬件配置空间容器)
//    snd_pcm_close(handle);
    int ret = snd_pcm_open(&handle,"hw:/dev/snd/controlC0",SND_PCM_STREAM_CAPTURE,0);//SND_PCM_STREAM_PLAYBACK 或 SND_PCM_STREAM_CAPTURE,分别表示播放和录音的PCM流
    if(ret < 0){
        fprintf(stderr,"unable to open pcm device: %s\n",snd_strerror(ret));
        exit(1);
    }
    else{
        qDebug()<<QString("open pcm device:%1").arg(snd_strerror(ret));
    }
    //params申请内存(使用标准alloca分配无效的snd_pcm_hw_参数)
    snd_pcm_hw_params_alloca(¶ms);
    //使用pcm设备初始化hwparams(用PCM的完整配置空间填充参数)
   snd_pcm_hw_params_any(handle,params);
   //设置多路数据在buffer中的存储方式
       //SND_PCM_ACCESS_RW_INTERLEAVED每个周期(period)左右声道的数据交叉存放
   snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED);//SND_PCM_ACCESS_RW_INTERLEAVED//SND_PCM_ACCESS_RW_NONINTERLEAVED
    //设置16位采样格式
   snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
   //设置声道数1
   ret = snd_pcm_hw_params_set_channels(handle,params,1);
   if(ret <0){
       fprintf(stderr,"Error setting channels.\n");
       exit(1);
   }
   else
      qDebug()<<"set channels ok";
   int dir=0;
   int rate = 44100; /* Sample rate */
   unsigned int exact_rate;   /* Sample rate returned by */
   //设置采样率44100,如果采样率不支持,会用硬件支持最接近的采样率
   exact_rate=rate;
   ret = snd_pcm_hw_params_set_rate_near(handle,params,&exact_rate,0);
  if(ret <0){
      fprintf(stderr,"Error setting rate.\n");
      exit(1);
  }
  else
     qDebug()<<"set rate ok";
  if (rate != exact_rate) {
    fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n Using %d Hz instead.\n", rate, exact_rate);
  }
  else
      qDebug()<<"best rate is "<<QString::number(exact_rate);
//    int periods = 2; //2      /* Number of periods */
//    /* Set number of periods. Periods used to be called fragments. */
//   ret = snd_pcm_hw_params_set_periods(handle,params,periods,0);
//   if(ret <0){
//       fprintf(stderr,"Error setting periods.\n");
//       exit(1);
//   }
//   else
//      qDebug()<<"set periods ok ,periods is "<<QString::number(periods);
   long unsigned int frames = 128;//32
//    //设置周期大小(将配置空间限制为最接近目标的周期大小)
//   snd_pcm_hw_params_set_period_size_near(handle,params,&frames,0);//0
//   qDebug()<<"frames near "<<QString::number(frames);
//   snd_pcm_uframes_t periodsize = 4096;
//   if (snd_pcm_hw_params_set_buffer_size(handle, params, (periodsize * periods)>>2) < 0) {
//         fprintf(stderr, "Error setting buffersize.\n");
//         exit(1);
//       }
//   else
//       qDebug()<<"set buffersize ok";
//让这些参数作用于PCM设备(安装从配置空间和SND_PCM_PREPARE选自配置空间的PCM硬件配置)
   ret = snd_pcm_hw_params(handle,params);
   if(ret <0){
       fprintf(stderr,"unable toset hw params: %s\n",snd_strerror(ret));
       exit(1);
   }
   else
       qDebug()<<"set hw params ok";
   //
//long unsigned int frames = 0;//32
 //从配置空间中提取周期大小。
   snd_pcm_hw_params_get_period_size(params,&frames,0);
   int size = frames *4;
   qDebug()<<"frames is "<<QString::number(frames)<<",size is "<<QString::number(size);
   char* pcmBuff = (char*)malloc(size);
//从配置空间中提取周期时间
   unsigned int val =0;
   snd_pcm_hw_params_get_period_time(params,&val,0);
   qDebug()<<"val is "<<QString::number(val);
//打开输出文件
   FILE* pFile;
   pFile = fopen("test.pcm","wb");
  int index=0;
   while(index<10)
   {
        //从PCM读取交错帧
       ret = snd_pcm_readi(handle,pcmBuff,frames);
       if(ret == -EPIPE){//发生暂停事件(Stream被暂停并等待应用程序恢复)
           fprintf(stderr,"overrun.... \n");
           snd_pcm_prepare(handle);
       }
       else if(ret < 0){
           fprintf(stderr,"error read: %s\n",snd_strerror(ret));
           exit(1);
       }
       else if(ret != frames ){
           fprintf(stderr,"less read: %s\n",ret);
       }
       ret = fwrite(pcmBuff,sizeof(char),size,pFile);
       if(ret != size){
           fprintf(stderr,"less write: %s\n",ret);
       }
       printf("%d\n",index);
       index++;
   }
   snd_pcm_drain(handle);
   snd_pcm_close(handle);
   free(pcmBuff);
   fclose(pFile);
   printf("audio capture exit.. \n");
   ui->label_44->setText(QString::number(qrand()%100));
}
 
各位看官先不要着急ctrl+c,这里源码未经过最终调试,因为我在第一步snd_pcm_open就卡住了,哭晕。
第一步解决了第二部又出现问题,真是一步一个坎啊,总共需要配置10多个函数,每个函数都要搞明白具体含义是什么,才能具体调试,上述代码是暂时能够简单录制声音,并生成pcm文件,通过audacity-win-2.2.2.exe软件可以播放录制的pcm声音。
程序执行打印信息

pcm文件解析播放声音
接下来就是详细梳理分析每个使用到的函数功能作用,并加上注释去消化理解,不知不觉凌晨2点了,下面(等哟时间的)功能还差将读取的数据解析细化出来每个帧含义,将数据分类显示成曲线和生成其他格式文件。
四、结语
有时候项目没有调完就写上博客了,为什么呢,所以这个文章的内容主要时干嘛的呢
第一:记录前期环境准备和铺路方式,后续在走就能少走点弯路
第二:立下此flag,以后要解决,以后解决更新此文
第三:把我的问题发布出来,如果有同道中人遇到此问题或有解决方式,可以互相探讨解决。
第四:分享给可能进度还没有到达我这一步的新人们,互相沟通互相交流i互相学习
未完待续
![状态设计模式(State Pattern)[论点:概念、相关角色、图示、示例代码、框架中的运用、适用场景]](https://img-blog.csdnimg.cn/dde5eda6e370408bbc329458b5d5d5dd.png#pic_center)


















