Compartilhar via


Controles de volume do ponto de extremidade

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 usará o controle de volume de hardware para o dispositivo de ponto de extremidade se o hardware de áudio implementar esse controle. Caso contrário, a 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 volume de hardware e controles mudos, alterações feitas no volume de software e controles mudos 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 de software.

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

Se um aplicativo exibir um controle de volume que usa a interfaceIAudioEndpointVolumepara 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 de extremidade exibido pelo programa de controle de volume do sistema, Sndvol. Conforme explicado anteriormente, o controle de volume do ponto de extremidade aparece no lado esquerdo da janela Sndvol, na caixa de grupo rotulada Device. Se o usuário alterar o volume de ponto de extremidade de um dispositivo por meio do controle de volume no Sndvol, o controle de volume do ponto de extremidade 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 por meio do controle de volume do ponto de extremidade na janela do aplicativo, o controle de volume correspondente no Sndvol deverá se mover em uníssono com o controle de volume do aplicativo.

Para garantir que o controle de volume do ponto de extremidade em uma janela de aplicativo espelha o controle de volume do ponto de extremidade no Sndvol, o aplicativo deve implementar um interface de IAudioEndpointVolumeCallback e registrar essa interface para receber notificações. Depois disso, sempre 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, sempre 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 o controle de volume do ponto de extremidade 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 da interfaceIAudioEndpointVolumeCallback:

// 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 de extremidade:

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 método IAudioEndpointVolume::RegisterControlChangeNotify para registrar sua interfaceIAudioEndpointVolumeCallbackpara receber notificações. Quando o aplicativo não requer mais notificações, ele chama o método IAudioEndpointVolume::UnregisterControlChangeNotify para excluir o registro.

O exemplo de código a seguir é um aplicativo do Windows que chama os métodos RegisterControlChangeNotify e UnregisterControlChangeNotify 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çãoWinMain chama a função CoCreateInstance para criar uma instância da interface IMMDeviceEnumerator, e chama o método IMMDeviceEnumerator::GetDefaultAudioEndpoint para obter a interface IMMDevice do dispositivo de renderização padrão. o WinMain chama o método IMMDevice::Activate para obter a interface deIAudioEndpointVolume dodo dispositivo e chama RegisterControlChangeNotify para registrar o aplicativo para receber notificações de alterações de volume de ponto de extremidade. 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 de 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 mute exibida 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, manipula as alterações feitas pelo usuário 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 a 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 atualizar as configurações de volume e mudo por meio dos controles na janela Sndvol, o método OnNotify na classe CAudioEndpointVolumeCallback atualizará os controles na caixa de diálogo para exibir as novas configurações.

Se o usuário alterar o volume por meio dos controles na caixa de diálogo, o método OnNotify na classe CAudioEndpointVolumeCallback não enviará mensagens para atualizar os controles na caixa de diálogo. Fazer isso seria redundante. OnNotify atualizará 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 chamadas de método SetMute na função DlgProc é um ponteiro para um GUID de contexto de evento que qualquer um dos métodos 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. Na funçãoWinMain, 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 combinam para formar um aplicativo simples para controlar e monitorar o volume de ponto de extremidade do dispositivo de renderização padrão. Um aplicativo mais útil também pode notificar o usuário quando o status do dispositivo for alterado. Por exemplo, o dispositivo pode estar desabilitado, desconectado ou removido. Para obter mais informações sobre como monitorar esses tipos de eventos, consulte de Eventos do Dispositivo.

controles de volume