摘要:本文通過執行個體代碼示範了如何通過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;
//其他音頻線路可以依次類推在下面添加
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))
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 true;
bool cmixer::getvolume(dword dwsrctype, dword* pdwlvalue, dword* pdwrvalue, bool bmono)
if (! getcontroldetails(&mxcd, mixer_getcontroldetailsf_value))
return false;
*pdwlvalue = mxcd_u1.dwvalue;
*pdwlvalue = mxcd_u[0].dwvalue;
*pdwrvalue = mxcd_u[1].dwvalue;
bool getlineinfo(lpmixerline pmxl, dword dwdsttype, dword dwsrctype)
mixercaps mxcaps;
if (! getdevcaps(&mxcaps))
uint u=0;
do
pmxl->cbstruct = sizeof(*pmxl);
pmxl->dwdestination = u;
u++;
if (mmsyserr_noerror != mixergetlinecontrols((hmixerobj)m_umxid, pmxl, mixer_getlineinfof_destination))
{
}
} while ((u < mxcaps.cdestinations) && (pmxl->dwcomponenttype != dwdsttype));
if (u > mxcaps.cdestinations)
if (dwdsttype == dwsrctype)
return true;
pmxl->dwdestination = u;
uint cconnections = (uint)pmxl->cconnections;
uint v=0;
u--;
pmxl->dwsource = v;
v++;
if (mmsyserr_noerror != mixergetlinecontrols((hmixerobj)m_umxid, pmxl, mixer_getlineinfof_source))
} while ((v < cconnections) && (pmxl->dwcomponenttype != dwsrctype));
if((v > cconnections) || (pmxl->dwcomponenttype !=dwsrctype))
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))
memcpy(pmxc, pamxctrl, sizeof(*pamxctrl));
localfree(pamxctrl);
3、下面示範一下如何設定volume裝置的靜音
這裡提供了兩個函數,getmute用來擷取系統設定中是否對某個音頻線路進行了靜音操作,setmute是用來對系統的某個音頻線路進行靜音操作。
bool cmixer::setmute(dword dwsrctype, bool bvalue)
if (! getlinecontrol(&mxc, &mxl, mixercontrol_controltype_mute))
mixercontroldetails_boolean mxcd_f;
mxcd.cchannels = 1;
mxcd.cbdetails = sizeof(mxcd_f);
mxcd.padetails = &mxcd_f;
mxcd_f.fvalue = bvalue;
bool cmixer::getmute(dword dwsrctype, bool* pbvalue)
if (! getlinecontrol(&mxc, &mxl, mixercontrol_controltype_multipleselect ))
if (! getcontroldetails(&mxcd, mixer_getcontroldetailsf_value))
*pbvalue = mxcd_f.fvalue;
如果用這兩個函數呢,下面我示範了如何設定和擷取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 消息中,發送消息的參數代表的意思如下
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);