VC 調 用ACM 音 頻 壓 縮 編 程 接 口 的 方 法 (轉)

worldblog發表於2007-12-05
VC 調 用ACM 音 頻 壓 縮 編 程 接 口 的 方 法 (轉)[@more@] 

音 頻 和 視 頻 數 據 是 大 多 數 多 媒 體 應 用 程 序 向 用 戶 提 供 信 息 的 主 要 方 式, 這 些 數 據 一 般 具 有較 高 的 採 樣 速 率, 如 果 不 經 過 壓 縮 的 話, 保 存 它 們 需 要 消 耗 大 量 的 存 貯 空 間, 在 網 絡 上 進 行 傳 輸的 效 率 也 很 低, 因 此 音 頻 視 頻 數 字 壓 縮 編 碼 在 多 媒 體 技 術 中 佔 有 很 重 要 的 地 位。 就 音 頻 數 據 而言, 目 前 常 用 的 壓 縮 方 法 有 很 多 種, 不 同 的 方 法 具 有 不 同 的 壓 縮 比 和 還 原 音 質, 編 碼 的 格 式 和 演算法 也 各 不 相 同, 其 中 某 些 壓 縮 算 法 相 當 復 雜, 普 通 程 序 不 可 能 去 實 現 其 編 解 碼 算 法。 所 幸 的 是,與 3.x 相 比,Windows 95/NT 4.0 為 多 媒 體 應 用 程 序 提 供 了 更 強 的支 持, 引 入 了ACM(Audio Compression Manager, 音 頻 壓 縮 管 理 器) 和VCM(Vo Compression Manager, 視 頻 壓 縮 管 理 器), 它 們 負 責 管 理 系 統 中 所 有 音 頻 和 視 頻 編 解 碼 器(Coder-Decoder,簡 稱CODEC, 是 實 現 音 頻 視 頻 數 據 編 解 碼 的 驅 動 程 序), 應 用 程 序 可 以 通 過ACM 或VCM 提 供 的 編 程接 口 調 用 這 些 系 統 中 現 成 的 編 解 碼 器 來 實 現 音 頻 或 視 頻 數 據 的 壓 縮 和 解 壓 縮。95/NT 4.0 系 統自 帶 的 音 頻CODECs 支 持 一 些 早 期 的 音 頻 數 據 壓 縮 標 準, 如ADPCM 等,Inte Explorer 4.0 等應 用 程 序 包 含 的 音 頻CODECs 支 持 一 些 比 較 新 的 壓 縮 標 準, 如MPEG Layer 3 等。 在 控 制 面 板 的 多媒 體 組 件 中 選 擇“ 高 級”, 打 開“ 音 頻 壓 縮 的 編 碼 解 碼 器”, 就 可 列 出 系 統 中 安 裝 的 所 有 音 頻CODECs。本 文 所 要 介 紹 的 就 是ACM 音 頻 壓 縮 接 口 的 編 程 方 法, 所 用 編 程 工 具 為VC++ 5.0。 :namespace prefix = o ns = "urn:schemas--com::office" />

獲 取CODECs 的 信 息

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

---- 前 面 提 到, 在控 制 面 板 中 可 以 查 看 系 統 中CODECs 的 信 息, 而 在 應 用 程 序 中 也 常 常 需 要 知 道 某 種 音 頻CODECs 是否 存 在, 並 獲 取 其 編 解 碼 參 數 等 信 息, 這 一 點 可 以 通 過 調 用 下 面 兩 個 函 數 來 實 現。

---- MMRESULT mmr=acmMetrics(NULL, ACM_METRIC_COUNT_CODECS, &dwCodecs);

---- mmr = acmEnum(CodecsEnumProc, 0, 0);

---- acmMetrics() 函 數 可 以 獲 取 許 多ACM 對 象 的 有 用 信 息, 例 如 向 其 中 傳 遞ACM_METRIC_COUNT_CODECS 可 以 查 詢 系 統 中 安 裝 的 音 頻CODECs 總 數。 函 數acmDriverEnum() 的 功 能 是 枚 舉 所 有 的 音 頻CODECs,在acmDriverEnum() 的 參 數 中 指 定 回 調 函 數CodecsEnumProc() 可 以 進 一 步 查 詢 每 個CODEC 的 資訊。Windows 編 程 中 經 常 要 用 到 回 調 函 數, 下 面 是 枚 舉 音 頻CODECs 的 一 個 回 調 函 數 的 示 例。

 


 


BOOL CALLBACK CodecsEnumProc(HACMDRIVERID


 


hadid, D dwInstance, DWORD fdwSupport) {


 


DWORD dwSize = 0;


 


if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC)


 


printf("多格式轉換n");


 


ACMDRIVERDETAILS add;


 


acmdd.cbStruct = sizeof(acmdd);


 


MMRESULT mmr = acmDriverDetails(hadid, &acmdd, 0);


 


if (mmr)  error_msg(mmr);


 


else {


 


printf(" 全稱:  %sn", acmdd.szLoame);


 


printf(" 描述:  %sn", acmdd.szFeatures);


 


}


 


HACMDRIVER had = NULL;


 


mmr = acmDriverOpen(&had, hadid, 0);  //開啟


 


if (mmr)  error_msg(mmr);


 


else {


 


mmr = acmMetrics(had, ACM_METRIC_


 


MAX_SIZE_FORMAT, &dwSize);


 


WAVEFORMATEX* pwf = (WAVEFORMATEX*) malloc(dwSize);


 


memset(pwf, 0, dwSize);


 


pwf->cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX);


 


pwf->wFormatTag = 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)  error_msg(mmr);


 


free(pwf);


 


acmDriverClose(had, 0);


 


}


 


return TRUE;


 


}


 


---- CodecsEnumProc() 共 有 三 個 參 數。 第 一 個 參 數 是 驅 動 程 序 的ID 值; 第 二 個 參 數 是 實 例 數 據, 本文 例 子 中 未 使 用; 第 三 個 參 數 描 述 該 驅 動 程 序 所 支 持 的 功 能, 它 由 一 組 標 識 進 行 或 運 算 構 成, 例如, 如 果 設 置 了 標 識ACMDRIVERDETAILS_SUPPORTF_CODEC, 則 說 明 該 驅 動 程 序 可 以 將 一 種 編 碼 格式 的 音 頻 信 號 轉 換 成 另 一 種 編 碼 格 式。 通 過acmDriverDetails() 函 數 可 以 獲 得 對 該 驅 動 程 序 進一 步 的 信 息, 如CODEC 的 名 稱、 簡 單 描 述 等。 以 上 信 息 實 際 上 是 由ACM 收 集, 並 保 存 在ACM 內 部, 所以 查 詢 以 上 信 息 時 並 未 真 正 將 驅 動 程 序 加 載 至 內 存。 而 要 獲 得 每 一 種 驅 動 程 序 支 持 的 音 頻 格 式信 息, 則 必 須 將 驅 動 程 序 加 載 至 內 存, 這 是 通 過acmDriverOpen() 完 成 的, 在 退 出CodecsEnumProc() 前, 還 要 用acmDriverClose() 來 關 閉 已 打 開 的 驅 動 程 序。 在 使 用 音 頻 格 式 枚 舉 函 數 前, 需 要 先 分配 一 塊 緩 衝 區 存 置 格 式 信 息, 緩 衝 區 的 大 小 可 通 過 調 用acmMetrics() 查 詢ACM_METRIC_MAX_SIZE_FORMAT 獲 得, 格 式 信 息 中 的 音 頻 格 式 標 識 設 為WAVE_FORMAT_UNKNOWN。 在 音 頻 格 式 枚 舉 中 同 樣 使 用 了 回撥 函 數, 此 回 調 函 數 只 是 列 出 了 該 音 頻 格 式 的 名 稱 和 標 識 值。

BOOL CALLBACK FormatEnumProc


 


(HACMDRIVERID hadid, LPACMFORMATDETAILS


 


pafd, DWORD dwInstance, DWORD fdwSupport) {


 


printf("%4.4lXH, %sn", pafd- >dwFormatTag, pafd- >szFormat);


 


return TRUE;


 


}


 


---- 上 面 介 紹 了 瀏覽 系 統 中 所 有 音 頻CODECs 及 每 種CODEC 所 支 持 的 音 頻 格 式 的 方 法, 某 些 典 型 的 應 用 程 序 可 能 需要 列 出 系 統 中 所 有 可 以 選 用 的CODECs, 並 由 用 戶 來 選 擇 使 用 哪 一 種CODEC 進 行 壓 縮, 此 時 就 需 要利 用 上 面 的 編 程 方 法 來 獲 取CODECs 的 信 息。

音 頻 數 據 的 壓 縮

---- 下 面 說 明 使 用 某 一CODEC 實 現 音 頻 壓 縮 的 過 程, 讀 者 朋 友 只 需 稍 加 改 動 就 可 編 寫 出 相 應 的 解 壓 程 序。 假 設 源 信 號 為8K 採 樣、16bits PCM 編 碼、 單 聲 道、 長 度 為1 秒 的 音 頻 信 號。 驅 動 程 序 採 用Windows 95 自 帶 的TrueSpeech 音 頻CODEC, 它 能 實 現 大 約10:1 的 壓 縮。 在 此 例 中,TrueSpeech CODEC 支 持 從 源 音 頻 格 式 到 目 標 格 式 的 轉 換, 而 在 實 際 應 用 中, 可 能 某 種CODEC 不 支 持 直 接 將 源 音 頻 格 式 轉 換 成 目 標 格 式, 這 時 可 以 採 取 兩 步 轉 換 法, 即 先 將 源 格 式 轉 換 成 一 種 中 間 格 式, 再 將 此 中 間 格 式 轉 換 成 目 標 格 式, 因 為 線 性PCM 編 碼 最 為 簡 單, 且 為 絕 大 多 數CODEC 所 支 持, 所 以 一 般 中 間 格 式 都 選 為 線 性PCM 格 式 的 一 種。

---- 在 進 行 壓 縮 之前 首 先 需 要 確 定TrueSpeech 驅 動 程 序 的ID 值。 為 此 需 要 用 到acmDriverEnum() 函 數, 對 枚 舉 到 的每 一 個 驅 動 程 序, 由acmDriverEnum() 指 定 的 回 調 函 數 將 檢 查 其 支 持 的 所 有 音 頻 格 式, 若 其 中 包括wFormatTag 值 為WAVE_FORMAT_DSPGROUP_TRUESPEECH 的 音 頻 格 式, 則 此 驅 動 程 序 就 是 要 尋 找的TrueSpeech CODEC, 它 所 支 持 的 第 一 種WAVE_FORMAT_DSPGROUP_TRUESPEECH 音 頻 格 式 即 為 目標 音 頻 壓 縮 格 式。 查 詢 所 需 的CODEC 及 其 支 持 的 音 頻 格 式 的 方 法 見 前 一 小 節 的 介 紹。

---- 根 據 查 詢 的 結果, 設hadID 為TrueSpeech CODEC 的ID 值,pwfDrv 為 指 向 目 標WAVEFORMATEX 結 構 的 指 針, 接 下 來利 用 獲 得 的ID 值 打 開 相 應 的 驅 動 程 序。

HACMDRIVER had = NULL;


 


mmr = acmDriverOpen(&had, hadID, 0);


 


if(mmr) {  printf(" 開啟驅動程式失敗n"); exit(1);  }


 


---- 壓 縮 和 解 壓 縮一 樣, 都 是 將 音 頻 信 號 從 一 種 音 頻 格 式 轉 換 成 另 一 種 格 式, 要 完 成 這 一 過 程, 首 先 要 打 開 轉 換 流。在 用acmStreamOpen 打 開 轉 換 流 時, 我 們 指 定 了ACM_STREAMOPENF_NONREALTIME 標 志, 它 表 示 轉換 無 需 實 時 進 行。 因 為 很 多 壓 縮 算 法 的 計 算 量 是 相 當 大 的, 實 時 完 成 幾 乎 是 不 可 能 的, 例 如 在 本例 中, 如 果 不 指 定 此 標 志,TrueSpeech CODEC 就 會 返 回“ 無 法 完 成” 的 錯 誤。

HACMSTREAM hstr = NULL;


 


DWORD dwSrcBytes = dwSrcSamples * wfSrc.wBitsPerSample / 8;


 


mmr = acmStreamOpen(&hstr,had, //驅動程式控制程式碼


 


pwfSrc, //指向源格式的指標


 


pwfDrv, //指向目標音訊格式的指標


 


NULL, //無過濾器


 


NULL, //無回撥


 


0,ACM_STREAMOPENF_NONREALTIME);


 


---- 在 真 正 進 行 轉換 之 前, 還 必 須 準 備 轉 換 流 的 信 息 頭。 下 面 一 段 代 碼 中, 先 利 用 源 數 據 的 大 小 以 及 目 標 格 式 的 平均 數 據 率 估 算 目 標 數 據 的 緩 存 區 大 小, 然 後 調 用acmStreamPrepareHeader 為 轉 換 準 備 信 息 頭。

---- DWORD dwDstBytes=pwfDrv->nAvgBytesPerSec*dwSrcSamples/wfSrc.nSamplesPerSec;

---- dwDstBytes = dwDstBytes*3/2; // 計 算 壓 縮 後 音 頻 數 據 大 小, 並 依 此 適 當 增 加 輸 出 緩 衝 區 的 大 小。

BYTE* pDstData = new BYTE [dwDstBytes];


 


ACMSTREAMHEADER shdr;


 


memset(&strhdr, 0, sizeof(shdr));


 


shdr.cbStruct = sizeof(shdr);


 


shdr.pbSrc = pSrcData;   //源音訊資料區


 


shdr.cbSrcLength = dwSrcBytes;


 


shdr.pbDst = pDstData; //後音訊資料緩衝區


 


shdr.cbDstLength = dwDstBytes;


 


mmr = acmStreamPrepareHeader(hstr, &shdr, 0);


 


---- 語 音 數 據 真 正的 壓 縮 過 程 是 由 函 數acmStreamConvert() 完 成 的。 在 調 用acmStreamConvert() 時 可 以 指 定 回 調函 數, 以 便 在 轉 換 過 程 中 顯 示 進 度 信 息 等。 在 本 例 中, 未 指 定 回 調 函 數, 只 是 簡 單 地 等 待 壓 縮 的結 束。

---- mmr = acmStreamConvert(hstr, &shdr, 0);

---- 數 據 壓 縮 完 畢後, 應 用 程 序 就 可 以 把 緩 衝 區 中 的 數 據 寫 入 目 標 文 件 中。

---- 最 後, 必 須 關閉 轉 換 流 和 驅 動 程 序。

mmr = acmStreamClose(hstr, 0);


 


mmr = acmDriverClose(had, 0);


 

---- 本 文 介 紹 了 利 用ACM 獲 取 音 頻CODEC 的 信 息 以 及 實 現 音 頻 壓 縮 的 一 般 方 法 和 過 程, 對ACM 編 程 感 興 趣 的 讀 者 可 以 進 一 步 參 考VC++ 5 的 聯 機 幫 助 中 關 於ACM 的 信 息。 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988754/,如需轉載,請註明出處,否則將追究法律責任。

相關文章