Audioereignisse für Legacy-Audioanwendungen

Ältere Audio-APIs wie DirectSound, DirectShow und die waveOutXxx-Funktionen ermöglichen Es Anwendungen, die Lautstärkepegel von Audiostreams abzurufen und festzulegen. Anwendungen können die Volumesteuerungsfunktionen in diesen APIs verwenden, um Lautstärkeregler in ihren Anwendungsfenstern anzuzeigen.

In Windows Vista ermöglicht das System-Lautstärkeregelungsprogramm Sndvol Benutzern die Steuerung der Audiolautstärken für einzelne Anwendungen. Die von Anwendungen angezeigten Lautstärkeschieberegler sollten mit den entsprechenden Lautstärkereglern in Sndvol verknüpft werden. Wenn ein Benutzer die Anwendungslautstärke über einen Lautstärkeregler in einem Anwendungsfenster anpasst, wird der entsprechende Lautstärkeregler in Sndvol sofort verschoben, um die neue Lautstärkeebene anzugeben. Wenn der Benutzer hingegen die Anwendungslautstärke über Sndvol anpasst, sollten die Lautstärkeregler im Anwendungsfenster verschoben werden, um die neue Lautstärkeebene anzugeben.

In Windows Vista spiegelt Sndvol sofort Volumeänderungen wider, die eine Anwendung über Aufrufe der IDirectSoundBuffer::SetVolume-Methode oder der waveOutSetVolume-Funktion durchführt. Eine Ältere Audio-API wie DirectSound oder die waveOutXxx-Funktionen bieten jedoch keine Möglichkeit, eine Anwendung zu benachrichtigen, wenn der Benutzer das Anwendungsvolume über Sndvol ändert. Wenn eine Anwendung einen Volumeschieberegler anzeigt, aber keine Benachrichtigungen über Volumeänderungen empfängt, kann der Schieberegler die vom Benutzer in Sndvol vorgenommenen Änderungen nicht widerspiegeln. Um das entsprechende Verhalten zu implementieren, muss der Anwendungs-Designer das Fehlen von Benachrichtigungen durch die Legacy-Audio-API irgendwie kompensieren.

Eine Lösung könnte darin liegen, dass die Anwendung einen Timer festlegen muss, um sie regelmäßig daran zu erinnern, die Volumeebene zu überprüfen, um festzustellen, ob sie sich geändert hat.

Eine elegantere Lösung besteht darin, dass die Anwendung die Ereignisbenachrichtigungsfunktionen der kernigen Audio-APIs verwendet. Insbesondere kann die Anwendung eine IAudioSessionEvents-Schnittstelle registrieren, um Rückrufe zu empfangen, wenn Lautstärkeänderungen oder andere Arten von Audioereignissen auftreten. Wenn sich das Volume ändert, kann die Volumeänderungsrückrufroutine sofort den Lautstärkeregler der Anwendung aktualisieren, um die Änderung widerzuspiegeln.

Das folgende Codebeispiel zeigt, wie sich eine Anwendung registrieren kann, um Benachrichtigungen über Volumeänderungen und andere Audioereignisse zu empfangen:

//-----------------------------------------------------------
// Register the application to receive notifications when the
// volume level changes on the default process-specific audio
// session (with session GUID value GUID_NULL) on the audio
// endpoint device with the specified data-flow direction
// (eRender or eCapture) and device role.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hr)  \
              if (FAILED(hr)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

class AudioVolumeEvents
{
    HRESULT _hrStatus;
    IAudioSessionManager *_pManager;
    IAudioSessionControl *_pControl;
    IAudioSessionEvents *_pAudioEvents;
public:
    AudioVolumeEvents(EDataFlow, ERole, IAudioSessionEvents*);
    ~AudioVolumeEvents();
    HRESULT GetStatus() { return _hrStatus; };
};

// Constructor
AudioVolumeEvents::AudioVolumeEvents(EDataFlow flow, ERole role,
                                     IAudioSessionEvents *pAudioEvents)
{
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;

    _hrStatus = S_OK;
    _pManager = NULL;
    _pControl = NULL;
    _pAudioEvents = pAudioEvents;

    if (_pAudioEvents == NULL)
    {
        _hrStatus = E_POINTER;
        return;
    }

    _pAudioEvents->AddRef();

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

    // Get the audio endpoint device with the specified data-flow
    // direction (eRender or eCapture) and device role.
    _hrStatus = pEnumerator->GetDefaultAudioEndpoint(flow, role,
                                                     &pDevice);
    EXIT_ON_ERROR(_hrStatus)

    // Get the session manager for the endpoint device.
    _hrStatus = pDevice->Activate(__uuidof(IAudioSessionManager),
                                  CLSCTX_INPROC_SERVER, NULL,
                                  (void**)&_pManager);
    EXIT_ON_ERROR(_hrStatus)

    // Get the control interface for the process-specific audio
    // session with session GUID = GUID_NULL. This is the session
    // that an audio stream for a DirectSound, DirectShow, waveOut,
    // or PlaySound application stream belongs to by default.
    _hrStatus = _pManager->GetAudioSessionControl(NULL, 0, &_pControl);
    EXIT_ON_ERROR(_hrStatus)

    _hrStatus = _pControl->RegisterAudioSessionNotification(_pAudioEvents);
    EXIT_ON_ERROR(_hrStatus)

Exit:
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
}

// Destructor
AudioVolumeEvents::~AudioVolumeEvents()
{
    if (_pControl != NULL)
    {
        _pControl->UnregisterAudioSessionNotification(_pAudioEvents);
    }
    SAFE_RELEASE(_pManager)
    SAFE_RELEASE(_pControl)
    SAFE_RELEASE(_pAudioEvents)
};

Im vorherigen Codebeispiel wird eine Klasse namens AudioVolumeEvents implementiert. Während der Programminitialisierung aktiviert die Audioanwendung Audioereignisbenachrichtigungen, indem ein AudioVolumeEvents-Objekt erstellt wird. Der Konstruktor für diese Klasse akzeptiert drei Eingabeparameter:

Der Konstruktor stellt die Flow- und Rollenwerte als Eingabeparameter für die IMMDeviceEnumerator::GetDefaultAudioEndpoint-Methode bereit. Die -Methode erstellt ein IMMDevice-Objekt , das das Audioendpunktgerät mit der angegebenen Datenflussrichtung und Geräterolle kapselt.

Die Anwendung implementiert das Objekt, auf das von pAudioEvents verwiesen wird. (Die Implementierung wird im vorherigen Codebeispiel nicht gezeigt. Ein Codebeispiel, das eine IAudioSessionEvents-Schnittstelle implementiert, finden Sie unter Audiositzungsereignisse.) Jede Methode in dieser Schnittstelle empfängt Benachrichtigungen zu einem bestimmten Audioereignistyp. Wenn die Anwendung nicht an einem bestimmten Ereignistyp interessiert ist, sollte die Methode für diesen Ereignistyp nichts anderes tun, als S_OK zurückzugeben.

Die IAudioSessionEvents::OnSimpleVolumeChanged-Methode empfängt Benachrichtigungen über Volumeänderungen. In der Regel aktualisiert diese Methode den Volumeschieberegler der Anwendung.

Im vorherigen Codebeispiel registriert der Konstruktor für die AudioVolumeEvents-Klasse Benachrichtigungen für die prozessspezifische Audiositzung , die durch den Sitzungs-GUID-Wert GUID_NULL identifiziert wird. Standardmäßig weisen ältere Audio-APIs wie DirectSound, DirectShow und die waveOutXxx-Funktionen ihre Streams dieser Sitzung zu. Eine DirectSound- oder DirectShow-Anwendung kann jedoch optional das Standardverhalten außer Kraft setzen und ihre Streams einer prozessübergreifenden Sitzung oder einer Sitzung zuweisen, die durch einen anderen GUID-Wert als GUID_NULL identifiziert wird. (Es wird derzeit kein Mechanismus für eine waveOutXxx-Anwendung bereitgestellt, um das Standardverhalten auf ähnliche Weise zu überschreiben.) Ein Codebeispiel für eine DirectShow-Anwendung mit diesem Verhalten finden Sie unter Geräterollen für DirectShow-Anwendungen. Um eine solche Anwendung aufzunehmen, können Sie den Konstruktor im vorherigen Codebeispiel so ändern, dass er zwei zusätzliche Eingabeparameter akzeptiert: eine Sitzungs-GUID und ein Flag, um anzugeben, ob es sich bei der zu überwachenden Sitzung um eine prozessübergreifende oder prozessspezifische Sitzung handelt. Übergeben Sie diese Parameter an den Aufruf der IAudioSessionManager::GetAudioSessionControl-Methode im Konstruktor.

Nachdem der Konstruktor die IAudioSessionControl::RegisterAudioSessionNotification-Methode aufgerufen hat, um sich für Benachrichtigungen zu registrieren, empfängt die Anwendung weiterhin Benachrichtigungen, solange entweder die IAudioSessionControl - oder IAudioSessionManager-Schnittstelle vorhanden ist. Das AudioVolumeEvents-Objekt im vorherigen Codebeispiel enthält Verweise auf diese Schnittstellen, bis sein Destruktor aufgerufen wird. Dieses Verhalten stellt sicher, dass die Anwendung weiterhin Benachrichtigungen für die Lebensdauer des AudioVolumeEvents-Objekts empfängt.

Anstatt ein Audiogerät basierend auf seiner Geräterolle implizit auszuwählen, ermöglicht eine DirectSound- oder Legacy-Windows-Multimediaanwendung dem Benutzer möglicherweise, ein Gerät explizit aus einer Liste der verfügbaren Geräte auszuwählen, die durch ihre Anzeigenamen gekennzeichnet sind. Um dieses Verhalten zu unterstützen, muss das vorherige Codebeispiel geändert werden, um Audioereignisbenachrichtigungen für das ausgewählte Gerät zu generieren. Zwei Änderungen sind erforderlich. Ändern Sie zunächst die Konstruktordefinition, um eine Endpunkt-ID-Zeichenfolge als Eingabeparameter zu akzeptieren (anstelle der Flow- und Rollenparameter im Codebeispiel). Diese Zeichenfolge identifiziert das Audioendpunktgerät, das dem ausgewählten DirectSound- oder Legacywellenformgerät entspricht. Ersetzen Sie anschließend den Aufruf der IMMDeviceEnumerator::GetDefaultAudioEndpoint-Methode durch einen Aufruf der IMMDeviceEnumerator::GetDevice-Methode . Der GetDevice-Aufruf verwendet die Endpunkt-ID-Zeichenfolge als Eingabeparameter und erstellt eine instance des Endpunktgeräts, das durch die Zeichenfolge identifiziert wird.

Das Verfahren zum Abrufen der Endpunkt-ID-Zeichenfolge für ein DirectSound-Gerät oder ein Älteres Wellenformgerät lautet wie folgt.

Zunächst stellt DirectSound während der Geräteenumeration die Endpunkt-ID-Zeichenfolge für jedes aufgezählte Gerät bereit. Um mit der Geräteenumeration zu beginnen, übergibt die Anwendung einen Rückruffunktionszeiger als Eingabeparameter an die DirectSoundCreate - oder DirectSoundCaptureCreate-Funktion . Die Definition der Rückruffunktion lautet:

BOOL DSEnumCallback(
  LPGUID  lpGuid,
  LPCSTR  lpcstrDescription,
  LPCSTR  lpcstrModule,
  LPVOID  lpContext
);

In Windows Vista zeigt der lpcstrModule-Parameter auf die Endpunkt-ID-Zeichenfolge. (In früheren Versionen von Windows, einschließlich Windows Server 2003, Windows XP und Windows 2000, verweist der Parameter lpcstrModule auf den Namen des Treibermoduls für das Gerät.) Der Parameter lpcstrDescription verweist auf eine Zeichenfolge, die den Anzeigenamen des Geräts enthält. Weitere Informationen zur DirectSound-Geräteenumeration finden Sie in der Windows SDK-Dokumentation.

Zweitens verwenden Sie zum Abrufen der Endpunkt-ID-Zeichenfolge für ein Älteres Waveform-Gerät die Funktion waveOutMessage oder waveInMessage , um eine DRV_QUERYFUNCTIONINSTANCEID Nachricht an den Waveform-Gerätetreiber zu senden. Ein Codebeispiel, das die Verwendung dieser Meldung zeigt, finden Sie unter Geräterollen für Legacy-Windows-Multimediaanwendungen.

Interoperabilität mit Legacyaudio-APIs