使用Audio Compression Manager(ACM)
作者/Peter Morris 翻譯/陳省
在幾年以前IP電話被炒的沸沸揚揚,但用過的人都覺得這個技術非常不成熟,語音品質很差,時斷時續,還經常有延遲,結果這項技術的應用沒有普及開來。但隨著Internet應用的越來越廣泛以及相關技術發展的日新月異,聲音品質不斷提高,通過Internet打電話早已不再是夢想,已經成為了我們生活中的一部分。今天我使用IP電話打長途,每分鐘只需要3毛錢,使用OICQ的語音交談,網友彷彿就在你的身邊。如果我們的頻寬足夠的,我們甚至還可以在網上收看QuicktimeApsaraVideo for Live,用RealAudio收聽電台廣播,也可以點播好聽的MP3歌曲。不過對於程式員來說,更好的訊息是我們對於這些媒體流的編程也也變得越來越容易。為什麼這麼說呢,下面讓我們先來瞭解一下Codec。
Codecs
什麼是Codecs?其實它就是音頻壓縮的解碼編碼器,實際上有點類似ActiveX控制項。ActiveX控制項使程式員可以調用一些他人實現好的功能而無需從頭做起。Codecs提供了類似的功能,只不過它集中在提供如何對媒體格式進行轉換的功能。例如,如果想寫一個把CD轉MP3的應用程式,我們只需要做下列工作:
l 從CD音軌中讀取資料。
l 產生一個有效MP3檔案頭。
l 調用相應的codec把音軌資料編碼為MP3。
Windows本身已經帶了很多的codec。下面是其中幾個的說明:
名稱 |
說明 |
GSM |
好像用於某些行動電話通訊網路。 |
DSP TrueSpeech |
可以產生一種一位的聲音格式用於語音通話-聲音非常清楚。 |
Fraunhoffer IIS MP3 |
這種可以用來產生MP3格式。 |
PCM |
用於產生Windows標準聲音格式。大多數Codec支援的聲音格式都可以和它相互轉化。 |
當前安裝的codec的完整列表可以通過察看控制台中多媒體的部分來獲得。
ACM API
ACM是Audio Compression Manager縮寫,翻譯過來就是聲音壓縮管理器。它是微軟編寫的用於調用Codec功能的介面函數庫。它本來應該聲明在MMSystem.pas單元中,但是由於某些原因Borland把它給省略了。所以我們要作的第一件事是找到它的API聲明單元MSACM.pas。必須要感謝Francois Piette,他共用了他轉換的單元檔案,我們可以從www.Delphi-Jedi.org下載它。
使用ACM轉換媒體格式包括以下幾步:
l 首先必須指定輸入輸出格式,我們需要設定TWaveFormatEX記錄, 但是這個結構記錄太小無法容納大多數Codec所需要的資訊。為瞭解決這個問題,我們使用一個自訂的 TACMFormat記錄,這個記錄在TWaveFormatEX的基礎上增加了128位元組。
l 開啟一個ACM流。首先調用acmStreamOpen函數,把輸入輸出格式作為參數傳遞過去。然後ACM或者返回一個有效控制代碼或者返回一個錯誤碼(比如ACMERR_NotPossible)來表明轉換的請求無法完成。
l 接下來要確定輸出緩衝區的大小。調用acmStreamSize函數會通知ACM每次我們將產生多少位元組的資料,然後函數會返回請求大小的緩衝區(我們總是應該高估一下大小,保證提供一個足夠大的緩衝區)。
l 然後,我們要產生一個轉換頭。需要調用acmStreamPrepareHeader函數,把先前調用acmStreamOpen函數返回的流控制代碼作為參數。產生的轉換頭會告訴ACM源緩衝區和目的緩衝區的地址。ACM不會自動分配記憶體,我們必須自己來申請記憶體。
l 所有的準備工作基本上完成了,只剩下如何轉換資料了。這是非常簡單的,只需要調用acmStreamConvert函數。AcmStreamConvert函數的參數包括流控制代碼和轉換頭控制代碼。這個函數通過設定轉換頭中的cbDstLengthUsed表明轉換過程中真正被使用的位元組數。
l 一旦完成了ACM的會話,我們必須釋放使用的全部資源。轉換頭用acmStreamUnprepareHeader函數來釋放,流用acmStreamClose來關閉。
選擇格式
正如前面提到的,在開始轉換以前必須先設定輸入輸出格式。TWaveFormatEX記錄(聲明在MMSystem.pas單元中),它僅僅指定了位元速率,頻率等等。除非我們只打算在不同的PCM格式間進行轉換,否則TWaveFormatEX是不夠用的。下面是它的替代格式:
TACMWaveFormat = packed record
case integer of
0 : (Format : TWaveFormatEx);
1 : (RawData : Array[0..128] of byte);
end;
這個變體記錄使我們仍然可以讀取TWaveFormatEX結構資料,同時RawData提供了足夠的空間來容納其它Codec需要的額外資訊。
雖然我們不知道額外資訊的大小,但我們可以使用acmFormatChoose函數來獲得。
AcmFormatChoose函數只需要一個TACMFormatChooseA類型的參數。這個參數是一個簡單的結構可以包括下列資訊:
成員 |
說明 |
Pwfx |
一個TWaveFormatEX結構指標用來接收結果(這裡我們實際上用的是TACMFormat)。 |
Cbwfx |
接收結果的緩衝區大小。 |
CbStruct |
結構大小。 |
另一個值得一提的成員是fdwStyle, 它包括用來指定格式額外資訊的標誌。特別是下面這個標誌:
ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT
這個標誌表明pwfx指向的緩衝區已經包括了一個有效格式,當acmFormatChoose函數調用後,會顯示一個格式選擇對話方塊,有效格式將顯示為預設值。
什麼情況下無法轉換?
一個原因就是一台機器上有的Codec可能另一台機器上沒有。這樣導致了你可以讀取一個聲音格式,但無法產生這個聲音格式。Fraunhoffer IIS MP3 Codec就有這個問題。在Windows 9x 和Windows NT下,我們可以產生MP3檔案,但在Windows 2000卻去掉了這一功能L,結果雖然在Windows2000下我們可以聽MP3,當我們無法產生MP3,除非我們交一筆錢,faint。
另一個原因是並非所有的ACM格式可以相互轉換。比如,我們無法在下面的格式間轉化:
GSM 8位 單聲道 > MP3 8位 單聲道
雖然無法直接轉換,但可以通過中間格式來間接轉換,中間格式通常是PCM格式,這是因為絕大多數Codec都支援PCM格式的轉換。新的轉換途徑就變成了:
GSM 8位 單聲道 > PCM 8位 單聲道 > MP3 8位 單聲道
而轉換為MP3 16位 立體聲還需要增加一步,就是把8位 PCM格式轉換為16位PCM格式。
ACM的潛在本領
可能從上面的介紹來看ACM的功能很有限,僅僅是能把一個媒體格式轉換為另一個媒體格式。不過一想到需要自己寫一個可用於Internet的聲音流的編碼解碼器的工作量,比如MP3的壓縮函數,你就會發現ACM真的是個好東東,價格便宜量又足J。
同時想像一下有了ACM我們實現一個Internet電話是多麼的簡單,首先從麥克風獲得輸入的聲音資料,壓縮為一個合適的基於低頻寬的流格式,然後通過TCP/IP協議傳到目的電腦。同時,目的電腦接收壓縮的資料,解壓縮,最後通過擴音器播放出來。
ACM的潛在本領就在這裡,有相當多的ACM codec是可映射為Wave格式的,這就是說它們可以作為標準的波裝置來即時的播放或記錄音頻。而網路電話的最主要的要求之一就是即時性。
比如,我們可以很容易的開啟一個GSM的聲音輸入源,一旦我們從波輸入裝置接收到資料,資料就已經被壓縮好了,可以立即傳輸。同時,一旦通過TCP/IP Socket接收到了資料,我們馬上可以通過波輸出裝置播放之。
要注意標準的PCM格式資料對於通過Modem實現即時的語音傳輸來說實在是太大了,而GSM 6.1隻要1.5K/秒就可以達到即時的效果,而16位單聲道的MP3 也僅僅需要2K/秒的頻寬。
另外對於MP3格式來說,它可以作為一個播放格式,但它不適合作為輸入格式(因為只有Windows NT平台上的MP3 Codec才支援MP3的編碼),通常我們需要用其它程式手工轉換產生MP3,所以它不適合應用在網路電話上,不過它是非常適合於網路點播的,因為它的壓縮比高,而且音質失真少。
原理就是象上面描述的那樣簡單,但很多事情總是說起來容易,做起來難。所以,下面實現了幾個控制項和程式來實際的示範一下。由於代碼比較長,這裡就不詳細列出來了,只簡單的說明一下。
控制項
l TACMConverter:這個控制項有兩個作用。第一,它可以在兩個不同媒體格式間轉化資料。第二,這個控制項可以用來指定ACM流的輸入輸出格式。(用右鍵調出控制項的控制項編輯器可以在設計時調用AcmFormatChoose函數顯示格式選擇對話方塊來指定格式)。
l TACMIn:用來從麥克風接收資料,我們使用標準的PCM格式或其它波輸入裝置支援的格式來記錄資料。
l TACMOut:這個控制項是用來回放聲音的。NumBuffers屬性可以用來指定開始播放前使用的緩衝區數。這對於即時音頻傳輸意義不大,但對於在Internet上音頻廣播卻是很方便,同時當串連資料波動時可以緩衝額外的音頻資料。
示範
第一個例子使用TACMConvertor控制項來指定輸入輸出格式,然後開啟一個ACMIn和ACMOut控制項 。麥克風輸入的資料立刻回放,但有一點延遲,來產生一點迴響