端點磁碟區控件
ISimpleAudioVolume、IChannelAudioVolume 和 IAudioStreamVolume 介面可讓用戶端控制音訊會話的音量層級,這是共用模式音訊數據流的集合。 這些介面不適用於獨佔模式音訊數據流。
管理獨佔模式數據流的應用程式可以透過 IAudioEndpointVolume 介面控制這些數據流的磁碟區層級。 此介面會控制音訊端點裝置的音量層級。 如果音訊硬體實作這類控件,它會使用端點裝置的硬體音量控制。 否則, IAudioEndpointVolume 介面會在軟體中實作磁碟區控制。
如果裝置具有硬體磁碟區控制,透過 IAudioEndpointVolume 介面對控件所做的變更會影響共用模式和獨佔模式中的磁碟區層級。 如果裝置缺少硬體磁碟區和靜音控件,則透過此介面對軟體磁碟區所做的變更和靜音控制會影響共用模式中的磁碟區層級,但不在獨佔模式中。 在獨佔模式中,應用程式和音訊硬體會直接交換音訊數據,略過軟體控件。
一般情況下,應用程式應該避免使用 IAudioEndpointVolume 介面來控制共用模式數據流的磁碟區層級。 相反地,應用程式應該針對該目的使用 ISimpleAudioVolume、IChannelAudioVolume 或 IAudioStreamVolume 介面。
如果應用程式顯示使用 IAudioEndpointVolume 介面來控制音訊端點裝置音量層級的音量控件,該音量控制應該鏡像系統音量控制程式 Sndvol 所顯示的端點音量控制。 如先前所述,端點磁碟區控件會出現在 [Sndvol] 視窗左側的 [裝置] 群組方塊中。 如果使用者透過 Sndvol 中的磁碟區控制變更裝置的端點磁碟區,應用程式中的對應端點磁碟區控件應該與 Sndvol 中的控制件一起移動。 同樣地,如果使用者透過應用程式視窗中的端點磁碟區控件變更磁碟區層級,則 Sndvol 中的對應磁碟區控件應該與應用程式的音量控制一致。
為了確保應用程式視窗中的端點磁碟區控制會鏡像 Sndvol 中的端點磁碟區控制,應用程式應該實 作 IAudioEndpointVolumeCallback 介面,並註冊該介面以接收通知。 之後,每次使用者變更 Sndvol 中的端點磁碟區層級時,應用程式都會透過其 IAudioEndpointVolumeCallback::OnNotify 方法收到通知呼叫。 在此呼叫期間, OnNotify 方法可以更新應用程式視窗中的端點磁碟區控件,以符合 Sndvol 中顯示的控制項設定。 同樣地,當使用者在應用程式視窗中透過磁碟區控制變更端點磁碟區層級時,Sndvol 會收到通知,並立即更新其端點磁碟區控件以顯示新的磁碟區層級。
下列程式代碼範例是頭檔,其中顯示IAudioEndpointVolumeCallback 介面的可能實作:
// Epvolume.h -- Implementation of IAudioEndpointVolumeCallback interface
#include <windows.h>
#include <commctrl.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include "resource.h"
// Dialog handle from dialog box procedure
extern HWND g_hDlg;
// Client's proprietary event-context GUID
extern GUID g_guidMyContext;
// Maximum volume level on trackbar
#define MAX_VOL 100
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
//-----------------------------------------------------------
// Client implementation of IAudioEndpointVolumeCallback
// interface. When a method in the IAudioEndpointVolume
// interface changes the volume level or muting state of the
// endpoint device, the change initiates a call to the
// client's IAudioEndpointVolumeCallback::OnNotify method.
//-----------------------------------------------------------
class CAudioEndpointVolumeCallback : public IAudioEndpointVolumeCallback
{
LONG _cRef;
public:
CAudioEndpointVolumeCallback() :
_cRef(1)
{
}
~CAudioEndpointVolumeCallback()
{
}
// IUnknown methods -- AddRef, Release, and QueryInterface
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IAudioEndpointVolumeCallback) == riid)
{
AddRef();
*ppvInterface = (IAudioEndpointVolumeCallback*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// Callback method for endpoint-volume-change notifications.
HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify)
{
if (pNotify == NULL)
{
return E_INVALIDARG;
}
if (g_hDlg != NULL && pNotify->guidEventContext != g_guidMyContext)
{
PostMessage(GetDlgItem(g_hDlg, IDC_CHECK_MUTE), BM_SETCHECK,
(pNotify->bMuted) ? BST_CHECKED : BST_UNCHECKED, 0);
PostMessage(GetDlgItem(g_hDlg, IDC_SLIDER_VOLUME),
TBM_SETPOS, TRUE,
LPARAM((UINT32)(MAX_VOL*pNotify->fMasterVolume + 0.5)));
}
return S_OK;
}
};
上述程式代碼範例中的 CAudioEndpointVolumeCallback 類別是 IAudioEndpointVolumeCallback 介面的實作。 由於 IAudioEndpointVolumeCallback 繼承自 IUnknown,因此類別定義包含 IUnknown 方法 AddRef、Release 和 QueryInterface 的實作。 每次下列其中一種方法變更端點磁碟區層級時,都會呼叫類別定義中的 OnNotify 方法:
- IAudioEndpointVolume::SetChannelVolumeLevel
- IAudioEndpointVolume::SetChannelVolumeLevelScalar
- IAudioEndpointVolume::SetMasterVolumeLevel
- IAudioEndpointVolume::SetMasterVolumeLevelScalar
- IAudioEndpointVolume::SetMute
- IAudioEndpointVolume::VolumeStepDown
- IAudioEndpointVolume::VolumeStepUp
上述程式代碼範例中的 OnNotify 方法實作會將訊息傳送至應用程式視窗中的磁碟區控件,以更新顯示的磁碟區層級。
應用程式會呼叫 IAudioEndpointVolume::RegisterControlChangeNotify 方法來註冊其 IAudioEndpointVolumeCallback 介面以接收通知。 當應用程式不再需要通知時,它會呼叫 IAudioEndpointVolume::UnregisterControlChangeNotify 方法來刪除註冊。
下列程式代碼範例是 Windows 應用程式,它會呼叫 RegisterControlChangeNotify 和 UnregisterControlChangeNotify 方法,以在上述程式代碼範例中註冊和取消註冊 CAudioEndpointVolumeCallback 類別:
// Epvolume.cpp -- WinMain and dialog box functions
#include "stdafx.h"
#include "Epvolume.h"
HWND g_hDlg = NULL;
GUID g_guidMyContext = GUID_NULL;
static IAudioEndpointVolume *g_pEndptVol = NULL;
static BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
#define EXIT_ON_ERROR(hr) \
if (FAILED(hr)) { goto Exit; }
#define ERROR_CANCEL(hr) \
if (FAILED(hr)) { \
MessageBox(hDlg, TEXT("The program will exit."), \
TEXT("Fatal error"), MB_OK); \
EndDialog(hDlg, TRUE); return TRUE; }
//-----------------------------------------------------------
// WinMain -- Registers an IAudioEndpointVolumeCallback
// interface to monitor endpoint volume level, and opens
// a dialog box that displays a volume control that will
// mirror the endpoint volume control in SndVol.
//-----------------------------------------------------------
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
CAudioEndpointVolumeCallback EPVolEvents;
if (hPrevInstance)
{
return 0;
}
CoInitialize(NULL);
hr = CoCreateGuid(&g_guidMyContext);
EXIT_ON_ERROR(hr)
// Get enumerator for audio endpoint devices.
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
// Get default audio-rendering device.
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(__uuidof(IAudioEndpointVolume),
CLSCTX_ALL, NULL, (void**)&g_pEndptVol);
EXIT_ON_ERROR(hr)
hr = g_pEndptVol->RegisterControlChangeNotify(
(IAudioEndpointVolumeCallback*)&EPVolEvents);
EXIT_ON_ERROR(hr)
InitCommonControls();
DialogBox(hInstance, L"VOLUMECONTROL", NULL, (DLGPROC)DlgProc);
Exit:
if (FAILED(hr))
{
MessageBox(NULL, TEXT("This program requires Windows Vista."),
TEXT("Error termination"), MB_OK);
}
if (g_pEndptVol != NULL)
{
g_pEndptVol->UnregisterControlChangeNotify(
(IAudioEndpointVolumeCallback*)&EPVolEvents);
}
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(g_pEndptVol)
CoUninitialize();
return 0;
}
//-----------------------------------------------------------
// DlgProc -- Dialog box procedure
//-----------------------------------------------------------
BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;
BOOL bMute;
float fVolume;
int nVolume;
int nChecked;
switch (message)
{
case WM_INITDIALOG:
g_hDlg = hDlg;
SendDlgItemMessage(hDlg, IDC_SLIDER_VOLUME, TBM_SETRANGEMIN, FALSE, 0);
SendDlgItemMessage(hDlg, IDC_SLIDER_VOLUME, TBM_SETRANGEMAX, FALSE, MAX_VOL);
hr = g_pEndptVol->GetMute(&bMute);
ERROR_CANCEL(hr)
SendDlgItemMessage(hDlg, IDC_CHECK_MUTE, BM_SETCHECK,
bMute ? BST_CHECKED : BST_UNCHECKED, 0);
hr = g_pEndptVol->GetMasterVolumeLevelScalar(&fVolume);
ERROR_CANCEL(hr)
nVolume = (int)(MAX_VOL*fVolume + 0.5);
SendDlgItemMessage(hDlg, IDC_SLIDER_VOLUME, TBM_SETPOS, TRUE, nVolume);
return TRUE;
case WM_HSCROLL:
switch (LOWORD(wParam))
{
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
case SB_LINERIGHT:
case SB_LINELEFT:
case SB_PAGERIGHT:
case SB_PAGELEFT:
case SB_RIGHT:
case SB_LEFT:
// The user moved the volume slider in the dialog box.
SendDlgItemMessage(hDlg, IDC_CHECK_MUTE, BM_SETCHECK, BST_UNCHECKED, 0);
hr = g_pEndptVol->SetMute(FALSE, &g_guidMyContext);
ERROR_CANCEL(hr)
nVolume = SendDlgItemMessage(hDlg, IDC_SLIDER_VOLUME, TBM_GETPOS, 0, 0);
fVolume = (float)nVolume/MAX_VOL;
hr = g_pEndptVol->SetMasterVolumeLevelScalar(fVolume, &g_guidMyContext);
ERROR_CANCEL(hr)
return TRUE;
}
break;
case WM_COMMAND:
switch ((int)LOWORD(wParam))
{
case IDC_CHECK_MUTE:
// The user selected the Mute check box in the dialog box.
nChecked = SendDlgItemMessage(hDlg, IDC_CHECK_MUTE, BM_GETCHECK, 0, 0);
bMute = (BST_CHECKED == nChecked);
hr = g_pEndptVol->SetMute(bMute, &g_guidMyContext);
ERROR_CANCEL(hr)
return TRUE;
case IDCANCEL:
EndDialog(hDlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
}
在上述程式代碼範例中,WinMain 函式會呼叫 CoCreateInstance 函式來建立 IMMDeviceEnumerator 介面的實例,並呼叫 IMMDeviceEnumerator::GetDefaultAudioEndpoint 方法,以取得預設轉譯裝置的 IMMDevice 介面。 WinMain 會呼叫 IMMDevice::Activate 方法來取得裝置的 IAudioEndpointVolume 介面,並呼叫 RegisterControlChangeNotify 來註冊應用程式以接收端點磁碟區變更的通知。 接下來, WinMain 會開啟對話框以顯示裝置的端點磁碟區控制。 對話框也會顯示一個複選框,指出裝置是否為靜音。 對話框中的端點磁碟區控制和靜音複選框會鏡像 Sndvol 所顯示的端點磁碟區控制和靜音複選框的設定。 如需 WinMain 和 CoCreateInstance 的詳細資訊,請參閱 Windows SDK 檔。 如需 IMMDeviceEnumerator 和 IMMDevice 的詳細資訊,請參閱列舉音訊裝置。
上述程式代碼範例中的對話框程式 DlgProc 會透過對話框中的控件處理使用者對磁碟區所做的變更,並將設定設為靜音。 當 DlgProc 呼叫 SetMasterVolumeLevelScalar 或 SetMute 時,Sndvol 會收到變更的通知,並更新其視窗中對應的控件,以反映新的磁碟區或靜音設定。 如果使用者不是使用對話框,而是透過 Sndvol 視窗中的控件更新磁碟區及靜音設定, 則 CAudioEndpointVolumeCallback 類別中的 OnNotify 方法會更新對話框中的控件以顯示新的設定。
如果使用者透過對話框中的控件變更磁碟區, CAudioEndpointVolumeCallback 類別中的 OnNotify 方法不會傳送訊息來更新對話框中的控件。 這樣做將是多餘的。 OnNotify 只有在磁碟區變更源自 Sndvol 或某些其他應用程式中時,才會更新對話框中的控件。 DlgProc 函式中 SetMasterVolumeLevelScalar 和 SetMute 方法呼叫中的第二個參數是事件內容 GUID 的指標,其中任一種方法都會傳遞至 OnNotify。 OnNotify 會檢查事件內容 GUID 的值,以判斷對話方塊是否為磁碟區變更的來源。 如需事件內容 GUID 的詳細資訊,請參閱 IAudioEndpointVolumeCallback::OnNotify。
當用戶結束對話框時, 上述程式代碼範例中的 UnregisterControlChangeNotify 呼叫會刪除程序終止之前 CAudioEndpointVolumeCallback 類別的註冊。
您可以輕鬆地修改上述程式代碼範例,以顯示預設擷取裝置的音量和靜音控件。 在 WinMain 函式中,將呼叫中第一個參數的值從 eRender 變更為 eCapture 的 IMMDeviceEnumerator::GetDefaultAudioEndpoint 方法。
下列程式代碼範例是資源腳本,可定義出現在上述程式代碼範例中的磁碟區和靜音控件:
// Epvolume.rc -- Resource script
#include "resource.h"
#include "windows.h"
#include "commctrl.h"
//
// Dialog box
//
VOLUMECONTROL DIALOGEX 0, 0, 160, 60
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT
CAPTION "Audio Endpoint Volume"
FONT 8, "Arial Rounded MT Bold", 400, 0, 0x0
BEGIN
LTEXT "Min",IDC_STATIC_MINVOL,10,10,20,12
RTEXT "Max",IDC_STATIC_MAXVOL,130,10,20,12
CONTROL "",IDC_SLIDER_VOLUME,"msctls_trackbar32",
TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,10,20,140,12
CONTROL "Mute",IDC_CHECK_MUTE,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,20,40,70,12
END
下列程式代碼範例是資源頭檔,可定義上述程式代碼範例中顯示的控件識別碼:
// Resource.h -- Control identifiers (epvolume)
#define IDC_SLIDER_VOLUME 1001
#define IDC_CHECK_MUTE 1002
#define IDC_STATIC_MINVOL 1003
#define IDC_STATIC_MAXVOL 1004
上述程式代碼範例結合以形成簡單的應用程式,以控制及監視預設轉譯裝置的端點磁碟區。 當裝置的狀態變更時,更實用的應用程式可能會另外通知使用者。 例如,裝置可能會停用、取消移轉或移除。 如需監視這些事件類型的詳細資訊,請參閱 裝置事件。
相關主題