Contrôles de volume de point de terminaison

Les interfaces ISimpleAudioVolume, IChannelAudioVolume et IAudioStreamVolume permettent aux clients de contrôler les niveaux de volume des sessions audio, qui sont des collections de flux audio en mode partagé. Ces interfaces ne fonctionnent pas avec des flux audio en mode exclusif.

Les applications qui gèrent des flux en mode exclusif peuvent contrôler les niveaux de volume de ces flux via l’interface IAudioEndpointVolume . Cette interface contrôle le niveau de volume de l’appareil de point de terminaison audio. Il utilise le contrôle du volume matériel pour l’appareil de point de terminaison si le matériel audio implémente un tel contrôle. Sinon, l’interface IAudioEndpointVolume implémente le contrôle de volume dans les logiciels.

Si un appareil dispose d’un contrôle de volume matériel, les modifications apportées au contrôle via l’interface IAudioEndpointVolume affectent le niveau du volume à la fois en mode partagé et en mode exclusif. Si un appareil ne dispose pas de contrôles de volume matériel et de désactivation du son, les modifications apportées au volume logiciel et désactiver le son des contrôles via cette interface affectent le niveau de volume en mode partagé, mais pas en mode exclusif. En mode exclusif, l’application et le matériel audio échangent directement des données audio, en contournant les contrôles logiciels.

En règle générale, les applications doivent éviter d’utiliser l’interface IAudioEndpointVolume pour contrôler les niveaux de volume des flux en mode partagé. À cet effet, les applications doivent utiliser l’interface ISimpleAudioVolume, IChannelAudioVolume ou IAudioStreamVolume .

Si une application affiche un contrôle de volume qui utilise l’interface IAudioEndpointVolume pour contrôler le niveau de volume d’un appareil de point de terminaison audio, ce contrôle de volume doit miroir le contrôle de volume de point de terminaison affiché par le programme de contrôle de volume système, Sndvol. Comme expliqué précédemment, le contrôle de volume de point de terminaison apparaît sur le côté gauche de la fenêtre Sndvol, dans la zone de groupe intitulée Appareil. Si l’utilisateur modifie le volume de point de terminaison d’un appareil via le contrôle de volume dans Sndvol, le contrôle de volume de point de terminaison correspondant dans l’application doit se déplacer à l’unisson avec le contrôle dans Sndvol. De même, si l’utilisateur modifie le niveau de volume via le contrôle de volume de point de terminaison dans la fenêtre d’application, le contrôle de volume correspondant dans Sndvol doit se déplacer à l’unisson avec le contrôle de volume de l’application.

Pour s’assurer que le contrôle de volume de point de terminaison dans une fenêtre d’application reflète le contrôle de volume de point de terminaison dans Sndvol, l’application doit implémenter une interface IAudioEndpointVolumeCallback et inscrire cette interface pour recevoir des notifications. Par la suite, chaque fois que l’utilisateur modifie le niveau de volume du point de terminaison dans Sndvol, l’application reçoit un appel de notification via sa méthode IAudioEndpointVolumeCallback::OnNotify . Au cours de cet appel, la méthode OnNotify peut mettre à jour le contrôle de volume de point de terminaison dans la fenêtre d’application pour qu’il corresponde au paramètre de contrôle affiché dans Sndvol. De même, chaque fois que l’utilisateur modifie le niveau de volume du point de terminaison via le contrôle de volume dans la fenêtre d’application, Sndvol reçoit une notification et met immédiatement à jour son contrôle de volume de point de terminaison pour afficher le nouveau niveau de volume.

L’exemple de code suivant est un fichier d’en-tête qui montre une implémentation possible de l’interface 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;
    }
};

La classe CAudioEndpointVolumeCallback dans l’exemple de code précédent est une implémentation de l’interface IAudioEndpointVolumeCallback . Étant donné que IAudioEndpointVolumeCallback hérite de IUnknown, la définition de classe contient des implémentations des méthodes IUnknownAddRef, Release et QueryInterface. La méthode OnNotify dans la définition de classe est appelée chaque fois qu’une des méthodes suivantes modifie le niveau du volume du point de terminaison :

L’implémentation de la méthode OnNotify dans l’exemple de code précédent envoie des messages au contrôle de volume dans la fenêtre de l’application pour mettre à jour le niveau de volume affiché.

Une application appelle la méthode IAudioEndpointVolume::RegisterControlChangeNotify pour inscrire son interface IAudioEndpointVolumeCallback pour recevoir des notifications. Lorsque l’application n’a plus besoin de notifications, elle appelle la méthode IAudioEndpointVolume::UnregisterControlChangeNotify pour supprimer l’inscription.

L’exemple de code suivant est une application Windows qui appelle les méthodes RegisterControlChangeNotify et UnregisterControlChangeNotify pour inscrire et annuler l’inscription de la classe CAudioEndpointVolumeCallback dans l’exemple de code précédent :

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

Dans l’exemple de code précédent, la fonction WinMain appelle la fonction CoCreateInstance pour créer un instance de l’interface IMMDeviceEnumerator, puis appelle la méthode IMMDeviceEnumerator::GetDefaultAudioEndpoint pour obtenir l’interface IMMDevice de l’appareil de rendu par défaut. WinMain appelle la méthode IMMDevice::Activate pour obtenir l’interface IAudioEndpointVolume de l’appareil et appelle RegisterControlChangeNotify pour inscrire l’application afin de recevoir des notifications de modifications du volume de point de terminaison. Ensuite, WinMain ouvre une boîte de dialogue pour afficher un contrôle de volume de point de terminaison pour l’appareil. La boîte de dialogue affiche également une case activée qui indique si l’appareil est désactivé. Le contrôle de volume de point de terminaison et désactiver le son de la zone case activée dans la boîte de dialogue miroir les paramètres du contrôle de volume de point de terminaison et désactiver la case activée zone affichée par Sndvol. Pour plus d’informations sur WinMain et CoCreateInstance, consultez la documentation du SDK Windows. Pour plus d’informations sur IMMDeviceEnumerator et IMMDevice, consultez Énumération de périphériques audio.

La procédure de boîte de dialogue, DlgProc, dans l’exemple de code précédent, gère les modifications apportées par l’utilisateur au volume et désactive les paramètres via les contrôles de la boîte de dialogue. Lorsque DlgProc appelle SetMasterVolumeLevelScalar ou SetMute, Sndvol reçoit une notification de la modification et met à jour le contrôle correspondant dans sa fenêtre pour refléter le nouveau volume ou désactiver le son. Si, au lieu d’utiliser la boîte de dialogue, l’utilisateur met à jour le volume et désactive les paramètres via les contrôles de la fenêtre Sndvol, la méthode OnNotify de la classe CAudioEndpointVolumeCallback met à jour les contrôles dans la boîte de dialogue pour afficher les nouveaux paramètres.

Si l’utilisateur modifie le volume via les contrôles de la boîte de dialogue, la méthode OnNotify de la classe CAudioEndpointVolumeCallback n’envoie pas de messages pour mettre à jour les contrôles dans la boîte de dialogue. Pour ce faire, il serait redondant. OnNotify met à jour les contrôles dans la boîte de dialogue uniquement si le changement de volume provient de Sndvol ou d’une autre application. Le deuxième paramètre dans les appels de méthode SetMasterVolumeLevelScalar et SetMute dans la fonction DlgProc est un pointeur vers un GUID de contexte d’événement que l’une ou l’autre méthode transmet à OnNotify. OnNotify vérifie la valeur du GUID de contexte d’événement pour déterminer si la boîte de dialogue est la source de la modification de volume. Pour plus d’informations sur les GUID de contexte d’événement, consultez IAudioEndpointVolumeCallback::OnNotify.

Lorsque l’utilisateur quitte la boîte de dialogue, l’appel UnregisterControlChangeNotify dans l’exemple de code précédent supprime l’inscription de la classe CAudioEndpointVolumeCallback avant la fin du programme.

Vous pouvez facilement modifier l’exemple de code précédent pour afficher les contrôles de volume et désactiver le son pour l’appareil de capture par défaut. Dans la fonction WinMain , remplacez la valeur du premier paramètre de l’appel à la méthode IMMDeviceEnumerator::GetDefaultAudioEndpoint d’eRender par eCapture.

L’exemple de code suivant est le script de ressource qui définit les contrôles de volume et de muet qui apparaissent dans l’exemple de code précédent :

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

L’exemple de code suivant est le fichier d’en-tête de ressource qui définit les identificateurs de contrôle qui apparaissent dans les exemples de code précédents :

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

Les exemples de code précédents se combinent pour former une application simple permettant de contrôler et de surveiller le volume de point de terminaison de l’appareil de rendu par défaut. Une application plus utile peut également avertir l’utilisateur lorsque le status de l’appareil change. Par exemple, l’appareil peut être désactivé, débranché ou supprimé. Pour plus d’informations sur la surveillance de ces types d’événements, consultez Événements d’appareil.

Contrôles de volume