自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux音頻設(shè)備驅(qū)動架構(gòu)及應(yīng)用編程

系統(tǒng) Linux
最早出現(xiàn)在Linux上的音頻編程接口是OSS(Open Sound System),它由一套完整的內(nèi)核驅(qū)動程序模塊組成,可以為絕大多數(shù)聲卡提供統(tǒng)一的編程接口。雖然OSS已經(jīng)非常成熟,但它畢竟是一個沒有完全開放源代碼的商業(yè)產(chǎn)品,ALSA(Advanced Linux Sound Architecture)恰好彌補(bǔ)了這一空白,它是在Linux下進(jìn)行音頻編程時另一個可供選擇的聲卡驅(qū)動程序。

[[209010]]

最早出現(xiàn)在Linux上的音頻編程接口是OSS(Open Sound System),它由一套完整的內(nèi)核驅(qū)動程序模塊組成,可以為絕大多數(shù)聲卡提供統(tǒng)一的編程接口。OSS出現(xiàn)的歷史相對較長,這些內(nèi)核模塊中的一部分(OSS/Free)是與Linux內(nèi)核源碼共同免費(fèi)發(fā)布的,另外一些則以二進(jìn)制的形式由4Front Technologies公司提供。由于得到了商業(yè)公司的鼎力支持,OSS已經(jīng)成為在Linux下進(jìn)行音頻編程的事實標(biāo)準(zhǔn),支持OSS的應(yīng)用程序能夠在絕大多數(shù)聲卡上工作良好。

雖然OSS已經(jīng)非常成熟,但它畢竟是一個沒有完全開放源代碼的商業(yè)產(chǎn)品,ALSA(Advanced Linux Sound Architecture)恰好彌補(bǔ)了這一空白,它是在Linux下進(jìn)行音頻編程時另一個可供選擇的聲卡驅(qū)動程序。ALSA除了像OSS那樣提供了一組內(nèi)核驅(qū)動程序模塊之外,還專門為簡化應(yīng)用程序的編寫提供了相應(yīng)的函數(shù)庫,與OSS提供的基于ioctl的原始編程接口相比,ALSA函數(shù)庫使用起來要更加方便一些。

ALSA的主要特點有:

1)支持多種聲卡設(shè)備

2)模塊化的內(nèi)核驅(qū)動程序

003)支持SMP和多線程

4)提供應(yīng)用開發(fā)函數(shù)庫

5)兼容OSS應(yīng)用程序

ALSA和OSS最大的不同之處在于ALSA是由志愿者維護(hù)的自由項目,OSS則是由公司提供的商業(yè)產(chǎn)品,因此在對硬件的適應(yīng)程度上OSS要優(yōu)于ALSA,它能夠支持的聲卡種類更多。ALSA雖然不及OSS運(yùn)用得廣泛,但卻具有更加友好編程接口,并且完全兼容于OSS,對應(yīng)用程序員來講無疑是一個更佳的選擇。

兩種音頻編程接口驅(qū)動的組成如下:

1) Linux OSS 音頻設(shè)備驅(qū)動的組成、mixer 接口、dsp 接口及用戶空間編程方法。

2) Linux ALSA 音頻設(shè)備驅(qū)動的組成、card 和組件管理、PCM 設(shè)備、control 接口、AC97 API及用戶空間編程方法。

1. 數(shù)字音頻設(shè)備

目前,手機(jī)、PDA、MP3 等許多嵌入式設(shè)備中包含了數(shù)字音頻設(shè)備,一個典型的數(shù)字音頻系統(tǒng)的電路組成為:嵌入式微控制器/DSP 中集成了PCM、IIS 或AC97 音頻接口,通過這些接口連接外部的音頻編解碼器即可實現(xiàn)聲音的AD 和DA 轉(zhuǎn)換,功放完成模擬信號的放大功能。

音頻編解碼器是數(shù)字音頻系統(tǒng)的核心,衡量它的指標(biāo)主要有:

• 采樣頻率

采樣的過程就是將通常的模擬音頻信號的電信號轉(zhuǎn)換成二進(jìn)制碼0 和1 的過程,這些0 和1 便構(gòu)成了數(shù)字音頻文件。如圖17.2 中的正弦曲線代表原始音頻曲線,方格代表采樣后得到的結(jié)果,二者越吻合說明采樣結(jié)果越好。采樣頻率是每秒鐘的采樣次數(shù),我們常說的 44.1kHz 采樣頻率就是每秒鐘采樣44100 次。理論上采樣頻率越高,轉(zhuǎn)換精度越高,目前主流的采樣頻率是48kHz。

• 量化精度

量化精度是指對采樣數(shù)據(jù)分析的精度,比如24bit 量化精度就是是將標(biāo)準(zhǔn)電平信號按照2 的24 次方進(jìn)行分析,也就是說將圖17.2 中的縱坐標(biāo)等分為224 等分。量化精度越高,聲音就越逼真。

2. 音頻設(shè)備硬件接口

2.1 PCM 接口

針對不同的數(shù)字音頻子系統(tǒng),出現(xiàn)了幾種微處理器或DSP 與音頻器件間用于數(shù)字轉(zhuǎn)換的接口。最簡單的音頻接口是PCM(脈沖編碼調(diào)制)接口,該接口由時鐘脈沖(BCLK)、幀同步信號(FS)及接收數(shù)據(jù)(DR)和發(fā)送數(shù)據(jù)(DX)組成。在FS 信號的上升沿,數(shù)據(jù)傳輸從MSB(Most Significant Bit)字開始,F(xiàn)S 頻率等于采樣率。FS 信號之后開始數(shù)據(jù)字的傳輸,單個的數(shù)據(jù)位按順序進(jìn)行傳輸,1 個時鐘周期傳輸1 個數(shù)據(jù)字。發(fā)送MSB 時,信號的等級首先降到最低,以避免在不同終端的接口使用不同的數(shù)據(jù)方案時造成MSB 的丟失。PCM 接口很容易實現(xiàn),原則上能夠支持任何數(shù)據(jù)方案和任何采樣率,但需要每個音頻通道獲得一個獨立的數(shù)據(jù)隊列。

2.2 IIS 接口

IIS 接口(Inter-IC Sound)在20 世紀(jì)80 年代首先被飛利浦用于消費(fèi)音頻,并在一個稱為LRCLK(Left/RightCLOCK)的信號機(jī)制中經(jīng)過多路轉(zhuǎn)換,將兩路音頻信號變成單一的數(shù)據(jù)隊列。當(dāng)LRCLK 為高時,左聲道數(shù)據(jù)被傳輸;LRCLK 為低時,右聲道數(shù)據(jù)被傳輸。與PCM 相比,IIS 更適合于立體聲系統(tǒng)。對于多通道系統(tǒng),在同樣的BCLK 和LRCLK 條件下,并行執(zhí)行幾個數(shù)據(jù)隊列也是可能的。

2.3 AC97 接口

AC'97(Audio Codec 1997)是以Intel 為首的五個PC 廠商Intel、Creative Labs、NS、Analog Device與Yamaha 共同提出的規(guī)格標(biāo)準(zhǔn)。與PCM 和IIS 不同,AC'97 不只是一種數(shù)據(jù)格式,用于音頻編碼的內(nèi)部架構(gòu)規(guī)格,它還具有控制功能。AC'97 采用AC-Link 與外部的編解碼器相連,AC-Link 接口包括位時鐘(BITCLK)、同步信號校正(SYNC)和從編碼到處理器及從處理器中解碼(SDATDIN 與SDATAOUT)的數(shù)據(jù)隊列。AC'97數(shù)據(jù)幀以SYNC 脈沖開始,包括12 個20 位時間段(時間段為標(biāo)準(zhǔn)中定義的不同的目的服務(wù))及16 位“tag”段,共計256 個數(shù)據(jù)序列。例如,時間段“1”和“2”用于訪問編碼的控制寄存器,而時間段“3”和“4”分別負(fù)載左、右兩個音頻通道。“tag”段表示其他段中哪一個包含有效數(shù)據(jù)。把幀分成時間段使傳輸控制信號和音頻數(shù)據(jù)僅通過4 根線到達(dá)9 個音頻通道或轉(zhuǎn)換成其他數(shù)據(jù)流成為可能。與具有分離控制接口的IIS方案相比,AC'97 明顯減少了整體管腳數(shù)。

PCM、IIS 和AC97 各有其優(yōu)點和應(yīng)用范圍,例如在CD、MD、MP3 隨身聽多采用IIS 接口,移動電話會采用PCM 接口,具有音頻功能的PDA 則多使用和PC 一樣的AC'97 編碼格式。

3. Linux OSS 音頻設(shè)備驅(qū)動及應(yīng)用編程

3.1 OSS 驅(qū)動的組成

OSS 標(biāo)準(zhǔn)中有2 個最基本的音頻設(shè)備:mixer(混音器)和DSP(數(shù)字信號處理器)。

在聲卡的硬件電路中,mixer 是一個很重要的組成部分,它的作用是將多個信號組合或者疊加在一起,對于不同的聲卡來說,其混音器的作用可能各不相同。OSS 驅(qū)動中,/dev/mixer 設(shè)備文件是應(yīng)用程序?qū)ixer進(jìn)行操作的軟件接口。

混音器電路通常由兩個部分組成:輸入混音器(input mixer)和輸出混音器(output mixer)。

輸入混音器負(fù)責(zé)從多個不同的信號源接收模擬信號,這些信號源有時也被稱為混音通道或者混音設(shè)備。模擬信號通過增益控制器和由軟件控制的音量調(diào)節(jié)器后,在不同的混音通道中進(jìn)行級別(level)調(diào)制,然后被送到輸入混音器中進(jìn)行聲音的合成?;煲羝魃系碾娮娱_關(guān)可以控制哪些通道中有信號與混音器相連,有些聲卡只允許連接一個混音通道作為錄音的音源,而有些聲卡則允許對混音通道做任意的連接。經(jīng)過輸入混音器處理后的信號仍然為模擬信號,它們將被送到A/D 轉(zhuǎn)換器進(jìn)行數(shù)字化處理。

輸出混音器的工作原理與輸入混音器類似,同樣也有多個信號源與混音器相連,并且事先都經(jīng)過了增益調(diào)節(jié)。當(dāng)輸出混音器對所有的模擬信號進(jìn)行了混合之后,通常還會有一個總控增益調(diào)節(jié)器來控制輸出聲音的大小,此外還有一些音調(diào)控制器來調(diào)節(jié)輸出聲音的音調(diào)。經(jīng)過輸出混音器處理后的信號也是模擬信號,它們最終會被送給喇叭或者其它的模擬輸出設(shè)備。對混音器的編程包括如何設(shè)置增益控制器的級別,以及怎樣在不同的音源間進(jìn)行切換,這些操作通常來講是不連續(xù)的,而且不會像錄音或者放音那樣需要占用大量的計算機(jī)資源。

由于混音器的操作不符合典型的讀/寫操作模式,因此除了open()和close()兩個系統(tǒng)調(diào)用之外,大部分的操作都是通過ioctl()系統(tǒng)調(diào)用來完成的。與/dev/dsp 不同,/dev/mixer 允許多個應(yīng)用程序同時訪問,并且混音器的設(shè)置值會一直保持到對應(yīng)的設(shè)備文件被關(guān)閉為止。DSP 也稱為編解碼器,實現(xiàn)錄音(錄音)和放音(播放),其對應(yīng)的設(shè)備文件是/dev/dsp 或/dev/sound/dsp。

OSS 聲卡驅(qū)動程序提供的/dev/dsp 是用于數(shù)字采樣和數(shù)字錄音的設(shè)備文件,向該設(shè)備寫數(shù)據(jù)即意味著激活聲卡上的D/A 轉(zhuǎn)換器進(jìn)行放音,而向該設(shè)備讀數(shù)據(jù)則意味著激活聲卡上的A/D 轉(zhuǎn)換器進(jìn)行錄音。在從DSP 設(shè)備讀取數(shù)據(jù)時,從聲卡輸入的模擬信號經(jīng)過A/D 轉(zhuǎn)換器變成數(shù)字采樣后的樣本,保存在聲卡驅(qū)動程序的內(nèi)核緩沖區(qū)中,當(dāng)應(yīng)用程序通過 read()系統(tǒng)調(diào)用從聲卡讀取數(shù)據(jù)時,保存在內(nèi)核緩沖區(qū)中的數(shù)字采樣結(jié)果將被復(fù)制到應(yīng)用程序所指定的用戶緩沖區(qū)中。需要指出的是,聲卡采樣頻率是由內(nèi)核中的驅(qū)動程序所決定的,而不取決于應(yīng)用程序從聲卡讀取數(shù)據(jù)的速度。如果應(yīng)用程序讀取數(shù)據(jù)的速度過慢,以致低于聲卡的采樣頻率,那么多余的數(shù)據(jù)將會被丟棄(即overflow);如果讀取數(shù)據(jù)的速度過快,以致高于聲卡的采樣頻率,那么聲卡驅(qū)動程序?qū)枞切┱埱髷?shù)據(jù)的應(yīng)用程序,直到新的數(shù)據(jù)到來為止。在向DSP 設(shè)備寫入數(shù)據(jù)時,數(shù)字信號會經(jīng)過D/A 轉(zhuǎn)換器變成模擬信號,然后產(chǎn)生出聲音。應(yīng)用程序?qū)懭霐?shù)據(jù)的速度應(yīng)該至少等于聲卡的采樣頻率,過慢會產(chǎn)生聲音暫?;蛘咄nD的現(xiàn)象(即underflow)。如果用戶寫入過快的話,它會被內(nèi)核中的聲卡驅(qū)動程序阻塞,直到硬件有能力處理新的數(shù)據(jù)為止。

與其它設(shè)備有所不同,聲卡通常不需要支持非阻塞(non-blocking)的I/O 操作。即便內(nèi)核OSS 驅(qū)動提供了非阻塞的I/O 支持,用戶空間也不宜采用。無論是從聲卡讀取數(shù)據(jù),或是向聲卡寫入數(shù)據(jù),事實上都具有特定的格式(format),如無符號8 位、單聲道、8KHz 采樣率,如果默認(rèn)值無法達(dá)到要求,可以通過ioctl()系統(tǒng)調(diào)用來改變它們。通常說來,在應(yīng)用程序中打開設(shè)備文件/dev/dsp 之后,接下去就應(yīng)該為其設(shè)置恰當(dāng)?shù)母袷剑缓蟛拍軓穆暱ㄗx取或者寫入數(shù)據(jù)。

3.2 mixer 接口

int register_sound_mixer(struct file_operations *fops, int dev);

上述函數(shù)用于注冊1 個混音器,第1 個參數(shù)fops 即是文件操作接口,第2 個參數(shù)dev 是設(shè)備編號,如果填入-1,則系統(tǒng)自動分配1 個設(shè)備編號。mixer 是1 個典型的字符設(shè)備,因此編碼的主要工作是實現(xiàn)file_operations 中的open()、ioctl()等函數(shù)。mixer 接口file_operations 中的最重要函數(shù)是ioctl(),它實現(xiàn)混音器的不同IO 控制命令,下面的代碼清單給出了1 個ioctl()的范例。

mixer()接口ioctl()函數(shù)范例:

[cpp] view plain copy

  1. static int mixdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)   
  2. {   
  3.  ...   
  4.  switch (cmd)   
  5.  {   
  6.  case SOUND_MIXER_READ_MIC:   
  7.  ...   
  8.  case SOUND_MIXER_WRITE_MIC:   
  9.  ...   
  10.  case SOUND_MIXER_WRITE_RECSRC:   
  11.  ...   
  12.  case SOUND_MIXER_WRITE_MUTE:   
  13.  ...   
  14.  }   
  15.  ...   
  16.  return mixer_ioctl(codec, cmd, arg);   
  17. }    

3.3 DSP 接口

  1. int register_sound_dsp(struct file_operations *fops, int dev); 

上述函數(shù)與register_sound_mixer()類似,它用于注冊1 個dsp 設(shè)備,第1 個參數(shù)fops 即是文件操作接口,第2 個參數(shù)dev 是設(shè)備編號,如果填入-1,則系統(tǒng)自動分配1 個設(shè)備編號。dsp 也是1 個典型的字符設(shè)備,因此編碼的主要工作是實現(xiàn)file_operations 中的read()、write()、ioctl()等函數(shù)。dsp 接口file_operations 中的read()和write()函數(shù)非常重要,read()函數(shù)從音頻控制器中獲取錄音數(shù)據(jù)到緩沖區(qū)并拷貝到用戶空間,write()函數(shù)從用戶空間拷貝音頻數(shù)據(jù)到內(nèi)核空間緩沖區(qū)并最終發(fā)送到音頻控制器。

dsp 接口file_operations 中的ioctl()函數(shù)處理對采樣率、量化精度、DMA 緩沖區(qū)塊大小等參數(shù)設(shè)置IO控制命令的處理。在數(shù)據(jù)從緩沖區(qū)拷貝到音頻控制器的過程中,通常會使用DMA,DMA對聲卡而言非常重要。例如,在放音時,驅(qū)動設(shè)置完DMA 控制器的源數(shù)據(jù)地址(內(nèi)存中DMA 緩沖區(qū))、目的地址(音頻控制器FIFO)和DMA 的數(shù)據(jù)長度,DMA 控制器會自動發(fā)送緩沖區(qū)的數(shù)據(jù)填充FIFO,直到發(fā)送完相應(yīng)的數(shù)據(jù)長度后才中斷一次。

在OSS 驅(qū)動中,建立存放音頻數(shù)據(jù)的環(huán)形緩沖區(qū)(ring buffer)通常是值得推薦的方法。此外,在OSS 驅(qū)動中,一般會將1 個較大的DMA 緩沖區(qū)分成若干個大小相同的塊(這些塊也被稱為“段”,即fragment),驅(qū)動程序使用DMA 每次在聲音緩沖區(qū)和聲卡之間搬移一個fragment。在用戶空間,可以使用ioctl()系統(tǒng)調(diào)用來調(diào)整塊的大小和個數(shù)。除了read()、write()和ioctl()外,dsp 接口的poll()函數(shù)通常也需要被實現(xiàn),以向用戶反饋目前能否讀寫DMA 緩沖區(qū)。在OSS 驅(qū)動初始化過程中,會調(diào)用register_sound_dsp()和register_sound_mixer()注冊dsp 和mixer 設(shè)備;在模塊卸載的時候,調(diào)用的代碼如下:

OSS 驅(qū)動初始化注冊dsp 和mixer設(shè)備:

[cpp] view plain copy

  1. static int myoss_init(void)   
  2. {   
  3.     struct oss_state *s = &myoss_state;   
  4.     ...   
  5.     //注冊dsp 設(shè)備   
  6.     if ((audio_dev_dsp = register_sound_dsp(&xxx_audio_fops, - 1)) < 0)   
  7.          goto err_dev1;   
  8.     //注冊mixer 設(shè)備   
  9.     if ((audio_dev_mixer = register_sound_mixer(&xxx_mixer_fops, - 1)) < 0)   
  10.          goto err_dev2;   
  11.     ...   
  12. }   
  13.    
  14. void __exit xxx_exit(void)   
  15. {   
  16.     //注銷dsp 和mixer 設(shè)備接口   
  17.     unregister_sound_dsp(audio_dev_dsp);   
  18.     unregister_sound_mixer(audio_dev_mixer);   
  19.     ...   
  20. }    

3.4 OSS 用戶空間編程

1、DSP 編程

對OSS 驅(qū)動聲卡的編程使用Linux 文件接口函數(shù),DSP 接口的操作一般包括如下幾個步驟:

① 打開設(shè)備文件/dev/dsp

采用何種模式對聲卡進(jìn)行操作也必須在打開設(shè)備時指定,對于不支持全雙工的聲卡來說,應(yīng)該使用只讀或者只寫的方式打開,只有那些支持全雙工的聲卡,才能以讀寫的方式打開,這還依賴于驅(qū)動程序的具體實現(xiàn)。Linux 允許應(yīng)用程序多次打開或者關(guān)閉與聲卡對應(yīng)的設(shè)備文件,從而能夠很方便地在放音狀態(tài)和錄音狀態(tài)之間進(jìn)行切換。

② 如果有需要,設(shè)置緩沖區(qū)大小

運(yùn)行在Linux 內(nèi)核中的聲卡驅(qū)動程序?qū)iT維護(hù)了一個緩沖區(qū),其大小會影響到放音和錄音時的效果,使用ioctl()系統(tǒng)調(diào)用可以對它的尺寸進(jìn)行恰當(dāng)?shù)脑O(shè)置。調(diào)節(jié)驅(qū)動程序中緩沖區(qū)大小的操作不是必須的,如果沒有特殊的要求,一般采用默認(rèn)的緩沖區(qū)大小也就可以了。如果想設(shè)置緩沖區(qū)的大小,則通常應(yīng)緊跟在設(shè)備

文件打開之后,這是因為對聲卡的其它操作有可能會導(dǎo)致驅(qū)動程序無法再修改其緩沖區(qū)的大小。

③ 設(shè)置聲道(channel)數(shù)量

根據(jù)硬件設(shè)備和驅(qū)動程序的具體情況,可以設(shè)置為單聲道或者立體聲。

④ 設(shè)置采樣格式和采樣頻率

采樣格式包括AFMT_U8(無符號8 位)、AFMT_S8(有符號8 位)、AFMT_U16_LE(小端模式,無符號16 位)、AFMT_U16_BE(大端模式,無符號16 位)、AFMT_MPEG、AFMT_AC3 等。使用SNDCTL_DSP_SETFMT IO 控制命令可以設(shè)置采樣格式。對于大多數(shù)聲卡來說,其支持的采樣頻率范圍一般為5kHz 到44.1kHz 或者48kHz,但并不意味著該范圍內(nèi)的所有連續(xù)頻率都會被硬件支持,在Linux 下進(jìn)行音頻編程時最常用到的幾種采樣頻率是11025Hz、16000Hz、22050Hz、32000Hz 和44100Hz。使用SNDCTL_DSP_SPEED IO 控制命令可以設(shè)置采樣頻率。

⑤ 讀寫/dev/dsp 實現(xiàn)播放或錄音

下面代碼實現(xiàn)了利用/dev/dsp 接口進(jìn)行聲音錄制和播放的過程,它的功能是先錄制幾秒鐘音頻數(shù)據(jù),將其存放在內(nèi)存緩沖區(qū)中,然后再進(jìn)行放音。

[cpp] view plain copy

  1. #include <unistd.h>   
  2.  #include <fcntl.h>   
  3.  #include <sys/types.h>   
  4.  #include <sys/ioctl.h>   
  5.  #include <stdlib.h>   
  6.  #include <stdio.h>   
  7.  #include <linux/soundcard.h>   
  8.  #define LENGTH 3   /* 存儲秒數(shù) */   
  9.  #define RATE 8000  /* 采樣頻率 */   
  10.  #define SIZE 8     /* 量化位數(shù) */   
  11.  #define CHANNELS 1 /* 聲道數(shù)目 */   
  12.  /* 用于保存數(shù)字音頻數(shù)據(jù)的內(nèi)存緩沖區(qū) */   
  13.  unsigned char buf[LENGTH *RATE * SIZE * CHANNELS / 8];   
  14.    
  15.  int main()   
  16.  {   
  17.    int fd; /* 聲音設(shè)備的文件描述符 */   
  18.    int arg; /* 用于ioctl 調(diào)用的參數(shù) */   
  19.    int status; /* 系統(tǒng)調(diào)用的返回值 */   
  20.       
  21.    /* 打開聲音設(shè)備 */   
  22.    fd = open("/dev/dsp", O_RDWR);   
  23.    if (fd < 0)   
  24.    {   
  25.       perror("open of /dev/dsp failed");   
  26.       exit(1);   
  27.    }   
  28.    /* 設(shè)置采樣時的量化位數(shù) */   
  29.    arg = SIZE;   
  30.    status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);   
  31.    if (status == - 1)   
  32.      perror("SOUND_PCM_WRITE_BITS ioctl failed");   
  33.     
  34.    if (arg != SIZE)   
  35.       perror("unable to set sample size");   
  36.    
  37.    /* 設(shè)置采樣時的通道數(shù)目 */   
  38.    arg = CHANNELS;   
  39.    status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg);   
  40.    if (status == - 1)   
  41.       perror("SOUND_PCM_WRITE_CHANNELS ioctl failed");   
  42.    if (arg != CHANNELS)   
  43.       perror("unable to set number of channels");   
  44.    
  45.    /* 設(shè)置采樣率 */   
  46.    arg = RATE;   
  47.    status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg);   
  48.    if (status == - 1)   
  49.       perror("SOUND_PCM_WRITE_WRITE ioctl failed");   
  50.     
  51.    /* 循環(huán),直到按下Control-C */   
  52.    while (1)   
  53.    {   
  54.       printf("Say something:\n");   
  55.       status = read(fd, buf, sizeof(buf)); /* 錄音 */   
  56.       if (status != sizeof(buf))   
  57.          perror("read wrong number of bytes");   
  58.     
  59.       printf("You said:\n");   
  60.       status = write(fd, buf, sizeof(buf)); /* 放音 */   
  61.       if (status != sizeof(buf))   
  62.          perror("wrote wrong number of bytes");   
  63.          
  64.       /* 在繼續(xù)錄音前等待放音結(jié)束 */   
  65.       status = ioctl(fd, SOUND_PCM_SYNC, 0);   
  66.       if (status == - 1)   
  67.          perror("SOUND_PCM_SYNC ioctl failed");   
  68.    }   
  69. }    

2、mixer 編程

聲卡上的混音器由多個混音通道組成,它們可以通過驅(qū)動程序提供的設(shè)備文件/dev/mixer 進(jìn)行編程。對混音器的操作一般都通過ioctl()系統(tǒng)調(diào)用來完成,所有控制命令都以SOUND_MIXER 或者M(jìn)IXER 開頭,下表列出了常用的混音器控制命令。

命 令 作 用

SOUND_MIXER_VOLUME 主音量調(diào)節(jié)

SOUND_MIXER_BASS 低音控制

SOUND_MIXER_TREBLE 高音控制

SOUND_MIXER_SYNTH FM 合成器

SOUND_MIXER_PCM 主D/A 轉(zhuǎn)換器

SOUND_MIXER_SPEAKER PC 喇叭

SOUND_MIXER_LINE 音頻線輸入

SOUND_MIXER_MIC 麥克風(fēng)輸入

SOUND_MIXER_CD CD 輸入

SOUND_MIXER_IMIX 放音音量

SOUND_MIXER_ALTPCM 從D/A 轉(zhuǎn)換器

SOUND_MIXER_RECLEV 錄音音量

SOUND_MIXER_IGAIN 輸入增益

SOUND_MIXER_OGAIN 輸出增益

SOUND_MIXER_LINE1 聲卡的第1 輸入

SOUND_MIXER_LINE2 聲卡的第2 輸入

SOUND_MIXER_LINE3 聲卡的第3 輸入

對聲卡的輸入增益和輸出增益進(jìn)行調(diào)節(jié)是混音器的一個主要作用,目前大部分聲卡采用的是8 位或者16 位的增益控制器,聲卡驅(qū)動程序會將它們變換成百分比的形式,也就是說無論是輸入增益還是輸出增益,其取值范圍都是從0 到100。

• SOUND_MIXER_READ

在進(jìn)行混音器編程時,可以使用 SOUND_MIXER_READ 宏來讀取混音通道的增益大小,例如如下代碼可以獲得麥克風(fēng)的輸入增益:

  1. ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol); 

對于只有一個混音通道的單聲道設(shè)備來說,返回的增益大小保存在低位字節(jié)中。而對于支持多個混音通道的雙聲道設(shè)備來說,返回的增益大小實際上包括兩個部分,分別代表左、右兩個聲道的值,其中低位字節(jié)保存左聲道的音量,而高位字節(jié)則保存右聲道的音量。下面的代碼可以從返回值中依次提取左右聲道的增益大?。?/p>

  1. int leftright
  2.  
  3. left = vol & 0xff; 
  4.  
  5. right = (vol & 0xff00) >> 8;  

• SOUND_MIXER_WRITE

如果想設(shè)置混音通道的增益大小,則可以通過SOUND_MIXER_WRITE 宏來實現(xiàn),例如下面的語句可以用來設(shè)置麥克風(fēng)的輸入增益: 

  1. vol = (right << 8) + left
  2.  
  3. ioctl(fd, SOUND_MIXER_WRITE(SOUND_MIXER_MIC), &vol);  

• 查詢Mixer信息

聲卡驅(qū)動程序提供了多個ioctl()系統(tǒng)調(diào)用來獲得混音器的信息,它們通常返回一個整型的位掩碼,其中每一位分別代表一個特定的混音通道,如果相應(yīng)的位為1,則說明與之對應(yīng)的混音通道是可用的。通過 SOUND_MIXER_READ_DEVMASK 返回的位掩碼查詢出能夠被聲卡支持的每一個混音通道,而通過SOUND_MIXER_READ_RECMAS 返回的位掩碼則可以查詢出能夠被當(dāng)作錄音源的每一個通道。例如,如下代碼可用來檢查CD 輸入是否是一個有效的混音通道:

  1. ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask); 
  2.  
  3. if (devmask & SOUND_MIXER_CD) 
  4.  
  5. printf("The CD input is supported");  

如下代碼可用來檢查CD 輸入是否是一個有效的錄音源:

  1. ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask); 
  2.  
  3. if (recmask & SOUND_MIXER_CD) 
  4.  
  5. printf("The CD input can be a recording source");  

大多數(shù)聲卡提供了多個錄音源,通過 SOUND_MIXER_READ_RECSRC 可以查詢出當(dāng)前正在使用的錄音源,同一時刻可使用2個或2個以上的錄音源,具體由聲卡硬件本身決定。相應(yīng)地,使用 SOUND_MIXER_WRITE_RECSRC可以設(shè)置聲卡當(dāng)前使用的錄音源,如下代碼可以將CD輸入作為聲卡的錄音源使用:

  1. devmask = SOUND_MIXER_CD; 
  2.  
  3. ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &devmask);  

此外,所有的混音通道都有單聲道和雙聲道的區(qū)別,如果需要知道哪些混音通道提供了對立體聲的支持,可以通過SOUND_MIXER_READ_STEREODEVS 來獲得。

以下代碼實現(xiàn)了利用/dev/mixer 接口對混音器進(jìn)行編程的過程,該程序可對各種混音通道的增益進(jìn)行調(diào)節(jié)。

[cpp] view plain copy

  1. #include <unistd.h>   
  2. #include <stdlib.h>   
  3. #include <stdio.h>   
  4. #include <sys/ioctl.h>   
  5. #include <fcntl.h>   
  6. #include <linux/soundcard.h>   
  7.    
  8. /* 用來存儲所有可用混音設(shè)備的名稱 */   
  9. const char *sound_device_names[] = SOUND_DEVICE_NAMES;   
  10. int fd; /* 混音設(shè)備所對應(yīng)的文件描述符 */   
  11. int devmask, stereodevs; /* 混音器信息對應(yīng)的bit 掩碼 */   
  12. char *name;   
  13.    
  14. /* 顯示命令的使用方法及所有可用的混音設(shè)備 */   
  15. void usage()   
  16. {   
  17.    int i;   
  18.    fprintf(stderr, "usage: %s <device> <left-gain%%> <right-gain%%>\n"   
  19.            "%s <device> <gain%%>\n\n""Where <device> is one of:\n"namename);   
  20.       
  21.    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)   
  22.        if ((1 << i) &devmask)   
  23.           /* 只顯示有效的混音設(shè)備 */   
  24.           fprintf(stderr, "%s ", sound_device_names[i]);   
  25.    fprintf(stderr, "\n");   
  26.    exit(1);   
  27. }   
  28.    
  29. int main(int argc, char *argv[])   
  30. {   
  31.    int leftrightlevel; /* 增益設(shè)置 */   
  32.    int status; /* 系統(tǒng)調(diào)用的返回值 */   
  33.    int device; /* 選用的混音設(shè)備 */   
  34.    char *dev; /* 混音設(shè)備的名稱 */   
  35.    int i;   
  36.    name = argv[0];   
  37.    
  38.    /* 以只讀方式打開混音設(shè)備 */   
  39.    fd = open("/dev/mixer", O_RDONLY);   
  40.    if (fd == - 1)   
  41.    {   
  42.       perror("unable to open /dev/mixer");   
  43.       exit(1);   
  44.    }   
  45.    
  46.    /* 獲得所需要的信息 */   
  47.    status = ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask);   
  48.    if (status == - 1)   
  49.       perror("SOUND_MIXER_READ_DEVMASK ioctl failed");   
  50.       
  51.    status = ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs);   
  52.    if (status == - 1)   
  53.        perror("SOUND_MIXER_READ_STEREODEVS ioctl failed");   
  54.    /* 檢查用戶輸入 */   
  55.    if (argc != 3 && argc != 4)   
  56.    usage();   
  57.       
  58.    /* 保存用戶輸入的混音器名稱 */   
  59.    dev = argv[1];   
  60.       
  61.    /* 確定即將用到的混音設(shè)備 */   
  62.    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)   
  63.    if (((1 << i) &devmask) && !strcmp(dev, sound_device_names[i]))   
  64.       break;   
  65.    if (i == SOUND_MIXER_NRDEVICES)   
  66.    {   
  67.       /* 沒有找到匹配項 */   
  68.       fprintf(stderr, "%s is not a valid mixer device\n", dev);   
  69.       usage();   
  70.    }   
  71.    
  72.    /* 查找到有效的混音設(shè)備 */   
  73.   device = i;   
  74.   /* 獲取增益值 */   
  75.   if (argc == 4)   
  76.   {   
  77.      /* 左、右聲道均給定 */   
  78.      left = atoi(argv[2]);   
  79.      right = atoi(argv[3]);   
  80.   }   
  81.   else   
  82.   {   
  83.      /* 左、右聲道設(shè)為相等 */   
  84.      left = atoi(argv[2]);   
  85.      right = atoi(argv[2]);   
  86.   }   
  87.    
  88.   /* 對非立體聲設(shè)備給出警告信息 */   
  89.   if ((left != right) && !((1 << i) &stereodevs))   
  90.   {   
  91.      fprintf(stderr, "warning: %s is not a stereo device\n", dev);   
  92.   }   
  93.    
  94.   /* 將兩個聲道的值合到同一變量中 */   
  95.   level = (right << 8) + left;   
  96.    
  97.   /* 設(shè)置增益 */   
  98.   status = ioctl(fd, MIXER_WRITE(device), &level);   
  99.   if (status == - 1)   
  100.   {   
  101.      perror("MIXER_WRITE ioctl failed");   
  102.      exit(1);   
  103.   }   
  104.   /* 獲得從驅(qū)動返回的左右聲道的增益 */   
  105.   left = level &0xff;   
  106.   right = (level &0xff00) >> 8;   
  107.    
  108.   /* 顯示實際設(shè)置的增益 */   
  109.   fprintf(stderr, "%s gain set to %d%% / %d%%\n", dev, leftright);   
  110.    
  111.   /* 關(guān)閉混音設(shè)備 */   
  112.   close(fd);   
  113.   return 0;   
  114. }    

編譯上述程序為可執(zhí)行文件mixer,執(zhí)行./mixer <device> <left-gain%> <right-gain%>或./mixer<device> <gain%>可設(shè)置增益,device 可以是vol、pcm、speaker、line、mic、cd、igain、line1、phin、video。 

責(zé)任編輯:龐桂玉 來源: 嵌入式Linux中文站
相關(guān)推薦

2020-04-14 10:51:38

物聯(lián)網(wǎng)智能技術(shù)

2025-04-11 08:35:00

漏洞網(wǎng)絡(luò)安全終端安全

2017-02-10 15:32:47

2021-02-05 12:04:45

LinuxUARTLinux系統(tǒng)

2023-10-07 09:37:53

2017-11-16 14:46:58

Linuxplatform總線驅(qū)動設(shè)備

2020-12-03 08:59:06

Linux設(shè)備驅(qū)動

2022-05-10 08:49:46

設(shè)備驅(qū)動Linux

2009-12-23 13:17:36

Linux設(shè)備驅(qū)動

2011-01-10 18:21:38

linux編寫程序

2017-03-01 13:06:39

Linux驅(qū)動技術(shù)DMA編程

2016-12-15 14:55:31

Linux定時延時

2017-11-06 17:16:55

Linux設(shè)備驅(qū)動并發(fā)控制

2023-05-15 08:58:41

塊設(shè)備驅(qū)動Linux

2021-11-29 07:55:45

Linux GPIO Linux 系統(tǒng)

2023-05-12 07:27:24

Linux內(nèi)核網(wǎng)絡(luò)設(shè)備驅(qū)動

2009-12-07 09:39:04

Linux設(shè)備驅(qū)動硬件通信

2021-04-12 12:00:13

Linux運(yùn)維Linux系統(tǒng)

2016-11-11 13:07:18

LinuxWindows設(shè)備驅(qū)動模型

2022-10-08 11:57:30

Linux內(nèi)核架構(gòu)
點贊
收藏

51CTO技術(shù)棧公眾號