Linux ARM 使用USB麦克风ALSA音频设备编程

近期有一个项目要用到音频处理,先是对标准的麦克风输入设备进行了测试,后来使用的USB麦克风,在编程时遇到了小问题,所以记下笔记。

一、环境

1.系统Linux (Lubuntu)

2.硬件CPU: RK3288(Coretex-A17)

3.USB 麦克风(本篇教程支持Alsa架构的USB麦克风和普通麦克风设备)

4.应用软件介绍

Audacity:这是一个免费的音频处理软件,Linux和WIndows版本都有,软件安装就不说明了很简单。地址:https://www.audacityteam.org/

二、代码

标准的代码在网上找的测试例子,直接复制保存为cap.c文件

编译

注意如果系统中没有asound类库需要安装,如下命令

sudo apt-get install libasound2-dev

如何有asound库就直接gcc编译如下即可

gcc cap.c -o cap -lasound

这样就能生成可执行文件cap

./cap运行就会录音了,得到的声音原始数据可以通过Audacity这个软件导入播放和处理(下边会简单介绍下)。

这里重点说一下打开设备(也是本人遇到的问题)

 rc = snd_pcm_open(&handle, "hw:3,0",SND_PCM_STREAM_CAPTURE,0);

这句打开设备,网上大部分的源码是

rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_CAPTURE,0);

一般默认的都是板载的声卡,所以default的时候肯定打开的不是我们的USB麦克风的(这里说一下,正常我们打开一个串口设备格式是"/dev/ttyS0"这种,我按照这个思路,default的位置更换成"/dev/snd/pcmC3D0c"是不可以的)。

音频设备文件节点

hw:3,0的得来参考着这个文章https://blog.csdn.net/explore_world/article/details/51013942对设备名的讲解

设备命名
API 库使用逻辑设备名而不是设备文件。设备名字可以是真实的硬件名字也可以是插件名字。硬件名
字使用hw:i,j这样的格式。其中i是卡号,j是这块声卡上的设备号。第一个声音设备是hw:0,0.这个
别名默认引用第一块声音设备并且在本文示例中一真会被用到。插件使用另外的唯一名字。比如plughw:,
表示一个插件,这个插件不提供对硬件设备的访问,而是提供像采样率转换这样的软件特性,硬件本身并
不支持这样的特性。

于是查询PCM这设备,得到这个hw:3,0(这里提示一下如果安装了linux-arm的audacity软件,在软件界面上也可以看到这个hw:3,0)

调试过程中要针对自己的麦克风进行相应的设置调整,比如本人测试中主要修改。一个是上文提到的open设备的名称。

还有就是USB麦克风是单声道的,这是需要根据实际情况进行设置。

设置单声道和下边size

 snd_pcm_hw_params_set_channels(handle,params,1);

 size = frames * 2; /* 2 bytes/sample, 1channels */

具体代码如下(来自网路,根据自己麦克的情况进行了相应修改):

/*
read from the default PCM device and writes to standard output for 5 seconds of data
修改声音采集配置时候,除了修改声音通道数量,还应该考虑申请的缓冲区时候足够大 
*/
 
#define ALSA_PCM_NEW_HW_PARAMS_API
 
#include <alsa/asoundlib.h>
 
int main()
{
   long loops;        //一个长整型变量, 
   int rc;            //一个int变量 ,用来存放 snd_pcm_open(访问硬件)的返回值 
   int size;        //一个int变量 
   snd_pcm_t * handle;        // 一个指向snd_pcm_t的指针 
   snd_pcm_hw_params_t * params;    // 一个指向 snd_pcm_hw_params_t的指针 
   unsigned int val;        // 无符号整型变量 ,用来存放录音时候的采样率 
   int dir;            // 整型变量 
   snd_pcm_uframes_t frames;        // snd_pcm_uframes_t 型变量 
   char * buffer;        // 一个字符型指针 
   FILE * out_fd;        // 一个指向文件的指针 
   out_fd = fopen("out_pcm.raw","wb+");        /* 将流与文件之间的关系建立起来,文
                                               件名为 out_pcm.raw,w是以文本方式
                                               打开文件,wb是二进制方式打开文件wb+ 
                                               读写打开或建立一个二进制文件,允许读和写。*/ 
   /* open PCM device for recording (capture). */
   // 访问硬件,并判断硬件是否访问成功 
   rc = snd_pcm_open(&handle, "hw:3,0",SND_PCM_STREAM_CAPTURE,0);
   if( rc < 0 )
   {
      fprintf(stderr,
              "unable to open pcm device: %s\n",
              snd_strerror(rc));
      exit(1);
   }
   /* allocate a hardware parameters object */
   // 分配一个硬件变量对象 
   snd_pcm_hw_params_alloca(&params);
   /* fill it with default values. */
   // 按照默认设置对硬件对象进行设置 
   snd_pcm_hw_params_any(handle,params);
   /* 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 */
   // 设置数据编码格式为PCM、有符号、16bit、LE格式 
   snd_pcm_hw_params_set_format(handle,params,
                                SND_PCM_FORMAT_S16_LE);
   /* two channels(stereo) */
   // 设置单声道 
   snd_pcm_hw_params_set_channels(handle,params,1);
   /* sampling rate */
   // 设置采样率 
   val = 44100;
   snd_pcm_hw_params_set_rate_near(handle,params,&val,&dir);
   /* set period size */
   // 周期长度(帧数) 
   frames = 32;
   snd_pcm_hw_params_set_period_size_near(handle,params,&frames,&dir);
   /* write parameters to the driver */
   // 将配置写入驱动程序中
   // 判断是否已经配置正确 
   rc = snd_pcm_hw_params(handle,params);
   if ( rc < 0 )
   {
       fprintf(stderr,
               "unable to set hw parameters: %s\n",
               snd_strerror(rc));
       exit(1);
   }
   /* use a buffer large enough to hold one period */
   // 配置一个缓冲区用来缓冲数据,缓冲区要足够大,此处看意思应该是只配置了
   // 够两个声道用的缓冲内存 
   snd_pcm_hw_params_get_period_size(params,&frames,&dir);
   size = frames * 2; /* 2 bytes/sample, 2channels */
   buffer = ( char * ) malloc(size);
   /* loop for 5 seconds */
   // 记录五秒长的声音 
   snd_pcm_hw_params_get_period_time(params, &val, &dir);
   loops = 5000000 / val;
   while( loops > 0 )
   {
       loops--;
       rc = snd_pcm_readi(handle,buffer,frames);
       if ( rc == -EPIPE )
       {
          /* EPIPE means overrun */
          fprintf(stderr,"overrun occured\n");
          snd_pcm_prepare(handle);
       }
       else if ( rc < 0 )
       {
          fprintf(stderr,"error from read: %s\n",
                  snd_strerror(rc));
       }
       else if ( rc != (int)frames)
       {
          fprintf(stderr,"short read, read %d frames\n",rc);
       }
       // 将音频数据写入文件 
       rc = fwrite(buffer, 1, size, out_fd);
       // rc = write(1, buffer, size);
       if ( rc != size )
       {
          fprintf(stderr,"short write: wrote %d bytes\n",rc);
       }
   }
   snd_pcm_drain(handle);
   snd_pcm_close(handle);
   free(buffer);
   fclose(out_fd);
}
 

三、测试结果。

代码如上,在cap运行后在同等级目录生成了out_pcm.raw文件,是声音的原始数据。

本人将其导入Audacity进行播放,当然,在执行程序的5S中内要对着麦克风说话录音。

导入方法

根据程序的实际情况设置参数

导入音频数据后点击绿色三角箭头就可以对音频进行播放了,另外,可以audacity软件对麦克风进行录音验证。这样可以先保证硬件连接的正确性,然后在测试程序。

猜你喜欢

转载自blog.csdn.net/maowendi/article/details/82348690