Événements audio pour les applications audio héritées

Les API audio héritées telles que DirectSound, DirectShow et les fonctions waveOutXxx permettent aux applications d’obtenir et de définir les niveaux de volume des flux audio. Les applications peuvent utiliser les fonctionnalités de contrôle de volume dans ces API pour afficher les curseurs de volume dans leurs fenêtres d’application.

Dans Windows Vista, le programme de contrôle de volume système, Sndvol, permet aux utilisateurs de contrôler les niveaux de volume audio pour des applications individuelles. Les curseurs de volume affichés par les applications doivent être liés aux curseurs de volume correspondants dans Sndvol. Si un utilisateur ajuste le volume de l’application via un curseur de volume dans une fenêtre d’application, le curseur de volume correspondant dans Sndvol se déplace immédiatement pour indiquer le nouveau niveau de volume. À l’inverse, si l’utilisateur ajuste le volume de l’application via Sndvol, les curseurs de volume de la fenêtre d’application doivent se déplacer pour indiquer le nouveau niveau de volume.

Dans Windows Vista, Sndvol reflète immédiatement les modifications de volume qu’une application effectue par le biais d’appels à la méthode IDirectSoundBuffer::SetVolume ou à la fonction waveOutSetVolume . Toutefois, une API audio héritée telle que DirectSound ou les fonctions waveOutXxx ne fournit aucun moyen d’avertir une application lorsque l’utilisateur modifie le volume de l’application via Sndvol. Si une application affiche un curseur de volume mais ne reçoit pas de notifications de modifications de volume, le curseur ne parvient pas à refléter les modifications apportées par l’utilisateur dans Sndvol. Pour implémenter le comportement approprié, le concepteur d’application doit compenser d’une manière ou d’une autre l’absence de notifications par l’API audio héritée.

Une solution peut être que l’application définisse un minuteur pour lui rappeler régulièrement de case activée le niveau de volume pour voir s’il a changé.

Une solution plus élégante consiste pour l’application à utiliser les fonctionnalités de notification d’événements des API audio principales. En particulier, l’application peut inscrire une interface IAudioSessionEvents pour recevoir des rappels lorsque des changements de volume ou d’autres types d’événements audio se produisent. Lorsque le volume change, la routine de rappel de changement de volume peut immédiatement mettre à jour le curseur de volume de l’application pour refléter la modification.

L’exemple de code suivant montre comment une application peut s’inscrire pour recevoir des notifications de modifications de volume et d’autres événements audio :

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

L’exemple de code précédent implémente une classe nommée AudioVolumeEvents. Pendant l’initialisation du programme, l’application audio active les notifications d’événements audio en créant un objet AudioVolumeEvents. Le constructeur de cette classe prend trois paramètres d’entrée :

Le constructeur fournit les valeurs de flux et de rôle en tant que paramètres d’entrée à la méthode IMMDeviceEnumerator::GetDefaultAudioEndpoint . La méthode crée un objet IMMDevice qui encapsule le périphérique de point de terminaison audio avec la direction de flux de données et le rôle d’appareil spécifiés.

L’application implémente l’objet pointé vers pAudioEvents. (L’implémentation n’est pas indiquée dans l’exemple de code précédent. Pour obtenir un exemple de code qui implémente une interface IAudioSessionEvents , consultez Événements de session audio.) Chaque méthode de cette interface reçoit des notifications d’un type particulier d’événement audio. Si l’application n’est pas intéressée par un type d’événement particulier, la méthode de ce type d’événement ne doit rien faire d’autre que retourner S_OK.

La méthode IAudioSessionEvents::OnSimpleVolumeChanged reçoit des notifications de modifications de volume. En règle générale, cette méthode met à jour le curseur de volume de l’application.

Dans l’exemple de code précédent, le constructeur de la classe AudioVolumeEvents s’inscrit pour les notifications sur la session audio spécifique au processus identifiée par la valeur GUID de session GUID_NULL. Par défaut, les API audio héritées telles que DirectSound, DirectShow et les fonctions waveOutXxx attribuent leurs flux à cette session. Toutefois, une application DirectSound ou DirectShow peut, en option, remplacer le comportement par défaut et affecter ses flux à une session inter-processus ou à une session identifiée par une valeur GUID autre que GUID_NULL. (Aucun mécanisme n’est actuellement fourni pour qu’une application waveOutXxx remplace le comportement par défaut de manière similaire.) Pour obtenir un exemple de code d’une application DirectShow avec ce comportement, consultez Rôles d’appareil pour les applications DirectShow. Pour prendre en charge une telle application, vous pouvez modifier le constructeur dans l’exemple de code précédent pour accepter deux paramètres d’entrée supplémentaires: un GUID de session et un indicateur pour indiquer si la session à surveiller est une session inter-processus ou spécifique au processus. Passez ces paramètres à l’appel à la méthode IAudioSessionManager::GetAudioSessionControl dans le constructeur.

Une fois que le constructeur a appelé la méthode IAudioSessionControl::RegisterAudioSessionNotification pour s’inscrire aux notifications, l’application continue de recevoir des notifications uniquement tant que l’interface IAudioSessionControl ou IAudioSessionManager existe. L’objet AudioVolumeEvents dans l’exemple de code précédent contient des références à ces interfaces jusqu’à ce que son destructeur soit appelé. Ce comportement garantit que l’application continue de recevoir des notifications pendant la durée de vie de l’objet AudioVolumeEvents.

Au lieu de sélectionner implicitement un appareil audio en fonction de son rôle d’appareil, une application multimédia DirectSound ou Windows héritée peut permettre à l’utilisateur de sélectionner explicitement un appareil dans une liste d’appareils disponibles qui sont identifiés par leurs noms conviviaux. Pour prendre en charge ce comportement, l’exemple de code précédent doit être modifié pour générer des notifications d’événements audio pour l’appareil sélectionné. Deux modifications sont requises. Tout d’abord, modifiez la définition du constructeur pour accepter une chaîne d’ID de point de terminaison comme paramètre d’entrée (à la place des paramètres de flux et de rôle dans l’exemple de code). Cette chaîne identifie le périphérique de point de terminaison audio qui correspond à l’appareil DirectSound ou à forme d’onde hérité sélectionné. Deuxièmement, remplacez l’appel à la méthode IMMDeviceEnumerator::GetDefaultAudioEndpoint par un appel à la méthode IMMDeviceEnumerator::GetDevice . L’appel GetDevice prend la chaîne d’ID de point de terminaison comme paramètre d’entrée et crée une instance de l’appareil de point de terminaison identifié par la chaîne.

La technique permettant d’obtenir la chaîne d’ID de point de terminaison pour un appareil DirectSound ou un appareil de forme d’onde hérité est la suivante.

Tout d’abord, lors de l’énumération de l’appareil, DirectSound fournit la chaîne d’ID de point de terminaison pour chaque appareil énuméré. Pour commencer l’énumération de l’appareil, l’application transmet un pointeur de fonction de rappel en tant que paramètre d’entrée à la fonction DirectSoundCreate ou DirectSoundCaptureCreate . La définition de la fonction de rappel est :

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

Dans Windows Vista, le paramètre lpcstrModule pointe vers la chaîne d’ID de point de terminaison. (Dans les versions antérieures de Windows, y compris Windows Server 2003, Windows XP et Windows 2000, le paramètre lpcstrModule pointe vers le nom du module de pilote pour l’appareil.) Le paramètre lpcstrDescription pointe vers une chaîne qui contient le nom convivial de l’appareil. Pour plus d’informations sur l’énumération d’appareils DirectSound, consultez la documentation du Kit de développement logiciel (SDK) Windows.

Deuxièmement, pour obtenir la chaîne d’ID de point de terminaison d’un appareil de forme d’onde hérité, utilisez la fonction waveOutMessage ou waveInMessage pour envoyer un message DRV_QUERYFUNCTIONINSTANCEID au pilote de périphérique waveform. Pour obtenir un exemple de code montrant l’utilisation de ce message, consultez Rôles d’appareil pour les applications multimédia Windows héritées.

Interopérabilité avec les API audio héritées