Peak Meter

Zur Unterstützung von Windows-Anwendungen, die Spitzenzähler anzeigen, enthält die EndpointVolume-API eine IAudioMeterInformation-Schnittstelle . Diese Schnittstelle stellt eine Spitzenmessung auf einem Audioendpunktgerät dar. Bei einem Renderinggerät stellt der aus dem Spitzenzähler abgerufene Wert den maximalen Stichprobenwert dar, der im Ausgabestream des Geräts während des vorherigen Messungszeitraums gefunden wurde. Bei einem Erfassungsgerät stellt der aus der Spitzenmessung abgerufene Wert den maximalen Stichprobenwert dar, der im Eingabedatenstrom des Geräts gefunden wird.

Die von den Methoden in der IAudioMeterInformation-Schnittstelle erhaltenen Spitzenzählerwerte sind Gleitkommazahlen im normalisierten Bereich von 0,0 bis 1,0. Wenn beispielsweise ein PCM-Stream 16-Bit-Stichproben enthält und der Spitzenwert der Stichprobe während eines bestimmten Messzeitraums 8914 ist, ist der vom Spitzenzähler aufgezeichnete absolute Wert 8914, und der von der IAudioMeterInformation-Schnittstelle gemeldete normalisierte Spitzenwert ist 8914/32768 = 0,272.

Wenn das Audioendpunktgerät den Spitzenzähler in Hardware implementiert, verwendet die IAudioMeterInformation-Schnittstelle die Hardware-Spitzenmessung. Andernfalls implementiert die Schnittstelle den Spitzenzähler in der Software.

Wenn ein Gerät über eine Hardware-Spitzenmessung verfügt, ist die Spitzenmessung sowohl im gemeinsamen Modus als auch im exklusiven Modus aktiv. Wenn auf einem Gerät keine Hardware-Spitzenmessung vorhanden ist, ist die Spitzenmessung im gemeinsamen Modus aktiv, aber nicht im exklusiven Modus. Im exklusiven Modus tauschen die Anwendung und die Audiohardware Audiodaten direkt aus und umgehen dabei den Softwarespitzenzähler (der immer einen Spitzenwert von 0,0 meldet).

Das folgende C++-Codebeispiel ist eine Windows-Anwendung, die eine Spitzenmessung für das Standardrenderinggerät anzeigt:

// Peakmeter.cpp -- WinMain and dialog box functions

#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include "resource.h"

static BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
static void DrawPeakMeter(HWND, float);

// Timer ID and period (in milliseconds)
#define ID_TIMER  1
#define TIMER_PERIOD  125

#define EXIT_ON_ERROR(hr)  \
              if (FAILED(hr)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

//-----------------------------------------------------------
// WinMain -- Opens a dialog box that contains a peak meter.
//   The peak meter displays the peak sample value that plays
//   through the default rendering device.
//-----------------------------------------------------------
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
    HRESULT hr;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioMeterInformation *pMeterInfo = NULL;

    if (hPrevInstance)
    {
        return 0;
    }

    CoInitialize(NULL);

    // Get enumerator for audio endpoint devices.
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                          NULL, CLSCTX_INPROC_SERVER,
                          __uuidof(IMMDeviceEnumerator),
                          (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)

    // Get peak meter for default audio-rendering device.
    hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
    EXIT_ON_ERROR(hr)

    hr = pDevice->Activate(__uuidof(IAudioMeterInformation),
                           CLSCTX_ALL, NULL, (void**)&pMeterInfo);
    EXIT_ON_ERROR(hr)

    DialogBoxParam(hInstance, L"PEAKMETER", NULL, (DLGPROC)DlgProc, (LPARAM)pMeterInfo);

Exit:
    if (FAILED(hr))
    {
        MessageBox(NULL, TEXT("This program requires Windows Vista."),
                   TEXT("Error termination"), MB_OK);
    }
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
    SAFE_RELEASE(pMeterInfo)
    CoUninitialize();
    return 0;
}

//-----------------------------------------------------------
// DlgProc -- Dialog box procedure
//-----------------------------------------------------------

BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static IAudioMeterInformation *pMeterInfo = NULL;
    static HWND hPeakMeter = NULL;
    static float peak = 0;
    HRESULT hr;

    switch (message)
    {
    case WM_INITDIALOG:
        pMeterInfo = (IAudioMeterInformation*)lParam;
        SetTimer(hDlg, ID_TIMER, TIMER_PERIOD, NULL);
        hPeakMeter = GetDlgItem(hDlg, IDC_PEAK_METER);
        return TRUE;

    case WM_COMMAND:
        switch ((int)LOWORD(wParam))
        {
        case IDCANCEL:
            KillTimer(hDlg, ID_TIMER);
            EndDialog(hDlg, TRUE);
            return TRUE;
        }
        break;

    case WM_TIMER:
        switch ((int)wParam)
        {
        case ID_TIMER:
            // Update the peak meter in the dialog box.
            hr = pMeterInfo->GetPeakValue(&peak);
            if (FAILED(hr))
            {
                MessageBox(hDlg, TEXT("The program will exit."),
                           TEXT("Fatal error"), MB_OK);
                KillTimer(hDlg, ID_TIMER);
                EndDialog(hDlg, TRUE);
                return TRUE;
            }
            DrawPeakMeter(hPeakMeter, peak);
            return TRUE;
        }
        break;

    case WM_PAINT:
        // Redraw the peak meter in the dialog box.
        ValidateRect(hPeakMeter, NULL);
        DrawPeakMeter(hPeakMeter, peak);
        break;
    }
    return FALSE;
}

//-----------------------------------------------------------
// DrawPeakMeter -- Draws the peak meter in the dialog box.
//-----------------------------------------------------------

void DrawPeakMeter(HWND hPeakMeter, float peak)
{
    HDC hdc;
    RECT rect;

    GetClientRect(hPeakMeter, &rect);
    hdc = GetDC(hPeakMeter);
    FillRect(hdc, &rect, (HBRUSH)(COLOR_3DSHADOW+1));
    rect.left++;
    rect.top++;
    rect.right = rect.left +
                 max(0, (LONG)(peak*(rect.right-rect.left)-1.5));
    rect.bottom--;
    FillRect(hdc, &rect, (HBRUSH)(COLOR_3DHIGHLIGHT+1));
    ReleaseDC(hPeakMeter, hdc);
}

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 IAudioMeterInformation-Schnittstelle des Geräts abzurufen, und öffnet ein Dialogfeld, in dem eine Spitzenmessung für das Gerät angezeigt wird. 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.

Im vorherigen Codebeispiel zeigt die DlgProc-Funktion den Spitzenzähler im Dialogfeld an. Während der Verarbeitung der WM_INITDIALOG Nachricht ruft DlgProc die SetTimer-Funktion auf, um einen Timer einzurichten, der in regelmäßigen Zeitintervallen WM_TIMER Nachrichten generiert. Wenn DlgProc eine WM_TIMER-Nachricht empfängt, ruft es IAudioMeterInformation::GetPeakValue auf, um den neuesten Spitzenzählerstand für den Stream zu erhalten. DlgProc ruft dann die DrawPeakMeter-Funktion auf, um den aktualisierten Spitzenzähler im Dialogfeld zu zeichnen. Weitere Informationen zu SetTimer und den WM_INITDIALOG- und WM_TIMER-Meldungen finden Sie in der Windows SDK-Dokumentation.

Sie können das vorangehende Codebeispiel einfach ändern, um eine Spitzenmessung für das Standardaufnahmegerät anzuzeigen. Ändern Sie in der WinMain-Funktion den Wert des ersten Parameters im Aufruf von IMMDeviceEnumerator::GetDefaultAudioEndpoint von eRender in eCapture.

Das folgende Codebeispiel ist das Ressourcenskript, das die Steuerelemente definiert, die im vorherigen Codebeispiel angezeigt werden:

// Peakmeter.rc -- Resource script

#include "resource.h"
#include "windows.h"

//
// Dialog
//
PEAKMETER DIALOGEX 0, 0, 150, 34
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT
CAPTION "Peak Meter"
FONT 8, "Arial Rounded MT Bold", 400, 0, 0x0
BEGIN
    CTEXT      "",IDC_PEAK_METER,34,14,82,5
    LTEXT      "Min",IDC_STATIC_MINVOL,10,12,20,12
    RTEXT      "Max",IDC_STATIC_MAXVOL,120,12,20,12
END

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

// Resource.h -- Control identifiers

#define IDC_STATIC_MINVOL      1001
#define IDC_STATIC_MAXVOL      1002
#define IDC_PEAK_METER         1003

Lautstärkeregler