1. 程式人生 > >Linux ARM 使用USB麥克風ALSA音訊裝置程式設計

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軟體對麥克風進行錄音驗證。這樣可以先保證硬體連線的正確性,然後在測試程式。