310
阿裏雲
技術社區[雲棲]
用Mixer API函數調節控製麵板的音頻設置
摘要:本文通過實例代碼演示了如何通過Mixer API函數在程序中調節控製麵板的音頻設備性能的設置。
關鍵詞:Mixer函數,控製麵板,音頻設備調節
如果你用過Windows的音頻設備,比如播放音樂或者錄音,聊天,調節麥克或者聲音的大小,以及設置靜音,都可以通過控製麵板中的音頻設置麵板來調節,你對於下麵的兩個設置麵板肯定不陌生。
播放時調節音量大小和左右聲道的控製板,還可以通過它將某個設備設置為靜音。
另一個就是錄音時控製麵板,在這裏我們可以選擇聲音輸入設備,以及調節錄音時左右聲道音量大小
這兩個控製板是windows提供給我們的,這兩個控製板是讓windows用戶在播放聲音或者錄音時萊調節音頻設備的,通過這兩個控製板,我們可以選擇播放或者錄音的音頻設備,設置音量的大小,調整左右聲道。但是如果我們自己開發的程序中也要用到這個功能該怎麼辦,比如你開發的程序想給用戶提供一個調節音頻設備的界麵,可以讓用戶很方便的通過你的程序提供的功能來調節和選擇相應的音頻設備,而不是每次都要轉到係統的控製麵板中來調節它們,當用戶通過我們提供的接口對設備進行的調整,在係統的控製麵板中的音頻設備設置要相應的發生改變,並且當用戶通過係統控製麵板調整音頻設置後,在我們程序的界麵上也會發生相應的改變。我最近在開發過程中就遇到這個問題,通過仔細的查閱msdn以及其他的資料,終於解決了這個問題,下麵我將我的經驗總結一下,如果你也遇到相類似的問題,希望能對你有所幫助。
如何來控製係統中任何的音頻輸出和輸入,比如波形音頻,MIDI ,CD音頻,合成語音等音頻輸出以及Line in ,麥克等輸入,windows給我們提供了一組API接口函數,稱為Mixer係列的函數,mixer也稱為混音器,通過混音器可以實現混音和音量控製。最基本的混音器結構單元是音頻線路,比如microphone ,line in ,cd,midi等都是一個音頻線路。音頻線路包含一個或者多個發源於單一音源或係統資源的聲道,例如,一個立體聲音頻線路有兩個聲道,但仍然被看成是一個音頻線路,因為它發源於一個音源。
下麵我要先簡單的介紹一下Mixer函數,其實反正總共也沒有幾個,使用起來很簡單的。
mixerOpen
mixerClose
mixerGetDeVCaps
mixerGetLineControls
mixerGetLineInfo
mixerGetControlDetails
mixerSetControlDetails
mixerGetID
mixerGetNumDevs
看到了吧,就這麼簡單的幾個函數,通過這9個API,我們就可以來控製音頻的輸入和輸出設備了,其實有關這幾個函數的定義你可以在C:/Program Files/Microsoft Visual Studio/VC98/Include/mmsystem.h文件中找到。下麵我簡單介紹一下這幾個函數,詳細地介紹你可以參見msdn。
mixerOpen和mixerClose函數用來打開和關閉混音器設備
mixerGetNumDevs可以確定係統中有多少混音器設備
mixerGetDevCaps函數可以確定混音器設備的能力
mixerGetLineInfo可以檢索指定音頻線路的信息
mixerGetLineControls用於檢索一個或者多個與音頻線路相關的控製的通用信息
mixerGetControlDetails用於檢索與某個音頻線路相關的一個控製的屬性
mixerSetControlDetails用於設置製定控製的屬性。
其實我們主要用到的就是後麵的四個函數,希望大家重點研究一下。混音器還提供了窗口回調服務,用戶在調用mixeropen的時候,可以將一個窗口句柄作為參數傳遞給mixer,這樣,當mixer設備發生變化時就會給回調窗口發送消息通知,比如用戶通過控製麵板調整了音量的大小,或者選擇了某個錄音設備。消息的類型就兩個MM_MIXM_LINE_CHANGE和MM_MIXM_CONTROL_CHANGE。
下麵就不多說了,我用一個例子告訴你如何在程序中對音頻設備進行設置。
這裏播放和錄音我都隻是選擇了幾個常用的設備,當然係統提供的設備比我這裏的舉例用到的設備要多,你可以根據我提供的方法來對其他的設備進行控製。還有說明一下,具有兩個滑動條的表示左右聲道。但是像麥克風隻有一個聲道。
通過我們的程序界麵我們就可以像在控製麵板裏一樣可以調節左右聲道的音量,以及選擇某個設備進行錄音,或者對某個音頻線路進行靜音,相應的係統的設置也會被改變,如果你通過係統的控製麵板進行設置,在我們的程序界麵也上同步的可以反映出來變化。
關於工程的建立我就不多少了,很簡單的,就是一個基於對話框的工程,上麵放了一些控件。下麵我主要講一下每個功能是如何實現的。主要有三個功能1 如何調整左右聲道音量的大小,2 如何將某個設備靜音,3 如何選擇錄音設備。
這裏關於mixer函數的用法還要先嘮叨幾句。一般來說,對音頻線路的操作流程如下:
1、通過GetLineInfo獲取指定音頻線路的信息,返回一個MIXERLINE結構
2、然後通過GetLineControl獲取音頻線路相關的控製的通用信息,通過MIXERCONTROL結構返回。
3、通過GetConrolDetails獲取指定控製的屬性值
4、通過SetControlDetails設置指定控製的屬性值,
對於每個線路設備,mixer都用一個類型值來標示,比如:
Volume對應的值 MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
CD 對於的值 MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
Midi對應的值為 MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
Wave對應的值為 MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
Line in對應的值為 MIXERLINE_COMPONENTTYPE_SRC_LINE
Microphone對應的值為 MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
我們可以通過音頻線路的類型值獲得相應的線路的信息,也可以通過音頻線路的設備ID來獲取相應的線路的信息。
下麵開始我們編程吧。
首先定義三個變量
UINT m_uMxId; //mixer的ID HWND m_hWnd; //回調窗口句柄 HMIXER m_hmx; // |
然後就是要打開mixer,可以在對話的初始化中作這些工作。
#define MAX_VOL_VALUE 65535 if (MMSYSERR_NOERROR != mixerOpen(&m_hmx, m_uMxId,(DWORD)m_hWnd, 0, CALLBACK_WINDOW)) { return FALSE; } if (MMSYSERR_NOERROR == mixerGetID((HMIXEROBJ)m_hmx, &m_uMxId, MIXER_OBJECTF_HMIXER)) { return m_uMxId; } //設置Volume的滑動條的範圍這裏隻以Volume為例。 m_SliderWaveL.SetRange(0, MAX_VOL_VALUE, TRUE); m_SliderWaveR.SetRange(0, MAX_VOL_VALUE, TRUE); |
接著我先演示一下如何獲取和設置錄音設備的左右聲道的音量值,以及如何靜音放音設備,這裏以Volume為例,其他的設備類似,你可以照著我的代碼,套用即可。
1、如何獲取Volume設備的音量大小
DWORD dwLValue; DWORD dwRValue; GetVolume(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, &dwLValue,&dwRValue); //GetVolume函數的定義見下麵,然後根據返回的值調整滑動條的位置 m_SliderVolR.SetPos(MAX_VOL_VALUE - dwLValue); m_SliderVolL.SetPos(MAX_VOL_VALUE - dwRValue); |
2、如何根據滑動條的位置來調整係統音量的大小
void CMixerControlDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // m_dwSpkR和m_dwSpkL是用來記錄Volume左右聲道的音量值,0~~65535 CSliderCtrl *pSlider = (CSliderCtrl *)pScrollBar; int nValue = MAX_VOL_VALUE - pSlider->GetPos(); //獲取滑動條的位置pos else if (m_SliderVolR.m_hWnd == pSlider->m_hWnd) { //如果拖動的是Volume的左聲道 m_dwSpkR = nValue; // 設置Volume的音量值 SetVolume(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, m_dwSpkL, m_dwSpkR); } else if (m_SliderVolL.m_hWnd == pSlider->m_hWnd) { //Volume右聲道 m_dwSpkL = nValue; // 設置Volume的音量值 SetVolume(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, m_dwSpkL, m_dwSpkR); } //其他音頻線路可以依次類推在下麵添加 }
|
GetVolume和SetVolume函數的定義下麵給出
BOOL CMixer::SetVolume(DWORD dwSrcType, DWORD dwLValue, DWORD dwRValue, BOOL bMono) { MIXERLINE mxl; if (! GetLineInfo(&mxl, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, dwSrcType)) return FALSE;
MIXERCONTROL mxc; if (! GetLineControl(&mxc, &mxl, MIXERCONTROL_CONTROLTYPE_VOLUME)) return FALSE;
MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_UNSIGNED mxcd_u1; MIXERCONTROLDETAILS_UNSIGNED mxcd_u[2];
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = mxc.dwControlID; mxcd.cMultipleItems = 0;
if (bMono) { mxcd.cChannels = 1; mxcd.cbDetails = sizeof(mxcd_u1); mxcd.paDetails = &mxcd_u1; mxcd_u1.dwValue = dwLValue; } else { mxcd.cChannels = mxl.cChannels; mxcd.cbDetails = sizeof(*mxcd_u); mxcd.paDetails = mxcd_u; mxcd_u[0].dwValue = dwLValue; mxcd_u[1].dwValue = dwRValue; }
if (! SetControlDetails(&mxcd, MIXER_OBJECTF_MIXER)) return FALSE;
return TRUE; }
BOOL CMixer::GetVolume(DWORD dwSrcType, DWORD* pdwLValue, DWORD* pdwRValue, BOOL bMono) { MIXERLINE mxl; if (! GetLineInfo(&mxl, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, dwSrcType)) return FALSE;
MIXERCONTROL mxc; if (! GetLineControl(&mxc, &mxl, MIXERCONTROL_CONTROLTYPE_VOLUME)) return FALSE;
MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_UNSIGNED mxcd_u1; MIXERCONTROLDETAILS_UNSIGNED mxcd_u[2];
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = mxc.dwControlID; mxcd.cMultipleItems = 0;
if (bMono) { mxcd.cChannels = 1; mxcd.cbDetails = sizeof(mxcd_u1); mxcd.paDetails = &mxcd_u1;
if (! GetControlDetails(&mxcd, MIXER_GETCONTROLDETAILSF_VALUE)) return FALSE;
*pdwLValue = mxcd_u1.dwValue; } else { mxcd.cChannels = mxl.cChannels; mxcd.cbDetails = sizeof(*mxcd_u); mxcd.paDetails = mxcd_u;
if (! GetControlDetails(&mxcd, MIXER_GETCONTROLDETAILSF_VALUE)) return FALSE;
*pdwLValue = mxcd_u[0].dwValue; *pdwRValue = mxcd_u[1].dwValue; } return TRUE; }
BOOL GetLineInfo(LPMIXERLINE pmxl, DWORD dwDstType, DWORD dwSrcType) { MIXERCAPS mxcaps; if (! GetDeVCaps(&mxcaps)) return FALSE;
UINT u=0; do { pmxl->cbStruct = sizeof(*pmxl); pmxl->dwDestination = u; u++; if (MMSYSERR_NOERROR != mixerGetLineControls((HMIXEROBJ)m_uMxId, pmxl, MIXER_GETLINEINFOF_DESTINATION)) { return FALSE; }
} while ((u < mxcaps.cDestinations) && (pmxl->dwComponentType != dwDstType));
if (u > mxcaps.cDestinations) return FALSE;
if (dwDstType == dwSrcType) return TRUE;
pmxl->dwDestination = u; UINT cConnections = (UINT)pmxl->cConnections;
UINT v=0; u--; do { pmxl->cbStruct = sizeof(*pmxl); pmxl->dwDestination = u; pmxl->dwSource = v; v++;
if (MMSYSERR_NOERROR != mixerGetLineControls((HMIXEROBJ)m_uMxId, pmxl, MIXER_GETLINEINFOF_SOURCE)) { return FALSE; } } while ((v < cConnections) && (pmxl->dwComponentType != dwSrcType));
if((v > cConnections) || (pmxl->dwComponentType !=dwSrcType)) return FALSE;
return TRUE; }
BOOL GetLineControl(LPMIXERCONTROL pmxc, LPMIXERLINE pmxl, DWORD dwType) { LPMIXERCONTROL pamxctrl; DWORD cbmxctrls = sizeof(*pamxctrl) * (UINT)pmxl->cControls; pamxctrl = (LPMIXERCONTROL)LocalAlloc(LPTR, cbmxctrls);
MIXERLINECONTROLS mxlc; mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = pmxl->dwLineID; mxlc.dwControlType = dwType; mxlc.cControls = pmxl->cControls; mxlc.cbmxctrl = sizeof(*pamxctrl); mxlc.pamxctrl = pamxctrl;
if (MMSYSERR_NOERROR != mixerGetControlDetails((HMIXEROBJ)m_uMxId, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE)) { return FALSE; } memcpy(pmxc, pamxctrl, sizeof(*pamxctrl)); LocalFree(pamxctrl); return TRUE; } |
|
3、下麵演示一下如何設置Volume設備的靜音
這裏提供了兩個函數,GetMute用來獲取係統設置中是否對某個音頻線路進行了靜音操作,SetMute是用來對係統的某個音頻線路進行靜音操作。
BOOL CMixer::SetMute(DWORD dwSrcType, BOOL bValue) { MIXERLINE mxl; if (! GetLineInfo(&mxl, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, dwSrcType)) return FALSE;
MIXERCONTROL mxc; if (! GetLineControl(&mxc, &mxl, MIXERCONTROL_CONTROLTYPE_MUTE)) return FALSE;
MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_BOOLEAN mxcd_f;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = mxc.dwControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(mxcd_f); mxcd.paDetails = &mxcd_f; mxcd_f.fValue = bValue;
if (! SetControlDetails(&mxcd, MIXER_OBJECTF_MIXER)) return FALSE;
return TRUE; }
BOOL CMixer::GetMute(DWORD dwSrcType, BOOL* PBValue) { MIXERLINE mxl; if (! GetLineInfo(&mxl, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, dwSrcType)) return FALSE;
MIXERCONTROL mxc; if (! GetLineControl(&mxc, &mxl, MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT )) return FALSE;
MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_BOOLEAN mxcd_f;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = mxc.dwControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(mxcd_f); mxcd.paDetails = &mxcd_f;
if (! GetControlDetails(&mxcd, MIXER_GETCONTROLDETAILSF_VALUE)) return FALSE;
*pbValue = mxcd_f.fValue;
return TRUE; } |
|
如果用這兩個函數呢,下麵我演示了如何設置和獲取Volume音頻線路的靜音操作。
BOOL bValue = TRUE; SetMute(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, bValue); GetMute(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, &bValue); |
|
4、下麵看看當係統的設置改變時,mixer是如何通知我們的吧。
還記得我前麵講過的,當我們調用mixeropen時可以傳遞一個窗口的句柄作為回調窗口,當係統的設置改變,比如音量改變,某個音頻線路被靜音時,mixer都會給我們的回調窗口發送消息的。
一般隻有兩個消息,如下
afx_msg void OnMLChange(WPARAM wParam, LPARAM lParam); afx_msg void OnMCChange(WPARAM wParam, LPARAM lParam);
ON_MESSAGE(MM_MIXM_LINE_CHANGE, OnMLChange) ON_MESSAGE(MM_MIXM_CONTROL_CHANGE, OnMCChange) |
|
其中MM_MIXM_CONTROL_CHANGE 消息中,發送消息的兩個參數代表的意思如下
wParam = (WPARAM) hMixer lParam = (LPARAM) dwControlID |
|
在MM_MIXM_LINE_CHANGE 消息中,發送消息的參數代表的意思如下
wParam = (WPARAM) hMixer lParam = (LPARAM) dwLineID |
|
在我們的應用程序中,我們可以在這兩個消息處理函數中調整我們的設置,以對應於係統的改變,比如你的代碼可以這樣寫:
void CMixerControlDlg::OnMCChange(WPARAM wParam, LPARAM lParam) { DWORD dwLValue; DWORD dwRValue; GetVolume(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, &dwLValue,&dwRValue); //GetVolume函數的定義見下麵,然後根據返回的值調整滑動條的位置 m_SliderVolR.SetPos(MAX_VOL_VALUE - dwLValue); m_SliderVolL.SetPos(MAX_VOL_VALUE - dwRValue); //你也可以在這裏調用GetMute查看Volume是否被靜音, } |
|
如此你的程序就可以自動的響應係統設置的改變了。
關於mixer API的應用開發就介紹到這裏,記著最後要關閉mixer如此:mixerClose(m_hmx);
最後更新:2017-04-02 06:51:37