Endpunktvolumesteuerung

Mit den Schnittstellen ISimpleAudioVolume, IChannelAudioVolume und IAudioStreamVolume können Clients die Lautstärkepegel von Audiositzungen steuern, bei denen es sich um Sammlungen von Audiostreams im freigegebenen Modus handelt. Diese Schnittstellen funktionieren nicht mit Audiostreams im exklusiven Modus.

Anwendungen, die Datenströme im exklusiven Modus verwalten, können die Lautstärkeebenen dieser Datenströme über die IAudioEndpointVolume-Schnittstelle steuern. Diese Schnittstelle steuert die Lautstärke des Audioendpunktgeräts. Es verwendet die Hardwarelautstärkeregelung für das Endpunktgerät, wenn die Audiohardware ein solches Steuerelement implementiert. Andernfalls implementiert die IAudioEndpointVolume-Schnittstelle die Lautstärkeregelung in der Software.

Wenn ein Gerät über eine Hardwarelautstärkeregelung verfügt, wirken sich Änderungen, die über die IAudioEndpointVolume-Schnittstelle am Steuerelement vorgenommen werden, sowohl im freigegebenen Modus als auch im exklusiven Modus auf die Volumeebene aus. Wenn ein Gerät keine Hardwarevolume- und Stummschaltungssteuerungen aufweist, wirken sich Änderungen am Softwarevolume und Stummschaltungssteuerelement über diese Schnittstelle auf die Lautstärkeebene im freigegebenen Modus aus, nicht jedoch im exklusiven Modus. Im exklusiven Modus tauschen die Anwendung und die Audiohardware Audiodaten direkt unter Umgehung der Softwaresteuerelemente aus.

In der Regel sollten Anwendungen die Verwendung der IAudioEndpointVolume-Schnittstelle vermeiden, um die Lautstärkestufen von Streams im freigegebenen Modus zu steuern. Stattdessen sollten Anwendungen für diesen Zweck die Schnittstelle ISimpleAudioVolume, IChannelAudioVolume oder IAudioStreamVolume verwenden.

Wenn eine Anwendung einen Lautstärkeregler anzeigt, der die IAudioEndpointVolume-Schnittstelle verwendet, um den Lautstärkepegel eines Audioendpunktgeräts zu steuern, sollte diese Lautstärkeregelung die vom System-Volume-Control-Programm Sndvol angezeigte Endpunktlautstärkesteuerung Spiegel. Wie bereits erläutert, wird das Volume-Steuerelement des Endpunkts auf der linken Seite des Sndvol-Fensters im Gruppenfeld Device (Gerät) angezeigt. Wenn der Benutzer das Endpunktvolume eines Geräts über die Lautstärkeregelung in Sndvol ändert, sollte sich die entsprechende Endpunktvolumesteuerung in der Anwendung im Einklang mit dem Steuerelement in Sndvol bewegen. Wenn der Benutzer die Volumeebene über die Endpunktlautstärkesteuerung im Anwendungsfenster ändert, sollte sich die entsprechende Lautstärkeregelung in Sndvol im Einklang mit der Lautstärkeregelung der Anwendung bewegen.

Um sicherzustellen, dass die Endpunktvolumesteuerung in einem Anwendungsfenster die Endpunktvolumesteuerung in Sndvol spiegelt, sollte die Anwendung eine IAudioEndpointVolumeCallback-Schnittstelle implementieren und diese Schnittstelle registrieren, um Benachrichtigungen zu empfangen. Danach erhält die Anwendung jedes Mal, wenn der Benutzer die Endpunktvolumeebene in Sndvol ändert, einen Benachrichtigungsaufruf über die IAudioEndpointVolumeCallback::OnNotify-Methode . Während dieses Aufrufs kann die OnNotify-Methode die Endpunktvolumesteuerung im Anwendungsfenster so aktualisieren, dass sie der in Sndvol angezeigten Steuerelementeinstellung entspricht. Ebenso erhält Sndvol jedes Mal, wenn der Benutzer die Endpunktvolumeebene über die Lautstärkesteuerung im Anwendungsfenster ändert, eine Benachrichtigung und aktualisiert sofort seine Endpunktvolumesteuerung, um die neue Volumeebene anzuzeigen.

Das folgende Codebeispiel ist eine Headerdatei, die eine mögliche Implementierung der IAudioEndpointVolumeCallback-Schnittstelle zeigt:

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

Die CAudioEndpointVolumeCallback-Klasse im vorherigen Codebeispiel ist eine Implementierung der IAudioEndpointVolumeCallback-Schnittstelle . Da IAudioEndpointVolumeCallback von IUnknown erbt, enthält die Klassendefinition Implementierungen der IUnknown-MethodenAddRef, Release und QueryInterface. Die OnNotify-Methode in der Klassendefinition wird jedes Mal aufgerufen, wenn eine der folgenden Methoden die Endpunktvolumeebene ändert:

Die Implementierung der OnNotify-Methode im vorherigen Codebeispiel sendet Nachrichten an die Volumesteuerung im Anwendungsfenster, um die angezeigte Volumeebene zu aktualisieren.

Eine Anwendung ruft die IAudioEndpointVolume::RegisterControlChangeNotify-Methode auf, um ihre IAudioEndpointVolumeCallback-Schnittstelle zum Empfangen von Benachrichtigungen zu registrieren. Wenn die Anwendung keine Benachrichtigungen mehr benötigt, ruft sie die IAudioEndpointVolume::UnregisterControlChangeNotify-Methode auf, um die Registrierung zu löschen.

Das folgende Codebeispiel ist eine Windows-Anwendung, die die Methoden RegisterControlChangeNotify und UnregisterControlChangeNotify aufruft, um die CAudioEndpointVolumeCallback-Klasse im vorherigen Codebeispiel zu registrieren und aufzuheben:

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

Im vorherigen Codebeispiel ruft die WinMain-Funktion die CoCreateInstance-Funktion auf, um eine instance der IMMDeviceEnumerator-Schnittstelle zu erstellen, und ruft die IMMDeviceEnumerator::GetDefaultAudioEndpoint-Methode auf, um die IMMDevice-Schnittstelle des Standardrenderinggeräts abzurufen. WinMain ruft die IMMDevice::Activate-Methode auf, um die IAudioEndpointVolume-Schnittstelle des Geräts abzurufen, und ruft RegisterControlChangeNotify auf, um die Anwendung zu registrieren, um Benachrichtigungen über Änderungen des Endpunktvolumens zu empfangen. Als Nächstes öffnet WinMain ein Dialogfeld, um eine Endpunktlautstärkesteuerung für das Gerät anzuzeigen. Das Dialogfeld zeigt auch ein Kontrollkästchen an, das angibt, ob das Gerät stummgeschaltet ist. Das Kontrollkästchen Endpunktvolumesteuerung und Stummschaltung im Dialogfeld Spiegel die Einstellungen des von Sndvol angezeigten Kontrollkästchens "Endpunktlautstärkesteuerung und Stummschaltung". Weitere Informationen zu WinMain und CoCreateInstance finden Sie in der Windows SDK-Dokumentation. Weitere Informationen zu IMMDeviceEnumerator und IMMDevice finden Sie unter Aufzählen von Audiogeräten.

Die Dialogfeldprozedur DlgProc im vorherigen Codebeispiel verarbeitet die Änderungen, die der Benutzer an den Volume- und Stummschaltungseinstellungen über die Steuerelemente im Dialogfeld vornimmt. Wenn DlgProc SetMasterVolumeLevelScalar oder SetMute aufruft, empfängt Sndvol eine Benachrichtigung über die Änderung und aktualisiert das entsprechende Steuerelement in seinem Fenster, um die neue Volume- oder Stummschaltungseinstellung widerzuspiegeln. Wenn der Benutzer anstelle des Dialogfelds die Einstellungen für Volume und Stummschaltung über die Steuerelemente im Sndvol-Fenster aktualisiert, aktualisiert die OnNotify-Methode in der CAudioEndpointVolumeCallback-Klasse die Steuerelemente im Dialogfeld, um die neuen Einstellungen anzuzeigen.

Wenn der Benutzer die Lautstärke über die Steuerelemente im Dialogfeld ändert, sendet die OnNotify-Methode in der CAudioEndpointVolumeCallback-Klasse keine Nachrichten, um die Steuerelemente im Dialogfeld zu aktualisieren. Dies wäre redundant. OnNotify aktualisiert die Steuerelemente im Dialogfeld nur, wenn die Volumeänderung von Sndvol oder einer anderen Anwendung stammt. Der zweite Parameter in den SetMasterVolumeLevelScalar - und SetMute-Methodenaufrufen in der DlgProc-Funktion ist ein Zeiger auf eine Ereigniskontext-GUID, die von beiden Methoden an OnNotify übergeben wird. OnNotify überprüft den Wert der Ereigniskontext-GUID, um zu bestimmen, ob das Dialogfeld die Quelle der Volumeänderung ist. Weitere Informationen zu Ereigniskontext-GUIDs finden Sie unter IAudioEndpointVolumeCallback::OnNotify.

Wenn der Benutzer das Dialogfeld beendet, löscht der Aufruf UnregisterControlChangeNotify im vorherigen Codebeispiel die Registrierung der CAudioEndpointVolumeCallback-Klasse, bevor das Programm beendet wird.

Sie können das vorherige Codebeispiel einfach ändern, um Lautstärke- und Stummschaltungssteuerelemente für das Standarderfassungsgerät anzuzeigen. Ändern Sie in der WinMain-Funktion den Wert des ersten Parameters im Aufruf der IMMDeviceEnumerator::GetDefaultAudioEndpoint-Methode von eRender in eCapture.

Das folgende Codebeispiel ist das Ressourcenskript, das die Volume- und Stummschaltungssteuerelemente definiert, die im vorherigen Codebeispiel angezeigt werden:

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

Das folgende Codebeispiel ist die Ressourcenheaderdatei, die die Steuerelementbezeichner definiert, die in den vorherigen Codebeispielen angezeigt werden:

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

Die vorherigen Codebeispiele werden zu einer einfachen Anwendung zum Steuern und Überwachen des Endpunktvolumens des Standardrenderinggeräts kombiniert. Eine nützlichere Anwendung kann den Benutzer darüber hinaus benachrichtigen, wenn sich die status des Geräts ändert. Beispielsweise kann das Gerät deaktiviert, nicht angeschlossen oder entfernt werden. Weitere Informationen zur Überwachung dieser Ereignistypen finden Sie unter Geräteereignisse.

Lautstärkeregler