




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、使用CODECs壓縮Wave音頻 簡介Win95及最近的WinNT都具有能過安裝的CODECs處理壓縮的wave格式的音頻和視頻數據流的 能力。一個CODEC是一小段用于壓縮(COmpress)及解壓縮(DECompress)數據流的代碼(因此, 得名CO-DEC)。許多CODECs即能壓縮又能解壓縮。而一些CODECs僅能用于解壓縮,這樣私 有數據可以在系統上播放,但數據格式不能在系統上創建。盡管一個CODEC原則上能夠用于壓縮解壓縮任一種數據流,還是設計有各種各樣的CODECs 以實現以高的壓縮比率,更好的保真度或實時壓縮性能來壓縮某種數據類型。例如,獲取 高的視頻壓縮數據壓縮率的最好方法
2、應用于音頻數據時未必就能得到相同的效果,反之也 然。本文著重于怎樣在自己的代碼中使用CODEC將音頻數據以你的系統中CODECs所支持的方式進 行壓縮。壓縮音頻數據的一個主要原因是降低存儲某一聲音序列所需數據量。少的數據量 意味著聲音所占有的空間更少,并且能夠以更快的速度在MODEM和網絡上傳遞。如果數據 以Windows系統所支持的某一通用格式壓縮的話,則可以不必手工解壓縮就直接播放-系 統將使用它自己的CODECs解壓縮數據并播放。我的系統中有什么CODECs?Win95和WinNT本身附帶有幾種標準的CODECs,也可由系統中所安裝的應用程序安裝其他的 CODECs。例如,DSP Gro
3、up,Inc. TrueSpeech CODEC隨Win95發送,因此你寫的任何應用于 Win95的程序都可應用此CODEC(假如用戶沒有在控制面板中刪除它或禁止它的話)。以后 可能要安裝的CODEC的一個例子是微軟網絡(MSN)軟件自已所用的音頻數據。所有安裝的CODECs由音頻壓縮管理器(ACM)管理。我們可以從一小程序中查詢ACM來查到安 裝了哪些CODECs,它們都支持什么格式。你也可雙擊控制面板中的多媒體選項,選擇高級 標簽,就能看到系統中所安裝的CODECs。介紹應用ACM,得知它所管理每一個CODEC都可以做些什么的一個好方法是寫一個簡單的 查詢ACM的命令行應用程序。本文所附帶
4、的CAPS程序完成的就是這個功能-讓我們看看它的 代碼,我將給你一起分析此程序,解釋每一步完成的什么功能。首先從調用ACM編程接口所需的包含的頭文件開始 :#include <windows.h>#include <mmsystem.h>#include <mmreg.h> / 多媒體注冊#include <msacm.h> / 音頻壓縮管理器#include <stdio.h>mmsystem.h頭文件定了Windows支持大部分的多媒體功能,但不包含ACM接口及任何廠商定義。 mmreg.h包含了對不
5、同廠商設計的各種wave數據類型的格式標簽的定義。它也包含了用于處理 不同的wave數據類型的結構( 基于WAVEFORAMTEX)的定義。msacm.h包含了ACM所需的API, 標志等等。我們要做的第一件事就是執行一些常見的ACM查詢來判斷版本號,獲取諸如它當前管理了多少 個驅動程序的的信息。下面是查詢ACM的部分代碼: / 取得ACM版本號 DWORD dwACMVer = acmGetVersion(); printf("ACM version %u.%.02u build %u", HIWORD(dwACMVer) >> 8, HIWORD(dwACM
6、Ver) & 0x00FF, LOWORD(dwACMVer); if (LOWORD(dwACMVer) = 0) printf(" (Retail)"); printf("n"); / 顯示一些ACM的信息 printf("ACM metrics:n"); DWORD dwCodecs = 0; MMRESULT mmr = acmMetrics(NULL, ACM_METRIC_COUNT_CODECS, &dwCodecs); if (mmr) show_error(mmr); else printf(&quo
7、t;%lu codecs installedn", dwCodecs); CAPS實例查詢了更多的ACM信息。你可以仔細查看它的代碼,運行程序得知結果。對ACM有了簡單了解后,現在可以要求它枚舉出系統中當前所有的驅動程序。我們在程序中所 調用的枚舉函數使用回調函數來匯報每個設備的數據,這在Windows編程是一種很普遍的方法。 下面的調用就是枚舉當前ACM所管理的所有設備: / 枚舉所有允許的驅動程序 printf("Enabled drivers:n"); mmr = acmDriverEnum(DriverEnumProc, 0, 0); if (mmr) s
8、how_error(mmr);如同其它多媒體函數,許多ACM函數調用返回一MMRESULT值,指出了可能發生的錯誤。此值為0 表示函數成功執行。現在,讓我們看看枚舉回調函數DriverEnumProc,它由系統中的每一個驅 動程序調用:BOOL CALLBACK DriverEnumProc(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport) printf(" id: %8.8lxH", hadid); printf(" supports:n"); if (fdwSupport & AC
9、MDRIVERDETAILS_SUPPORTF_ASYNC) printf(" async conversionsn"); if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) printf(" different format conversionsn"); if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER) printf(" same format conversionsn"); if (fdwSupport &a
10、mp; ACMDRIVERDETAILS_SUPPORTF_FILTER) printf(" filteringn"); / 獲得一些具體信息 ACMDRIVERDETAILS dd; dd.cbStruct = sizeof(dd); MMRESULT mmr = acmDriverDetails(hadid, &dd, 0); if (mmr) printf(" "); show_error(mmr); else printf(" Short name: %sn", dd.szShortName); printf(&quo
11、t; Long name: %sn", dd.szLongName); printf(" Copyright: %sn", dd.szCopyright); printf(" Licensing: %sn", dd.szLicensing); printf(" Features: %sn", dd.szFeatures); printf(" Supports %u formatsn", dd.cFormatTags); printf(" Supports %u filter formatsn&q
12、uot;, dd.cFilterTags); / 打開驅動程序 HACMDRIVER had = NULL; mmr = acmDriverOpen(&had, hadid, 0); if (mmr) printf(" "); show_error(mmr); else DWORD dwSize = 0; mmr = acmMetrics(had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize); if (dwSize cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX); pwf->w
13、FormatTag = WAVE_FORMAT_UNKNOWN; ACMFORMATDETAILS fd; memset(&fd, 0, sizeof(fd); fd.cbStruct = sizeof(fd); fd.pwfx = pwf; fd.cbwfx = dwSize; fd.dwFormatTag = WAVE_FORMAT_UNKNOWN; mmr = acmFormatEnum(had, &fd, FormatEnumProc, 0, 0); if (mmr) printf(" "); show_error(mmr); free(pwf);
14、acmDriverClose(had, 0); return TRUE; / 繼續枚舉驅動程序向回調函數傳遞了描述驅動程序所支持類型的一組標志。一些驅動程序可以異步 操作,而另一些驅動程序則不能。一些驅動程序能夠將一種wave數據格式轉換成另一種格 式(稱作CODECs),而另一些驅動程序僅能完成過濾操作,其輸入輸出格式是一樣的。注 意ACM維護著這類數據及驅動程序的名字,版權信息等等,這樣我們可以不必裝載或打開 指定的驅動程序就可以得到這些數據。這樣很方便,譬如當需將數據放在列表框中由用戶 選擇時。要獲得有關某一驅動程序能力更多的詳細信息,必須裝載驅動程序并打開它,可通過調用 acmOpen
15、Driver實現。一旦驅動程序打開,可請求枚舉它所支持的wave數據格式。同時有一 個小問題-盡管所有wave格式描述結構基于WAVEFORAMTEX,許多格式使用此結構的擴展形 式來保存其特定的信息。如果我們想枚舉所有格式,需要知道為此結構分配多少供驅動 程序填寫詳細信息的空間。可通過向acmMetrics函數傳遞ACM_METRIC_MAX_SIZE_FORMAT標 志得到所需的最大的結構的尺寸。如果你讀過上面的代碼,你會發現我只是簡單的將分配的空間強制轉換為WAVEFORMATEX指針。 我只對通用信息而不是任一特定類型的數據感興趣,因此這個指針符合我的要求。為結構分配了空間后,現在我可
16、以acmFormatEnum來枚舉所支持的格式。這次又用到一個回 調函數來取得枚舉出的格式的相關數據:BOOL CALLBACK FormatEnumProc(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd, DWORD dwInstance, DWORD fdwSupport) printf(" %4.4lXH, %sn", pafd->dwFormatTag, pafd->szFormat); return TRUE; / 繼續枚舉如你所看到的,這是一次嘗試并僅打印出格式的某些信息。這樣,通過上面的代碼,你能夠查詢AC
17、M所有的驅動程序,查找每一個驅動程序所支持的 格式。我建議你現在運行CAPS程序,看看你的系統上安裝了些什么。 使用特定的CODEC好了,我們已得知你的系統上安裝了什么CODECs-現在來看看怎樣查找某一特定的CODEC并 使用它壓縮音頻數據。讓我們看看CONV實例,它使用一種有效的CODEC壓縮一個簡單的wave 數據包。為了使代碼更趨簡單,我以控制臺應用程序的形式的實現它,也沒有嘗試去 播放壓縮過的數據或將其存入文件。這個實例的代碼僅向你展示怎樣找到你所需要的驅 動程序并使用它將數據轉換為壓縮格式。剩下的就靠你了。兩步實現壓縮在理想的情況下,壓縮一些數據可能只不過是向系統說:“這有一些數據
18、,請壓縮成這種格式。” 不幸的是,Windows編程與理想相去甚遠,象通常一樣,我們得自已做許多瑣碎的工作。 要解決的第一個也是最重要的問題是給定的CODEC可能不能壓縮你所使用的數據格式。例 如,我們錄入了一些11025Hz,8位,單聲道的PCM數據(或許是用戶向麥克風說的話),此 種格式幾乎所有的多媒體PC都能錄制。我們可能要將數據通過MODEM傳遞,因此我們想盡可能 的壓縮數據,使數據量減少。我們選擇了TrucSpeech CODEC,它安裝在Windows中,能夠 獲得大約10:1的壓縮率。我們所要碰到的問題就是TrueSpeech CODEC不能處理11025Hz, 8位,單聲道的P
19、CM數據。它只能處理8000Hz,16位,單聲道的數據(某些情況下是8位)。 因此我們必須先將源數據轉換為TrueSpeech CODEC所支持的中間PCM格式,然后在使用它 將中間數據轉換為最終所需的格式。可使用隨Windows分發的某種不同的CODEC將一種PCM格式轉換為另一種格式,因此你需使用 某種CODEC將數據轉換為其它CODEC能夠處理的格式。我們已知道怎樣去枚舉CODECs及其所 支持的格式,因此這樣做是可以實現的。但還有一個問題,我在實例代碼中忽略了,留給你們解決。如果某一CODEC能夠創建我們所 想要的壓縮格式,但支持幾種不同的輸入格式,我們怎樣選擇最佳的中間格式呢?按照
20、Nigel的準則,那就是“總是做最少量的工作”,我選擇使用CODEC所支持的枚舉出的第一 種PCM格式。由于很容易實現,可能會導致數據失真。假設我們要使用的某一CODEC有一些 近乎無失真的壓縮算法,能夠接收8位或16位的11025Hz或22050Hz的PCM數據。我們要轉換 以441000Hz,16位立體聲錄制的高保真的樣本。我們試圖降低數據量,而不在乎損失質量。 如果我們枚舉此CODEC所支持的格式,第一個得到的可能是11025Hz,8位單聲道的格式。我 們先將數據轉換為此格式,然后進行壓縮,這其間肯定要損失一些質量,因為這種中間格 式不夠好。如果使用16位22050Hz的話會好一些。已告
21、訴過你這種缺憾啦,讓我們瞧瞧CONV 實例,看它是怎樣工作的。CONV實例程序CONV實例分四個階段:它創建一些wave格式數據的樣本,找到一個合適的CODEC,將數據轉 換為此CODEC可處理的中間格式,最后將數據轉換成所需的格式。為了簡單其間,源數據用 程序創建而不是錄入或是從wave文件中讀取: / 首先我們創建一個可能是剛剛才錄制的wave 其格式為11.025kHz, / 8位單聲道PCM,這是一個所有機器上都可用的錄入格式,我們的例 / 子是1秒長的1kHz的正弦波wave,剛好1000個周期 WAVEFORMATEX wfSrc; memset(&wfSrc, 0, si
22、zeof(wfSrc); wfSrc.cbSize = 0; wfSrc.wFormatTag = WAVE_FORMAT_PCM; / PCM wfSrc.nChannels = 1; / Mono wfSrc.nSamplesPerSec = 11025; / 11.025 kHz wfSrc.wBitsPerSample = 8; / 8 bit wfSrc.nBlockAlign = wfSrc.nChannels * wfSrc.wBitsPerSample / 8; wfSrc.nAvgBytesPerSec = wfSrc.nSamplesPerSec * wfSrc.nBloc
23、kAlign; DWORD dwSrcSamples = wfSrc.nSamplesPerSec; BYTE* pSrcData = new BYTE dwSrcSamples; / 1秒種的長度 BYTE* pData = pSrcData; double f = 1000.0; double pi = 4.0 * atan(1.0); double w = 2.0 * pi * f; for (DWORD dw = 0; dw 上面的代碼創建了一個WAVEFORMATEX結構用來描述源數據格式,并用簡單的數學方法生成了1秒鐘長的11.025kHz,8位單聲道的PCM的wave數據。下一步
24、就是選擇要將數據轉換成什么格式及選定一個合適的CODEC。 WORD wFormatTag = WAVE_FORMAT_DSPGROUP_TRUESPEECH; / 現在我們選定一個支持目標格式標簽的CODEC HACMDRIVERID hadid = find_driver(wFormatTag); if (hadid = NULL) printf("No driver foundn"); exit(1); printf("Driver found (hadid: %4.4lXH)n", hadid);find_driver函數枚舉所有的驅動程序直到找
25、到一個支持給定標簽值的驅動程序(本例為WAVE_FORMAT_DSPGROUP_TRUESPEECH)。我沒有在此給出代碼是因為它與前面的枚舉代碼非常相象。隨后你可以查看它是怎樣工作的。選定了驅動程序,現在要為最終驅動程序將產生的壓縮數據格式創建一個WAVEFORMATEX結構,并為驅動程序用于輸入的中間PCM格式產生一個WAVEFORMATEX結構。 / 獲得格式的詳情 / 注意:這只是一個給定格式簽的第一種或是最可能的格式 WAVEFORMATEX* pwfDrv = get_driver_format(hadid, wFormatTag); if (pwfDrv = NULL) prin
26、tf("Error getting format infon"); exit(1); printf("Driver format: %u bits, %lu samples per secondn", pwfDrv->wBitsPerSample, pwfDrv->nSamplesPerSec); / 獲取驅動程序所支持的PCM格式標簽 / 注意:我們只是選取第一支持的PCM格式但不一定是最好的選擇 WAVEFORMATEX* pwfPCM = get_driver_format(hadid, WAVE_FORMAT_PCM); if (pw
27、fPCM = NULL) printf("Error getting PCM format infon"); exit(1); printf("PCM format: %u bits, %lu samples per secondn", pwfPCM->wBitsPerSample, pwfPCM->nSamplesPerSec);有點重復了,要注意的是get_driver_format函數僅僅枚舉出第一種匹配的格式-也許不能獲得可能的最好的質量。現在我們有了WAVEFORMATEX結構描述源格式,中間PCM格式,以及最終的壓縮格式。可以開始
28、轉換數據了。轉換由被ACM稱作流的對象來實現。我們可以打開流,將源格式、目標格式傳遞給它,要求它進行轉換。在此要注意如果CODEC的算法復雜的話同步轉換是很耗時的。一些CODEC可以異步工作,通過向窗口發送一個消息,或調用一個回調函數,或設置一個事件告知你轉換進程。下面的代碼是以最少麻煩準則完成任務的-你必須等待它直到完成。還有另外一點,很重要。如你所知,我們打開轉換流,指明ACM_STREAMOPENF_NONREALTIME標志。這非常重要。若省略此標志,那么一些驅動程序(例如TrueSpeech驅動程序)將會報告錯誤512(意思是不可能)。此錯誤告訴你所要求的轉換不能實時進行。我的實例代
29、碼中沒有這個問題,但如果你試圖在播放數據的同時轉換大量數據,就要注意這點了。讓我們看看第一步的轉換,它完成的是將源數據轉換為中間格式: / / 將源wave轉換為CODEC所支持的PCM格式 / 我們使用任一種能實現PCM格式間轉換驅動程序 HACMSTREAM hstr = NULL; mmr = acmStreamOpen(&hstr, NULL, / 任一驅動程序 &wfSrc, / 源格式 pwfPCM, / 目標格式 NULL, / 無過濾 NULL, / 沒回調 0, / 實例數據(未使用) ACM_STREAMOPENF_NONREALTIME); / 標志 if
30、 (mmr) printf("Failed to open a stream to do PCM to PCM conversionn"); exit(1); / 為轉換結果開辟一個緩沖區 DWORD dwSrcBytes = dwSrcSamples * wfSrc.wBitsPerSample / 8; DWORD dwDst1Samples = dwSrcSamples * pwfPCM->nSamplesPerSec / wfSrc.nSamplesPerSec; DWORD dwDst1Bytes = dwDst1Samples * pwfPCM->w
31、BitsPerSample / 8; BYTE* pDst1Data = new BYTE dwDst1Bytes; / 填寫轉換信息 ACMSTREAMHEADER strhdr; memset(&strhdr, 0, sizeof(strhdr); strhdr.cbStruct = sizeof(strhdr); strhdr.pbSrc = pSrcData; / 要轉換的源數據 strhdr.cbSrcLength = dwSrcBytes; strhdr.pbDst = pDst1Data; strhdr.cbDstLength = dwDst1Bytes; / 準備好頭
32、mmr = acmStreamPrepareHeader(hstr, &strhdr, 0); / 轉換數據 printf("Converting to intermediate PCM format.n"); mmr = acmStreamConvert(hstr, &strhdr, 0); if (mmr) printf("Failed to do PCM to PCM conversionn"); exit(1); printf("Converted OKn"); / 關閉流 acmStreamClose(hst
33、r, 0);當流打開時,第二個參數為NULL,表示接受任何驅動程序進行轉換。復雜的只是計算需要給輸出數據分配多大的緩沖區。由于PCM格式間的轉換不牽扯壓縮和解壓縮,直接就計算出來了。你可能注意到調用了acmStreamPrepareHeader,它為驅動程序安排好一切并允許驅動程序在轉換前鎖定內存。最后一步是將中間格式轉換為最終的壓縮格式:/ / 將中間格式轉換為最終的壓縮格式 / 打開驅動程序 HACMDRIVER had = NULL; mmr = acmDriverOpen(&had, hadid, 0); if (mmr) printf("Failed to open
34、 drivern"); exit(1); / 打開轉換流 / 注意使用了ACM_STREAMOPENF_NONREALTIME標志. / 沒有此標志一些軟件壓縮程序會報告512號錯誤-即不可能 mmr = acmStreamOpen(&hstr, had, / 驅動程序句柄 pwfPCM, / 源格式 pwfDrv, / 目標格式 NULL, / 不過濾 NULL, / 無回調函數 0, / 實例數據(未使用) ACM_STREAMOPENF_NONREALTIME); / 標志 if (mmr) printf("Failed to open a stream to
35、 do PCM to driver format conversionn"); exit(1); / 為轉換結果分配一個緩沖區 / 根據以字節計的平均速率計算輸出緩沖區的尺寸 / 并加上一機動位(bit) / 沒有此額外的空間IMA_ADPCM驅動程序將不能轉換 DWORD dwDst2Bytes = pwfDrv->nAvgBytesPerSec * dwDst1Samples / pwfPCM->nSamplesPerSec; dwDst2Bytes = dwDst2Bytes * 3 / 2; / 增加一點空間 BYTE* pDst2Data = new BYTE dwDst2Bytes; / 填寫轉換信息 ACMSTREAMHEADER strhdr2; memset(&strhdr2, 0, sizeof(strhdr2); strhdr2.cbStruct =
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年中國晶瑩玻璃清潔劑市場調查研究報告
- 2025年中國日曬鹽市場調查研究報告
- 2025年中國數碼機芯板市場調查研究報告
- 2025-2030年中國中厚板產業發展趨勢分析及投資風險評估報告
- 2025至2031年中國繡花工藝杯墊行業投資前景及策略咨詢研究報告
- 2025-2030年中國ABS產業運行態勢及投資前景預測研究報告
- 新疆石河子職業技術學院《中國古典文學史料學》2023-2024學年第二學期期末試卷
- 2025至2031年中國立式超細微球磨機行業投資前景及策略咨詢研究報告
- 2025至2031年中國硫酸腺嘌呤行業投資前景及策略咨詢研究報告
- 小兒骶管麻醉
- (正式版)QC∕T 1206.1-2024 電動汽車動力蓄電池熱管理系統 第1部分:通 用要求
- 【標準】電力人工智能訓練數據集歸集標準
- AQ 1044-2007 礦井密閉防滅火技術規范(正式版)
- 足太陽膀胱經(經絡腧穴課件)
- 感悟考古智慧樹知到期末考試答案章節答案2024年北京大學
- 2024年全國青少年航天創新大賽航天知識競賽試題
- DZ∕T 0204-2022 礦產地質勘查規范 稀土(正式版)
- ~保安勞動勞務合同書范本
- 2024年7月浙江省高中學業水平考試語文試卷試題(含答案詳解)
- 2022年第三屆大學生組織管理能力大賽真題題庫和答案
- 理綜-新疆烏魯木齊市2024年高三三模考試試題和答案
評論
0/150
提交評論