1. 程式人生 > >【ALSA】使用ALSA進行音訊資料擷取 之其二

【ALSA】使用ALSA進行音訊資料擷取 之其二

針對前一篇的 main (int argc, char *argv[]) 裡面的argc以及argv[]引數做個簡單解釋,

argc 代表命令列的引數數量

argv[] 將資料引數 記錄的位置,

範例: target# > ./MiniCapture default

那麼argc = 2 , argv[0] =MiniCapture ,argv[1] = default ,以上為主程式引數簡易說明。

底下就目前所瞭解的 ALSA API 程式碼,做一個簡單說明:

使用ALSA播放/錄製大概是這樣一個流程,

1、開啟裝置

2、引數設定

3、資料擷取/播放

4、關閉/釋放空間等..

以下是簡易說明:

1、開啟裝置--

snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)

**pcm 似乎是設定擋的名稱!?

name: 是硬體裝置,目前使用default,另外有 plughw:0,0或是hw:0,0等,細部仍不甚瞭解

steam:錄音為SND_PCM_STREAM_CAPTURE,放音則為SND_PCM_STREAM_PLAYBACK等

mode: 0為標準模式,另外有SND_PCM_NONBLOCK以及SND_PCM_ASYNC引數,

NONBLOCK為立即的讀寫,ASYNC當做完一個週期讀寫後會使用SIGIO(不懂!?)

2、引數設定--

需要設定的引數有: a、記憶體空間配置-使用malloc或alloca進行空間配置

b、使用snd_pcm_hw_params_any 建立 對應硬體結構 的 完整引數定義

c、access type-設定交錯/非交錯的處理模式

d、sample format- 設定取樣的資料格式,SND_PCM_FORMAT_S16_LE格式為16bit資料(=2bytes),應該還有其他種格式,未查明。

e、sample rate-取樣率,一般CD音質使用44100Hz,若僅做語音辨識,可設定為32000或16000即可,

映體不見得可完全依照所設定的取樣率,可使用snd_pcm_hw_params_set_rate_near

進行做接近的設定。

f、channel 可定通道數量,一般為單聲道或雙聲道,在交錯式雙聲道的設定下,一次取聲音(1 frame)將有 L(2Bytes)R(2Bytes)=1 word長度的資料

g、snd_pcm_hw_params()指令會將引數資料寫入硬體進行設定。

3、資料擷取/播放:
擷取交錯式資料使用 snd_pcm_readi,而非交錯式則為snd_pcm_readn,

播放交錯式資料使用 snd_pcm_writei,而非交錯式則為snd_pcm_writen,

4、關閉/釋放空間:

使用snd_pcm_close、snd_pcm_drop、snd_pcm_drain等命令進行PCM裝置的關閉,並free(buf)釋放掉相關空間。

其中較為複雜的部分,在規劃buffer空間,以及檔案資料的讀取,

假設使用 16bit的資料格式,雙通道模式,frame數量為 frames,進行播放

參考下列程式碼:

[cpp] view plaincopyprint?
  1. <span style="white-space: pre;">    </span>buff_size = frames * channels * 2/* 2 -> sample size */
  2.     buff = (char *) malloc(buff_size); 
  3.     snd_pcm_hw_params_get_period_time(params, &tmp, NULL); 
  4.     for (loops = (seconds * 1000000) / tmp; loops > 0; loops--) { 
  5.         if (pcm = read(0, buff, buff_size) == 0) { 
  6.             printf("Early end of file.\n"); 
  7.             return 0; 
  8.         } 
  9.         if (pcm = snd_pcm_writei(pcm_handle, buff, frames) == -EPIPE) { 
  10.             printf("XRUN.\n"); 
  11.             snd_pcm_prepare(pcm_handle); 
  12.         } elseif (pcm < 0) { 
  13.             printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm)); 
  14.         } 
  15.     } 
<span style="white-space: pre;">	</span>buff_size = frames * channels * 2 /* 2 -> sample size */;
	buff = (char *) malloc(buff_size);

	snd_pcm_hw_params_get_period_time(params, &tmp, NULL);

	for (loops = (seconds * 1000000) / tmp; loops > 0; loops--) {

		if (pcm = read(0, buff, buff_size) == 0) {
			printf("Early end of file.\n");
			return 0;
		}
		if (pcm = snd_pcm_writei(pcm_handle, buff, frames) == -EPIPE) {
			printf("XRUN.\n");
			snd_pcm_prepare(pcm_handle);
		} else if (pcm < 0) {
			printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
		}
	}

buffer_size=frame數量*4byte,並使用malloc()配置buff空間,用來放要播放的資料,

seconds為要播放的秒數,用ger_period_time取得播放一個週期所需時間,計算出需要loop的次數,

然後將讀取的資料使用snd_pcm_writei寫到pcm_handle設定的機器,資料由buff指向的位置取資料,

一次取1frames(=16bit LR=4bytes),取的次數為frames次,這樣為一個period,

依據'設定的sample rate,若44100的取樣率,一秒則需要44100筆frame(4byte),

一次period若為100筆frame,那麼計算出來loop就會是441次,

buffer_size就會是100*4Bytes,將資料取出相對應大小放置在此區域,重複使用此空間進行播放。