Share via


Événements d’appareil (API audio de base)

Un événement d’appareil avertit les clients d’une modification de la status d’un appareil de point de terminaison audio dans le système. Voici des exemples d’événements d’appareil :

  • L’utilisateur active ou désactive un appareil de point de terminaison audio à partir de Gestionnaire de périphériques ou du panneau de configuration multimédia Windows, Mmsys.cpl.
  • L’utilisateur ajoute une carte audio au système ou supprime une carte audio du système.
  • L’utilisateur connecte un périphérique de point de terminaison audio à une prise audio avec détection de présence jack ou supprime un périphérique de point de terminaison audio de ce type de prise.
  • L’utilisateur modifie le rôle d’appareil attribué à un appareil.
  • La valeur d’une propriété d’un appareil change.

L’ajout ou la suppression d’une carte audio génère des événements d’appareil pour tous les appareils de point de terminaison audio qui se connectent à l’adaptateur. Les quatre premiers éléments de la liste précédente sont des exemples de changements d’état de l’appareil. Pour plus d’informations sur les états des appareils de point de terminaison audio, consultez constantes DEVICE_STATE_XXX. Pour plus d’informations sur la détection de présence jack, consultez Appareils de point de terminaison audio.

Un client peut s’inscrire pour être averti lorsque des événements d’appareil se produisent. En réponse à ces notifications, le client peut changer dynamiquement la façon dont il utilise un appareil particulier ou sélectionner un autre appareil à utiliser dans un but particulier.

Par exemple, si une application lit une piste audio via un ensemble d’haut-parleurs USB et que l’utilisateur déconnecte les haut-parleurs du connecteur USB, l’application reçoit une notification d’événement d’appareil. En réponse à l’événement, si l’application détecte qu’un ensemble d’haut-parleurs de bureau est connecté à la carte audio intégrée sur la carte mère système, l’application peut reprendre la lecture de la piste audio via les haut-parleurs de bureau. Dans cet exemple, la transition des haut-parleurs USB aux haut-parleurs de bureau se produit automatiquement, sans que l’utilisateur intervienne en redirigeant explicitement l’application.

Pour s’inscrire afin de recevoir des notifications d’appareil, un client appelle la méthode IMMDeviceEnumerator::RegisterEndpointNotificationCallback . Lorsque le client n’a plus besoin de notifications, il les annule en appelant la méthode IMMDeviceEnumerator::UnregisterEndpointNotificationCallback . Les deux méthodes prennent un paramètre d’entrée, nommé pClient, qui est un pointeur vers une interface IMMNotificationClient instance.

L’interface IMMNotificationClient est implémentée par un client. L’interface contient plusieurs méthodes, chacune d’elles servant de routine de rappel pour un type particulier d’événement d’appareil. Lorsqu’un événement d’appareil se produit dans un appareil de point de terminaison audio, le module MMDevice appelle la méthode appropriée dans l’interface IMMNotificationClient de chaque client actuellement inscrit pour recevoir des notifications d’événement d’appareil. Ces appels transmettent une description de l’événement aux clients. Pour plus d’informations, consultez IMMNotificationClient Interface.

Un client inscrit pour recevoir des notifications d’événements d’appareil reçoit des notifications de tous les types d’événements d’appareil qui se produisent dans tous les appareils de point de terminaison audio du système. Si un client s’intéresse uniquement à certains types d’événements ou à certains appareils, les méthodes de son implémentation IMMNotificationClient doivent filtrer les événements de manière appropriée.

Le Kit de développement logiciel (SDK) Windows fournit des exemples qui incluent plusieurs implémentations pour l’interface IMMNotificationClient. Pour plus d’informations, consultez Exemples sdk qui utilisent les API audio principales.

L’exemple de code suivant montre une implémentation possible de l’interface IMMNotificationClient :

//-----------------------------------------------------------
// Example implementation of IMMNotificationClient interface.
// When the status of audio endpoint devices change, the
// MMDevice module calls these methods to notify the client.
//-----------------------------------------------------------

#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

class CMMNotificationClient : public IMMNotificationClient
{
    LONG _cRef;
    IMMDeviceEnumerator *_pEnumerator;

    // Private function to print device-friendly name
    HRESULT _PrintDeviceName(LPCWSTR  pwstrId);

public:
    CMMNotificationClient() :
        _cRef(1),
        _pEnumerator(NULL)
    {
    }

    ~CMMNotificationClient()
    {
        SAFE_RELEASE(_pEnumerator)
    }

    // 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(IMMNotificationClient) == riid)
        {
            AddRef();
            *ppvInterface = (IMMNotificationClient*)this;
        }
        else
        {
            *ppvInterface = NULL;
            return E_NOINTERFACE;
        }
        return S_OK;
    }

    // Callback methods for device-event notifications.

    HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
                                EDataFlow flow, ERole role,
                                LPCWSTR pwstrDeviceId)
    {
        char  *pszFlow = "?????";
        char  *pszRole = "?????";

        _PrintDeviceName(pwstrDeviceId);

        switch (flow)
        {
        case eRender:
            pszFlow = "eRender";
            break;
        case eCapture:
            pszFlow = "eCapture";
            break;
        }

        switch (role)
        {
        case eConsole:
            pszRole = "eConsole";
            break;
        case eMultimedia:
            pszRole = "eMultimedia";
            break;
        case eCommunications:
            pszRole = "eCommunications";
            break;
        }

        printf("  -->New default device: flow = %s, role = %s\n",
               pszFlow, pszRole);
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
    {
        _PrintDeviceName(pwstrDeviceId);

        printf("  -->Added device\n");
        return S_OK;
    };

    HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
    {
        _PrintDeviceName(pwstrDeviceId);

        printf("  -->Removed device\n");
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
                                LPCWSTR pwstrDeviceId,
                                DWORD dwNewState)
    {
        char  *pszState = "?????";

        _PrintDeviceName(pwstrDeviceId);

        switch (dwNewState)
        {
        case DEVICE_STATE_ACTIVE:
            pszState = "ACTIVE";
            break;
        case DEVICE_STATE_DISABLED:
            pszState = "DISABLED";
            break;
        case DEVICE_STATE_NOTPRESENT:
            pszState = "NOTPRESENT";
            break;
        case DEVICE_STATE_UNPLUGGED:
            pszState = "UNPLUGGED";
            break;
        }

        printf("  -->New device state is DEVICE_STATE_%s (0x%8.8x)\n",
               pszState, dwNewState);

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
                                LPCWSTR pwstrDeviceId,
                                const PROPERTYKEY key)
    {
        _PrintDeviceName(pwstrDeviceId);

        printf("  -->Changed device property "
               "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d\n",
               key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3,
               key.fmtid.Data4[0], key.fmtid.Data4[1],
               key.fmtid.Data4[2], key.fmtid.Data4[3],
               key.fmtid.Data4[4], key.fmtid.Data4[5],
               key.fmtid.Data4[6], key.fmtid.Data4[7],
               key.pid);
        return S_OK;
    }
};

// Given an endpoint ID string, print the friendly device name.
HRESULT CMMNotificationClient::_PrintDeviceName(LPCWSTR pwstrId)
{
    HRESULT hr = S_OK;
    IMMDevice *pDevice = NULL;
    IPropertyStore *pProps = NULL;
    PROPVARIANT varString;

    CoInitialize(NULL);
    PropVariantInit(&varString);

    if (_pEnumerator == NULL)
    {
        // Get enumerator for audio endpoint devices.
        hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                              NULL, CLSCTX_INPROC_SERVER,
                              __uuidof(IMMDeviceEnumerator),
                              (void**)&_pEnumerator);
    }
    if (hr == S_OK)
    {
        hr = _pEnumerator->GetDevice(pwstrId, &pDevice);
    }
    if (hr == S_OK)
    {
        hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
    }
    if (hr == S_OK)
    {
        // Get the endpoint device's friendly-name property.
        hr = pProps->GetValue(PKEY_Device_FriendlyName, &varString);
    }
    printf("----------------------\nDevice name: \"%S\"\n"
           "  Endpoint ID string: \"%S\"\n",
           (hr == S_OK) ? varString.pwszVal : L"null device",
           (pwstrId != NULL) ? pwstrId : L"null ID");

    PropVariantClear(&varString);

    SAFE_RELEASE(pProps)
    SAFE_RELEASE(pDevice)
    CoUninitialize();
    return hr;
}

La classe CMMNotificationClient dans l’exemple de code précédent est une implémentation de l’interface IMMNotificationClient . ImmNotificationClient hérite d’IUnknown, la définition de classe contient des implémentations des méthodes IUnknownAddRef, Release et QueryInterface. Les méthodes publiques restantes dans la définition de classe sont spécifiques à l’interface IMMNotificationClient . Ces méthodes sont les suivantes :

  • OnDefaultDeviceChanged, appelé lorsque l’utilisateur modifie le rôle d’appareil d’un appareil de point de terminaison audio.
  • OnDeviceAdded, appelé lorsque l’utilisateur ajoute un périphérique de point de terminaison audio au système.
  • OnDeviceRemoved, appelé lorsque l’utilisateur supprime un appareil de point de terminaison audio du système.
  • OnDeviceStateChanged, qui est appelé lorsque l’état de l’appareil d’un appareil de point de terminaison audio change. (Pour plus d’informations sur les états de l’appareil, consultez DEVICE_STATE_ XXX Constantes.)
  • OnPropertyValueChanged, appelé lorsque la valeur d’une propriété d’un appareil de point de terminaison audio change.

Chacune de ces méthodes prend un paramètre d’entrée, pwstrDeviceId, qui est un pointeur vers une chaîne d’ID de point de terminaison. La chaîne identifie l’appareil de point de terminaison audio dans lequel l’événement d’appareil s’est produit.

Dans l’exemple de code précédent, _PrintDeviceName est une méthode privée dans la classe CMMNotificationClient qui imprime le nom convivial de l’appareil. _PrintDeviceName prend la chaîne d’ID de point de terminaison comme paramètre d’entrée. Il transmet la chaîne à la méthode IMMDeviceEnumerator::GetDevice . GetDevice crée un objet d’appareil de point de terminaison pour représenter l’appareil et fournit l’interface IMMDevice à cet objet. Ensuite, _PrintDeviceName appelle la méthode IMMDevice::OpenPropertyStore pour récupérer l’interface IPropertyStore dans le magasin de propriétés de l’appareil. Enfin, _PrintDeviceName appelle la méthode IPropertyStore::GetItem pour obtenir la propriété friendly-name de l’appareil. Pour plus d’informations sur IPropertyStore, consultez la documentation du Kit de développement logiciel (SDK) Windows.

En plus des événements d’appareil, les clients peuvent s’inscrire pour recevoir des notifications d’événements de session audio et d’événements de volume de point de terminaison. Pour plus d’informations, consultez IAudioSessionEvents Interface et IAudioEndpointVolumeCallback Interface.

Appareils de point de terminaison audio