1. 程式人生 > >OSS--跨平臺的音訊介面簡介

OSS--跨平臺的音訊介面簡介

OSS(Open Sound System)是unix平臺上一個統一的音訊介面。以前,每個Unix廠商都會提供一個自己專有的API,用來處理音訊。這就意味著為一種Unix平臺編寫的音訊處理應用程式,在移植到另外一種Unix平臺上時,必須要重寫。不僅如此,在一種平臺上具備的功能,可能在另外一個平臺上無法實現。但是,OSS出現以後情況就大不一樣了,只要音訊處理應用程式按照OSS的API來編寫,那麼在移植到另外一個平臺時,只需要重新編譯即可。因此,OSS提供了原始碼級的可移植性。

同時,很多的Unix工作站中,只能提供錄音與放音的功能。有了OSS後,給這些工作站帶來了MIDI功能,加上音訊流、語音識別/生成、計算機電話(CT)、JAVA以及其它的多媒體技術,在Unix工作站中,同樣可以享受到同Windows、Macintosh環境一樣的音訊世界。另外,OSS還提供了與視訊和動畫播放同步的音訊能力,這對在Unix中實現動畫、遊戲提供了幫助。

本文首先解釋在音訊程式設計時經常遇到的名詞、裝置檔案的含義,然後分別在錄音、播放、Mixer方面對OSS介面的使用方法進行介紹。由於OSS API十分豐富,因此在本文中只介紹那些最為常用的介面。對於OSS API的一個完整描述,可以參考[1]。

一、基礎知識

數字音訊裝置(有時也稱codec,PCM,DSP,ADC/DAC裝置):播放或錄製數字化的聲音。它的指標主要有:取樣速率(電話為8K,DVD為96K)、channel數目(單聲道,立體聲)、取樣解析度(8-bit,16-bit)。

mixer(混頻器):用來控制多個輸入、輸出的音量,也控制輸入(microphone,line-in,CD)之間的切換。

synthesizer(合成器):通過一些預先定義好的波形來合成聲音,有時用在遊戲中聲音效果的產生。

MIDI 介面:MIDI介面是為了連線舞臺上的synthesizer、鍵盤、道具、燈光控制器的一種序列介面。

在Unix系統中,所有的裝置都被統一成檔案,通過對檔案的訪問方式(首先open,然後read/write,同時可以使用ioctl讀取/設定引數,最後close)來訪問裝置。在OSS中,主要有以下的幾種裝置檔案:

  • /dev/mixer:訪問音效卡中內建的mixer,調整音量大小,選擇音源。
  • /dev/sndstat:測試音效卡,執行cat /dev/sndstat會顯示音效卡驅動的資訊。
  • /dev/dsp 、/dev/dspW、/dev/audio:讀這個裝置就相當於錄音,寫這個裝置就相當於放音。/dev/dsp與/dev/audio之間的區別在於取樣的編碼不同,/dev/audio使用μ律編碼,/dev/dsp使用8-bit(無符號)線性編碼,/dev/dspW使用16-bit(有符號)線形編碼。/dev/audio主要是為了與SunOS相容,所以儘量不要使用。
  • l /dev/sequencer:訪問音效卡內建的,或者連線在MIDI介面的synthesizer。

這些裝置檔案的裝置編號見[1]。

二、音訊程式設計

OSS為音訊程式設計提供三種裝置,分別是/dev/dsp,/dev/dspW和/dev/audio,前面已經提到了它們之間的區別。

使用者可以直接使用Unix的命令來放音和錄音,命令cat /dev/dsp >xyz可用來錄音,錄音的結果放在xyz檔案中;命令cat xyz >/dev/dsp播放聲音檔案xyz。

如果通過程式設計的方式來使用這些裝置,那麼Unix平臺通過檔案系統提供了統一的訪問介面。程式設計師可以通過檔案的操作函式直接控制這些裝置,這些操作函式包括:open、close、read、write、ioctl等。下面我們就分別討論開啟音訊裝置、放音、錄音和引數調整。

1. 開啟音訊裝置

1) 標頭檔案定義

1

2

3

4

5

6

7

8

9

10

11

12

13

/*

* Standard includes

*/

#include <ioctl.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/soundcard.h>

/*

* Mandatory variables.

*/

#define BUF_SIZE 4096

int audio_fd;

unsigned char audio_buffer[BUF_SIZE];

2) 開啟裝置

1

2

3

4

5

if ((audio_fd = open(DEVICE_NAME, open_mode, 0)) == -1) {

/* Open of device failed */

perror(DEVICE_NAME);

exit(1);

}

open_mode有三種選擇:O_RDONLY,O_WRONLY和O_RDWR,分別表示只讀、只寫和讀寫。OSS建議儘量使用只讀或只寫,只有在全雙工的情況下(即錄音和放音同時)才使用讀寫模式。

2. 錄音

1

2

3

4

5

int len;

if ((len = read(audio_fd, audio_buffer, count)) == -1) {

perror("audio read");

exit(1);

}

count為錄音資料的位元組個數(建議為2的指數),但不能超過audio_buffer的大小。從讀位元組的個數可以精確的測量時間,例如8kHZ 16-bit stereo的速率為8000*2*2=32000bytes/second,這是知道何時停止錄音的唯一方法。

3. 放音

放音實際上和錄音很類似,只不過把read改成write即可,相應的audio_buffer中為音訊資料,count為資料的長度。

注意,使用者始終要讀/寫一個完整的取樣。例如一個16-bit的立體聲模式下,每個取樣有4個位元組,所以應用程式每次必須讀/寫4的倍數個位元組。

另外,由於OSS是一個跨平臺的音訊介面,所以使用者在程式設計的時候,要考慮到可移植性的問題,其中一個重要的方面是讀/寫時的位元組順序。

4. 設定引數

  • 設定取樣格式

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    int format;

    format = AFMT_S16_LE;

    if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format) == -1) {

    /* fatal error */

    perror("SNDCTL_DSP_SETFMT");

    exit(1);

    }

    if (format != AFMT_S16_LE) {

    /* 本裝置不支援選擇的取樣格式. */

    }

    在設定取樣格式之前,可以先測試裝置能夠支援那些取樣格式,方法如下:

    int mask;

    if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {

    /* Handle fatal error ... */

    }

    if (mask & AFMT_MPEG) {

    /* 本裝置支援MPEG取樣格式 ... */}

  • 設定通道數目

    1

    2

    3

    4

    5

    6

    7

    8

    int channels = 2; /* 1=mono, 2=stereo */

    if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {

    /* Fatal error */

    perror("SNDCTL_DSP_CHANNELS");

    exit(1);

    }

    if (channels != 2)

    {/* 本裝置不支援立體聲模式 ... */}

  • 設定取樣速率

    1

    2

    3

    4

    5

    6

    7

    8

    9

    int speed = 11025;

    if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) {

    /* Fatal error */

    perror("SNDCTL_DSP_SPEED");

    exit(Error code);

    }

    if ( /* 返回的速率(即硬體支援的速率)與需要的速率差別很大... */ ) {

    /* 本裝置不支援需要的速率... */

    }

音訊裝置通過分頻的方法產生需要的取樣時鐘,因此不可能產生所有的頻率。驅動程式會計算出最接近要求的頻率來,使用者程式要檢查返回的速率值,如果誤差較小,可以忽略,但誤差不能太大。

三、Mixer程式設計

對Mixer的控制,包括調節音量(volume)、選擇錄音音源(microphone,line-in)、查詢mixer的功能和狀態,主要是通過Mixer裝置/dev/mixer的ioctl介面。相應的,ioctl介面提供的功能也分為三類:調節音量、查詢mixer的能力、選擇mixer的錄音通道。下面就分別介紹使用的方法:

下面的mixer_fd是對mixer裝置執行open操作返回的檔案描述符。

  • 調節音量

    應用程式通過ioctl的SOUND_MIXER_READ和SOUND_MIXER_WIRTE功能號來讀取/設定音量。在OSS中,音量的大小範圍在0-100之間。使用方法如下:

    1

    2

    3

    int vol;

    if (ioctl(mixer_fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol) == -1) {

    /* 訪問了沒有定義的mixer通道... */

    SOUND_MIXER_MIC是通道引數,表示讀microphone通道的音量,結果放置在vol中。如果通道是立體聲,那麼vol的最低有效位元組為左聲道的音量值,接著的位元組為右聲道的音量值,另外的兩個位元組不用。如果通道是單聲道,vol中左聲道與右聲道具有相同的值。

  • 查詢mixer的能力

    1

    2

    3

    4

    int mask;

    if (ioctl(mixer_fd, SOUND_MIXER_READ_xxxx, &mask) == -1) {

    /* Mixer 的沒有此能力... */

    }

    SOUND_MIXER_READ_xxxx 中的xxxx代表具體要查詢的內容,比如檢查可用的mixer通道用SOUND_MIXER_READ_DEVMASK;檢查可用的錄音裝置,用SOUND_MIXER_READ_RECMASK;檢查單聲道/立體聲,用SOUND_MIXER_READ_STEREODEVS;檢查mixer的一般能力,用SOUND_MIXER_READ_CAPS等等。所有通道的查詢的結果都放在mask中,所以要區分出特定通道的狀況,使用mask& (1 << channel_no)。

  • 選擇mixer的錄音通道

    首先可以通過SOUND_MIXER_READ_RECMASK檢查可用的錄音通道,然後通過SOUND_MIXER_WRITE_RECSRC選擇錄音通道。可以隨時通過SOUND_MIXER_READ_RECSRC查詢當前音效卡中已經被選擇的錄音通道。

    OSS建議把mixer的使用者控制功能單獨出來形成一個通用的程式。但前提是,在使用mixer之前,首先通過API的查詢功能檢查音效卡的能力。在linux中,就有一個專門的mixer程式--aumix。

四、結束語

前面討論的是OSS中一些最基本的內容,實際上OSS中還有很多高階的特性,比如在音訊程式設計時十分重要的實時性問題,畫面與聲音的同步問題,這裡都沒有介紹。如果讀者對這些特性感興趣的話,可以進一步參考[1]。另外,在[2]中,還可以下載使用OSS介面的樣例程式。