トレーニング
エンドポイント ボリューム コントロール
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 メソッドを呼び出して登録を削除します。
次のコード例は、RegisterControlChangeNotify メソッドと UnregisterControlChangeNotify メソッドを呼び出して、前のコード例の CAudioEndpointVolumeCallback クラスを登録および登録解除する Windows アプリケーションです。
// 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 メソッド呼び出しの 2 番目のパラメーターは、いずれかのメソッドが OnNotify に渡すイベント コンテキスト GUID へのポインターです。 OnNotify は、イベント コンテキスト GUID の値を確認し、ダイアログ ボックスがボリューム変更のソースであるかどうかを判断します。 イベント コンテキスト GUID について詳しくは、「IAudioEndpointVolumeCallback::OnNotify」をご覧ください。
ユーザーがダイアログ ボックスを終了すると、前のコード例の UnregisterControlChangeNotify 呼び出しは、プログラムが終了する前に CAudioEndpointVolumeCallback クラスの登録を削除します。
前のコード例を簡単に変更して、既定のキャプチャ デバイスの音量コントロールとミュート コントロールを表示できます。 WinMain 関数で、IMMDeviceEnumerator::GetDefaultAudioEndpoint メソッドへの呼び出しの最初のパラメーターの値を eRender から eCapture に変更します。
次のコード例は、前のコード例に表示されるボリューム コントロールとミュート コントロールを定義するリソース スクリプトです。
// 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
上のコード例は、既定のレンダリング デバイスのエンドポイント ボリュームを制御および監視するための単純なアプリケーションを形成するために組み合わせています。 さらに便利なアプリケーションでは、デバイスの状態が変更されたときにユーザーに通知することもあります。 たとえば、デバイスが無効になっている、接続されていない、または取り外されている可能性があります。 これらの種類のイベントの監視について詳しくは、「デバイス イベント」をご覧ください。