亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

VC下調用ACM音頻編程接口壓縮Wave音頻

2019-11-17 05:07:29
字體:
來源:轉載
供稿:網友
  摘要:本文介紹了在Microsoft Visual C++ 6.0下如何調用ACM(Audio ComPRession Manager,音頻 壓縮治理器)音頻壓縮編程接口對原始錄入的Wave音頻進行數據壓縮。

  要害字:ACM、CODECs、Wave音頻、音頻編碼壓縮

  引言

   音頻和視頻是多媒體應用程序向用戶提供信息的主要方式,這些音頻、視頻數據一般都具有較高的采樣率,經過壓縮的原始數據才具有實用價值,否則不僅要占用大量存儲空間而且在播放或進行網絡傳輸時效率也是非常低下的,所以音頻、視頻數字壓縮編碼在多媒體應用中有著廣泛而又重要的用途。本文主要對音頻的編碼壓縮作了闡述。

   音頻的編碼壓縮方式有許多種,如基于ITU-T G.728語音編碼協議的LD-CELP 低時延碼激勵線性猜測編碼、基于ITU-T G.711語音編碼協議的PCM(Pulse Code Modulation ,脈沖編碼調制)編碼以及我們非常熟悉的GSM數字蜂窩移動電話的語音編碼標準等等。這些不同的壓縮方式有著不同的數據壓縮比和還原音質,具體的編碼格式和算法更是大相徑庭。多數協議都比較復雜,普通程序難以實現其加、解壓算法,而為多媒體提供了較強支持的Windows 98操作系統引入了ACM和VCM技術,用來治理系統中存在的所有的音頻和視頻編、解碼器(Coder-Decoder,即CODECs,用來實現音頻、視頻數據編解碼的驅動程序)??梢酝ㄟ^它們提供的編程接口調用系統中存在的現成的編解碼器來實現音頻數據的加、解壓。Windows 98系統自帶的音頻CODECs 支持一些早期的音頻數據壓縮標準,如ADPCM (Adaptive Differential Pulse Code Modulation,自適應差分脈沖編碼調制)編碼等,而Internet EXPlorer 5.0 等應用程序包含的音頻CODECs支持一些較新 的壓縮標準, 如MPEG Layer 3等。本文所要介 紹的就是ACM音頻壓縮接口的編程方法,所使用的編程工具為Microsoft Visual C++ 6.0。

  實現思路

   盡管一個CODEC在理論上能夠用于壓縮、解壓縮任一種數據流,但還是設計有各種各樣的CODECs 以實現更高的壓縮比、更高的保真度或實時壓縮性能來壓縮某種特定的數據類型。例如,把獲取很高的視頻壓縮數據壓縮率的最好方法應用到音頻數據時未必就能得到相同的效果。

   壓縮音頻數據的主要原理是降低存儲某一聲音序列所需的數據量。少的數據量就意味著聲音所占有的空間更少,就能夠以更快的速度通過MODEM在網絡上傳遞。假如數據以Windows系統所支持的某種通用格式壓縮的話,就可不經手工解壓縮而直接播放--系 統將使用它自己的CODECs解壓縮數據并播放。Windows 98本身附帶有幾種標準的CODECs,如DSP Group,Inc. TrueSpeech CODEC等。因此我們寫的任何應用于 Windows 98下的程序都可應用這些CODEC,具體系統中都存在有哪些CODECs可以在控制面版的"多媒體"選項的"設備"標簽頁中查到。

   CODEC 支持從源音頻格式到目標格式的轉換,而在實際應用中, 可能某種CODEC 不支持直接將源音頻格式轉換成目標格式,比如我們通過麥克風向多媒體計算機錄入了一些頻率為11025Hz、8位數據、單聲道的PCM數據,假如選用系統的TrueSpeech CODEC進行處理,就會引起失敗,因為這種CODEC只能處理頻率為8KHz,16位單聲道的數據。所以轉換時要采取兩步轉換法,即先將源格式轉換成一種中間格式,再將此中間格式轉換成目標格式,因為線性PCM 編碼 最為簡單,且為絕大多數CODEC 所支持,所以一般中間格式都選為線性PCM 格式的一種。比如就可以先將原始數據轉換成TrueSpeech CODEC所支持的中間PCM格式,然后再將其通過TrueSpeech CODEC轉換成最終的壓縮格式。

  程序的設計實現

   有關ACM的API函數定義在頭文件msacm.h中, 除了在工程中加入對此頭文件的引用之外, 對ACM編程還必須包含頭文件mmsystem.h和mmreg.h,這兩個頭文件定義了多媒體編程中最基本 的常量和數據結構。為了避免有些高 版 本ACM才提供的函數和功能在較低版本的ACM中上不可用,程序中應調用acmGetVersion函 數查詢用戶機器中ACM 的版本信息。


   雖然可以根據控制面版手工得到關于某種音頻CODECs的信息,但在應用程序中也經常需要知道某種音頻CODECs是否存在,并獲取其編解碼參數等信息,可以通過回調函數find_format_enum來枚舉系統中的音頻壓縮格式:

BOOL CALLBACK find_format_enum(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd, DWord dwInstance, DWORD fdwSupport)
{
  FIND_DRIVER_INFO* pdi = (FIND_DRIVER_INFO*) dwInstance;
  if (pafd->dwFormatTag == (DWORD)pdi->wFormatTag) {
   pdi->hadid = hadid;
   return FALSE; //停止枚舉
  }
  return TRUE; //繼續枚舉
}
   在該回調函數中用到的FIND_DRIVER_INFO是自定義的數據結構,其兩個成員變量分別用來保存ACM驅動器號的句柄和要轉換的數據格式:

typedef strUCt {
  HACMDRIVERID hadid;
  WORD wFormatTag;
} FIND_DRIVER_INFO;
   現在可以枚舉出系統中當前所有的驅動程序。我們在程序中所調用的枚舉函數使用回調函數來匯報每個設備的數據,這在Windows編程是一種很普遍的方法。要獲得有關某一驅動程序能力更多的具體信息,必須裝載驅動程序并打開它,可通過調用 acmOpenDriver實現。一旦驅動程序打開,可請求枚舉它所支持的wave數據格式。但這就存在一個問題:所有wave格式描述結構都基于WAVEFORAMTEX,許多格式使用此結構的擴展形式來保存其特定的信息。假如我們想枚舉所有格式,需要知道為此結構分配多少供驅動 程序填寫具體信息的空間??梢酝ㄟ^向acmMetrics函數傳遞ACM_METRIC_MAX_SIZE_FORMAT標 志得到所需的最大的結構的尺寸。打開驅動程序后要通過acmMetrics函數枚舉到所支持的格式,該函數可以獲取到許多ACM對象的有用信息。實現該過程的主要代碼如下:

BOOL CALLBACK find_driver_enum (HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport)
{
  ……
  MMRESULT mmr = acmDriverOpen(&had, hadid, 0);
  //枚舉所支持的格式
  ……
  mmr = acmMetrics((HACMOBJ)had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize);
  if (dwSize < sizeof(WAVEFORMATEX)) dwSize = sizeof(WAVEFORMATEX);
  WAVEFORMATEX* pwf = (WAVEFORMATEX*) malloc(dwSize);
  ……
  pwf->cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX);
  pwf->wFormatTag = pdi->wFormatTag;
  ACMFORMATDETAILS fd;
  ……
  fd.cbStruct = sizeof(fd);
  fd.pwfx = pwf;
  fd.cbwfx = dwSize;
  fd.dwFormatTag = pdi->wFormatTag;
  mmr=acmFormatEnum(had, &fd, find_format_enum, (DWORD)(VOID*)pdi, 0); //枚舉格式
  ……
  acmDriverClose(had, 0); //關閉驅動器
  ……
}
   根據指定的格式要找到其所對應的ACM驅動器號可以用枚舉所有音頻CODECs的ACM API函數acmDriverEnum來實現,在acmDriverEnum() 的參數中指定了在前面描述過的回調函數find_driver_enum,可以 進 一 步查詢每個CODEC的信息,最終可以獲取到ACM驅動器號的句柄。實現此功能的回調函數名為find_driver,本文后面將會用到。

   在把原始Wave音頻數據轉換到中間PCM格式數據之前,需要做些前期預備工作,填充一些相關的結構信息,具體有:WAVEFORMATEX結構描述源格式、中間PCM格式、以及最終的壓縮格式等。下面先填充一個用來描述源數據格式的WAVEFORMATEX結構:

WAVEFORMATEX wfSrc;
memset(&wfSrc, 0, sizeof(wfSrc));
wfSrc.cbSize = 0;
wfSrc.wFormatTag = WAVE_FORMAT_PCM; //PCM脈沖編碼調制
wfSrc.nChannels = 1; //單聲道
wfSrc.nSamplesPerSec = 11025; //11.025kHz
wfSrc.wBitsPerSample = 8; //8 bit
wfSrc.nBlockAlign = wfSrc.nChannels * wfSrc.wBitsPerSample / 8;
wfSrc.nAvgBytesPerSec = wfSrc.nSamplesPerSec * wfSrc.nBlockAlign;
   然后通過前面提到的回調函數find_driver來獲取由wFormatTag指定的中間數據格式所對應的驅動程序的ACM驅動器號,在此設定的是由WAVE_FORMAT_DSPGROUP_TRUESPEECH指定的有Windows 98系統自帶的TrueSpeech CODEC:

WORD wFormatTag = WAVE_FORMAT_DSPGROUP_TRUESPEECH;
HACMDRIVERID hadid = find_driver(wFormatTag);

   選定了驅動程序,現在要為最終驅動程序將產生的壓縮數據格式創建一個WAVEFORMATEX結構,并為驅動程序用于輸入的中間PCM格式產生一個WAVEFORMATEX結構:

WAVEFORMATEX* pwfDrv = get_driver_format(hadid, wFormatTag); // 獲得格式的詳情
   在結構pwfDrv的成員變量wBitsPerSample里存放著驅動格式的位數,在nSamplesPerSec里存放著驅動格式的采樣率。然后可以用非常類似的方法獲取驅動程序所支持的PCM格式標簽:

WAVEFORMATEX* pwfPCM = get_driver_format(hadid, WAVE_FORMAT_PCM);
   當以上所需信息都以獲取到后就可以開始轉換數據了。轉換由被ACM稱作流的對象來實現。我們可以打開流,將源格式、目標格式傳遞給它,要求它進行轉換。先將其轉換成中間PCM格式。

  將Wave音頻轉換為CODEC所支持的PCM格式

   通過CODEC將源Wave音頻轉換成CODEC所支持的PCM格式,可以使用任何可以做PCM間轉換的驅動器。另外還有一點很重要:我們打開轉換流時,要指明ACM_STREAMOPENF_NONREALTIME標志。若省略此標志,那么一些驅動程序(例如TrueSpeech CODEC)將會報告發生第512號"不可能發生的"錯誤。該錯誤指明所要求的轉換不能實時進行,假如在試圖播放數據的同時轉換大量數據,就必須注重這點。下面是該步轉換過程的簡要描述:

mmr = acmStreamOpen(&hstr,
NULL, //任意驅動器
&wfSrc, //源格式
pwfPCM, //目標格式
NULL, //無過濾
NULL, //無回調
0, //初始數據
ACM_STREAMOPENF_NONREALTIME);
   根據以字節計的平均速率計算出輸出緩沖區的大小,并加上一機動位(bit)假如沒有此額外的空間IMA_ADPCM驅動程序將不能轉換。中間的轉換結果將存放在pDst1Data中:

DWORD dwSrcBytes = dwSrcSamples * wfSrc.wBitsPerSample / 8;
DWORD dwDst1Samples = dwSrcSamples * pwfPCM->nSamplesPerSec / wfSrc.nSamplesPerSec;
DWORD dwDst1Bytes = dwDst1Samples * pwfPCM->wBitsPerSample / 8;
unsigned char * pDst1Data = new unsigned char[dwDst1Bytes];
……
ACMSTREAMHEADER strhdr; //填充轉換信息
memset(&strhdr, 0, sizeof(strhdr));
strhdr.cbStruct = sizeof(strhdr);
strhdr.pbSrc = cpBuf; //指定要轉換的源Wave音頻數據為cpBuf中的數據
strhdr.cbSrcLength = dwSrcBytes;
strhdr.pbDst = pDst1Data;
strhdr.cbDstLength = dwDst1Bytes;
mmr = acmStreamPrepareHeader(hstr, &strhdr, 0);
mmr = acmStreamConvert(hstr, &strhdr, 0); //轉換數據
……
acmStreamClose(hstr, 0);
   當流打開時,第二個參數為NULL,表示接受任何驅動程序進行轉換。復雜的只是計算需要給輸出數據分配多大的緩沖區。PCM格式間的轉換不牽扯壓縮和解壓縮,緩沖區大小直接就計算出來了。至于調用acmStreamPrepareHeader這個ACM API函數,是由于它可以為驅動程序安排好一切并答應驅動程序在轉換前鎖定內存。

  生成最終的壓縮格式

   向最終壓縮格式的轉換過程與前面的PCM格式間轉換非常相似,只不過此次轉換我們提供了打開流時想要使用的驅動程序的句柄。實際上,此處仍可使用NULL,因為已預知此驅動程序存在,但提供句柄避免了系統浪費時間為我們查找此驅動程序:

mmr = acmStreamOpen(&hstr,
had, //驅動器句柄
pwfPCM, //源格式
pwfDrv, //目標格式
NULL, //無過濾
NULL, //無回調
0, //實例化數據
ACM_STREAMOPENF_NONREALTIME);
   另外,計算用于壓縮數據的緩沖區的尺寸有點難辦,需要憑猜測。WAVEFORMATEX結構的nAvgBytesPerSec 域表示回放期間讀取字節的平均速率。我們可使用它來估計存儲壓縮的wave需要多大空間。一 些驅動程序給出的數據確實是平均的,而不是最差場合下的值,因此我選擇多增加50%的空間。 此方法在實踐中雖然有些浪費但很有效:

DWORD dwDst2Bytes = pwfDrv->nAvgBytesPerSec * dwDst1Samples / pwfPCM->nSamplesPerSec;
dwDst2Bytes = dwDst2Bytes * 3 / 2;
unsigned char * pDst2Data = new unsigned char [dwDst2Bytes];
   其中,無符號字符型指針pDst2Data用于存儲壓縮的最終Wave音頻數據,其大小經上式估算后存到dwDst2Bytes中。一旦轉換完成,ACMSTREAMHEADER的結構的cbDstLengthUsed 域指出緩沖區實際用了多少字節??梢酝ㄟ^它來計算出壓縮比:

double result= (double) dwSrcBytes / (double) strhdr2.cbDstLengthUsed;

   當源信號為8K采樣、16bits PCM編碼、單聲道、長度為1秒的Wave音頻信號, 驅動程序采用Windows 98自帶的TrueSpeech 音頻CODEC,它能實現大約10:1的壓縮,這樣高的壓縮率還是比較另人滿足的。

  小結:

   本文以TrueSpeech CODEC為例對使用ACM音頻壓縮編程接口實現Wave音頻壓縮的過程作了介紹。假如有自已的壓縮格式,也可創建并安裝自已的CODEC,實現的方法與之基本類似。在理解了上述編程思想的前提下,對代碼稍加改動就可編寫出相 應的解壓程序。本程序在windows 2000 Professional下,由Microsoft Visual C++ 6.0編譯通過。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲人成77777在线观看网| 久久久久久91香蕉国产| 久久久亚洲福利精品午夜| 国产偷亚洲偷欧美偷精品| 亚洲另类激情图| 中文字幕亚洲综合| 久久免费视频在线| 国产精品国产福利国产秒拍| 日韩精品在线观看视频| 欧美极品少妇全裸体| 日韩中文字幕在线播放| 亚洲国产97在线精品一区| 久久资源免费视频| 日韩欧美中文在线| 国产精品色午夜在线观看| 欧美与欧洲交xxxx免费观看| 欧美国产欧美亚洲国产日韩mv天天看完整| 久久国产精品首页| 26uuu另类亚洲欧美日本老年| 亚洲欧美激情视频| 亚洲精品视频免费| 奇米影视亚洲狠狠色| 日韩成人中文字幕在线观看| 51午夜精品视频| 国内外成人免费激情在线视频| 欧美日韩一区二区三区在线免费观看| 国产成人免费av| 91免费福利视频| 色综合亚洲精品激情狠狠| 亚洲天堂色网站| 亚洲四色影视在线观看| 国产成人精品一区二区| 亚洲欧美日韩成人| 久久国产精品久久精品| 国语自产精品视频在线看| 精品久久久久久中文字幕一区奶水| 欧美美女操人视频| 国产在线一区二区三区| 日韩av在线最新| 成人精品视频久久久久| 精品女同一区二区三区在线播放| 91精品国产一区| 国产精品一区二区av影院萌芽| 亚洲人成网7777777国产| zzjj国产精品一区二区| 国产小视频国产精品| 亚洲欧美综合精品久久成人| 在线看欧美日韩| 精品国产一区二区三区久久久狼| www.日韩欧美| 国产免费一区二区三区在线能观看| 国产在线播放不卡| 成人欧美在线观看| 国产99久久精品一区二区永久免费| 欧美成人激情视频免费观看| 中文一区二区视频| 久久久久久999| 精品中文字幕乱| 最近中文字幕mv在线一区二区三区四区| 欧美日韩久久久久| 亚洲精美色品网站| 久久久在线视频| 成人情趣片在线观看免费| 中文字幕国产精品久久| 欧美大尺度在线观看| 夜夜躁日日躁狠狠久久88av| 国产欧美 在线欧美| 亚洲a∨日韩av高清在线观看| 亚洲国产美女精品久久久久∴| 亚洲人成在线观| 国产日产欧美a一级在线| 亚洲美女久久久| 精品精品国产国产自在线| 欧美大片免费看| 久久精品2019中文字幕| 久久久人成影片一区二区三区| 欧美在线视频观看免费网站| 日韩欧美亚洲成人| 国产亚洲成av人片在线观看桃| 国产精品99久久久久久久久| 92版电视剧仙鹤神针在线观看| 午夜精品视频网站| 成人福利网站在线观看| 国产va免费精品高清在线| 日韩高清免费在线| 26uuu日韩精品一区二区| 久久精品久久精品亚洲人| 日韩av一区在线观看| 精品久久久av| 欧美日韩在线视频一区| 亚洲精品视频播放| 最近2019年手机中文字幕| 国产一区二区丝袜| 久久91亚洲精品中文字幕| 国产男人精品视频| 久久久久九九九九| 国产精品99久久久久久白浆小说| 久久夜精品香蕉| 成人网址在线观看| 欧美国产激情18| 91av在线看| 亚洲欧美国产制服动漫| 日本精品视频网站| 亚洲精品免费一区二区三区| 国产精品免费电影| 91视频国产精品| 国产精品久久电影观看| 另类美女黄大片| 久久香蕉国产线看观看av| 亚洲欧美在线一区| 国产一区在线播放| 亚洲国产成人精品女人久久久| 这里只有精品丝袜| 中文字幕免费国产精品| 亚洲天堂视频在线观看| 亚洲电影免费观看高清完整版在线观看| 亚洲成在人线av| 亚洲欧美三级伦理| 亚洲毛片在线观看| 91青草视频久久| 美女撒尿一区二区三区| 91爱爱小视频k| 亚洲色图美腿丝袜| 欧美成人激情在线| 国产精品综合网站| 91av网站在线播放| 亚洲欧美一区二区精品久久久| 亚洲欧美一区二区激情| 欧美激情久久久| 欧美肥臀大乳一区二区免费视频| 青青青国产精品一区二区| 国产精品美女视频网站| 欧洲亚洲女同hd| 欧美日韩在线一区| 亚洲精品免费网站| 欧美电影免费观看网站| 欧美日韩国产一中文字不卡| 国产精品人人做人人爽| 欧美一级大胆视频| 欧美精品久久一区二区| 午夜精品一区二区三区视频免费看| 日韩在线观看精品| 成人在线视频网| 一个人www欧美| 狠狠爱在线视频一区| 久久久久九九九九| 亚洲精品电影久久久| 日本aⅴ大伊香蕉精品视频| 在线看欧美日韩| 97视频在线播放| 久久久久久免费精品| 国产成人精品久久亚洲高清不卡| 国产成人精品999| 国产97免费视| 精品国偷自产在线视频99| 草民午夜欧美限制a级福利片| 在线观看日韩av| 最近更新的2019中文字幕| 国产xxx69麻豆国语对白| 亚洲一区中文字幕| 国产精品视频永久免费播放| 色偷偷88888欧美精品久久久| 日本一区二区不卡| 国产精品美女呻吟|