Partilhar via


Controles de volume de ponto final

As interfaces ISimpleAudioVolume, IChannelAudioVolumee IAudioStreamVolume permitem que os clientes controlem os níveis de volume de sessões de áudio, que são coleções de fluxos de áudio de modo compartilhado. Essas interfaces não funcionam com fluxos de áudio de modo exclusivo.

Os aplicativos que gerenciam fluxos de modo exclusivo podem controlar os níveis de volume desses fluxos por meio da interfaceIAudioEndpointVolume. Essa interface controla o nível de volume do dispositivo de ponto de extremidade de áudio . Ele usa o controle de volume de hardware para o dispositivo de ponto de extremidade se o hardware de áudio implementar tal controle. Caso contrário, o interface IAudioEndpointVolume implementa o controle de volume no software.

Se um dispositivo tiver um controle de volume de hardware, as alterações feitas no controle por meio da interfaceIAudioEndpointVolumeafetarão o nível de volume no modo compartilhado e no modo exclusivo. Se um dispositivo não tiver controles de volume de hardware e mudo, as alterações feitas no volume do software e nos controles de mudo por meio dessa interface afetarão o nível de volume no modo compartilhado, mas não no modo exclusivo. No modo exclusivo, o aplicativo e o hardware de áudio trocam dados de áudio diretamente, ignorando os controles do software.

Como regra geral, os aplicativos devem evitar usar a interface IAudioEndpointVolume para controlar os níveis de volume de fluxos de modo compartilhado. Em vez disso, os aplicativos devem usar o ISimpleAudioVolume, IChannelAudioVolumeou interface IAudioStreamVolume para essa finalidade.

Se um aplicativo exibir um controle de volume que usa a interface deIAudioEndpointVolumepara controlar o nível de volume de um dispositivo de ponto de extremidade de áudio, esse controle de volume deverá espelhar o controle de volume do ponto final exibido pelo programa de controle de volume do sistema, Sndvol. Como explicado anteriormente, o controle de volume do ponto de extremidade aparece no lado esquerdo da janela do Sndvol, na caixa de grupo rotulada Device. Se o usuário alterar o volume do ponto final de um dispositivo através do controle de volume no Sndvol, o controle de volume do ponto final correspondente no aplicativo deverá ser movido em uníssono com o controle no Sndvol. Da mesma forma, se o usuário alterar o nível de volume através do controle de volume do ponto final na janela do aplicativo, o controle de volume correspondente no Sndvol deverá ser movido em uníssono com o controle de volume do aplicativo.

Para garantir que o controle de volume de ponto de extremidade em uma janela de aplicativo espelhe o controle de volume de ponto de extremidade no Sndvol, o aplicativo deve implementar uma interface deIAudioEndpointVolumeCallback dee registrar essa interface para receber notificações. Depois disso, cada vez que o usuário altera o nível de volume do ponto de extremidade no Sndvol, o aplicativo recebe uma chamada de notificação por meio de seu método IAudioEndpointVolumeCallback::OnNotify. Durante essa chamada, o método OnNotify pode atualizar o controle de volume do ponto de extremidade na janela do aplicativo para corresponder à configuração de controle mostrada no Sndvol. Da mesma forma, cada vez que o usuário altera o nível de volume do ponto de extremidade por meio do controle de volume na janela do aplicativo, o Sndvol recebe uma notificação e atualiza imediatamente seu controle de volume do ponto final para exibir o novo nível de volume.

O exemplo de código a seguir é um arquivo de cabeçalho que mostra uma possível implementação do IAudioEndpointVolumeCallback interface:

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

A classe CAudioEndpointVolumeCallback no exemplo de código anterior é uma implementação da interfaceIAudioEndpointVolumeCallback. Como IAudioEndpointVolumeCallback herda de IUnknown, a definição de classe contém implementações dos métodos IUnknownAddRef, Releasee QueryInterface. O método OnNotify na definição de classe é chamado sempre que um dos seguintes métodos altera o nível de volume do ponto final:

A implementação do método OnNotify no exemplo de código anterior envia mensagens para o controle de volume na janela do aplicativo para atualizar o nível de volume exibido.

Um aplicativo chama o IAudioEndpointVolume::RegisterControlChangeNotify método para registrar seu IAudioEndpointVolumeCallback interface para receber notificações. Quando o aplicativo não requer mais notificações, ele chama o IAudioEndpointVolume::UnregisterControlChangeNotify método para excluir o registro.

O exemplo de código a seguir é um aplicativo do Windows que chama o RegisterControlChangeNotify e UnregisterControlChangeNotify métodos para registrar e cancelar o registro da classe CAudioEndpointVolumeCallback no exemplo de código anterior:

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

No exemplo de código anterior, a função WinMain chama a função CoCreateInstance para criar uma instância da interfaceIMMDeviceEnumerator e chama o método IMMDeviceEnumerator::GetDefaultAudioEndpoint para obter a interface IMMDevice do dispositivo de renderização padrão. WinMain chama o método IMMDevice::Activate para obter a interfaceIAudioEndpointVolume do dispositivoe chama RegisterControlChangeNotify para registrar o aplicativo para receber notificações de alterações no volume do ponto final. Em seguida, WinMain abre uma caixa de diálogo para exibir um controle de volume de ponto de extremidade para o dispositivo. A caixa de diálogo também exibe uma caixa de seleção que indica se o dispositivo está mudo. A caixa de seleção controle de volume do ponto de extremidade e mudo na caixa de diálogo espelha as configurações do controle de volume do ponto de extremidade e a caixa de seleção mudo exibidas pelo Sndvol. Para obter mais informações sobre WinMain e CoCreateInstance, consulte a documentação do SDK do Windows. Para obter mais informações sobre IMMDeviceEnumerator e IMMDevice, consulte enumerando dispositivos de áudio.

O procedimento da caixa de diálogo, DlgProc, no exemplo de código anterior, lida com as alterações que o usuário faz nas configurações de volume e mudo por meio dos controles na caixa de diálogo. Quando DlgProc chama SetMasterVolumeLevelScalar ou SetMute, o Sndvol recebe notificação da alteração e atualiza o controle correspondente em sua janela para refletir a nova configuração de volume ou mudo. Se, em vez de usar a caixa de diálogo, o usuário atualiza as configurações de volume e mudo por meio dos controles na janela Sndvol, o método OnNotify na classe CAudioEndpointVolumeCallback atualiza os controles na caixa de diálogo para exibir as novas configurações.

Se o usuário alterar o volume através dos controles na caixa de diálogo, o método OnNotify na classe CAudioEndpointVolumeCallback não envia mensagens para atualizar os controles na caixa de diálogo. Fazê-lo seria redundante. OnNotify atualiza os controles na caixa de diálogo somente se a alteração de volume tiver se originado no Sndvol ou em algum outro aplicativo. O segundo parâmetro no SetMasterVolumeLevelScalar e SetMute método chama na função DlgProc é um ponteiro para um GUID de contexto de evento que qualquer método passa para OnNotify. OnNotify verifica o valor do GUID de contexto de evento para determinar se a caixa de diálogo é a origem da alteração de volume. Para obter mais informações sobre GUIDs de contexto de evento, consulte IAudioEndpointVolumeCallback::OnNotify.

Quando o usuário sai da caixa de diálogo, a chamada UnregisterControlChangeNotify no exemplo de código anterior exclui o registro da classe CAudioEndpointVolumeCallback antes que o programa seja encerrado.

Você pode modificar facilmente o exemplo de código anterior para exibir controles de volume e mudo para o dispositivo de captura padrão. No função WinMain, altere o valor do primeiro parâmetro na chamada para o método IMMDeviceEnumerator::GetDefaultAudioEndpoint de eRender para eCapture.

O exemplo de código a seguir é o script de recurso que define os controles de volume e mudo que aparecem no exemplo de código anterior:

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

O exemplo de código a seguir é o arquivo de cabeçalho de recurso que define os identificadores de controle que aparecem nos exemplos de código anteriores:

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

Os exemplos de código anteriores se combinam para formar um aplicativo simples para controlar e monitorar o volume do ponto de extremidade do dispositivo de renderização padrão. Um aplicativo mais útil pode notificar adicionalmente o usuário quando o status do dispositivo muda. Por exemplo, o dispositivo pode ser desativado, desligado ou removido. Para obter mais informações sobre como monitorar esses tipos de eventos, consulte Device Events.

de controles de volume