Поделиться через


Элементы управления томами конечной точки

Интерфейсы ISimpleAudioVolume, IChannelAudioVolume и IAudioStreamVolume позволяют клиентам управлять уровнями громкости звуковых сеансов, которые являются коллекциями аудиопотоков общего режима. Эти интерфейсы не работают с аудиопотоками в монопольном режиме.

Приложения, управляющие потоками в монопольном режиме, могут управлять уровнями томов этих потоков через интерфейс IAudioEndpointVolume. Этот интерфейс управляет уровнем громкости устройства звуковой конечной точки. Он использует аппаратный элемент управления громкости для устройства конечной точки, если звуковое оборудование реализует такой элемент управления. В противном случае интерфейс IAudioEndpointVolume реализует управление томами в программном обеспечении.

Если устройство имеет аппаратный элемент управления томом, изменения, внесенные в элемент управления через интерфейс IAudioEndpointVolume , влияют на уровень тома как в общем режиме, так и в монопольном режиме. Если на устройстве отсутствуют аппаратные тома и элементы управления отключением, изменения, внесенные в том программного обеспечения и отключение элементов управления с помощью этого интерфейса, влияют на уровень тома в общем режиме, но не в монопольном режиме. В монопольном режиме приложение и звуковое оборудование обмен данными аудио напрямую, обходя элементы управления программным обеспечением.

Как правило, приложения должны избегать использования интерфейса IAudioEndpointVolume для управления уровнями томов потоков общего режима. Вместо этого приложения должны использовать интерфейс ISimpleAudioVolume, IChannelAudioVolume или IAudioStreamVolume для этой цели.

Если приложение отображает элемент управления громкости, использующий интерфейс IAudioEndpointVolume для управления уровнем громкости устройства аудио конечной точки, этот элемент управления громкости должен зеркало элемент управления томом конечной точки, отображаемый программой управления томами системы, Sndvol. Как описано ранее, элемент управления томом конечной точки отображается в левой части окна Sndvol в поле группы с меткой Device. Если пользователь изменяет том конечной точки устройства с помощью элемента управления томом в 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 в определении класса вызывается каждый раз, когда один из следующих методов изменяет уровень тома конечной точки:

Реализация метода 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 см. в документации по пакету SDK для Windows. Дополнительные сведения об IMMDeviceEnumerator и IMMDevice см. в разделе "Перечисление аудиоустройств".

Процедура диалогового окна DlgProc в предыдущем примере кода обрабатывает изменения, внесенные пользователем в том, и отключает параметры с помощью элементов управления в диалоговом окне. Когда DlgProc вызывает SetMasterVolumeLevelScalar или SetMute, Sndvol получает уведомление об изменении и обновляет соответствующий элемент управления в окне, чтобы отразить новый том или отключить параметр. Если вместо использования диалогового окна пользователь обновляет том и отключает параметры с помощью элементов управления в окне Sndvol, метод OnNotify в классе CAudioEndpointVolumeCallback обновляет элементы управления в диалоговом окне, чтобы отобразить новые параметры.

Если пользователь изменяет том с помощью элементов управления в диалоговом окне, метод OnNotify в классе CAudioEndpointVolumeCallback не отправляет сообщения для обновления элементов управления в диалоговом окне. Для этого будет избыточным. OnNotify обновляет элементы управления в диалоговом окне только в том случае, если изменение тома произошло в Sndvol или в другом приложении. Второй параметр в вызовах метода SetMasterVolumeLevelScalar и SetMute в функции DlgProc — это указатель на идентификатор GUID контекста событий, который любой метод передает в OnNotify. 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

Приведенные выше примеры кода объединяются для создания простого приложения для управления и мониторинга тома конечной точки устройства отрисовки по умолчанию. Более полезное приложение может дополнительно уведомить пользователя о том, что состояние устройства изменяется. Например, устройство может быть отключено, отключено или удалено. Дополнительные сведения о мониторинге этих типов событий см. в разделе "События устройства".

Элементы управления томами