linux audio device driver音訊裝置驅動
第十七章 Linux 音訊裝置驅動
本章導讀
在Linux 中,先後出現了音訊裝置的兩種框架OSS 和ALSA,本節將在介紹數字音訊裝置及音訊裝置硬體接
口的基礎上,展現OSS 和ALSA 驅動的結構。
17.1~17.2 節講解了音訊裝置及PCM、IIS 和AC97 硬體介面。
17.3 節闡述了Linux OSS 音訊裝置驅動的組成、mixer 介面、dsp 介面及使用者空間程式設計方法。
17.4 節闡述了Linux ALSA 音訊裝置驅動的組成、card 和元件管理、PCM 裝置、control 介面、AC97 API
及使用者空間程式設計方法。
17.5 節以S3C2410 通過IIS 介面外接UDA1341 編解碼器的例項講解了OSS 驅動。
17.6 節以PXA255 通過AC97 介面外接AC97 編解碼器的例項講解了ALSA 驅動。
17.1 數字音訊裝置
目前,手機、PDA、MP3 等許多嵌入式裝置中包含了數字音訊裝置,一個典型的數字音訊系統的電路組成如
圖17.1 所示。圖17.1 中的嵌入式微控制器/DSP 中集成了PCM、IIS 或AC97 音訊介面,通過這些介面連線
外部的音訊編解碼器即可實現聲音的AD 和DA 轉換,圖中的功放完成模擬訊號的放大功能。
圖17.1 典型的數字音訊系統電路
音訊編解碼器是數字音訊系統的核心,衡量它的指標主要有:
• 取樣頻率
取樣的過程就是將通常的模擬音訊訊號的電訊號轉換成二進位制碼0 和1 的過程,這些0 和1 便構成了數字
音訊檔案。如圖17.2 中的正弦曲線代表原始音訊曲線,方格代表取樣後得到的結果,二者越吻合說明取樣
結果越好。
取樣頻率是每秒鐘的取樣次數,我們常說的 44.1kHz 取樣頻率就是每秒鐘取樣44100 次。理論上取樣頻
率越高,轉換精度越高,目前主流的取樣頻率是48kHz。
• 量化精度
量化精度是指對取樣資料分析的精度,比如24bit 量化精度就是是將標準電平訊號按照2 的24 次方進行分
析,也就是說將圖17.2 中的縱座標等分為224 等分。量化精度越高,聲音就越逼真。
圖17.2 數字音訊取樣
17.2 音訊裝置硬體介面
17.2.1 PCM 介面
針對不同的數字音訊子系統,出現了幾種微處理器或DSP 與音訊器件間用於數字轉換的介面。
最簡單的音訊介面是PCM(脈衝編碼調製)介面,該介面由時鐘脈衝(BCLK)、幀同步訊號(FS)及接收
資料(DR)和傳送資料(DX)組成。在FS 訊號的上升沿,資料傳輸從MSB(Most Significant Bit)字開
始,FS 頻率等於取樣率。FS 訊號之後開始資料字的傳輸,單個的資料位按順序進行傳輸,1 個時鐘週期傳
輸1 個數據字。傳送MSB 時,訊號的等級首先降到最低,以避免在不同終端的介面使用不同的資料方案時
造成MSB 的丟失。
PCM 介面很容易實現,原則上能夠支援任何資料方案和任何取樣率,但需要每個音訊通道獲得一個獨立的
資料佇列。
17.2.2 IIS 介面
IIS 介面(Inter-IC Sound)在20 世紀80 年代首先被飛利浦用於消費音訊,並在一個稱為LRCLK(Left/Right
CLOCK)的訊號機制中經過多路轉換,將兩路音訊訊號變成單一的資料佇列。當LRCLK 為高時,左聲道資料
被傳輸;LRCLK 為低時,右聲道資料被傳輸。與PCM 相比,IIS 更適合於立體聲系統。對於多通道系統,在
同樣的BCLK 和LRCLK 條件下,並行執行幾個資料佇列也是可能的。
17.2.3 AC97 介面
AC'97(Audio Codec 1997)是以Intel 為首的五個PC 廠商Intel、Creative Labs、NS、Analog Device
與Yamaha 共同提出的規格標準。與PCM 和IIS 不同,AC'97 不只是一種資料格式,用於音訊編碼的內部架
構規格,它還具有控制功能。AC'97 採用AC-Link 與外部的編解碼器相連,AC-Link 介面包括位時鐘(BITCLK)、
同步訊號校正(SYNC)和從編碼到處理器及從處理器中解碼(SDATDIN 與SDATAOUT)的資料佇列。AC'97
資料幀以SYNC 脈衝開始,包括12 個20 位時間段(時間段為標準中定義的不同的目的服務)及16 位“tag”
段,共計256 個數據序列。例如,時間段“1”和“2”用於訪問編碼的控制暫存器,而時間段“3”和“4”
分別負載左、右兩個音訊通道。“tag”段表示其他段中哪一個包含有效資料。把幀分成時間段使傳輸控制
訊號和音訊資料僅通過4 根線到達9 個音訊通道或轉換成其他資料流成為可能。與具有分離控制介面的IIS
方案相比,AC'97 明顯減少了整體管腳數。一般來說,AC'97 編解碼器採用TQFP48 封裝,如圖17.3 所示。
圖17.3 AC97 Codec 晶片
PCM、IIS 和AC97 各有其優點和應用範圍,例如在CD、MD、MP3 隨身聽多采用IIS 介面,行動電話會採用
PCM 介面,具有音訊功能的PDA 則多使用和PC 一樣的AC'97 編碼格式。
17.3 Linux OSS 音訊裝置驅動
17.3.1 OSS 驅動的組成
OSS 標準中有2 個最基本的音訊裝置:mixer(混音器)和DSP(數字訊號處理器)。
在音效卡的硬體電路中,mixer 是一個很重要的組成部分,它的作用是將多個訊號組合或者疊加在一起,對
於不同的音效卡來說,其混音器的作用可能各不相同。OSS 驅動中,/dev/mixer 裝置檔案是應用程式對mixer
進行操作的軟體介面。
混音器電路通常由兩個部分組成:輸入混音器(input mixer)和輸出混音器(output mixer)。輸入混音
器負責從多個不同的訊號源接收模擬訊號,這些訊號源有時也被稱為混音通道或者混音裝置。模擬訊號通
過增益控制器和由軟體控制的音量調節器後,在不同的混音通道中進行級別(level)調製,然後被送到輸
入混音器中進行聲音的合成。混音器上的電子開關可以控制哪些通道中有訊號與混音器相連,有些音效卡只
允許連線一個混音通道作為錄音的音源,而有些音效卡則允許對混音通道做任意的連線。經過輸入混音器處
理後的訊號仍然為模擬訊號,它們將被送到A/D 轉換器進行數字化處理。
輸出混音器的工作原理與輸入混音器類似,同樣也有多個訊號源與混音器相連,並且事先都經過了增益調
節。當輸出混音器對所有的模擬訊號進行了混合之後,通常還會有一個總控增益調節器來控制輸出聲音的
大小,此外還有一些音調控制器來調節輸出聲音的音調。經過輸出混音器處理後的訊號也是模擬訊號,它
們最終會被送給喇叭或者其它的模擬輸出裝置。對混音器的程式設計包括如何設定增益控制器的級別,以及怎
樣在不同的音源間進行切換,這些操作通常來講是不連續的,而且不會像錄音或者放音那樣需要佔用大量
的計算機資源。由於混音器的操作不符合典型的讀/寫操作模式,因此除了open()和close()兩個系統呼叫
之外,大部分的操作都是通過ioctl()系統呼叫來完成的。與/dev/dsp 不同,/dev/mixer 允許多個應用程
序同時訪問,並且混音器的設定值會一直保持到對應的裝置檔案被關閉為止。
DSP 也稱為編解碼器,實現錄音(錄音)和放音(播放),其對應的裝置檔案是/dev/dsp 或/dev/sound/dsp。
OSS 音效卡驅動程式提供的/dev/dsp 是用於數字取樣和數字錄音的裝置檔案,向該裝置寫資料即意味著啟用
音效卡上的D/A 轉換器進行放音,而向該裝置讀資料則意味著啟用音效卡上的A/D 轉換器進行錄音。
在從DSP 裝置讀取資料時,從音效卡輸入的模擬訊號經過A/D 轉換器變成數字取樣後的樣本,儲存在音效卡驅
動程式的核心緩衝區中,當應用程式通過 read()系統呼叫從音效卡讀取資料時,儲存在核心緩衝區中的數字
取樣結果將被複制到應用程式所指定的使用者緩衝區中。需要指出的是,音效卡取樣頻率是由核心中的驅動程
序所決定的,而不取決於應用程式從音效卡讀取資料的速度。如果應用程式讀取資料的速度過慢,以致低於
音效卡的取樣頻率,那麼多餘的資料將會被丟棄(即overflow);如果讀取資料的速度過快,以致高於音效卡
的取樣頻率,那麼音效卡驅動程式將會阻塞那些請求資料的應用程式,直到新的資料到來為止。
在向DSP 裝置寫入資料時,數字訊號會經過D/A 轉換器變成模擬訊號,然後產生出聲音。應用程式寫入數
據的速度應該至少等於音效卡的取樣頻率,過慢會產生聲音暫停或者停頓的現象(即underflow)。如果用
戶寫入過快的話,它會被核心中的音效卡驅動程式阻塞,直到硬體有能力處理新的資料為止。
與其它裝置有所不同,音效卡通常不需要支援非阻塞(non-blocking)的I/O 操作。即便核心OSS 驅動提供
了非阻塞的I/O 支援,使用者空間也不宜採用。
無論是從音效卡讀取資料,或是向音效卡寫入資料,事實上都具有特定的格式(format),如無符號8 位、單
聲道、8KHz 取樣率,如果預設值無法達到要求,可以通過ioctl()系統呼叫來改變它們。通常說來,在應
用程式中開啟裝置檔案/dev/dsp 之後,接下去就應該為其設定恰當的格式,然後才能從音效卡讀取或者寫入
資料。
17.3.2 mixer 介面
int register_sound_mixer(struct file_operations *fops, int dev);
上述函式用於註冊1 個混音器,第1 個引數fops 即是檔案操作介面,第2 個引數dev 是裝置編號,如果填
入-1,則系統自動分配1 個裝置編號。mixer 是1 個典型的字元裝置,因此編碼的主要工作是實現
file_operations 中的open()、ioctl()等函式。
mixer 介面file_operations 中的最重要函式是ioctl(),它實現混音器的不同IO 控制命令,程式碼清單17.1
給出了1 個ioctl()的範例。
程式碼清單17.1 mixer()介面ioctl()函式範例
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 }
17.3.3 DSP 介面
int register_sound_dsp(struct file_operations *fops, int dev);
上述函式與register_sound_mixer()類似,它用於註冊1 個dsp 裝置,第1 個引數fops 即是檔案操作接
口,第2 個引數dev 是裝置編號,如果填入-1,則系統自動分配1 個裝置編號。dsp 也是1 個典型的字元
裝置,因此編碼的主要工作是實現file_operations 中的read()、write()、ioctl()等函式。
dsp 介面file_operations 中的read()和write()函式非常重要,read()函式從音訊控制器中獲取錄音數
據到緩衝區並拷貝到使用者空間,write()函式從使用者空間拷貝音訊資料到核心空間緩衝區並最終傳送到音訊
控制器。
dsp 介面file_operations 中的ioctl()函式處理對取樣率、量化精度、DMA 緩衝區塊大小等引數設定IO
控制命令的處理。
在資料從緩衝區拷貝到音訊控制器的過程中,通常會使用DMA,DMA 對音效卡而言非常重要。例如,在放音時,
驅動設定完DMA 控制器的源資料地址(記憶體中DMA 緩衝區)、目的地址(音訊控制器FIFO)和DMA 的資料
長度,DMA 控制器會自動傳送緩衝區的資料填充FIFO,直到傳送完相應的資料長度後才中斷一次。
在OSS 驅動中,建立存放音訊資料的環形緩衝區(ring buffer)通常是值得推薦的方法。此外,在OSS 驅
動中,一般會將1 個較大的DMA 緩衝區分成若干個大小相同的塊(這些塊也被稱為“段”,即fragment),
驅動程式使用DMA 每次在聲音緩衝區和音效卡之間搬移一個fragment。在使用者空間,可以使用ioctl()系統
呼叫來調整塊的大小和個數。
除了read()、write()和ioctl()外,dsp 介面的poll()函式通常也需要被實現,以向用戶反饋目前能否讀
寫DMA 緩衝區。
在OSS 驅動初始化過程中,會呼叫register_sound_dsp()和register_sound_mixer()註冊dsp 和mixer 設
備;在模組解除安裝的時候,會呼叫如程式碼清單17.2。
程式碼清單17.2 OSS 驅動初始化註冊dsp 和mixer 裝置
1 static int xxx_init(void)
2 {
3 struct xxx_state *s = &xxx_state;
4 ...
5 //註冊dsp 裝置
6 if ((audio_dev_dsp = register_sound_dsp(&xxx_audio_fops, - 1)) < 0)
7 goto err_dev1;
8 //裝置mixer 裝置
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 裝置介面
17 unregister_sound_dsp(audio_dev_dsp);
18 unregister_sound_mixer(audio_dev_mixer);
19 ...
20 }
根據17.3.2 和17.3.3 節的分析,可以畫出一個Linux OSS 驅動結構的簡圖,如圖17.4 所示。
圖17.4 Linux OSS 驅動結構
17.3.4 OSS 使用者空間程式設計
1、DSP 程式設計
對OSS 驅動音效卡的程式設計使用Linux 檔案介面函式,如圖17.5,DSP 介面的操作一般包括如下幾個步驟:
① 開啟裝置檔案/dev/dsp。
採用何種模式對音效卡進行操作也必須在開啟裝置時指定,對於不支援全雙工的音效卡來說,應該使用只讀或
者只寫的方式開啟,只有那些支援全雙工的音效卡,才能以讀寫的方式開啟,這還依賴於驅動程式的具體實
現。Linux 允許應用程式多次開啟或者關閉與音效卡對應的裝置檔案,從而能夠很方便地在放音狀態和錄音
狀態之間進行切換。
② 如果有需要,設定緩衝區大小。
執行在Linux 核心中的音效卡驅動程式專門維護了一個緩衝區,其大小會影響到放音和錄音時的效果,使用
ioctl()系統呼叫可以對它的尺寸進行恰當的設定。調節驅動程式中緩衝區大小的操作不是必須的,如果沒
有特殊的要求,一般採用預設的緩衝區大小也就可以了。如果想設定緩衝區的大小,則通常應緊跟在裝置
檔案開啟之後,這是因為對音效卡的其它操作有可能會導致驅動程式無法再修改其緩衝區的大小。
③ 設定聲道(channel)數量。
根據硬體裝置和驅動程式的具體情況,可以設定為單聲道或者立體聲。
④ 設定取樣格式和取樣頻率
取樣格式包括AFMT_U8(無符號8 位)、AFMT_S8(有符號8 位)、AFMT_U16_LE(小端模式,無符號16 位)、
AFMT_U16_BE(大端模式,無符號16 位)、AFMT_MPEG、AFMT_AC3 等。使用SNDCTL_DSP_SETFMT IO 控制命
令可以設定取樣格式。
對於大多數音效卡來說,其支援的取樣頻率範圍一般為5kHz 到44.1kHz 或者48kHz,但並不意味著該範圍內
的所有連續頻率都會被硬體支援,在Linux 下進行音訊程式設計時最常用到的幾種取樣頻率是11025Hz、
16000Hz、22050Hz、32000Hz 和44100Hz。使用SNDCTL_DSP_SPEED IO 控制命令可以設定取樣頻率。
⑤ 讀寫/dev/dsp 實現播放或錄音。
圖17.5 OSS dsp 介面使用者空間操作流程
程式碼清單17.3 的程式實現了利用/dev/dsp 介面進行聲音錄製和播放的過程,它的功能是先錄製幾秒鐘音
頻資料,將其存放在記憶體緩衝區中,然後再進行放音。
程式碼清單17.3 OSS DSP 介面應用程式設計範例
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 /* 儲存秒數 */
9 #define RATE 8000 /* 取樣頻率 */
10 #define SIZE 8 /* 量化位數 */
11 #define CHANNELS 1 /* 聲道數目 */
12 /* 用於儲存數字音訊資料的記憶體緩衝區 */
13 unsigned char buf[LENGTH *RATE * SIZE * CHANNELS / 8];
14 int main()
15 {
16 int fd; /* 聲音裝置的檔案描述符 */
17 int arg; /* 用於ioctl 呼叫的引數 */
18 int status; /* 系統呼叫的返回值 */
19 /* 開啟聲音裝置 */
20 fd = open("/dev/dsp", O_RDWR);
21 if (fd < 0)
22 {
23 perror("open of /dev/dsp failed");
24 exit(1);
25 }
26 /* 設定取樣時的量化位數 */
27 arg = SIZE;
28 status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
29 if (status == - 1)
30 perror("SOUND_PCM_WRITE_BITS ioctl failed");
31 if (arg != SIZE)
32 perror("unable to set sample size");
33 /* 設定取樣時的通道數目 */
34 arg = CHANNELS;
35 status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg);
36 if (status == - 1)
37 perror("SOUND_PCM_WRITE_CHANNELS ioctl failed");
38 if (arg != CHANNELS)
39 perror("unable to set number of channels");
40 /* 設定取樣率 */
41 arg = RATE;
42 status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg);
43 if (status == - 1)
44 perror("SOUND_PCM_WRITE_WRITE ioctl failed");
45 /* 迴圈,直到按下Control-C */
46 while (1)
47 {
48 printf("Say something:\n");
49 status = read(fd, buf, sizeof(buf)); /* 錄音 */
50 if (status != sizeof(buf))
51 perror("read wrong number of bytes");
52 printf("You said:\n");
53 status = write(fd, buf, sizeof(buf)); /* 放音 */
54 if (status != sizeof(buf))
55 perror("wrote wrong number of bytes");
56 /* 在繼續錄音前等待放音結束 */
57 status = ioctl(fd, SOUND_PCM_SYNC, 0);
58 if (status == - 1)
59 perror("SOUND_PCM_SYNC ioctl failed");
60 }
61 }
2、mixer 程式設計
音效卡上的混音器由多個混音通道組成,它們可以通過驅動程式提供的裝置檔案/dev/mixer 進行程式設計。對混
音器的操作一般都通過ioctl()系統呼叫來完成,所有控制命令都以SOUND_MIXER 或者MIXER 開頭,表17.1
列出了常用的混音器控制命令。
表17.1 混音器常用命令
命 令 作 用
SOUND_MIXER_VOLUME 主音量調節
SOUND_MIXER_BASS 低音控制
SOUND_MIXER_TREBLE 高音控制
SOUND_MIXER_SYNTH FM 合成器
SOUND_MIXER_PCM 主D/A 轉換器
SOUND_MIXER_SPEAKER PC 喇叭
SOUND_MIXER_LINE 音訊線輸入
SOUND_MIXER_MIC 麥克風輸入
SOUND_MIXER_CD CD 輸入
SOUND_MIXER_IMIX 放音音量
SOUND_MIXER_ALTPCM 從D/A 轉換器
SOUND_MIXER_RECLEV 錄音音量
SOUND_MIXER_IGAIN 輸入增益
SOUND_MIXER_OGAIN 輸出增益
SOUND_MIXER_LINE1 音效卡的第1 輸入
SOUND_MIXER_LINE2 音效卡的第2 輸入
SOUND_MIXER_LINE3 音效卡的第3 輸入
對音效卡的輸入增益和輸出增益進行調節是混音器的一個主要作用,目前大部分音效卡採用的是8 位或者16 位
的增益控制器,音效卡驅動程式會將它們變換成百分比的形式,也就是說無論是輸入增益還是輸出增益,其
取值範圍都是從0 到100。
• SOUND_MIXER_READ 巨集
在進行混音器程式設計時,可以使用 SOUND_MIXER_READ 巨集來讀取混音通道的增益大小,例如如下程式碼可以獲
得麥克風的輸入增益:
ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol);
對於只有一個混音通道的單聲道裝置來說,返回的增益大小儲存在低位位元組中。而對於支援多個混音通道
的雙聲道裝置來說,返回的增益大小實際上包括兩個部分,分別代表左、右兩個聲道的值,其中低位位元組
儲存左聲道的音量,而高位位元組則儲存右聲道的音量。下面的程式碼可以從返回值中依次提取左右聲道的增
益大小:
int left, right;
left = vol & 0xff;
right = (vol & 0xff00) >> 8;
• SOUND_MIXER_WRITE 巨集
如果想設定混音通道的增益大小,則可以通過SOUND_MIXER_WRITE 巨集來實現,例如下面的語句可以用來設
置麥克風的輸入增益:
vol = (right << 8) + left;
ioctl(fd, SOUND_MIXER_WRITE(SOUND_MIXER_MIC), &vol);
• 查詢Mixer 資訊
音效卡驅動程式提供了多個ioctl()系統呼叫來獲得混音器的資訊,它們通常返回一個整型的位掩碼,其中
每一位分別代表一個特定的混音通道,如果相應的位為1,則說明與之對應的混音通道是可用的。
通過 SOUND_MIXER_READ_DEVMASK 返回的位掩碼查詢出能夠被音效卡支援的每一個混音通道,而通過
SOUND_MIXER_READ_RECMAS 返回的位掩碼則可以查詢出能夠被當作錄音源的每一個通道。例如,如下程式碼
可用來檢查CD 輸入是否是一個有效的混音通道:
ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask);
if (devmask & SOUND_MIXER_CD)
printf("The CD input is supported");
如下程式碼可用來檢查CD 輸入是否是一個有效的錄音源:
ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask);
if (recmask & SOUND_MIXER_CD)
printf("The CD input can be a recording source");
大多數音效卡提供了多個錄音源,通過 SOUND_MIXER_READ_RECSRC 可以查詢出當前正在使用的錄音源,同一
時刻可使用2 個或2 個以上的錄音源,具體由音效卡硬體本身決定。相應地,使用 SOUND_MIXER_WRITE_RECSRC
可以設定音效卡當前使用的錄音源,如下程式碼可以將CD 輸入作為音效卡的錄音源使用:
devmask = SOUND_MIXER_CD;
ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &devmask);
此外,所有的混音通道都有單聲道和雙聲道的區別,如果需要知道哪些混音通道提供了對立體聲的支援,
可以通過SOUND_MIXER_READ_STEREODEVS 來獲得。
程式碼清單17.4 的程式實現了利用/dev/mixer 介面對混音器進行程式設計的過程,該程式可對各種混音通道的
增益進行調節。
程式碼清單17.4 OSS mixer 介面應用程式設計範例
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 const char *sound_device_names[] = SOUND_DEVICE_NAMES;
9 int fd; /* 混音裝置所對應的檔案描述符 */
10 int devmask, stereodevs; /* 混音器資訊對應的bit 掩碼 */
11 char *name;
12 /* 顯示命令的使用方法及所有可用的混音裝置 */
13 void usage()
14 {
15 int i;
16 fprintf(stderr, "usage: %s <device> <left-gain%%> <right-gain%%>\n"
17 "%s <device> <gain%%>\n\n""Where <device> is one of:\n", name, name);
18 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
19 if ((1 << i) &devmask)
20 /* 只顯示有效的混音裝置 */
21 fprintf(stderr, "%s ", sound_device_names[i]);
22 fprintf(stderr, "\n");
23 exit(1);
24 }
25
26 int main(int argc, char *argv[])
27 {
28 int left, right, level; /* 增益設定 */
29 int status; /* 系統呼叫的返回值 */
30 int device; /* 選用的混音裝置 */
31 char *dev; /* 混音裝置的名稱 */
32 int i;
33 name = argv[0];
34 /* 以只讀方式開啟混音裝置 */
35 fd = open("/dev/mixer", O_RDONLY);
36 if (fd == - 1)
37 {
38 perror("unable to open /dev/mixer");
39 exit(1);
40 }
41
42 /* 獲得所需要的資訊 */
43 status = ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask);
44 if (status == - 1)
45 perror("SOUND_MIXER_READ_DEVMASK ioctl failed");
46 status = ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs);
47 if (status == - 1)
48 perror("SOUND_MIXER_READ_STEREODEVS ioctl failed");
49 /* 檢查使用者輸入 */
50 if (argc != 3 && argc != 4)
51 usage();
52 /* 儲存使用者輸入的混音器名稱 */
53 dev = argv[1];
54 /* 確定即將用到的混音裝置 */
55 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
56 if (((1 << i) &devmask) && !strcmp(dev, sound_device_names[i]))
57 break;
58 if (i == SOUND_MIXER_NRDEVICES)
59 {
60 /* 沒有找到匹配項 */
61 fprintf(stderr, "%s is not a valid mixer device\n", dev);
62 usage();
63 }
64 /* 查詢到有效的混音裝置 */
65 device = i;
66 /* 獲取增益值 */
67 if (argc == 4)
68 {
69 /* 左、右聲道均給定 */
70 left = atoi(argv[2]);
71 right = atoi(argv[3]);
72 }
73 else
74 {
75 /* 左、右聲道設為相等 */
76 left = atoi(argv[2]);
77 right = atoi(argv[2]);
78 }
79
80 /* 對非立體聲裝置給出警告資訊 */
81 if ((left != right) && !((1 << i) &stereodevs))
82 {
83 fprintf(stderr, "warning: %s is not a stereo device\n", dev);
84 }
85
86 /* 將兩個聲道的值合到同一變數中 */
87 level = (right << 8) + left;
88
89 /* 設定增益 */
90 status = ioctl(fd, MIXER_WRITE(device), &level);
91 if (status == - 1)
92 {
93 perror("MIXER_WRITE ioctl failed");
94 exit(1);
95 }
96 /* 獲得從驅動返回的左右聲道的增益 */
97 left = level &0xff;
98 right = (level &0xff00) >> 8;
99 /* 顯示實際設定的增益 */
100 fprintf(stderr, "%s gain set to %d%% / %d%%\n", dev, left, right);
101 /* 關閉混音裝置 */
102 close(fd);
103 return 0;
104 }
編譯上述程式為可執行檔案mixer,執行./mixer <device> <left-gain%> <right-gain%>或./mixer
<device> <gain%>可設定增益,device 可以是vol、pcm、speaker、line、mic、cd、igain、line1、phin、
video。
17.4 Linux ALSA 音訊裝置驅動
17.4.1 ALSA 的組成
雖然OSS 已經非常成熟,但它畢竟是一個沒有完全開放原始碼的商業產品,而ALSA (Advanced Linux Sound
Architecture)恰好彌補了這一空白,它符合GPL,是在Linux 下進行音訊程式設計時另一種可供選擇的音效卡
驅動體系結構,其官方網站為