国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

VC下調(diào)用ACM音頻編程接口壓縮Wave音頻

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

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

  引言

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

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

  實現(xiàn)思路

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

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

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

  程序的設計實現(xiàn)

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


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

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; //繼續(xù)枚舉
}
   在該回調(diào)函數(shù)中用到的FIND_DRIVER_INFO是自定義的數(shù)據(jù)結構,其兩個成員變量分別用來保存ACM驅(qū)動器號的句柄和要轉(zhuǎn)換的數(shù)據(jù)格式:

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

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); //關閉驅(qū)動器
  ……
}
   根據(jù)指定的格式要找到其所對應的ACM驅(qū)動器號可以用枚舉所有音頻CODECs的ACM API函數(shù)acmDriverEnum來實現(xiàn),在acmDriverEnum() 的參數(shù)中指定了在前面描述過的回調(diào)函數(shù)find_driver_enum,可以 進 一 步查詢每個CODEC的信息,最終可以獲取到ACM驅(qū)動器號的句柄。實現(xiàn)此功能的回調(diào)函數(shù)名為find_driver,本文后面將會用到。

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

WAVEFORMATEX wfSrc;
memset(&wfSrc, 0, sizeof(wfSrc));
wfSrc.cbSize = 0;
wfSrc.wFormatTag = WAVE_FORMAT_PCM; //PCM脈沖編碼調(diào)制
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;
   然后通過前面提到的回調(diào)函數(shù)find_driver來獲取由wFormatTag指定的中間數(shù)據(jù)格式所對應的驅(qū)動程序的ACM驅(qū)動器號,在此設定的是由WAVE_FORMAT_DSPGROUP_TRUESPEECH指定的有Windows 98系統(tǒng)自帶的TrueSpeech CODEC:

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

   選定了驅(qū)動程序,現(xiàn)在要為最終驅(qū)動程序?qū)a(chǎn)生的壓縮數(shù)據(jù)格式創(chuàng)建一個WAVEFORMATEX結構,并為驅(qū)動程序用于輸入的中間PCM格式產(chǎn)生一個WAVEFORMATEX結構:

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

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

  將Wave音頻轉(zhuǎn)換為CODEC所支持的PCM格式

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

mmr = acmStreamOpen(&hstr,
NULL, //任意驅(qū)動器
&wfSrc, //源格式
pwfPCM, //目標格式
NULL, //無過濾
NULL, //無回調(diào)
0, //初始數(shù)據(jù)
ACM_STREAMOPENF_NONREALTIME);
   根據(jù)以字節(jié)計的平均速率計算出輸出緩沖區(qū)的大小,并加上一機動位(bit)假如沒有此額外的空間IMA_ADPCM驅(qū)動程序?qū)⒉荒苻D(zhuǎn)換。中間的轉(zhuǎn)換結果將存放在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; //填充轉(zhuǎn)換信息
memset(&strhdr, 0, sizeof(strhdr));
strhdr.cbStruct = sizeof(strhdr);
strhdr.pbSrc = cpBuf; //指定要轉(zhuǎn)換的源Wave音頻數(shù)據(jù)為cpBuf中的數(shù)據(jù)
strhdr.cbSrcLength = dwSrcBytes;
strhdr.pbDst = pDst1Data;
strhdr.cbDstLength = dwDst1Bytes;
mmr = acmStreamPrepareHeader(hstr, &strhdr, 0);
mmr = acmStreamConvert(hstr, &strhdr, 0); //轉(zhuǎn)換數(shù)據(jù)
……
acmStreamClose(hstr, 0);
   當流打開時,第二個參數(shù)為NULL,表示接受任何驅(qū)動程序進行轉(zhuǎn)換。復雜的只是計算需要給輸出數(shù)據(jù)分配多大的緩沖區(qū)。PCM格式間的轉(zhuǎn)換不牽扯壓縮和解壓縮,緩沖區(qū)大小直接就計算出來了。至于調(diào)用acmStreamPrepareHeader這個ACM API函數(shù),是由于它可以為驅(qū)動程序安排好一切并答應驅(qū)動程序在轉(zhuǎn)換前鎖定內(nèi)存。

  生成最終的壓縮格式

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

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

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

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

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

  小結:

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

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 竹山县| 泽库县| 奉节县| 永川市| 逊克县| 桐乡市| 万荣县| 观塘区| 开平市| 湟中县| 皋兰县| 镇坪县| 阿拉善右旗| 桃源县| 皮山县| 根河市| 闻喜县| 堆龙德庆县| 郴州市| 绥滨县| 南涧| 台州市| 永川市| 佛坪县| 阿城市| 雷波县| 交口县| 濉溪县| 陵水| 怀来县| 万载县| 万安县| 潜江市| 桦川县| 密云县| 怀集县| 华宁县| 黄大仙区| 达日县| 青海省| 星座|