Kontrol Volume Titik Akhir

Antarmuka ISimpleAudioVolume, IChannelAudioVolume, dan IAudioStreamVolume memungkinkan klien mengontrol tingkat volume sesi audio, yang merupakan koleksi aliran audio mode bersama. Antarmuka ini tidak berfungsi dengan aliran audio mode eksklusif.

Aplikasi yang mengelola aliran mode eksklusif dapat mengontrol tingkat volume aliran tersebut melalui antarmuka IAudioEndpointVolume. Antarmuka ini mengontrol tingkat volume perangkat titik akhir audio. Ini menggunakan kontrol volume perangkat keras untuk perangkat titik akhir jika perangkat keras audio menerapkan kontrol seperti itu. Jika tidak, antarmuka IAudioEndpointVolume mengimplementasikan kontrol volume dalam perangkat lunak.

Jika perangkat memiliki kontrol volume perangkat keras, perubahan yang dilakukan pada kontrol melalui antarmuka IAudioEndpointVolume memengaruhi tingkat volume baik dalam mode bersama maupun dalam mode eksklusif. Jika perangkat tidak memiliki volume perangkat keras dan kontrol bisu, perubahan yang dilakukan pada volume perangkat lunak dan kontrol bisukan melalui antarmuka ini memengaruhi tingkat volume dalam mode bersama, tetapi tidak dalam mode eksklusif. Dalam mode eksklusif, aplikasi dan perangkat keras audio bertukar data audio secara langsung, melewati kontrol perangkat lunak.

Sebagai aturan umum, aplikasi harus menghindari penggunaan antarmuka IAudioEndpointVolume untuk mengontrol tingkat volume aliran mode bersama. Sebagai gantinya, aplikasi harus menggunakan antarmuka ISimpleAudioVolume, IChannelAudioVolume, atau IAudioStreamVolume untuk tujuan tersebut.

Jika aplikasi menampilkan kontrol volume yang menggunakan antarmuka IAudioEndpointVolume untuk mengontrol tingkat volume perangkat titik akhir audio, kontrol volume tersebut harus mencerminkan kontrol volume titik akhir yang ditampilkan oleh program kontrol volume sistem, Sndvol. Seperti yang dijelaskan sebelumnya, kontrol volume titik akhir muncul di sisi kiri jendela Sndvol, dalam kotak grup berlabel Perangkat. Jika pengguna mengubah volume titik akhir perangkat melalui kontrol volume di Sndvol, kontrol volume titik akhir yang sesuai dalam aplikasi harus bergerak sepihak dengan kontrol di Sndvol. Demikian pula, jika pengguna mengubah tingkat volume melalui kontrol volume titik akhir di jendela aplikasi, kontrol volume yang sesuai di Sndvol harus bergerak sepihak dengan kontrol volume aplikasi.

Untuk memastikan bahwa kontrol volume titik akhir di jendela aplikasi mencerminkan kontrol volume titik akhir di Sndvol, aplikasi harus menerapkan antarmuka IAudioEndpointVolumeCallback dan mendaftarkan antarmuka tersebut untuk menerima pemberitahuan. Setelah itu, setiap kali pengguna mengubah tingkat volume titik akhir di Sndvol, aplikasi menerima panggilan pemberitahuan melalui metode IAudioEndpointVolumeCallback::OnNotify. Selama panggilan ini, metode OnNotify dapat memperbarui kontrol volume titik akhir di jendela aplikasi agar sesuai dengan pengaturan kontrol yang ditampilkan di Sndvol. Demikian pula, setiap kali pengguna mengubah tingkat volume titik akhir melalui kontrol volume di jendela aplikasi, Sndvol menerima pemberitahuan dan segera memperbarui kontrol volume titik akhirnya untuk menampilkan tingkat volume baru.

Contoh kode berikut adalah file header yang menunjukkan kemungkinan implementasi antarmuka 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;
    }
};

Kelas CAudioEndpointVolumeCallback dalam contoh kode sebelumnya adalah implementasi antarmuka IAudioEndpointVolumeCallback. Karena IAudioEndpointVolumeCallback mewarisi dari IUnknown, definisi kelas berisi implementasi metode IUnknown AddRef, Release, dan QueryInterface. Metode OnNotify dalam definisi kelas dipanggil setiap kali salah satu metode berikut mengubah tingkat volume titik akhir:

Implementasi metode OnNotify dalam contoh kode sebelumnya mengirim pesan ke kontrol volume di jendela aplikasi untuk memperbarui tingkat volume yang ditampilkan.

Aplikasi memanggil metode IAudioEndpointVolume::RegisterControlChangeNotify untuk mendaftarkan antarmuka IAudioEndpointVolumeCallback untuk menerima pemberitahuan. Ketika aplikasi tidak lagi memerlukan pemberitahuan, aplikasi memanggil metode IAudioEndpointVolume::UnregisterControlChangeNotify untuk menghapus pendaftaran.

Contoh kode berikut adalah aplikasi Windows yang memanggil metode RegisterControlChangeNotify dan UnregisterControlChangeNotify untuk mendaftarkan dan membatalkan pendaftaran kelas CAudioEndpointVolumeCallback dalam contoh kode sebelumnya:

// 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;
}

Dalam contoh kode sebelumnya, fungsi WinMain memanggil fungsi CoCreateInstance untuk membuat instans antarmuka IMMDeviceEnumerator, dan memanggil metode IMMDeviceEnumerator::GetDefaultAudioEndpoint untuk mendapatkan antarmuka IMMDevice dari perangkat penyajian default. WinMain memanggil metode IMMDevice::Activate untuk mendapatkan antarmuka IAudioEndpointVolume perangkat, dan memanggil RegisterControlChangeNotify untuk mendaftarkan aplikasi untuk menerima pemberitahuan perubahan volume titik akhir. Selanjutnya, WinMain membuka kotak dialog untuk menampilkan kontrol volume titik akhir untuk perangkat. Kotak dialog juga menampilkan kotak centang yang menunjukkan apakah perangkat dibisukan. Kotak centang kontrol volume titik akhir dan matikan suara dalam kotak dialog mencerminkan pengaturan kontrol volume titik akhir dan kotak centang bisukan yang ditampilkan oleh Sndvol. Untuk informasi selengkapnya tentang WinMain dan CoCreateInstance, lihat dokumentasi Windows SDK. Untuk informasi selengkapnya tentang IMMDeviceEnumerator dan IMMDevice, lihat Menghitung Perangkat Audio.

Prosedur kotak dialog, DlgProc, dalam contoh kode sebelumnya, menangani perubahan yang dilakukan pengguna ke volume dan membisukan pengaturan melalui kontrol dalam kotak dialog. Ketika DlgProc memanggil SetMasterVolumeLevelScalar atau SetMute, Sndvol menerima pemberitahuan tentang perubahan dan memperbarui kontrol yang sesuai di jendelanya untuk mencerminkan volume baru atau pengaturan bisu. Jika, alih-alih menggunakan kotak dialog, pengguna memperbarui volume dan mematikan suara pengaturan melalui kontrol di jendela Sndvol, metode OnNotify di kelas CAudioEndpointVolumeCallback memperbarui kontrol dalam kotak dialog untuk menampilkan pengaturan baru.

Jika pengguna mengubah volume melalui kontrol dalam kotak dialog, metode OnNotify di kelas CAudioEndpointVolumeCallback tidak mengirim pesan untuk memperbarui kontrol dalam kotak dialog. Untuk melakukannya akan berlebihan. OnNotify memperbarui kontrol dalam kotak dialog hanya jika perubahan volume berasal dari Sndvol atau di beberapa aplikasi lain. Parameter kedua dalam panggilan metode SetMasterVolumeLevelScalar dan SetMute dalam fungsi DlgProc adalah penunjuk ke GUID konteks peristiwa yang diteruskan ke OnNotify. OnNotify memeriksa nilai GUID konteks peristiwa untuk menentukan apakah kotak dialog adalah sumber perubahan volume. Untuk informasi selengkapnya tentang GUID konteks peristiwa, lihat IAudioEndpointVolumeCallback::OnNotify.

Ketika pengguna keluar dari kotak dialog, panggilan UnregisterControlChangeNotify dalam contoh kode sebelumnya menghapus pendaftaran kelas CAudioEndpointVolumeCallback sebelum program berakhir.

Anda dapat dengan mudah memodifikasi contoh kode sebelumnya untuk menampilkan kontrol volume dan bisukan untuk perangkat pengambilan default. Dalam fungsi WinMain, ubah nilai parameter pertama dalam panggilan ke metode IMMDeviceEnumerator::GetDefaultAudioEndpoint dari eRender ke eCapture.

Contoh kode berikut adalah skrip sumber daya yang menentukan volume dan kontrol bisukan yang muncul dalam contoh kode sebelumnya:

// 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

Contoh kode berikut adalah file header sumber daya yang menentukan pengidentifikasi kontrol yang muncul dalam contoh kode sebelumnya:

// 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

Contoh kode sebelumnya digabungkan untuk membentuk aplikasi sederhana untuk mengontrol dan memantau volume titik akhir perangkat penyajian default. Aplikasi yang lebih berguna juga dapat memberi tahu pengguna saat status perangkat berubah. Misalnya, perangkat mungkin dinonaktifkan, dilepas, atau dihapus. Untuk informasi selengkapnya tentang memantau jenis peristiwa ini, lihat Peristiwa Perangkat.

Kontrol Volume